diff --git a/.code.yml b/.code.yml index b53d93970a..894d4c84d1 100644 --- a/.code.yml +++ b/.code.yml @@ -1,3 +1,59 @@ +######## Code generated by gonote. DO NOT EDIT. >>>>> + +# [!勿修改模板!] 生成了默认值模板,请根据实际情况调整对应的配置值 +# [!最高优先级!] 本项目的特殊配置,会覆盖命令行输入的参数 +gonote: + split_ch_and_en: false # [选项开关] 修复行注释(//...)中英文词没有空格间隔,但不修复注释块(/*...*/) + without_import: true # [选项开关] 关闭 import 强制分组功能 + no_line_break: false # [选项开关] 关闭过长自动换行功能 + no_complete_general: false # [选项开关] 关闭补全通用注释,默认填充 ServeHTTP/String/Error 等通用注释 + complete_general_config: # 内置了腾讯代码规范中的例外函数,你可以覆盖或添加更多配置 + - pattern: "Run()" + annotation: Command Run + limits: + - type_func + - pattern: "Init()" + annotation: prepare run env + limits: + - type_func + - pattern: "Validate()" + annotation: run selfdefine validate function + limits: + - type_func + - pattern: "Example()" + annotation: subcommand example input + limits: + - type_func + - pattern: "PreCheck()" + annotation: pre run pre check + limits: + - type_func + - pattern: "New.*Command" + annotation: create new subcommand + limits: + - func + - pattern: "Install()" + annotation: install + limits: + - type_func + - func + # - pattern: '(.*)Req' # 匹配该正则所有的关键词 + # annotation: '函数 $1 入参' # 参考 nginx 的 URL 重写 ListReq =正则匹配并解析group=> (List)Req =取出group=> ($1)Req =group替换=> 函数 List 入参 + # limits: + # - func # 函数注释 + # - type_func # 类型函数注释,常见结构体函数 + # - type # 类型,常见结构体注释 + linters: + disable_all: false + enable: + - oversize_func_arg # 函数入参过多,不能超过5个 + - large_file # 超大文件检查,普通<800行,单测<1600行 + - large_func # 超长函数体检查,普通<80行,单测<160行 + - switch_default # switch 需要 default 分支 + - bad_name # 不符合规范的命名 + +######## Code generated by gonote. DO NOT EDIT. <<<<< + source: # 文件或目录使用绝对路径,绝对路径按代码库根目录计算,以/开头。 # 提供产品代码库中编写的测试代码存放目录或文件名格式,以便后续代码统计环节进行排除等特殊处理 @@ -8,10 +64,10 @@ source: filepath_regex: [".*/test.py"] # 提供产品代码库中工具或框架自动生成的且在代码库中的代码,没有可为空。以便后续代码统计环节进行排除等特殊处理。 auto_generate_source: - # 自动生成代码文件的正则表达式,若无统一标识格式,可以指定具体目录,样例可参考test_source举例 + # 自动生成代码文件的正则表达式,若无统一标识格式,可以指定具体目录,样例可参考test_source举例 filepath_regex: [".*/migrations/.*"] # 提供产品代码库中直接以源码形式存在的第三方代码目录或代码文件名的正则表达。 # 此处备注的第三方代码在后续统计代码量环节会被排除,若代码库中不存在需要排除的第三方代码,该项配置标识可为空 third_party_source: #第三方代码文件的正则表达式,若无统一标识格式,可以指定具体目录,样例可参考test_source举例 - filepath_regex: \ No newline at end of file + filepath_regex: diff --git a/.github/workflows/check_hard_code_ip.yml b/.github/workflows/check_hard_code_ip.yml index 74cea37d7a..affe479b48 100644 --- a/.github/workflows/check_hard_code_ip.yml +++ b/.github/workflows/check_hard_code_ip.yml @@ -10,7 +10,7 @@ jobs: - uses: actions/checkout@v2 - name: Check hard code ip run: | - RESULT=$(grep -nrE '\b([0-9]{1,3}\.){3}[0-9]{1,3}\b' * | grep -vE '\b[012345678]\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | grep -vE '127\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}') || true + RESULT=$(grep -nrE '\b([0-9]{1,3}\.){3}[0-9]{1,3}\b' * | grep -vE '\b[012345678]\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | grep -vE '127\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | grep -vE '192\.168\.[0-9]{1,3}\.[0-9]{1,3}') || true if [[ ${RESULT} == '' ]]; then echo "good job!" else diff --git a/.github/workflows/python_code_check.yml b/.github/workflows/python_code_check.yml index 27567cded6..3cdd2838c4 100644 --- a/.github/workflows/python_code_check.yml +++ b/.github/workflows/python_code_check.yml @@ -8,9 +8,11 @@ on: push: paths: - "dbm-ui/backend/**" + - "dbm-ui/config/**" pull_request: paths: - "dbm-ui/backend/**" + - "dbm-ui/config/**" jobs: build: diff --git a/.gtmproject.yaml b/.gtmproject.yaml index 0fef035cda..e911e274fe 100644 --- a/.gtmproject.yaml +++ b/.gtmproject.yaml @@ -6,13 +6,13 @@ github: repo_name: "blueking-dbm" # 指定里程碑ID, - milestone_id: "1" + milestone_id: "6" project: # 主分支 default_branch: "master" # 默认目标分支 - default_target_branch: "master" + default_target_branch: "v1.2.0" # 默认单据来源 default_source: "github" # 默认事件类型 @@ -28,6 +28,9 @@ project: - hdfs - influxdb - pulsar + - mongodb + - riak + - sqlserver - dbm-services - helm-charts - other diff --git a/dbm-services/.gitignore b/dbm-services/.gitignore new file mode 100644 index 0000000000..ec33037082 --- /dev/null +++ b/dbm-services/.gitignore @@ -0,0 +1,3 @@ +.codecc +.idea +.vscode \ No newline at end of file diff --git a/dbm-services/bigdata/db-tools/dbactuator/go.mod b/dbm-services/bigdata/db-tools/dbactuator/go.mod index 0d21e151c7..8cc8aff466 100644 --- a/dbm-services/bigdata/db-tools/dbactuator/go.mod +++ b/dbm-services/bigdata/db-tools/dbactuator/go.mod @@ -9,6 +9,7 @@ require ( github.com/golang/glog v1.1.1 github.com/pkg/errors v0.9.1 github.com/robfig/cron/v3 v3.0.1 + github.com/shirou/gopsutil/v3 v3.23.8 github.com/spf13/cobra v1.7.0 go.uber.org/zap v1.24.0 gopkg.in/ini.v1 v1.67.0 @@ -18,9 +19,13 @@ require ( github.com/go-ole/go-ole v1.2.6 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kr/fs v0.1.0 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/yusufpapurcu/wmi v1.2.2 // indirect - golang.org/x/sys v0.7.0 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + github.com/yusufpapurcu/wmi v1.2.3 // indirect + golang.org/x/sys v0.11.0 // indirect golang.org/x/text v0.9.0 // indirect ) @@ -31,6 +36,7 @@ require ( github.com/fatih/color v1.13.0 // indirect github.com/go-playground/locales v0.14.1 github.com/go-playground/universal-translator v0.18.1 + github.com/go-zookeeper/zk v1.0.3 github.com/hashicorp/go-version v1.6.0 github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f // indirect github.com/juju/ratelimit v1.0.2 @@ -41,7 +47,6 @@ require ( github.com/pkg/sftp v1.13.5 github.com/rogpeppe/go-internal v1.8.0 // indirect github.com/russross/blackfriday v1.6.0 - github.com/shirou/gopsutil v3.21.11+incompatible go.uber.org/atomic v1.9.0 // indirect go.uber.org/goleak v1.1.12 // indirect go.uber.org/multierr v1.8.0 // indirect diff --git a/dbm-services/bigdata/db-tools/dbactuator/go.sum b/dbm-services/bigdata/db-tools/dbactuator/go.sum index 5f551cb68f..84e17c3504 100644 --- a/dbm-services/bigdata/db-tools/dbactuator/go.sum +++ b/dbm-services/bigdata/db-tools/dbactuator/go.sum @@ -25,8 +25,13 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.12.0 h1:E4gtWgxWxp8YSxExrQFv5BpCahla0PVF2oTTEYaWQGI= github.com/go-playground/validator/v10 v10.12.0/go.mod h1:hCAPuzYvKdP33pxWa+2+6AIKXEKqjIUyqsNCtbsSJrA= +github.com/go-zookeeper/zk v1.0.3 h1:7M2kwOsc//9VeeFiPtf+uSJlVpU66x9Ba5+8XK7/TDg= +github.com/go-zookeeper/zk v1.0.3/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= github.com/golang/glog v1.1.1 h1:jxpi2eWoU84wbX9iIEyAeeoac3FLuifZpY9tcNUD9kw= github.com/golang/glog v1.1.1/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f h1:7LYC+Yfkj3CTRcShK0KOL/w6iTiKyqqBA9a41Wnggw8= @@ -47,6 +52,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.2.3 h1:6BE2vPT0lqoz3fmOesHZiaiFh7889ssCo2GMvLCfiuA= github.com/leodido/go-urn v1.2.3/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= @@ -61,6 +68,8 @@ github.com/pkg/sftp v1.13.5 h1:a3RLUqkyjYRtBTZJZ1VRrKbN3zhuPLlUc3sphVz81go= github.com/pkg/sftp v1.13.5/go.mod h1:wHDZ0IZX6JcBYRK1TH9bcVq8G7TLpVHYIGJRFnmPfxg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= @@ -69,8 +78,10 @@ github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6po github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= -github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shirou/gopsutil/v3 v3.23.8 h1:xnATPiybo6GgdRoC4YoGnxXZFRc3dqQTGi73oLvvBrE= +github.com/shirou/gopsutil/v3 v3.23.8/go.mod h1:7hmCaBn+2ZwaZOr6jmPBZDfawwMGuo1id3C6aM8EDqQ= +github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= +github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -82,11 +93,16 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= -github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= +github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= @@ -118,6 +134,7 @@ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -126,8 +143,9 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -141,6 +159,7 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/dbm-services/bigdata/db-tools/dbactuator/pkg/components/kafka/decom_broker.go b/dbm-services/bigdata/db-tools/dbactuator/pkg/components/kafka/decom_broker.go index fb824a11b4..e4f72dd83b 100644 --- a/dbm-services/bigdata/db-tools/dbactuator/pkg/components/kafka/decom_broker.go +++ b/dbm-services/bigdata/db-tools/dbactuator/pkg/components/kafka/decom_broker.go @@ -11,6 +11,8 @@ import ( "dbm-services/bigdata/db-tools/dbactuator/pkg/rollback" "dbm-services/bigdata/db-tools/dbactuator/pkg/util/kafkautil" "dbm-services/common/go-pubpkg/logger" + + "github.com/go-zookeeper/zk" ) // DecomBrokerComp TODO @@ -22,7 +24,7 @@ type DecomBrokerComp struct { // DecomBrokerParams TODO type DecomBrokerParams struct { - ZookeeperIp string `json:"zookeeper_ip" validate:"required"` // 连接zk + ZookeeperIP string `json:"zookeeper_ip" validate:"required"` // 连接zk Username string `json:"username"` // 管理用户 Password string `json:"password"` // 管理密码 ExcludeBrokers []string `json:"exclude_brokers" validate:"required"` // 要缩容的broker @@ -44,13 +46,20 @@ func (d *DecomBrokerComp) DoReplaceBrokers() (err error) { const SleepInterval = 300 * time.Second - zkHost := d.Params.ZookeeperIp + ":2181" + zkHost := d.Params.ZookeeperIP + ":2181" oldBrokers := d.Params.ExcludeBrokers newBrokers := d.Params.NewBrokers + conn, _, err := zk.Connect([]string{zkHost}, 10*time.Second) // *10) + defer conn.Close() + if err != nil { + logger.Error("Connect zk failed, %s", err) + return err + } + var newBrokerIds []string for _, broker := range newBrokers { - id, err := kafkautil.GetBrokerIdByHost(broker, zkHost) + id, err := kafkautil.GetBrokerIDByHost(conn, broker) if err != nil { logger.Error("cant get %s broker id, %v", broker, err) return err @@ -60,26 +69,26 @@ func (d *DecomBrokerComp) DoReplaceBrokers() (err error) { logger.Info("newBrokerIds: %v", newBrokerIds) for i, broker := range oldBrokers { - oldBrokerId, err := kafkautil.GetBrokerIdByHost(broker, zkHost) - logger.Info("oldBrokerId: [%s]", oldBrokerId) + oldBrokerID, err := kafkautil.GetBrokerIDByHost(conn, broker) + logger.Info("oldBrokerId: [%s]", oldBrokerID) if err != nil { logger.Error("cant get %s broker id, %v", broker, err) return err } - topicJson, err := kafkautil.GenReplaceReassignmentJson(oldBrokerId, newBrokerIds[i], zkHost) + topicJSON, err := kafkautil.GenReplaceReassignmentJSON(oldBrokerID, newBrokerIds[i], zkHost) if err != nil { logger.Error("GenReassignmentJson failed", err) return err } - logger.Info("topicJson, %s", topicJson) + logger.Info("topicJson, %s", topicJSON) // /data/kafkaenv/host.json jsonFile := fmt.Sprintf("%s/%s.json", cst.DefaultKafkaEnv, broker) logger.Info("jsonfile: %s", jsonFile) - if err = ioutil.WriteFile(jsonFile, []byte(topicJson), 0644); err != nil { + if err = ioutil.WriteFile(jsonFile, []byte(topicJSON), 0644); err != nil { logger.Error("write %s failed, %v", jsonFile, err) return err } - if !strings.Contains(topicJson, "topic") { + if !strings.Contains(topicJSON, "topic") { logger.Info("无需搬迁数据") continue } @@ -115,13 +124,19 @@ func (d *DecomBrokerComp) DoReplaceBrokers() (err error) { * @description: * @return */ -func (d *DecomBrokerComp) DoDecomBrokers() (err error) { - - const SleepInterval = 300 * time.Second +func (d *DecomBrokerComp) DoDecomBrokers() error { - zkHost := d.Params.ZookeeperIp + ":2181" + zkHost := d.Params.ZookeeperIP + ":2181" brokers := d.Params.ExcludeBrokers + // connect to zk + conn, _, err := zk.Connect([]string{zkHost}, 10*time.Second) // *10) + if err != nil { + logger.Error("Connect zk failed, %s", err) + return err + } + defer conn.Close() + /* allIds, err := kafkautil.GetBrokerIds(zkHost) if err != nil { @@ -132,7 +147,7 @@ func (d *DecomBrokerComp) DoDecomBrokers() (err error) { var excludeIds []string for _, broker := range brokers { - id, err := kafkautil.GetBrokerIdByHost(broker, zkHost) + id, err := kafkautil.GetBrokerIDByHost(conn, broker) if err != nil { logger.Error("cant get %s broker id, %v", broker, err) return err @@ -141,54 +156,28 @@ func (d *DecomBrokerComp) DoDecomBrokers() (err error) { } logger.Info("excludeIds: %v", excludeIds) - for _, broker := range brokers { - brokerId, err := kafkautil.GetBrokerIdByHost(broker, zkHost) - logger.Info("brokerId: [%s]", brokerId) - if err != nil { - logger.Error("cant get %s broker id, %v", broker, err) - return err - } - topicJson, err := kafkautil.GenReassignmentJson(brokerId, zkHost, excludeIds) - if err != nil { - logger.Error("GenReassignmentJson failed", err) - return err - } - logger.Info("topicJson, %s", topicJson) - // /data/kafkaenv/host.json - jsonFile := fmt.Sprintf("%s/%s.json", cst.DefaultKafkaEnv, broker) - logger.Info("jsonfile: %s", jsonFile) - if err = ioutil.WriteFile(jsonFile, []byte(topicJson), 0644); err != nil { - logger.Error("write %s failed, %v", jsonFile, err) - return err - } - if !strings.Contains(topicJson, "topic") { - logger.Info("无需搬迁数据") - continue - } - // do - if err = kafkautil.DoReassignPartitions(zkHost, jsonFile); err != nil { - logger.Error("DoReassignPartitions failed, %v", err) - return err - } - for { - - out, err := kafkautil.CheckReassignPartitions(zkHost, jsonFile) - if err != nil { - logger.Error("CheckReassignPartitions failed %v", err) - return err - } - - if len(out) == 0 { - logger.Info("数据搬迁完毕") - break - } - - time.Sleep(SleepInterval) - } - logger.Info("broker [%s] 搬迁 finished", broker) + logger.Info("Creating topic.json file") + err = kafkautil.WriteTopicJSON(zkHost) + if err != nil { + logger.Error("Create topic.json failed %s", err) + return err + } + logger.Info("Creating plan.json file") + err = kafkautil.GenReassignmentJSON(conn, zkHost, excludeIds) + if err != nil { + logger.Error("Create plan.json failed %s", err) + return err } + logger.Info("Execute the plan") + planJSONFile := fmt.Sprintf("%s/plan.json", cst.DefaultKafkaEnv) + err = kafkautil.DoReassignPartitions(zkHost, planJSONFile) + if err != nil { + logger.Error("Execute partitions reassignment failed %s", err) + return err + } + logger.Info("Execute partitions reassignment end") return nil } @@ -196,24 +185,20 @@ func (d *DecomBrokerComp) DoDecomBrokers() (err error) { func (d *DecomBrokerComp) DoPartitionCheck() (err error) { const MaxRetry = 5 count := 0 - zkHost := d.Params.ZookeeperIp + ":2181" - brokers := d.Params.ExcludeBrokers + zkHost := d.Params.ZookeeperIP + ":2181" + jsonFile := fmt.Sprintf("%s/plan.json", cst.DefaultKafkaEnv) + for { count++ logger.Info("检查搬迁状态,次数[%d]", count) - sum := 0 - for _, broker := range brokers { - jsonFile := fmt.Sprintf("%s/%s.json", cst.DefaultKafkaEnv, broker) - out, err := kafkautil.CheckReassignPartitions(zkHost, jsonFile) - if err != nil { - logger.Error("检查partition搬迁进度失败 %v", err) - return err - } - sum += len(out) + out, err := kafkautil.CheckReassignPartitions(zkHost, jsonFile) + if err != nil { + logger.Error("检查partition搬迁进度失败 %v", err) + return err } - - if sum == 0 { + logger.Info("当前进度: [%s]", out) + if len(out) == 0 { logger.Info("数据搬迁完成") break } @@ -225,7 +210,7 @@ func (d *DecomBrokerComp) DoPartitionCheck() (err error) { time.Sleep(60 * time.Second) } - logger.Info("数据变迁完毕") + logger.Info("分区已搬空, 若有新增topic, 请检查分区分布") return nil } diff --git a/dbm-services/bigdata/db-tools/dbactuator/pkg/components/kafka/install_kafka.go b/dbm-services/bigdata/db-tools/dbactuator/pkg/components/kafka/install_kafka.go index 975e0e854f..720806e1cd 100644 --- a/dbm-services/bigdata/db-tools/dbactuator/pkg/components/kafka/install_kafka.go +++ b/dbm-services/bigdata/db-tools/dbactuator/pkg/components/kafka/install_kafka.go @@ -27,7 +27,7 @@ import ( type InstallKafkaComp struct { GeneralParam *components.GeneralParam Params *InstallKafkaParams - KafkaConfig + KfConfig RollBackContext rollback.RollBackObjects } @@ -41,17 +41,18 @@ type InstallKafkaParams struct { Replication int `json:"replication" ` // 默认副本数 Partition int `json:"partition" ` // 默认分区数 Factor int `json:"factor" ` // __consumer_offsets副本数 - ZookeeperIp string `json:"zookeeper_ip" ` // zookeeper ip, eg: ip1,ip2,ip3 + ZookeeperIP string `json:"zookeeper_ip" ` // zookeeper ip, eg: ip1,ip2,ip3 ZookeeperConf string `json:"zookeeper_conf" ` // zookeeper ip, eg: ip1,ip2,ip3 - MyId int `json:"my_id" ` // 默认副本数 + MyID int `json:"my_id" ` // 默认副本数 JvmMem string `json:"jvm_mem"` // eg: 10g Host string `json:"host" ` ClusterName string `json:"cluster_name" ` // 集群名 Username string `json:"username" ` Password string `json:"password" ` - BkBizId int `json:"bk_biz_id"` + BkBizID int `json:"bk_biz_id"` DbType string `json:"db_type"` ServiceType string `json:"service_type"` + NoSecurity int `json:"no_security"` // 兼容现有,0:有鉴权,1:无鉴权 } // InitDirs TODO @@ -59,10 +60,9 @@ type InitDirs = []string // Port TODO type Port = int -type socket = string -// KafkaConfig 目录定义等 -type KafkaConfig struct { +// KfConfig 目录定义等 +type KfConfig struct { InstallDir string `json:"install_dir"` // /data KafkaEnvDir string `json:"kafkaenv_dir"` // /data/kafkaenv KafkaDir string @@ -73,11 +73,22 @@ type KafkaConfig struct { type RenderConfig struct { ClusterName string NodeName string - HttpPort int + HTTPPort int CharacterSetServer string InnodbBufferPoolSize string Logdir string - ServerId uint64 + ServerID uint64 +} + +// CmakConfig Kafka manager config +type CmakConfig struct { + ZookeeperIP string + ClusterName string + Version string + Username string + Password string + NodeIP string + HTTPPath string } // InitDefaultParam TODO @@ -96,7 +107,7 @@ func (i *InstallKafkaComp) InitDefaultParam() (err error) { /* 创建实例相关的数据,日志目录以及修改权限 */ -func (i *InstallKafkaComp) InitKafkaNode() (err error) { +func (i *InstallKafkaComp) InitKafkaNode() error { execUser := cst.DefaultExecUser logger.Info("检查用户[%s]是否存在", execUser) @@ -143,7 +154,7 @@ export PATH=/usr/local/mysql/bin/:$PATH'>> /etc/profile source /etc/profile`) scriptFile := "/data/kafkaenv/init.sh" - if err = ioutil.WriteFile(scriptFile, scripts, 0644); err != nil { + if err := ioutil.WriteFile(scriptFile, scripts, 0644); err != nil { logger.Error("write %s failed, %v", scriptFile, err) } @@ -313,18 +324,18 @@ func configCrontab() (err error) { * @description: 安装zookeeper * @return {*} */ -func (i *InstallKafkaComp) InstallZookeeper() (err error) { +func (i *InstallKafkaComp) InstallZookeeper() error { var ( - nodeIp string = i.Params.Host - myId int = i.Params.MyId - zookeeperConf string = i.Params.ZookeeperConf - username string = i.Params.Username - password string = i.Params.Password - ZookeeperBaseDir string = fmt.Sprintf("%s/zookeeper-%s", cst.DefaultKafkaEnv, cst.DefaultZookeeperVersion) + nodeIP = i.Params.Host + myID = i.Params.MyID + zookeeperConf = i.Params.ZookeeperConf + username = i.Params.Username + password = i.Params.Password + ZookeeperBaseDir = fmt.Sprintf("%s/zookeeper-%s", cst.DefaultKafkaEnv, cst.DefaultZookeeperVersion) ) - if _, err := net.Dial("tcp", fmt.Sprintf("%s:%d", nodeIp, 2181)); err == nil { + if _, err := net.Dial("tcp", fmt.Sprintf("%s:%d", nodeIP, 2181)); err == nil { logger.Error("zookeeper process exist") return errors.New("zookeeper process exist") } @@ -351,40 +362,37 @@ func (i *InstallKafkaComp) InstallZookeeper() (err error) { return err } - if err = configZookeeper(username, password, zookeeperLink, zookeeperConf, myId); err != nil { + if err := configZookeeper(username, password, zookeeperLink, zookeeperConf, myID); err != nil { return err } logger.Info("生成zookeeper.ini文件") zookeeperini := esutil.GenZookeeperini() zookeeperiniFile := fmt.Sprintf("%s/zookeeper.ini", cst.DefaultKafkaSupervisorConf) - if err = ioutil.WriteFile(zookeeperiniFile, zookeeperini, 0); err != nil { + if err := ioutil.WriteFile(zookeeperiniFile, zookeeperini, 0); err != nil { logger.Error("write %s failed, %v", zookeeperiniFile, err) } extraCmd = fmt.Sprintf("chmod 777 %s/zookeeper.ini ", cst.DefaultKafkaSupervisorConf) - if _, err = osutil.ExecShellCommand(false, extraCmd); err != nil { + if _, err := osutil.ExecShellCommand(false, extraCmd); err != nil { logger.Error("%s execute failed, %v", extraCmd, err) return err } extraCmd = fmt.Sprintf("chown -R mysql %s ", i.KafkaEnvDir) - if _, err = osutil.ExecShellCommand(false, extraCmd); err != nil { + if _, err := osutil.ExecShellCommand(false, extraCmd); err != nil { logger.Error("%s execute failed, %v", extraCmd, err) return err } - if err = esutil.SupervisorctlUpdate(); err != nil { + if err := esutil.SupervisorctlUpdate(); err != nil { logger.Error("supervisort update failed %v", err) } - extraCmd = fmt.Sprintf("sleep 10") - if _, err = osutil.ExecShellCommand(false, extraCmd); err != nil { - logger.Error("%s execute failed, %v", extraCmd, err) - return err - } + // sleep 30 + time.Sleep(30 * time.Second) - if _, err := net.Dial("tcp", fmt.Sprintf("%s:%d", nodeIp, 2181)); err != nil { + if _, err := net.Dial("tcp", fmt.Sprintf("%s:%d", nodeIP, 2181)); err != nil { logger.Error("zookeeper start failed %v", err) return err } @@ -393,7 +401,7 @@ func (i *InstallKafkaComp) InstallZookeeper() (err error) { } func configZookeeper(username string, password string, zookeeperLink string, zookeeperConf string, - myId int) (err error) { + myID int) (err error) { extraCmd := fmt.Sprintf(`echo 'export USERNAME=%s export PASSWORD=%s'>> /etc/profile`, username, password) if _, err = osutil.ExecShellCommand(false, extraCmd); err != nil { @@ -415,7 +423,7 @@ export PASSWORD=%s'>> /etc/profile`, username, password) return err } - extraCmd = fmt.Sprintf(`echo %d > %s`, myId, cst.DefaultZookeeperDataDir+"/myid") + extraCmd = fmt.Sprintf(`echo %d > %s`, myID, cst.DefaultZookeeperDataDir+"/myid") if _, err = osutil.ExecShellCommand(false, extraCmd); err != nil { logger.Error("%s execute failed, %v", extraCmd, err) return err @@ -446,17 +454,17 @@ dynamicConfigFile=%s" > %s` func (i *InstallKafkaComp) InitKafkaUser() (err error) { var ( - zookeeperIp string = i.Params.ZookeeperIp - version string = i.Params.Version - username string = i.Params.Username - password string = i.Params.Password - kafkaBaseDir string = fmt.Sprintf("%s/kafka-%s", cst.DefaultKafkaEnv, version) + zookeeperIP = i.Params.ZookeeperIP + version = i.Params.Version + username = i.Params.Username + password = i.Params.Password + kafkaBaseDir = fmt.Sprintf("%s/kafka-%s", cst.DefaultKafkaEnv, version) ) - zookeeperIpList := strings.Split(zookeeperIp, ",") + zookeeperIPList := strings.Split(zookeeperIP, ",") extraCmd := fmt.Sprintf( "%s %s:2181,%s:2181,%s:2181/ %s \"%s=[iterations=8192,password=%s],%s=[password=%s]\" %s %s", kafkaBaseDir+"/bin/kafka-configs.sh --zookeeper", - zookeeperIpList[0], zookeeperIpList[1], zookeeperIpList[2], + zookeeperIPList[0], zookeeperIPList[1], zookeeperIPList[2], "--alter --add-config", "SCRAM-SHA-256", password, @@ -477,22 +485,23 @@ func (i *InstallKafkaComp) InitKafkaUser() (err error) { * @description: 安装broker * @return {*} */ -func (i *InstallKafkaComp) InstallBroker() (err error) { +func (i *InstallKafkaComp) InstallBroker() error { var ( - retentionHours int = i.Params.Retention - replicationNum int = i.Params.Replication - partitionNum int = i.Params.Partition - factor int = i.Params.Factor - nodeIp string = i.Params.Host - port int = i.Params.Port - jmxPort int = i.Params.JmxPort - listeners string = fmt.Sprintf("%s:%d", nodeIp, port) - version string = i.Params.Version - processors int = runtime.NumCPU() - zookeeperIp string = i.Params.ZookeeperIp - kafkaBaseDir string = fmt.Sprintf("%s/kafka-%s", cst.DefaultKafkaEnv, version) - username string = i.Params.Username - password string = i.Params.Password + retentionHours = i.Params.Retention + replicationNum = i.Params.Replication + partitionNum = i.Params.Partition + factor = i.Params.Factor + nodeIP = i.Params.Host + port = i.Params.Port + jmxPort = i.Params.JmxPort + listeners = fmt.Sprintf("%s:%d", nodeIP, port) + version = i.Params.Version + processors = runtime.NumCPU() + zookeeperIP = i.Params.ZookeeperIP + kafkaBaseDir = fmt.Sprintf("%s/kafka-%s", cst.DefaultKafkaEnv, version) + username = i.Params.Username + password = i.Params.Password + noSecurity = i.Params.NoSecurity ) // ln -s /data/kafkaenv/kafka-$version /data/kafkaenv/kafka @@ -533,48 +542,61 @@ func (i *InstallKafkaComp) InstallBroker() (err error) { } } - zookeeperIpList := strings.Split(zookeeperIp, ",") + zookeeperIPList := strings.Split(zookeeperIP, ",") extraCmd = fmt.Sprintf(kafkaConfig, retentionHours, replicationNum, partitionNum, processors, factor, processors, - processors, kafkaDataDir, listeners, listeners, zookeeperIpList[0], zookeeperIpList[1], zookeeperIpList[2], + processors, kafkaDataDir, listeners, listeners, zookeeperIPList[0], zookeeperIPList[1], zookeeperIPList[2], kafkaLink+"/config/server.properties") - if _, err = osutil.ExecShellCommand(false, extraCmd); err != nil { + if _, err := osutil.ExecShellCommand(false, extraCmd); err != nil { logger.Error("%s execute failed, %v", extraCmd, err) return err } - if err = configKafka(username, password, kafkaLink, jmxPort); err != nil { + // not enabled security + if noSecurity == 1 { + // remove sasl config + extraCmd := fmt.Sprintf(`sed -i -e "/sasl.enabled.mechanisms=/d" \ + -e "/sasl.mechanism.inter.broker.protocol=/d" \ + -e "/security.inter.broker.protocol=/d" \ + -e "s/SASL_PLAINTEXT/PLAINTEXT/g" %s`, kafkaLink+"/config/server.properties") + if _, err := osutil.ExecShellCommand(false, extraCmd); err != nil { + logger.Error("%s execute failed, %v", extraCmd, err) + return err + } + } + + if err := configKafka(username, password, kafkaLink, jmxPort); err != nil { return err } - if err = startKafka(i.KafkaEnvDir); err != nil { + if err := startKafka(i.KafkaEnvDir, noSecurity); err != nil { return err } // sleep 60s for wating kafka up time.Sleep(30 * time.Second) - if _, err := net.Dial("tcp", fmt.Sprintf("%s:%d", nodeIp, port)); err != nil { + if _, err := net.Dial("tcp", fmt.Sprintf("%s:%d", nodeIP, port)); err != nil { logger.Error("broker start failed %v", err) return err } return nil } -func configKafka(username string, password string, kafkaLink string, jmxPort int) (err error) { +func configKafka(username string, password string, kafkaLink string, jmxPort int) error { logger.Info("配置jaas") extraCmd := fmt.Sprintf(`echo 'KafkaServer { org.apache.kafka.common.security.scram.ScramLoginModule required username="%s" password="%s"; };' > %s`, username, password, kafkaLink+"/config/kafka_server_scram_jaas.conf") - if _, err = osutil.ExecShellCommand(false, extraCmd); err != nil { + if _, err := osutil.ExecShellCommand(false, extraCmd); err != nil { logger.Error("%s execute failed, %v", extraCmd, err) return err } logger.Info("配置run-class.sh") extraCmd = fmt.Sprintf("sed -i 's/esenv/kafkaenv/g' %s", kafkaLink+"/bin/kafka-run-class.sh") - if _, err = osutil.ExecShellCommand(false, extraCmd); err != nil { + if _, err := osutil.ExecShellCommand(false, extraCmd); err != nil { logger.Error("%s execute failed, %v", extraCmd, err) return err } @@ -583,25 +605,25 @@ func configKafka(username string, password string, kafkaLink string, jmxPort int extraCmd = fmt.Sprintf("sed -i '/export KAFKA_HEAP_OPTS=\"-Xmx1G -Xms1G\"/a\\ export JMX_PORT=\"%d\"' %s", jmxPort, kafkaLink+"/bin/kafka-server-start.sh") - if _, err = osutil.ExecShellCommand(false, extraCmd); err != nil { + if _, err := osutil.ExecShellCommand(false, extraCmd); err != nil { logger.Error("%s execute failed, %v", extraCmd, err) return err } // 配置jvm参数 - if err = configJVM(kafkaLink); err != nil { + if err := configJVM(kafkaLink); err != nil { return err } extraCmd = fmt.Sprintf( - "echo \"export KAFKA_OPTS=\\\"\\${KAFKA_OPTS} -javaagent:%s=7071:%s/kafka-2_0_0.yml\\\"\" >> %s", + "echo \"export KAFKA_OPTS=\\\"\\${KAFKA_OPTS} -javaagent:%s=7071:%s/kafka-2_0_0.yml\\\"\" > %s", kafkaLink+"/libs/jmx_prometheus_javaagent-0.17.2.jar", kafkaLink+"/config", "insert.txt") - if _, err = osutil.ExecShellCommand(false, extraCmd); err != nil { + if _, err := osutil.ExecShellCommand(false, extraCmd); err != nil { logger.Error("%s execute failed, %v", extraCmd, err) return err } extraCmd = fmt.Sprintf("sed -i '23 r insert.txt' %s", kafkaLink+"/bin/kafka-server-start.sh") - if _, err = osutil.ExecShellCommand(false, extraCmd); err != nil { + if _, err := osutil.ExecShellCommand(false, extraCmd); err != nil { logger.Error("%s execute failed, %v", extraCmd, err) return err } @@ -614,7 +636,7 @@ func configKafka(username string, password string, kafkaLink string, jmxPort int } extraCmd = fmt.Sprintf("sed -i '$d' %s", kafkaLink+"/bin/kafka-server-scram-start.sh") - if _, err = osutil.ExecShellCommand(false, extraCmd); err != nil { + if _, err := osutil.ExecShellCommand(false, extraCmd); err != nil { logger.Error("%s execute failed, %v", extraCmd, err) return err } @@ -624,7 +646,7 @@ func configKafka(username string, password string, kafkaLink string, jmxPort int "-Djava.security.auth.login.config", "config/kafka_server_scram_jaas.conf", kafkaLink+"/bin/kafka-server-scram-start.sh") - if _, err = osutil.ExecShellCommand(false, extraCmd); err != nil { + if _, err := osutil.ExecShellCommand(false, extraCmd); err != nil { logger.Error("%s execute failed, %v", extraCmd, err) return err } @@ -638,13 +660,13 @@ func configJVM(kafkaLink string) (err error) { logger.Error("获取实例内存失败, err: %w", err) return fmt.Errorf("获取实例内存失败, err: %w", err) } - jvmSize := instMem / 1024 - if jvmSize > 30 { - jvmSize = 30 + jvmSize := instMem + if jvmSize > 30720 { + jvmSize = 30720 } else { jvmSize = jvmSize / 2 } - extraCmd := fmt.Sprintf("sed -i 's/-Xmx1G -Xms1G/-Xmx%dG -Xms%dG/g' %s", jvmSize, jvmSize, + extraCmd := fmt.Sprintf("sed -i 's/-Xmx1G -Xms1G/-Xmx%dM -Xms%dM/g' %s", jvmSize, jvmSize, kafkaLink+"/bin/kafka-server-start.sh") if _, err = osutil.ExecShellCommand(false, extraCmd); err != nil { logger.Error("%s execute failed, %v", extraCmd, err) @@ -653,7 +675,7 @@ func configJVM(kafkaLink string) (err error) { return nil } -func startKafka(kafkaEnvDir string) (err error) { +func startKafka(kafkaEnvDir string, noSecurity int) (err error) { logger.Info("生成kafka.ini文件") kafkaini := esutil.GenKafkaini() kafkainiFile := fmt.Sprintf("%s/kafka.ini", cst.DefaultKafkaSupervisorConf) @@ -673,6 +695,16 @@ func startKafka(kafkaEnvDir string) (err error) { return err } + // Security related + if noSecurity == 1 { + extraCmd = fmt.Sprintf("sed -i 's/kafka-server-scram-start.sh/kafka-server-start.sh/' %s/kafka.ini", + cst.DefaultKafkaSupervisorConf) + if _, err = osutil.ExecShellCommand(false, extraCmd); err != nil { + logger.Error("%s execute failed, %v", extraCmd, err) + return err + } + } + if err = esutil.SupervisorctlUpdate(); err != nil { logger.Error("supervisort update failed %v", err) return err @@ -720,45 +752,43 @@ security.inter.broker.protocol=SASL_PLAINTEXT" > %s` * @description: 安装kafka manager * @return {*} */ -func (i *InstallKafkaComp) InstallManager() (err error) { +func (i *InstallKafkaComp) InstallManager() error { var ( - nodeIp string = i.Params.Host - port int = i.Params.Port - clusterName string = i.Params.ClusterName - zookeeperIp string = i.Params.ZookeeperIp - version string = i.Params.Version - username string = i.Params.Username - password string = i.Params.Password - ZookeeperBaseDir string = fmt.Sprintf("%s/zookeeper-%s", cst.DefaultKafkaEnv, cst.DefaultZookeeperVersion) - bkBizId int = i.Params.BkBizId - dbType string = i.Params.DbType - serviceType string = i.Params.ServiceType + nodeIP = i.Params.Host + port = i.Params.Port + clusterName = i.Params.ClusterName + zookeeperIP = i.Params.ZookeeperIP + version = i.Params.Version + username = i.Params.Username + password = i.Params.Password + ZookeeperBaseDir = fmt.Sprintf("%s/zookeeper-%s", cst.DefaultKafkaEnv, cst.DefaultZookeeperVersion) + bkBizID = i.Params.BkBizID + dbType = i.Params.DbType + serviceType = i.Params.ServiceType + noSecurity = i.Params.NoSecurity ) - if err = installZookeeper(ZookeeperBaseDir, i.KafkaEnvDir); err != nil { + if err := installZookeeper(ZookeeperBaseDir, i.KafkaEnvDir); err != nil { return err } - extraCmd := fmt.Sprintf("sleep 5") - if _, err = osutil.ExecShellCommand(false, extraCmd); err != nil { - logger.Error("%s execute failed, %v", extraCmd, err) - return err - } + // Sleep 10 secs + time.Sleep(10 * time.Second) - extraCmd = fmt.Sprintf("sed -i 's/kafka-manager-zookeeper/%s/g' %s", nodeIp, + extraCmd := fmt.Sprintf("sed -i 's/kafka-manager-zookeeper/%s/g' %s", nodeIP, i.KafkaEnvDir+"/cmak-3.0.0.5/conf/application.conf") - if _, err = osutil.ExecShellCommand(false, extraCmd); err != nil { + if _, err := osutil.ExecShellCommand(false, extraCmd); err != nil { logger.Error("%s execute failed, %v", extraCmd, err) return err } // 修改 play.http.contex="/{bkid}/{dbtype}/{cluster}/kafka_manager" - httpPath := fmt.Sprintf("/%d/%s/%s/%s", bkBizId, dbType, clusterName, serviceType) + httpPath := fmt.Sprintf("/%d/%s/%s/%s", bkBizID, dbType, clusterName, serviceType) extraCmd = fmt.Sprintf("sed -i '/play.http.context/s#/#%s#' %s", httpPath, i.KafkaEnvDir+"/cmak-3.0.0.5/conf/application.conf") - if _, err = osutil.ExecShellCommand(false, extraCmd); err != nil { + if _, err := osutil.ExecShellCommand(false, extraCmd); err != nil { logger.Error("%s execute failed, %v", extraCmd, err) return err } @@ -768,29 +798,46 @@ func (i *InstallKafkaComp) InstallManager() (err error) { basicAuthentication.username="" basicAuthentication.password="" */ - if err = startManager(i.KafkaEnvDir); err != nil { + extraCmd = fmt.Sprintf(`sed -i -e '/basicAuthentication.enabled/s/false/true/' \ + -e '/basicAuthentication.username=/d' \ + -e '/basicAuthentication.password=/d' \ + -e '$a\basicAuthentication.username="%s"' \ + -e '$a\basicAuthentication.password="%s"' %s `, username, password, + i.KafkaEnvDir+"/cmak-3.0.0.5/conf/application.conf") + + logger.Info("Exec: [%s]", extraCmd) + if _, err := osutil.ExecShellCommand(false, extraCmd); err != nil { + logger.Error("%s execute failed, %v", extraCmd, err) + return err + } + + if err := startManager(i.KafkaEnvDir); err != nil { return err } for i := 0; i < 30; i++ { - extraCmd = fmt.Sprintf("sleep 10") - if _, err = osutil.ExecShellCommand(false, extraCmd); err != nil { - logger.Error("%s execute failed, %v", extraCmd, err) - return err - } - if _, err := net.Dial("tcp", fmt.Sprintf("%s:%d", nodeIp, port)); err == nil { + time.Sleep(10 * time.Second) + if _, err := net.Dial("tcp", fmt.Sprintf("%s:%d", nodeIP, port)); err == nil { break } } - - if err = configCluster(zookeeperIp, clusterName, version, username, password, nodeIp, httpPath); err != nil { + cmak := CmakConfig{ + ZookeeperIP: zookeeperIP, + ClusterName: clusterName, + Version: version, + Username: username, + Password: password, + NodeIP: nodeIP, + HTTPPath: httpPath, + } + if err := configCluster(cmak, noSecurity); err != nil { return err } return nil } -func installZookeeper(zookeeperBaseDir string, kafkaEnvDir string) (err error) { +func installZookeeper(zookeeperBaseDir string, kafkaEnvDir string) error { zookeeperLink := fmt.Sprintf("%s/zk", cst.DefaultKafkaEnv) extraCmd := fmt.Sprintf("ln -s %s %s ", zookeeperBaseDir, zookeeperLink) if output, err := osutil.ExecShellCommand(false, extraCmd); err != nil { @@ -814,14 +861,14 @@ func installZookeeper(zookeeperBaseDir string, kafkaEnvDir string) (err error) { logger.Info("zoo.cfg") extraCmd = fmt.Sprintf(`cp %s %s`, zookeeperLink+"/conf/zoo_sample.cfg", zookeeperLink+"/conf/zoo.cfg") - if _, err = osutil.ExecShellCommand(false, extraCmd); err != nil { + if _, err := osutil.ExecShellCommand(false, extraCmd); err != nil { logger.Error("%s execute failed, %v", extraCmd, err) return err } extraCmd = fmt.Sprintf("sed -i 's#dataDir=/tmp/zookeeper#dataDir=%s/zookeeper/data#g' %s", cst.DefaultKafkaEnv, zookeeperLink+"/conf/zoo.cfg") - if _, err = osutil.ExecShellCommand(false, extraCmd); err != nil { + if _, err := osutil.ExecShellCommand(false, extraCmd); err != nil { logger.Error("%s execute failed, %v", extraCmd, err) return err } @@ -829,24 +876,24 @@ func installZookeeper(zookeeperBaseDir string, kafkaEnvDir string) (err error) { logger.Info("生成zookeeper.ini文件") zookeeperini := esutil.GenZookeeperini() zookeeperiniFile := fmt.Sprintf("%s/zookeeper.ini", cst.DefaultKafkaSupervisorConf) - if err = ioutil.WriteFile(zookeeperiniFile, zookeeperini, 0); err != nil { + if err := ioutil.WriteFile(zookeeperiniFile, zookeeperini, 0); err != nil { logger.Error("write %s failed, %v", zookeeperiniFile, err) } extraCmd = fmt.Sprintf("chmod 777 %s/zookeeper.ini ", cst.DefaultKafkaSupervisorConf) - if _, err = osutil.ExecShellCommand(false, extraCmd); err != nil { + if _, err := osutil.ExecShellCommand(false, extraCmd); err != nil { logger.Error("%s execute failed, %v", extraCmd, err) return err } extraCmd = fmt.Sprintf("chown -R mysql %s ", kafkaEnvDir) - if _, err = osutil.ExecShellCommand(false, extraCmd); err != nil { + if _, err := osutil.ExecShellCommand(false, extraCmd); err != nil { logger.Error("%s execute failed, %v", extraCmd, err) return err } logger.Info("启动zk") - if err = esutil.SupervisorctlUpdate(); err != nil { + if err := esutil.SupervisorctlUpdate(); err != nil { logger.Error("supervisort update failed %v", err) } return nil @@ -879,23 +926,22 @@ func startManager(kafkaEnvDir string) (err error) { return nil } -func configCluster(zookeeperIp string, clusterName string, version string, username string, password string, - nodeIp string, httpPath string) (err error) { +func configCluster(cmak CmakConfig, noSecurity int) (err error) { extraCmd := fmt.Sprintf(`%s/zk/bin/zkCli.sh create /kafka-manager/mutex ""`, cst.DefaultKafkaEnv) - osutil.ExecShellCommand(false, extraCmd) + _, _ = osutil.ExecShellCommand(false, extraCmd) extraCmd = fmt.Sprintf(`%s/zk/bin/zkCli.sh create /kafka-manager/mutex/locks ""`, cst.DefaultKafkaEnv) - osutil.ExecShellCommand(false, extraCmd) + _, _ = osutil.ExecShellCommand(false, extraCmd) extraCmd = fmt.Sprintf(`%s/zk/bin/zkCli.sh create /kafka-manager/mutex/leases ""`, cst.DefaultKafkaEnv) - osutil.ExecShellCommand(false, extraCmd) + _, _ = osutil.ExecShellCommand(false, extraCmd) - zookeeperIpList := strings.Split(zookeeperIp, ",") - zkHosts := fmt.Sprintf("%s:2181,%s:2181,%s:2181/", zookeeperIpList[0], zookeeperIpList[1], zookeeperIpList[2]) + zookeeperIPList := strings.Split(cmak.ZookeeperIP, ",") + zkHosts := fmt.Sprintf("%s:2181,%s:2181,%s:2181/", zookeeperIPList[0], zookeeperIPList[1], zookeeperIPList[2]) jaasConfig := fmt.Sprintf("%s required username=%s password=%s ;", - "org.apache.kafka.common.security.scram.ScramLoginModule", username, password) + "org.apache.kafka.common.security.scram.ScramLoginModule", cmak.Username, cmak.Password) postData := url.Values{} - postData.Add("name", clusterName) + postData.Add("name", cmak.ClusterName) postData.Add("zkHosts", zkHosts) - postData.Add("kafkaVersion", version) + postData.Add("kafkaVersion", cmak.Version) postData.Add("jmxEnabled", "true") postData.Add("jmxUser", "") postData.Add("jmxPass", "") @@ -922,11 +968,13 @@ func configCluster(zookeeperIp string, clusterName string, version string, usern postData.Add("tuning.kafkaManagedOffsetMetadataCheckMillis", "30000") postData.Add("tuning.kafkaManagedOffsetGroupCacheSize", "1000000") postData.Add("tuning.kafkaManagedOffsetGroupExpireDays", "7") - postData.Add("securityProtocol", "SASL_PLAINTEXT") - postData.Add("saslMechanism", "SCRAM-SHA-512") - postData.Add("jaasConfig", jaasConfig) + if noSecurity == 0 { + postData.Add("securityProtocol", "SASL_PLAINTEXT") + postData.Add("saslMechanism", "SCRAM-SHA-512") + postData.Add("jaasConfig", jaasConfig) + } // http://localhost:9000/{prefix}/clusters - url := "http://" + nodeIp + ":9000" + httpPath + "/clusters" + url := "http://" + cmak.NodeIP + ":9000" + cmak.HTTPPath + "/clusters" contentType := "application/x-www-form-urlencoded" if _, err = http.Post(url, contentType, strings.NewReader(postData.Encode())); err != nil { logger.Error("post manager failed, %v", err) diff --git a/dbm-services/bigdata/db-tools/dbactuator/pkg/components/pulsar/install_pulsar.go b/dbm-services/bigdata/db-tools/dbactuator/pkg/components/pulsar/install_pulsar.go index f0d2abf114..d90d75865b 100644 --- a/dbm-services/bigdata/db-tools/dbactuator/pkg/components/pulsar/install_pulsar.go +++ b/dbm-services/bigdata/db-tools/dbactuator/pkg/components/pulsar/install_pulsar.go @@ -265,7 +265,8 @@ func (i *InstallPulsarComp) InitCluster() (err error) { } logger.Info("生成token") extraCmd = fmt.Sprintf( - "%s/bin/pulsar tokens create --secret-key file:///%s/my-secret.key --subject super-user > %s/token.txt", cst.DefaultPulsarZkDir, cst.DefaultPulsarZkDir, cst.DefaultPulsarZkDir) + "%s/bin/pulsar tokens create --secret-key file:///%s/my-secret.key --subject super-user > %s/token.txt", + cst.DefaultPulsarZkDir, cst.DefaultPulsarZkDir, cst.DefaultPulsarZkDir) if output, err := osutil.ExecShellCommand(false, extraCmd); err != nil { logger.Error("token generation failed, %s, %s", output, err.Error()) return err @@ -565,7 +566,7 @@ func (i *InstallPulsarComp) InstallSupervisor() (err error) { return err } - extraCmd = fmt.Sprintf("chown -R mysql:mysql %s ", i.PulsarenvDir) + extraCmd = fmt.Sprintf("chown -R mysql %s ", i.PulsarenvDir) if _, err = osutil.ExecShellCommand(false, extraCmd); err != nil { logger.Error("%s execute failed, %v", extraCmd, err) return err @@ -639,7 +640,8 @@ func (i *InstallPulsarComp) InstallPulsarManager() (err error) { logger.Info("部署Pulsar Manager开始...") // 修改application.properties extraCmd := fmt.Sprintf( - "sed -i \"s/backend.broker.pulsarAdmin.authParams=/backend.broker.pulsarAdmin.authParams=%s/g\" %s", i.Params.Token, cst.DefaultPulsarManagerConf) + "sed -i \"s/backend.broker.pulsarAdmin.authParams=/backend.broker.pulsarAdmin.authParams=%s/g\" %s", i.Params.Token, + cst.DefaultPulsarManagerConf) if _, err := osutil.ExecShellCommand(false, extraCmd); err != nil { logger.Error("修改backend.broker.pulsarAdmin.authParams失败: %s, command: %s", err.Error(), extraCmd) return err @@ -652,7 +654,8 @@ func (i *InstallPulsarComp) InstallPulsarManager() (err error) { return err } extraCmd = fmt.Sprintf( - "sed -i \"s/default.environment.service_url=/default.environment.service_url=http:\\/\\/%s:%d/g\" %s", i.Params.Domain, i.Params.BrokerWebServicePort, cst.DefaultPulsarManagerConf) + "sed -i \"s/default.environment.service_url=/default.environment.service_url=http:\\/\\/%s:%d/g\" %s", + i.Params.Domain, i.Params.BrokerWebServicePort, cst.DefaultPulsarManagerConf) if _, err := osutil.ExecShellCommand(false, extraCmd); err != nil { logger.Error("修改default.environment.name失败: %s, command: %s", err.Error(), extraCmd) return err diff --git a/dbm-services/bigdata/db-tools/dbactuator/pkg/util/esutil/es_operate.go b/dbm-services/bigdata/db-tools/dbactuator/pkg/util/esutil/es_operate.go index 2319df7854..0efec1ccfa 100644 --- a/dbm-services/bigdata/db-tools/dbactuator/pkg/util/esutil/es_operate.go +++ b/dbm-services/bigdata/db-tools/dbactuator/pkg/util/esutil/es_operate.go @@ -12,7 +12,7 @@ import ( "dbm-services/bigdata/db-tools/dbactuator/pkg/util/osutil" "dbm-services/common/go-pubpkg/logger" - "github.com/shirou/gopsutil/mem" + "github.com/shirou/gopsutil/v3/mem" ) // DiskTypePath TODO diff --git a/dbm-services/bigdata/db-tools/dbactuator/pkg/util/kafkautil/kafkautil.go b/dbm-services/bigdata/db-tools/dbactuator/pkg/util/kafkautil/kafkautil.go index b99eab6d33..7e26acfdba 100644 --- a/dbm-services/bigdata/db-tools/dbactuator/pkg/util/kafkautil/kafkautil.go +++ b/dbm-services/bigdata/db-tools/dbactuator/pkg/util/kafkautil/kafkautil.go @@ -2,67 +2,87 @@ package kafkautil import ( + "encoding/json" "fmt" "io/ioutil" "math/rand" + "regexp" "strings" "time" "dbm-services/bigdata/db-tools/dbactuator/pkg/core/cst" "dbm-services/bigdata/db-tools/dbactuator/pkg/util/osutil" "dbm-services/common/go-pubpkg/logger" + + "github.com/go-zookeeper/zk" ) -// GetBrokerIds TODO -func GetBrokerIds(zk string) (ids []string, err error) { - var output string - extraCmd := fmt.Sprintf(` - export BROKERIDS=$(%s %s <<< 'ls /brokers/ids' | tail -1) - export BROKERIDS=${BROKERIDS//[!0-9 ]/} - echo $BROKERIDS - `, cst.DefaultZookeeperShell, zk) - logger.Info("extraCmd: %s", extraCmd) - if output, err = osutil.ExecShellCommand(false, extraCmd); err != nil { - logger.Error("Get zk ids failed, %s, %s", output, err.Error()) - return nil, err - } - logger.Info("output, %s", output) - trimRes := strings.TrimSuffix(output, "\n") - ids = strings.Fields(trimRes) - return ids, nil +// TP define topic +type TP struct { + Topic string `json:"topic"` +} + +// TPs topic.json struct +type TPs struct { + Topics []TP `json:"topics"` + Version int `json:"version"` } -// GetBrokerIdByHost TODO -func GetBrokerIdByHost(host string, zk string) (id string, err error) { - brokerIds, err := GetBrokerIds(zk) - logger.Info("brokerIds, %v", brokerIds) +// GetHostByID TODO +func GetHostByID(conn *zk.Conn, id string) (string, error) { + data, _, err := conn.Get(fmt.Sprintf("/brokers/ids/%s", id)) if err != nil { - logger.Error("Get broker id failed, %v", err) + logger.Error("get /brokers/ids/%s failed: %s", id, err) + return "", err + } + result := make(map[string]interface{}) + if err = json.Unmarshal(data, &result); err != nil { + logger.Error("Parse json failed, %s", err) return "", err } - for _, kfid := range brokerIds { - var output string - extraCmd := fmt.Sprintf(` - DETAIL=$(%s %s <<< "get /brokers/ids/%s") - [[ $DETAIL =~ PLAINTEXT:\/\/(.*?)\"\] ]] - BROKERS=${BASH_REMATCH[1]} - echo $BROKERS - `, cst.DefaultZookeeperShell, zk, kfid) - logger.Info("extraCmd: %s", extraCmd) - if output, err = osutil.ExecShellCommand(false, extraCmd); err != nil { - logger.Error("Get zk ids failed, %s, %s", output, err.Error()) + ep := result["endpoints"].([]interface{})[0].(string) + // brokerhost:9092 + m1 := regexp.MustCompile(`.*://`) + hostPort := m1.ReplaceAllString(ep, "") + // brokerhost + brokerHost := strings.Split(hostPort, ":")[0] + logger.Info("brokerHost:[%s]", brokerHost) + return brokerHost, nil +} + +// GetBrokerIds TODO +func GetBrokerIds(conn *zk.Conn) ([]string, error) { + // zk: ls /brokers/ids + // output: [0,1,2] + ids, _, err := conn.Children("/brokers/ids") + if err != nil { + logger.Error("Get broker ids failed, %s", err) + return ids, err + } + + logger.Info("Broker ids: %v", ids) + return ids, nil +} + +// GetBrokerIDByHost brokerhost -> 0 +func GetBrokerIDByHost(conn *zk.Conn, host string) (string, error) { + var brokerID string + logger.Info("Getting broker id of host ...") + ids, _ := GetBrokerIds(conn) + for _, kfid := range ids { + kfHost, err := GetHostByID(conn, kfid) + if err != nil { + logger.Error("Cant get host by id, %s", err) return "", err } - logger.Info("output", output) - kfHost := strings.Split(strings.TrimSuffix(output, "\n"), ":")[0] if kfHost == host { - id = kfid + logger.Info("host:[%s] id is [%s]", kfHost, kfid) + brokerID = kfid break } } - - return id, nil + return brokerID, nil } // PickRandom TODO @@ -71,9 +91,9 @@ func PickRandom(arr []string) string { return arr[rand.Intn(len(arr))] } -// GenReassignmentJson TODO -func GenReassignmentJson(brokerId string, zk string, xBrokerIds []string) (output string, err error) { - idsArr, _ := GetBrokerIds(zk) +// GenReassignmentJSON TODO +func GenReassignmentJSON(conn *zk.Conn, zk string, xBrokerIds []string) error { + idsArr, _ := GetBrokerIds(conn) logger.Info("idsArr %v", idsArr) tempArr := make([]string, len(idsArr)) copy(tempArr, idsArr) @@ -85,119 +105,46 @@ func GenReassignmentJson(brokerId string, zk string, xBrokerIds []string) (outpu logger.Info("idsArr %v", idsArr) // 获取brokerid, eg: 1,2,3 - allIds := strings.Join(idsArr[:], ",") tempIds := strings.Join(tempArr[:], ",") - extraCmd := fmt.Sprintf(` - function random_broker { - IFS=$',' read -a brokers <<< %s - selectedexpression=${brokers[ $RANDOM %% ${#brokers[@]} ]} - echo $selectedexpression - } - function array_contains { - local array="$1[@]" - local seeking=$2 - local in=1 - for element in "${!array}"; do - if [[ $element == $seeking ]]; then - in=0 - break - fi - done - return $in - } - - function other_broker { - local brokers_string=$1 - local all_brokers_string=%s - if [ ${#brokers_string} -ge ${#all_brokers_string} ]; then - local no_other_broker_available="" - echo $no_other_broker_available - else - IFS=$',' read -a brokers <<< "$brokers_string" - local new_broker=$(random_broker) - while array_contains brokers $new_broker; do - new_broker=$(random_broker) - done - echo $new_broker - fi - } - - function all_but_broker { - local brokers_string=$1 - local broker=$2 - IFS=$',' read -a brokers <<< "$brokers_string" - local new_brokers="" - for curr_broker in "${brokers[@]}"; do - if [ "$curr_broker" != "$broker" ]; then - new_brokers="$new_brokers,$curr_broker" - fi - done - # Remove leading comma, if any. - new_brokers=${new_brokers#","} - echo $new_brokers - } - - function replace_broker { - local brokers_string=$1 - local broker=$2 - local remaining_brokers=$(all_but_broker $brokers_string $broker) - local replacement_broker=$(other_broker $brokers_string) - new_brokers="$remaining_brokers,$replacement_broker" - # Remove leading comma, if any. - new_brokers=${new_brokers#","} - # Remove trailing comma, if any. - new_brokers=${new_brokers%%","} - echo $new_brokers - } - - json="{\n" - json="$json \"partitions\": [\n" + planJSONFile := fmt.Sprintf("%s/plan.json", cst.DefaultKafkaEnv) + extraCmd := fmt.Sprintf("%s --topics-to-move-json-file %s/topic.json --generate --zookeeper %s --broker-list %s >%s", + cst.DefaultReassignPartitionsBin, cst.DefaultKafkaEnv, zk, tempIds, planJSONFile) - # Actual partition reassignments - for topicPartitionReplicas in $(%s --zookeeper %s --describe | grep -w "Leader: %s" | awk '{ print $2"#"$4"#"$6"#"$8 }'); do - #echo "topicPartitionReplicas: $topicPartitionReplicas" - # Note: We use '#' as field separator in awk (see above) and here - # because it is not a valid character for a Kafka topic name. - IFS=$'#' read -a array <<< "$topicPartitionReplicas" - topic="${array[0]}" # e.g. "zerg.hydra" - partition="${array[1]}" # e.g. "4" - leaders="${array[2]}" - replicas="${array[3]}" # e.g. "0,8" (= comma-separated list of broker IDs) - if [ $leaders == $replicas ];then - new_replicas=$(replace_broker $replicas %s) - if [ -z "$new_replicas" ]; then - echo "ERROR: Cannot find any replacement broker. Maybe you have only a single broker in your cluster?" - exit 60 - fi - json="$json {\"topic\": \"${topic}\", \"partition\": ${partition}, \"replicas\": [${new_replicas}] },\n" - fi - done - - # Remove tailing comma, if any. - json=${json%%",\n"} - json="${json}\n" - - # "Footer" of JSON file - json="$json ],\n" - json="$json \"version\": 1\n" - json="${json}}\n" - - # Print JSON to STDOUT - echo -e $json - `, tempIds, allIds, cst.DefaultTopicBin, zk, brokerId, brokerId) logger.Info("extraCmd, %s", extraCmd) - if output, err = osutil.ExecShellCommand(false, extraCmd); err != nil { - logger.Error("gen json failed, %s, %s", output, err.Error()) - return "", err + output, _ := osutil.ExecShellCommand(false, extraCmd) + logger.Info("output: %s", output) + /* + if output, err := osutil.ExecShellCommand(false, extraCmd); err != nil { + logger.Error("Gen plan json failed, %s, %s", output, err) + return err + } + */ + b, err := ioutil.ReadFile(planJSONFile) + if err != nil { + logger.Error("Cant read plan.json, %s", err) + return err + } + // plan.json content + s := string(b) + failedKeyWord := "Partitions reassignment failed" + if strings.Contains(s, failedKeyWord) { + logger.Error(s) + return fmt.Errorf(s) } - logger.Info("output %s", output) - return output, nil + // Delete Current part + extraCmd = fmt.Sprintf("sed -i '1,4d' %s", planJSONFile) + if _, err := osutil.ExecShellCommand(false, extraCmd); err != nil { + logger.Error("sed plan.json failed, %s", err) + return err + } + logger.Info("Generate plan.json done") + return nil } -// GenReplaceReassignmentJson TODO -func GenReplaceReassignmentJson(oldBrokerId string, newBrokerId string, zk string) (output string, err error) { +// GenReplaceReassignmentJSON TODO +func GenReplaceReassignmentJSON(oldBrokerID string, newBrokerID string, zk string) (output string, err error) { extraCmd := fmt.Sprintf(` json="{\n" json="$json \"partitions\": [\n" @@ -229,7 +176,7 @@ func GenReplaceReassignmentJson(oldBrokerId string, newBrokerId string, zk strin # Print JSON to STDOUT echo -e $json - `, cst.DefaultTopicBin, zk, oldBrokerId, oldBrokerId, newBrokerId) + `, cst.DefaultTopicBin, zk, oldBrokerID, oldBrokerID, newBrokerID) logger.Info("extraCmd, %s", extraCmd) if output, err = osutil.ExecShellCommand(false, extraCmd); err != nil { logger.Error("gen json failed, %s, %s", output, err.Error()) @@ -243,13 +190,16 @@ func GenReplaceReassignmentJson(oldBrokerId string, newBrokerId string, zk strin // DoReassignPartitions TODO func DoReassignPartitions(zk string, jsonFile string) error { - extraCmd := fmt.Sprintf(`%s --zookeeper %s --reassignment-json-file %s --execute`, cst.DefaultReassignPartitionsBin, - zk, jsonFile) + // default limit 30MB/s + speedLimit := 30000000 + extraCmd := fmt.Sprintf(`%s --zookeeper %s --reassignment-json-file %s --throttle %d --execute `, + cst.DefaultReassignPartitionsBin, + zk, jsonFile, speedLimit) logger.Info("extraCmd: %s", extraCmd) - if output, err := osutil.ExecShellCommand(false, extraCmd); err != nil { - logger.Error("exec reassignparttions failed, [%s], [%s]", output, err.Error()) - return err - } + output, _ := osutil.ExecShellCommand(false, extraCmd) + logger.Info("output %s", output) + logger.Info("Doing patitions reassignment, default speed rate is 30MB/s") + logger.Info("Changing the rate, please rerun [%s] with other rate", extraCmd) return nil } @@ -260,27 +210,63 @@ func CheckReassignPartitions(zk string, jsonFile string) (output string, err err zk, jsonFile) logger.Info("cmd: [%s]", extraCmd) // 这里不判断status状态 - output, _ = osutil.ExecShellCommand(false, extraCmd) + output, _, _ = osutil.ExecShellCommandBd(false, extraCmd) logger.Info("output %s", output) return strings.TrimSuffix(output, "\n"), nil } -// GetTopics TODO +// GetTopics return topic list func GetTopics(zk string) (topicList []string, err error) { extraCmd := fmt.Sprintf(`%s --zookeeper %s --list`, cst.DefaultTopicBin, zk) logger.Info("cmd: [%s]", extraCmd) - output, err := osutil.ExecShellCommand(false, extraCmd) - if err != nil { - logger.Error("获取kafka topic列表失败 %v", err) - return topicList, err - } + output, _, _ := osutil.ExecShellCommandBd(false, extraCmd) + logger.Info("output %s", output) + /* + if err != nil { + logger.Error("获取kafka topic列表失败 %v", err) + return topicList, err + } + */ topicList = strings.Split(strings.TrimSuffix(output, "\n"), "\n") return topicList, nil } +// WriteTopicJSON TODO +func WriteTopicJSON(zk string) error { + topics, err := GetTopics(zk) + if err != nil { + logger.Error("Get topics list failed, %s", err) + return err + } + logger.Info("Topics list %v", topics) + var tps []TP + for _, t := range topics { + tps = append(tps, TP{Topic: t}) + } + tpJSON := &TPs{ + Topics: tps, + Version: 1, + } + b, err := json.Marshal(tpJSON) + if err != nil { + logger.Info("Pase topic json failed, %s", err) + return err + } + logger.Info("topic.json: %s", string(b)) + + // write to /data/kafkaenv/topic.json + topicJSONFile := fmt.Sprintf("%s/topic.json", cst.DefaultKafkaEnv) + if err := ioutil.WriteFile(topicJSONFile, b, 0644); err != nil { + logger.Error("write %s failed, %s", topicJSONFile, err) + return err + } + + return nil +} + // GenerateReassginFile TODO func GenerateReassginFile(zk, topic, idStrs, host string) error { - topicJson := fmt.Sprintf(` + topicJSON := fmt.Sprintf(` { "version": 1, "topics": [ @@ -288,12 +274,13 @@ func GenerateReassginFile(zk, topic, idStrs, host string) error { ] }`, topic) topicFile := "/tmp/topic.json" - if err := ioutil.WriteFile(topicFile, []byte(topicJson), 0644); err != nil { + if err := ioutil.WriteFile(topicFile, []byte(topicJSON), 0644); err != nil { logger.Error("write %s failed, %v", topicFile, err) return err } extraCmd := fmt.Sprintf( - `%s --zookeeper %s --topics-to-move-json-file %s --broker-list %s --generate | egrep -A1 ^Proposed|egrep -v ^Proposed`, + `%s --zookeeper %s --topics-to-move-json-file %s \ + --broker-list %s --generate | egrep -A1 ^Proposed|egrep -v ^Proposed`, cst.DefaultReassignPartitionsBin, zk, topicFile, idStrs) logger.Info("cmd: [%s]", extraCmd) @@ -314,9 +301,9 @@ func GenerateReassginFile(zk, topic, idStrs, host string) error { return err } - planJsonFile := fmt.Sprintf("%s/%s.json", jsonDir, topic) - if err := ioutil.WriteFile(planJsonFile, []byte(output), 0644); err != nil { - logger.Error("write %s failed, %v", planJsonFile, err) + planJSONFile := fmt.Sprintf("%s/%s.json", jsonDir, topic) + if err := ioutil.WriteFile(planJSONFile, []byte(output), 0644); err != nil { + logger.Error("write %s failed, %v", planJSONFile, err) return err } return nil diff --git a/dbm-services/bigdata/db-tools/dbactuator/pkg/util/osutil/cmdexec.go b/dbm-services/bigdata/db-tools/dbactuator/pkg/util/osutil/cmdexec.go index c34ba87aa5..d7340fb6fb 100644 --- a/dbm-services/bigdata/db-tools/dbactuator/pkg/util/osutil/cmdexec.go +++ b/dbm-services/bigdata/db-tools/dbactuator/pkg/util/osutil/cmdexec.go @@ -6,6 +6,7 @@ import ( "os" "os/exec" "strings" + "syscall" "github.com/golang/glog" "github.com/pkg/errors" @@ -149,3 +150,44 @@ func ExecShellCommand(isSudo bool, param string) (stdoutStr string, err error) { func CleanExecShellOutput(s string) string { return strings.ReplaceAll(strings.TrimSpace(s), "\n", "") } + +// ExecShellCommandBd 执行 shell 命令 +// 大数据部分命令行工具,无法通过err是否为空来判断执行的结果 +// 区分stdout,stderr,返回exit code +func ExecShellCommandBd(isSudo bool, param string) (stdoutStr string, stderrStr string, exitCode int) { + defaultFailedCode := 1 + + if isSudo { + param = "sudo " + param + } + cmd := exec.Command("bash", "-c", param) + + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + err := cmd.Run() + stdoutStr = stdout.String() + stderrStr = stderr.String() + + if err != nil { + // try to get the exit code + if exitError, ok := err.(*exec.ExitError); ok { + ws := exitError.Sys().(syscall.WaitStatus) + exitCode = ws.ExitStatus() + } else { + // This will happen (in OSX) if `name` is not available in $PATH, + // in this situation, exit code could not be get, and stderr will be + // empty string very likely, so we use the default fail code, and format err + // to string and set to stderr + exitCode = defaultFailedCode + if stderrStr == "" { + stderrStr = err.Error() + } + } + } else { + // success, exitCode should be 0 if go is ok + ws := cmd.ProcessState.Sys().(syscall.WaitStatus) + exitCode = ws.ExitStatus() + } + return stdoutStr, stderrStr, exitCode +} diff --git a/dbm-services/bigdata/db-tools/dbactuator/pkg/util/pulsarutil/pulsar_operate.go b/dbm-services/bigdata/db-tools/dbactuator/pkg/util/pulsarutil/pulsar_operate.go index ae879a08b9..14fb4f8271 100644 --- a/dbm-services/bigdata/db-tools/dbactuator/pkg/util/pulsarutil/pulsar_operate.go +++ b/dbm-services/bigdata/db-tools/dbactuator/pkg/util/pulsarutil/pulsar_operate.go @@ -8,7 +8,7 @@ import ( "dbm-services/common/go-pubpkg/logger" "github.com/go-ini/ini" - "github.com/shirou/gopsutil/mem" + "github.com/shirou/gopsutil/v3/mem" ) // GetMemSizeInMi TODO diff --git a/dbm-services/common/celery-service/.gitignore b/dbm-services/common/celery-service/.gitignore new file mode 100644 index 0000000000..7ca471568a --- /dev/null +++ b/dbm-services/common/celery-service/.gitignore @@ -0,0 +1,2 @@ +build/* +logs/ \ No newline at end of file diff --git a/dbm-services/common/celery-service/Dockerfile b/dbm-services/common/celery-service/Dockerfile new file mode 100644 index 0000000000..e81f9a7e0a --- /dev/null +++ b/dbm-services/common/celery-service/Dockerfile @@ -0,0 +1,7 @@ +FROM golang:1.19 + +ADD build/celery-service / +ADD build/external-tasks.yaml / +ADD build/collect / +WORKDIR / +ENTRYPOINT ["/celery-service", "run", "--log-console"] \ No newline at end of file diff --git a/dbm-services/common/celery-service/Makefile b/dbm-services/common/celery-service/Makefile new file mode 100644 index 0000000000..155540a707 --- /dev/null +++ b/dbm-services/common/celery-service/Makefile @@ -0,0 +1,32 @@ +PROJ="celery-service" +MODULE="dbm-services/common/celery-service" +VERSION=$(error please set VERSION flag) +PKG=${PROJ}.tar.gz +OUTPUT_DIR=build +RELEASE_BUILD_FLAG = "-X main.version=${VERSION} -X main.buildStamp=`date -u '+%Y-%m-%d_%I:%M:%S%p'` -X main.gitHash=`git rev-parse HEAD` " +DEV_BUILD_FLAG = "-X main.version="develop" -X main.buildStamp=`date -u '+%Y-%m-%d_%I:%M:%S%p'` -X main.gitHash="" " +BK_NAMESPACE = blueking +BK_DH_URL = mirrors.tencent.com/build + +.PHONY: release-bin +release-bin: + @CGO_ENABLE=0 GOARCH=amd64 GOOS=linux go build -ldflags ${RELEASE_BUILD_FLAG} -o ${OUTPUT_DIR}/${$PROJ} + @cp -r collect ${OUTPUT_DIR} + @cp external-tasks.yaml ${OUTPUT_DIR} + @tar -C ${OUTPUT_DIR} -zcf ${OUTPUT_DIR}/${PROJ}.tar.gz ${PROJ} external-tasks.yaml collect + +.PHONY: dev-bin +dev-bin: + @go build -ldflags ${DEV_BUILD_FLAG} -o ${OUTPUT_DIR}/${PROJ} + @cp -r collect ${OUTPUT_DIR} + @cp external-tasks.yaml ${OUTPUT_DIR} + @tar -C ${OUTPUT_DIR} -zcf ${OUTPUT_DIR}/${PROJ}.tar.gz ${PROJ} external-tasks.yaml collect + +.PHONY: bk-image +bk-image: release-bin + docker build --build-arg SRV_NAME=${PROJ} --rm -t ${BK_DH_URL}/${BK_NAMESPACE}/${PROJ}:${VERSION} . + docker push ${BK_DH_URL}/${BK_NAMESPACE}/${PROJ}:${VERSION} + +.PHONY: clean +clean: + @rm -rf $(OUTPUT_DIR) \ No newline at end of file diff --git a/dbm-services/common/celery-service/collect/counter.sh b/dbm-services/common/celery-service/collect/counter.sh new file mode 100755 index 0000000000..fe8c032bf0 --- /dev/null +++ b/dbm-services/common/celery-service/collect/counter.sh @@ -0,0 +1,7 @@ +set -e +COUNTER=$1 +for IDX in `seq 1 $COUNTER` +do + echo "$IDX: $(date)" + sleep 1 +done \ No newline at end of file diff --git a/dbm-services/common/celery-service/external-tasks.yaml b/dbm-services/common/celery-service/external-tasks.yaml new file mode 100644 index 0000000000..9f17c7bd80 --- /dev/null +++ b/dbm-services/common/celery-service/external-tasks.yaml @@ -0,0 +1,11 @@ +- name: shell-echo + cluster_type: TendbCluster + language: sh + executable: echo hello $CS_DB_USER + args: ["aaa", "bbb"] + collected: false +- name: shell-counter + cluster_type: TendbCluster + language: sh + executable: counter.sh + collected: true diff --git a/dbm-services/common/celery-service/go.mod b/dbm-services/common/celery-service/go.mod new file mode 100644 index 0000000000..84834d7376 --- /dev/null +++ b/dbm-services/common/celery-service/go.mod @@ -0,0 +1,43 @@ +module celery-service + +go 1.19 + +require ( + github.com/alecthomas/kingpin/v2 v2.3.2 + github.com/gin-gonic/gin v1.9.1 + github.com/go-playground/validator/v10 v10.14.0 + github.com/google/uuid v1.3.0 + github.com/iancoleman/strcase v0.3.0 + github.com/pkg/errors v0.9.1 + golang.org/x/exp v0.0.0-20230728194245-b0cb94b80691 + gopkg.in/natefinch/lumberjack.v2 v2.2.1 + gopkg.in/yaml.v2 v2.2.2 +) + +require ( + github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect + github.com/bytedance/sonic v1.9.1 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.2.4 // indirect + github.com/leodido/go-urn v1.2.4 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.11 // indirect + github.com/xhit/go-str2duration/v2 v2.1.0 // indirect + golang.org/x/arch v0.3.0 // indirect + golang.org/x/crypto v0.9.0 // indirect + golang.org/x/net v0.10.0 // indirect + golang.org/x/sys v0.8.0 // indirect + golang.org/x/text v0.9.0 // indirect + google.golang.org/protobuf v1.30.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/dbm-services/common/celery-service/go.sum b/dbm-services/common/celery-service/go.sum new file mode 100644 index 0000000000..28cf160eec --- /dev/null +++ b/dbm-services/common/celery-service/go.sum @@ -0,0 +1,103 @@ +github.com/alecthomas/kingpin/v2 v2.3.2 h1:H0aULhgmSzN8xQ3nX1uxtdlTHYoPLu5AhHxWrKI6ocU= +github.com/alecthomas/kingpin/v2 v2.3.2/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= +github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= +github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= +github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI= +github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= +github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= +github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= +github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= +golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/exp v0.0.0-20230728194245-b0cb94b80691 h1:/yRP+0AN7mf5DkD3BAI6TOFnd51gEoDEb8o35jIFtgw= +golang.org/x/exp v0.0.0-20230728194245-b0cb94b80691/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/dbm-services/common/celery-service/main.go b/dbm-services/common/celery-service/main.go new file mode 100644 index 0000000000..27570726f8 --- /dev/null +++ b/dbm-services/common/celery-service/main.go @@ -0,0 +1,84 @@ +package main + +import ( + "fmt" + "os" + + "github.com/alecthomas/kingpin/v2" + + "celery-service/pkg/config" + "celery-service/pkg/handler/externalhandler" + "celery-service/pkg/log" + "celery-service/pkg/service" +) + +var ( + version = "" + buildStamp = "" + gitHash = "" +) + +var ( + root = kingpin.New("celery-service", "dbm celery task service") + externalTaskConfig = root.Flag("external-task-config", "external tasks"). + Default("external-tasks.yaml"). + Envar("CS_EXTERNAL_TASK"). + ExistingFile() + logToConsole = root.Flag("log-console", "print log to console"). + Envar("CS_LOG_CONSOLE"). + Bool() + runCmd = root.Command("run", "start service") + runCmdAddress = runCmd.Flag("address", "service listen address"). + Default("0.0.0.0:80"). + Envar("CS_ADDRESS"). + TCP() + resultDBAddress = runCmd.Flag("db-address", "result db address"). + Envar("CS_DB_ADDRESS"). + TCP() + resultDBUser = runCmd.Flag("db-user", "result db user"). + Envar("CS_DB_USER"). + String() + resultDBPassword = runCmd.Flag("db-password", "result db password"). + Envar("CS_DB_PASSWORD"). + String() + resultDBName = runCmd.Flag("db-name", "result database name"). + Envar("CS_DB_NAME"). + String() + listCmd = root.Command("list", "list all tasks") + versionCmd = root.Command("version", "print version") + + command string +) + +func init() { + command = kingpin.MustParse(root.Parse(os.Args[1:])) + log.Init(*logToConsole) +} + +func main() { + switch command { + case runCmd.FullCommand(): + err := externalhandler.LoadExternal(*externalTaskConfig) //config.LoadExternalTasks(*externalTaskConfig) + if err != nil { + panic(err) + } + + config.DBAddress = (*resultDBAddress).String() + config.DBUser = *resultDBUser + config.DBPassword = *resultDBPassword + config.DBName = *resultDBName + + err = service.Start((*runCmdAddress).String()) + if err != nil { + panic(err) + } + case listCmd.FullCommand(): + err := externalhandler.LoadExternal(*externalTaskConfig) //config.LoadExternalTasks(*externalTaskConfig) + if err != nil { + panic(err) + } + service.List() + case versionCmd.FullCommand(): + fmt.Printf("Version: %s, GitHash: %s, BuildAt: %s\n", version, gitHash, buildStamp) + } +} diff --git a/dbm-services/common/celery-service/pkg/asyncsession/init.go b/dbm-services/common/celery-service/pkg/asyncsession/init.go new file mode 100644 index 0000000000..0f6108fee0 --- /dev/null +++ b/dbm-services/common/celery-service/pkg/asyncsession/init.go @@ -0,0 +1,22 @@ +package asyncsession + +import ( + "context" + "sync" + "time" +) + +type Session struct { + ID string `json:"id"` + Message string `json:"message"` + Err string `json:"error"` + Done bool `json:"done"` + StartAt time.Time `json:"start_at"` + Cancel context.CancelFunc `json:"-"` +} + +var SessionMap sync.Map + +func init() { + SessionMap = sync.Map{} +} diff --git a/dbm-services/common/celery-service/pkg/config/init.go b/dbm-services/common/celery-service/pkg/config/init.go new file mode 100644 index 0000000000..a53b6da84f --- /dev/null +++ b/dbm-services/common/celery-service/pkg/config/init.go @@ -0,0 +1,28 @@ +package config + +import ( + "os" + "path/filepath" +) + +var Executable string +var BaseDir string +var LogDir string +var CollectDir string + +var DBAddress string +var DBUser string +var DBPassword string +var DBName string + +func init() { + executable, _ := os.Executable() + + Executable = filepath.Base(executable) + BaseDir = filepath.Dir(executable) + LogDir = filepath.Join(BaseDir, "logs") + CollectDir = filepath.Join(BaseDir, "collect") + + _ = os.MkdirAll(CollectDir, 0755) + _ = os.MkdirAll(LogDir, 0755) +} diff --git a/dbm-services/common/celery-service/pkg/handler/async_kill.go b/dbm-services/common/celery-service/pkg/handler/async_kill.go new file mode 100644 index 0000000000..e6e7324755 --- /dev/null +++ b/dbm-services/common/celery-service/pkg/handler/async_kill.go @@ -0,0 +1,67 @@ +package handler + +import ( + "fmt" + "net/http" + + "github.com/gin-gonic/gin" + + "celery-service/pkg/asyncsession" +) + +func HandleAsyncKill(engine *gin.Engine) { + g := engine.Group("async") + g.POST("kill", + func(ctx *gin.Context) { + var postArg struct { + SessionID *string `json:"session_id"` + } + + if err := ctx.ShouldBindJSON(&postArg); err != nil { + ctx.JSON( + http.StatusBadRequest, + gin.H{ + "code": 1, + "data": "", + "msg": err.Error(), + }) + return + } + + if postArg.SessionID == nil { + ctx.JSON( + http.StatusBadRequest, + gin.H{ + "code": 1, + "data": "", + "msg": "session_id required", + }) + return + } + + v, ok := asyncsession.SessionMap.Load(*postArg.SessionID) + if !ok { + ctx.JSON( + http.StatusOK, + gin.H{ + "code": 0, + "data": "", + "msg": fmt.Sprintf("session %s not found", *postArg.SessionID), + }) + return + } + + session := v.(*asyncsession.Session) + session.Cancel() + + asyncsession.SessionMap.Delete(postArg.SessionID) + + ctx.JSON( + http.StatusOK, + gin.H{ + "code": 0, + "data": "", + "msg": "", + }) + }) +} diff --git a/dbm-services/common/celery-service/pkg/handler/async_query_status.go b/dbm-services/common/celery-service/pkg/handler/async_query_status.go new file mode 100644 index 0000000000..7db7d6ae35 --- /dev/null +++ b/dbm-services/common/celery-service/pkg/handler/async_query_status.go @@ -0,0 +1,79 @@ +package handler + +import ( + "fmt" + "io" + "net/http" + + "github.com/gin-gonic/gin" + "golang.org/x/exp/slog" + + "celery-service/pkg/asyncsession" +) + +func HandleAsyncQuery(engine *gin.Engine) { + g := engine.Group("async") + g.POST("query", + func(ctx *gin.Context) { + var postArg struct { + SessionID *string `json:"session_id"` + } + + if err := ctx.ShouldBindJSON(&postArg); err != nil && err != io.EOF { + logger.Error("bind post args", slog.Any("error", err)) + ctx.JSON( + http.StatusBadRequest, + gin.H{ + "code": 1, + "data": "", + "msg": err.Error(), + }) + return + } + + if postArg.SessionID != nil { + logger.Info("query", slog.String("post session id", *postArg.SessionID)) + + v, ok := asyncsession.SessionMap.Load(*postArg.SessionID) + if !ok { + ctx.JSON( + http.StatusOK, + gin.H{ + "code": 1, + "data": "", + "msg": fmt.Sprintf("session %s not found", *postArg.SessionID), + }) + return + } + + session := v.(*asyncsession.Session) + + ctx.JSON( + http.StatusOK, + gin.H{ + "code": 0, + "data": []*asyncsession.Session{session}, + "msg": "", + }) + return + } else { + logger.Info("query all sessions") + + var sessions []*asyncsession.Session + asyncsession.SessionMap.Range(func(_, v any) bool { + sessions = append(sessions, v.(*asyncsession.Session)) + return true + }) + + ctx.JSON( + http.StatusOK, + gin.H{ + "code": 0, + "data": sessions, + "msg": "", + }) + return + } + + }) +} diff --git a/dbm-services/common/celery-service/pkg/handler/discovery.go b/dbm-services/common/celery-service/pkg/handler/discovery.go new file mode 100644 index 0000000000..eaa5291b75 --- /dev/null +++ b/dbm-services/common/celery-service/pkg/handler/discovery.go @@ -0,0 +1,61 @@ +package handler + +import ( + "encoding/json" + "net/http" + "net/url" + + "github.com/gin-gonic/gin" + "github.com/iancoleman/strcase" +) + +type descriptor struct { + Name string `json:"name"` + ClusterType string `json:"cluster_type"` + EmptyParam json.RawMessage `json:"empty_param"` + Enable bool `json:"enable"` + Url string `json:"url"` +} + +func HandleDiscovery(engine *gin.Engine) { + engine.GET("discovery", + func(ctx *gin.Context) { + var dess []descriptor + + for _, hs := range Handlers { + for _, h := range hs { + handlerUrl, err := url.JoinPath( + strcase.ToKebab(h.ClusterType()), + strcase.ToKebab(h.Name())) + + if err != nil { + ctx.JSON( + http.StatusInternalServerError, + gin.H{ + "code": 1, + "data": "", + "msg": err.Error(), + }) + return + } + + dess = append(dess, descriptor{ + Name: h.Name(), + ClusterType: h.ClusterType(), + EmptyParam: h.EmptyParam(), + Enable: h.Enable(), + Url: handlerUrl, + }) + } + } + + ctx.JSON( + http.StatusOK, + gin.H{ + "code": 0, + "data": dess, + "msg": "", + }) + return + }) +} diff --git a/dbm-services/common/celery-service/pkg/handler/externalhandler/execute.go b/dbm-services/common/celery-service/pkg/handler/externalhandler/execute.go new file mode 100644 index 0000000000..3bbc7b37ce --- /dev/null +++ b/dbm-services/common/celery-service/pkg/handler/externalhandler/execute.go @@ -0,0 +1,25 @@ +package externalhandler + +import ( + "github.com/pkg/errors" + "golang.org/x/exp/slog" +) + +func (h *Handler) execute() (string, error) { + err := h.setupOutputStream() + if err != nil { + return "", err + } + + if err := h.cmd.Start(); err != nil { + h.logger.Error("exec start", slog.String("error", err.Error())) + return "", errors.Errorf("exec start failed: %s", err.Error()) + } + + if err := h.cmd.Wait(); err != nil { + h.logger.Error("exec wait", slog.String("error", err.Error())) + return "", errors.Errorf("exec error: %s (%s)", err.Error(), h.latestStderr) + } + + return h.latestStdout, nil +} diff --git a/dbm-services/common/celery-service/pkg/handler/externalhandler/external_item.go b/dbm-services/common/celery-service/pkg/handler/externalhandler/external_item.go new file mode 100644 index 0000000000..76e5d02098 --- /dev/null +++ b/dbm-services/common/celery-service/pkg/handler/externalhandler/external_item.go @@ -0,0 +1,65 @@ +package externalhandler + +import ( + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/go-playground/validator/v10" + "github.com/pkg/errors" + "golang.org/x/exp/slog" + "gopkg.in/yaml.v2" + + "celery-service/pkg/config" +) + +type externalItem struct { + Name string `yaml:"name" validate:"required"` + ClusterType string `yaml:"cluster_type" validate:"required"` + Language string `yaml:"language" validate:"required,oneof=python python2 python3 perl sh bash binary"` + Executable string `yaml:"executable" validate:"required"` + Args []string `yaml:"args"` + Collected *bool `yaml:"collected" validate:"required"` +} + +var externalItems []*externalItem + +func LoadExternal(filePath string) error { + content, err := os.ReadFile(filePath) + if err != nil { + logger.Error("read external task config", slog.String("error", err.Error())) + return err + } + + err = yaml.Unmarshal(content, &externalItems) + if err != nil { + logger.Error("unmarshal external task config", slog.String("error", err.Error())) + return err + } + + validate := validator.New() + + for _, ei := range externalItems { + + // validate + err := validate.Struct(ei) + if err != nil { + logger.Error("validate external task", slog.Any(ei.Name, ei), slog.String("error", err.Error())) + return errors.Wrap(err, fmt.Sprintf("validate %v", ei)) + } + + if *ei.Collected { + splitExecutable := strings.Split(ei.Executable, " ") + if filepath.IsAbs(splitExecutable[0]) { + err := errors.Errorf("absolute path not allowed: %s", ei.Executable) + logger.Error("load external", slog.String("error", err.Error())) + return err + } + + splitExecutable[0] = filepath.Join(config.CollectDir, splitExecutable[0]) + ei.Executable = strings.Join(splitExecutable, " ") + } + } + return nil +} diff --git a/dbm-services/common/celery-service/pkg/handler/externalhandler/handler_factory.go b/dbm-services/common/celery-service/pkg/handler/externalhandler/handler_factory.go new file mode 100644 index 0000000000..7bf52faaa7 --- /dev/null +++ b/dbm-services/common/celery-service/pkg/handler/externalhandler/handler_factory.go @@ -0,0 +1,9 @@ +package externalhandler + +func ExternalHandlers() []*Handler { + var handlers []*Handler + for _, item := range externalItems { + handlers = append(handlers, newHandler(item)) + } + return handlers +} diff --git a/dbm-services/common/celery-service/pkg/handler/externalhandler/impl_ihandler.go b/dbm-services/common/celery-service/pkg/handler/externalhandler/impl_ihandler.go new file mode 100644 index 0000000000..6a37763728 --- /dev/null +++ b/dbm-services/common/celery-service/pkg/handler/externalhandler/impl_ihandler.go @@ -0,0 +1,78 @@ +package externalhandler + +import ( + "context" + "encoding/json" + "os/exec" + "strings" + + "golang.org/x/exp/slog" + + "celery-service/pkg/log" +) + +type Handler struct { + item *externalItem + bin string + args []string + cmd *exec.Cmd + ctx context.Context + latestStdout string + latestStderr string + logger *slog.Logger +} + +func (h *Handler) ClusterType() string { + return h.item.ClusterType +} + +func (h *Handler) Name() string { + return h.item.Name +} + +func (h *Handler) Worker(body []byte, ctx context.Context) (string, error) { + var postArgs []string + if len(body) > 0 { + if err := json.Unmarshal(body, &postArgs); err != nil { + h.logger.Error("unmarshal body", slog.String("error", err.Error())) + return "", err + } + } + h.logger.Info("external handler", slog.Any("post args", postArgs)) + + var cmdArgs []string + switch h.bin { + case "sh", "bash": + cmdArgs = []string{ + "-c", + strings.Join(mergeSlices(h.args, postArgs), " "), + } + default: + cmdArgs = mergeSlices(h.args, postArgs) + } + + h.cmd = exec.CommandContext(ctx, h.bin, cmdArgs...) + h.logger.Info("generate cmd", slog.Any("command", h.cmd)) + + return h.execute() +} + +func (h *Handler) Enable() bool { + return true +} + +func (h *Handler) EmptyParam() json.RawMessage { + empty, _ := json.Marshal([]string{}) + return empty +} + +func newHandler(item *externalItem) *Handler { + bin, args := splitBinArgs(item) + + return &Handler{ + item: item, + bin: bin, + args: args, + logger: log.GetLogger(item.Name), + } +} diff --git a/dbm-services/common/celery-service/pkg/handler/externalhandler/init.go b/dbm-services/common/celery-service/pkg/handler/externalhandler/init.go new file mode 100644 index 0000000000..00283088c0 --- /dev/null +++ b/dbm-services/common/celery-service/pkg/handler/externalhandler/init.go @@ -0,0 +1,13 @@ +package externalhandler + +import ( + "golang.org/x/exp/slog" + + "celery-service/pkg/log" +) + +var logger *slog.Logger + +func init() { + logger = log.GetLogger("root") +} diff --git a/dbm-services/common/celery-service/pkg/handler/externalhandler/output_stream.go b/dbm-services/common/celery-service/pkg/handler/externalhandler/output_stream.go new file mode 100644 index 0000000000..045261375f --- /dev/null +++ b/dbm-services/common/celery-service/pkg/handler/externalhandler/output_stream.go @@ -0,0 +1,42 @@ +package externalhandler + +import ( + "bufio" + "io" + + "golang.org/x/exp/slog" +) + +func (h *Handler) setupOutputStream() error { + stdout, err := h.cmd.StdoutPipe() + if err != nil { + h.logger.Error("open stdout pipe", slog.String("error", err.Error())) + return err + } + + stderr, err := h.cmd.StderrPipe() + if err != nil { + h.logger.Error("open stderr pipe", slog.String("error", err.Error())) + return err + } + + go func(r io.Reader) { + scanner := bufio.NewScanner(r) + scanner.Split(bufio.ScanLines) + for scanner.Scan() { + h.latestStdout = scanner.Text() + h.logger.Info("stream output", slog.String("stdout", h.latestStdout)) + } + }(stdout) + + go func(r io.Reader) { + scanner := bufio.NewScanner(r) + scanner.Split(bufio.ScanLines) + for scanner.Scan() { + h.latestStderr = scanner.Text() + h.logger.Error("stream output", slog.String("stderr", h.latestStderr)) + } + }(stderr) + + return nil +} diff --git a/dbm-services/common/celery-service/pkg/handler/externalhandler/split_bin_args.go b/dbm-services/common/celery-service/pkg/handler/externalhandler/split_bin_args.go new file mode 100644 index 0000000000..49136bdd25 --- /dev/null +++ b/dbm-services/common/celery-service/pkg/handler/externalhandler/split_bin_args.go @@ -0,0 +1,36 @@ +package externalhandler + +import ( + "regexp" +) + +var splitPattern = regexp.MustCompile(`\s+`) + +func splitBinArgs(item *externalItem) (bin string, args []string) { + splitExec := splitPattern.Split(item.Executable, -1) + + switch item.Language { + case "sh", "shell": + bin = "sh" + args = mergeSlices(splitExec, item.Args) + case "bash": + bin = "bash" + args = mergeSlices(splitExec, item.Args) + case "python", "python2", "python3", "perl": + bin = item.Language + args = mergeSlices(splitExec, item.Args) + case "binary": + bin = splitExec[0] + args = mergeSlices(splitExec[1:], item.Args) + } + + return +} + +func mergeSlices[S ~[]E, E any](v ...S) S { + ret := make(S, 0) + for _, o := range v { + ret = append(ret, o...) + } + return ret +} diff --git a/dbm-services/common/celery-service/pkg/handler/init.go b/dbm-services/common/celery-service/pkg/handler/init.go new file mode 100644 index 0000000000..e9a3e2ab52 --- /dev/null +++ b/dbm-services/common/celery-service/pkg/handler/init.go @@ -0,0 +1,83 @@ +package handler + +import ( + "context" + "encoding/json" + + "github.com/pkg/errors" + "golang.org/x/exp/slices" + "golang.org/x/exp/slog" + + "celery-service/pkg/handler/externalhandler" + "celery-service/pkg/handler/internalhandler" + "celery-service/pkg/log" +) + +var logger *slog.Logger + +type IHandler interface { + ClusterType() string + Name() string + Worker([]byte, context.Context) (string, error) + Enable() bool + EmptyParam() json.RawMessage +} + +var Handlers = make(map[string][]IHandler) + +func InitHandlers() error { + logger = log.GetLogger("root") + + for _, eh := range externalhandler.ExternalHandlers() { + if eh.Name() == "root" { + err := errors.Errorf("task name can't be 'root'") + logger.Error("register external handler", slog.String("error", err.Error())) + return err + } + + if _, ok := Handlers[eh.ClusterType()]; !ok { + Handlers[eh.ClusterType()] = []IHandler{} + } + + if slices.ContainsFunc(Handlers[eh.ClusterType()], func(i IHandler) bool { + return eh.Name() == i.Name() + }) { + err := errors.Errorf("duplicate task found: %s/%s", eh.ClusterType(), eh.Name()) + logger.Error("register external handler", slog.String("error", err.Error())) + return err + } + + Handlers[eh.ClusterType()] = append(Handlers[eh.ClusterType()], eh) + } + + for _, ii := range internalhandler.InternalHandlers() { + ih, ok := ii.(IHandler) + if !ok { + err := errors.Errorf("%v can't cast to IHandler", ii) + logger.Error("register internal handler", slog.String("error", err.Error())) + return err + } + + if ih.Name() == "root" { + err := errors.Errorf("task name can't be 'root'") + logger.Error("register internal handler", slog.String("error", err.Error())) + return err + } + + if _, ok := Handlers[ih.ClusterType()]; !ok { + Handlers[ih.ClusterType()] = []IHandler{} + } + + if slices.ContainsFunc(Handlers[ih.ClusterType()], func(i IHandler) bool { + return ih.Name() == i.Name() + }) { + err := errors.Errorf("duplicate task found: %s/%s", ih.ClusterType(), ih.Name()) + logger.Error("register internal handler", slog.String("error", err.Error())) + return err + } + + Handlers[ih.ClusterType()] = append(Handlers[ih.ClusterType()], ih) + } + + return nil +} diff --git a/dbm-services/common/celery-service/pkg/handler/internalhandler/democounter/counter.go b/dbm-services/common/celery-service/pkg/handler/internalhandler/democounter/counter.go new file mode 100644 index 0000000000..b04ff5d7a5 --- /dev/null +++ b/dbm-services/common/celery-service/pkg/handler/internalhandler/democounter/counter.go @@ -0,0 +1,76 @@ +package democounter + +import ( + "context" + "encoding/json" + "time" + + "github.com/pkg/errors" + "golang.org/x/exp/slog" + + "celery-service/pkg/config" + "celery-service/pkg/log" +) + +type demoArg struct { + Counter int `json:"counter"` +} + +type Handler struct { + logger *slog.Logger +} + +func (h *Handler) ClusterType() string { + return "TendbHA" +} + +func (h *Handler) Name() string { + return "Counter" +} + +func (h *Handler) Enable() bool { + return false +} + +func (h *Handler) EmptyParam() json.RawMessage { + empty, _ := json.Marshal(demoArg{Counter: 0}) + return empty +} + +// Worker +/* +当工作可能耗时很长, 或者需要循环很多次时 +需要在需要的位置监听 ctx.Done 来响应调用方的终止信号 +否则任务一旦启动, 就只能等到完成或者出错 +*/ +func (h *Handler) Worker(body []byte, ctx context.Context) (string, error) { + var postArg demoArg + + err := json.Unmarshal(body, &postArg) + if err != nil { + h.logger.Error("unmarshal post arg", slog.String("error", err.Error())) + return "", err + } + + for i := 0; i < postArg.Counter; i++ { + select { + case <-ctx.Done(): + err := errors.Errorf("canceled") + h.logger.Error("worker", slog.String("error", err.Error())) + return "", err + default: + h.logger.Info("demo", + slog.Time("time", time.Now()), + slog.Int("i", i), + slog.String("user", config.DBUser)) + time.Sleep(1 * time.Second) + } + } + return "hello world", nil +} + +func NewHandler() *Handler { + return &Handler{ + logger: log.GetLogger("demo"), + } +} diff --git a/dbm-services/common/celery-service/pkg/handler/internalhandler/init.go b/dbm-services/common/celery-service/pkg/handler/internalhandler/init.go new file mode 100644 index 0000000000..e3945c031f --- /dev/null +++ b/dbm-services/common/celery-service/pkg/handler/internalhandler/init.go @@ -0,0 +1,15 @@ +package internalhandler + +import ( + "celery-service/pkg/handler/internalhandler/democounter" +) + +var handlers []interface{} + +func InternalHandlers() []interface{} { + handlers = append(handlers, []interface{}{ + democounter.NewHandler(), + }...) + + return handlers +} diff --git a/dbm-services/common/celery-service/pkg/handler/list.go b/dbm-services/common/celery-service/pkg/handler/list.go new file mode 100644 index 0000000000..4c050cc82c --- /dev/null +++ b/dbm-services/common/celery-service/pkg/handler/list.go @@ -0,0 +1,32 @@ +package handler + +import ( + "net/http" + + "github.com/gin-gonic/gin" +) + +type rt struct { + Method string `json:"method"` + Path string `json:"path"` +} + +func HandleList(engine *gin.Engine) { + engine.GET("list", + func(ctx *gin.Context) { + var routes []*rt + + for _, r := range engine.Routes() { + routes = append(routes, &rt{Method: r.Method, Path: r.Path}) + } + + ctx.JSON( + http.StatusOK, + gin.H{ + "code": 0, + "data": routes, + "msg": "", + }) + return + }) +} diff --git a/dbm-services/common/celery-service/pkg/log/init.go b/dbm-services/common/celery-service/pkg/log/init.go new file mode 100644 index 0000000000..3d62ff4bc9 --- /dev/null +++ b/dbm-services/common/celery-service/pkg/log/init.go @@ -0,0 +1,55 @@ +package log + +import ( + "fmt" + "io" + "os" + "path/filepath" + + "github.com/iancoleman/strcase" + "golang.org/x/exp/slog" + "gopkg.in/natefinch/lumberjack.v2" + + "celery-service/pkg/config" +) + +var toConsole bool + +func Init(logToConsole bool) { + toConsole = logToConsole +} + +func GetLogger(name string) *slog.Logger { + name = strcase.ToSnake(name) + + var filePath string + if name == "root" { + filePath = filepath.Join(config.LogDir, fmt.Sprintf("%s.log", config.Executable)) + } else { + filePath = filepath.Join(config.LogDir, name, fmt.Sprintf("%s.log", name)) + } + + fileWriter := &lumberjack.Logger{ + Filename: filePath, + MaxSize: 100, + MaxAge: 30, + MaxBackups: 50, + } + + var logWriter io.Writer + if toConsole { + logWriter = io.MultiWriter(fileWriter, os.Stdout) + } else { + logWriter = fileWriter + } + + return slog.New( + slog.NewTextHandler( + logWriter, + &slog.HandlerOptions{ + AddSource: true, + Level: slog.LevelDebug, + }, + ), + ).With("name", name) +} diff --git a/dbm-services/common/celery-service/pkg/service/build_router.go b/dbm-services/common/celery-service/pkg/service/build_router.go new file mode 100644 index 0000000000..1ffafc1763 --- /dev/null +++ b/dbm-services/common/celery-service/pkg/service/build_router.go @@ -0,0 +1,183 @@ +package service + +import ( + "context" + "fmt" + "io" + "net/http" + "time" + + "github.com/gin-gonic/gin" + "github.com/google/uuid" + "github.com/iancoleman/strcase" + + "celery-service/pkg/asyncsession" + "celery-service/pkg/handler" +) + +func buildRouter(engine *gin.Engine) error { + err := handler.InitHandlers() + if err != nil { + return err + } + + buildSyncRouter(engine) + buildAsyncRouter(engine) + + return nil +} + +type workerResPack struct { + Msg string + Err error +} + +func buildSyncRouter(engine *gin.Engine) { + syncGroup := engine.Group("sync") + for dbType, hs := range handler.Handlers { + g := syncGroup.Group(strcase.ToKebab(dbType)) + for _, h := range hs { + g.POST( + strcase.ToKebab(h.Name()), + func(h handler.IHandler) func(ctx *gin.Context) { + return func(ctx *gin.Context) { + body, err := io.ReadAll(ctx.Request.Body) + if err != nil { + ctx.JSON( + http.StatusInternalServerError, + gin.H{ + "code": 1, + "data": "", + "msg": err.Error(), + }) + return + } + + resPackChan := make(chan *workerResPack) + hCtx, cancel := context.WithCancel(context.Background()) + defer cancel() + + go func() { + msg, err := h.Worker(body, hCtx) + resPackChan <- &workerResPack{ + Msg: msg, + Err: err, + } + }() + + for { + select { + case resPack := <-resPackChan: + if resPack.Err != nil { + ctx.JSON( + http.StatusOK, + gin.H{ + "code": 1, + "data": "", + "msg": resPack.Err.Error(), + }) + } else { + ctx.JSON( + http.StatusOK, + gin.H{ + "code": 0, + "data": resPack.Msg, + "msg": "", + }) + } + return + case <-ctx.Request.Context().Done(): + ctx.JSON( + http.StatusTooEarly, + gin.H{ + "code": 0, + "data": "", + "msg": "canceled", + }) + return + } + } + } + }(h), + ) + } + } +} + +func buildAsyncRouter(engine *gin.Engine) { + asyncGroup := engine.Group("async") + + for dbType, hs := range handler.Handlers { + g := asyncGroup.Group(strcase.ToKebab(dbType)) + for _, h := range hs { + g.POST( + strcase.ToKebab(h.Name()), + func(h handler.IHandler) func(ctx *gin.Context) { + return func(ctx *gin.Context) { + body, err := io.ReadAll(ctx.Request.Body) + if err != nil { + ctx.JSON( + http.StatusInternalServerError, + gin.H{ + "code": 1, + "data": "", + "msg": err.Error(), + }) + return + } + + sessionID := uuid.New().String() + hCtx, cancel := context.WithCancel(context.Background()) + + go func(body []byte) { + asyncsession.SessionMap.Store( + sessionID, + &asyncsession.Session{ + ID: sessionID, + Message: "", + Err: "", + Done: false, + Cancel: cancel, + StartAt: time.Now(), + }, + ) + + msg, err := h.Worker(body, hCtx) + + select { + case <-hCtx.Done(): + logger.Info("canceled") + default: + v, ok := asyncsession.SessionMap.Load(sessionID) + if !ok { + ctx.JSON( + http.StatusInternalServerError, + gin.H{ + "code": 1, + "data": "", + "msg": fmt.Sprintf("%s not found", sessionID), + }) + } + st := v.(*asyncsession.Session) + st.Message = msg + st.Done = true + if err != nil { + st.Err = err.Error() + } + } + }(body) + + ctx.JSON( + http.StatusOK, + gin.H{ + "code": 0, + "data": sessionID, + "msg": "", + }) + return + } + }(h), + ) + } + } +} diff --git a/dbm-services/common/celery-service/pkg/service/init.go b/dbm-services/common/celery-service/pkg/service/init.go new file mode 100644 index 0000000000..0c68708945 --- /dev/null +++ b/dbm-services/common/celery-service/pkg/service/init.go @@ -0,0 +1,64 @@ +package service + +import ( + "bytes" + "io" + "time" + + "github.com/gin-gonic/gin" + "golang.org/x/exp/slog" + + "celery-service/pkg/handler" + "celery-service/pkg/log" +) + +var r *gin.Engine +var logger *slog.Logger + +func Init() error { + logger = log.GetLogger("root") + + r = gin.New() + + r.Use(gin.Recovery()) + r.Use(func(ctx *gin.Context) { + start := time.Now() + + body, _ := io.ReadAll(ctx.Request.Body) + ctx.Request.Body = io.NopCloser(bytes.NewReader(body)) + + ctx.Next() + + logger.With( + "METHOD", ctx.Request.Method, + "URI", ctx.Request.RequestURI, + "STATUS", ctx.Writer.Status(), + "LATENCY", time.Now().Sub(start), + "CLIENT", ctx.ClientIP(), + "BODY", string(body), + ).Info("HTTP REQUEST") + + ctx.Next() + }) + + err := buildRouter(r) + if err != nil { + return err + } + + handler.HandleAsyncKill(r) + handler.HandleAsyncQuery(r) + handler.HandleList(r) + handler.HandleDiscovery(r) + + for _, rt := range r.Routes() { + logger.Info( + "init service", + slog.String("method", rt.Method), + slog.String("path", rt.Path), + slog.String("handler", rt.Handler), + ) + } + + return nil +} diff --git a/dbm-services/common/celery-service/pkg/service/list.go b/dbm-services/common/celery-service/pkg/service/list.go new file mode 100644 index 0000000000..79f939b34a --- /dev/null +++ b/dbm-services/common/celery-service/pkg/service/list.go @@ -0,0 +1,12 @@ +package service + +import "fmt" + +func List() error { + err := Init() + if err != nil { + return err + } + fmt.Printf("%v\n", r.Routes()) + return nil +} diff --git a/dbm-services/common/celery-service/pkg/service/start.go b/dbm-services/common/celery-service/pkg/service/start.go new file mode 100644 index 0000000000..8567e014cc --- /dev/null +++ b/dbm-services/common/celery-service/pkg/service/start.go @@ -0,0 +1,35 @@ +package service + +import ( + "time" + + "golang.org/x/exp/slog" + + "celery-service/pkg/asyncsession" +) + +func Start(address string) error { + err := Init() + if err != nil { + return err + } + + go func() { + for { + select { + case <-time.Tick(10 * time.Minute): + asyncsession.SessionMap.Range(func(k, v any) bool { + sessionID := k.(string) + session := v.(*asyncsession.Session) + if session.Done && session.StartAt.Add(1*time.Minute).Before(time.Now()) { + logger.Info("clean session", slog.Any(sessionID, session)) + asyncsession.SessionMap.Delete(sessionID) + } + return true + }) + } + } + }() + + return r.Run(address) +} diff --git a/dbm-services/common/celery-service/readme.md b/dbm-services/common/celery-service/readme.md new file mode 100644 index 0000000000..20647e32f0 --- /dev/null +++ b/dbm-services/common/celery-service/readme.md @@ -0,0 +1,266 @@ +# 简介 +1. 集中管理遗留的定时任务脚本, 需要统一放入 _collect_ 文件夹 +2. 新增的定时任务建议用 _go_ 实现 +3. 所有定时任务都暴露出唯一的 _http_ 接口, 在 _dbm django_ 工程中用 _celery_ 调度 + +# 启动 +`celery-service run` + +```shell +build/celery-service run --help +usage: celery-service run [] + +start service + + +Flags: + --[no-]help Show context-sensitive help (also try --help-long and --help-man). + --external-task-config=/external-tasks.yaml + external tasks ($CS_EXTERNAL_TASK) + --[no-]log-console print log to console ($CS_LOG_CONSOLE) + --address=0.0.0.0:80 service listen address ($CS_ADDRESS) + --db-address=DB-ADDRESS result db address ($CS_DB_ADDRESS) + --db-user=DB-USER result db user ($CS_DB_USER) + --db-password=DB-PASSWORD result db password ($CS_DB_PASSWORD) + --db-name=DB-NAME result database name ($CS_DB_NAME) +``` + +_--log-console_ 会把日志打印到标准输出, 方便调试. 改成 _--no-log-console_ 禁用 + +# _API_ +## 通用 _API_ +### 获取 _API_ 列表 +`GET /list` + +#### _response_ + +```json +{ + "code": 0, + "data": [ + { + "method": "POST", + "path": "/async/tendb-cluster/shell-echo" + }, + { + "method": "POST", + "path": "/sync/tendb-ha/counter" + }, + ... + { + "method": "POST", + "path": "/async/kill" + }, + { + "method": "POST", + "path": "/async/query" + }, + { + "method": "GET", + "path": "/list" + } + ], + "msg": "" +} +``` + +### 查询异步会话 +`POST /async/query` + +#### 参数 +```json +{ + "session_id": STRING +} +``` +当不传入参数时 `curl -XPOS /async/query` , 会返回所有会话信息 + +#### _response_ +```json +{ + "code": 0, + "data": [ + { + "id": "9e9dee40-2fd8-4226-a462-fadaff2bd2c3", + "message": "", + "error": "unexpected end of JSON input", + "done": true, + "start_at": "2023-08-14T09:16:37.961224+08:00" + }, + ... + ], + "msg": "" +} +``` + +### 结束异步会话 +`POST /async/kill` + +#### 参数 +```json +{ + "session_id": STRING +} +``` + +## 合成 _API_ +每一个任务会自动生成`同步, 异步` _2_ 个 _API_ + +如 +```json + { + "method": "POST", + "path": "/sync/tendb-cluster/shell-echo" + }, + { + "method": "POST", + "path": "/async/tendb-cluster/shell-echo" + }, +``` + +### 参数 +1. 用 _golang_ 实现的任务需要和开发者协商 +2. 由参数文件配置的外部任务接收字符串数组 + +### _response_ +1. 用 _golang_ 实现的任务需要和开发者协商 +2. 外部任务在同步模式下 + ```json + { + "code": 0, # 有错误时为 1 + "data": "hello aaa bbb fasdfas g34efasd", # 最后一行标准输出 + "msg": "" # 最后一行标准错误 + } + ``` +3. 外部任务在异步模式下 + ```json + { + "code": 0, + "data": "a660e652-5f40-4424-981d-eb2ba383d96a", + "msg": "" + } + ``` + +# 会话清理 +状态 `done == true` 的异步会话会被自动清理 + +# 遗留脚本接入 + +1. 目前支持 `python, perl, shell` 脚本 +2. 需要全部放入 _collect_ 文件夹 +3. 脚本需要有可执行权限 +4. 修改 `--external-task-config e.yaml` 指定的文件, 如 `e.yaml` + +## 一些必要的改造 +为了能 +1. 准确捕捉脚本执行状态 +2. 正确记录脚本执行日志 + +最好能做到这么几件事情 +1. 严格区分 `stdout, stderr` 的输出 +2. 正确使用 `exit code` +3. 对于 `bash sh` 脚本, 强烈推荐添加 `set -e` + +同时, 由于计划使用 _mongodb_ 存储结果, 入库部分可能需要改造 + +## _external task config_ +```yaml +- name: demo1 + cluster_type: TendbCluster + language: sh + executable: echo hello + args: ["aaa", "bbb"] + collected: false +- name: demo2 + cluster_type: TendbCluster + language: sh + executable: s.sh + collected: true +- name: demo3 + cluster_type: TendbCluster + language: binary + executable: testbin + collected: true +- name: demopython + cluster_type: sqlsvr + language: binary + executable: aaa + collected: true +- name: demoperl + cluster_type: redis + language: perl + executable: kkk.pl + collected: true +``` + +```golang +type ExternalTask struct { + Name string `yaml:"name" validate:"required"` + ClusterType string `yaml:"cluster_type" validate:"required"` + Language string `yaml:"language" validate:"required,oneof=python python2 python3 perl sh bash binary"` + Executable string `yaml:"executable" validate:"required"` + Args []string `yaml:"args"` + Collected *bool `yaml:"collected" validate:"required"` +} +``` + +* _language_: 指定脚本的实现语言, 决定如何执行脚本 + * 值为 `sh, shell` 时会以 `sh -c $Executable $Args` 方式执行 + * 值为 `binary` 时会直接以 `$Executable $Args` 方式执行, 对于设置了 `Shebang` 的脚本也可以用这种方式执行 + * 其他值会以 `$Language $Executable $Args` 方式执行 +* _executable_: 包含脚本文件名的路径, 可以是绝对路径和相对路径 +* _collected_: 是否统一管理 + * 为 _true_ 时执行的脚本路径是 `collect/$executable`, 所以此时 _executable_ 不能是绝对路径 + * 为 _false_ 时执行的脚本路径就是 `$executable`, 只要在 `$PATH` 能找到就可以 + +## 参数 +有 _3_ 个地方可以传入参数 +1. `$executable` 实际上可以是如 _somescriptpath 1 2 3_ 这样的字符串, 会被切分成两部分 + * _somescriptpath_ 是实际的脚本 + * _["1", "2", "3"]_ 作为参数 +2. `$args` 直接接受字符串数组 +3. 导出的 _http_ 接口可以用 `post` 的方式传入字符串数组 +4. 参数的拼接顺序按上面 _1_ 到 _3_ 的顺序 + +如 +```yaml +- name: demo1 + cluster_type: TendbCluster + language: sh + executable: echo hello + args: ["aaa", "bbb"] + collected: false +``` + +的调用 _url_ 是 `/tendb-cluster/demo1` + +当用 `curl -XPOST http://localhost/tendb-cluster/demo1 -d '["123", "456"]'` 调用时, 实际执行的命令是 + +`sh -c 'echo hello aaa bbb 123 456'` + +# 新增/开发 +新增的定时任务推荐在本工程内用 _go_ 开发 + +1. 定义一个专用的 `struct` 如 `SomeTask` +2. `SomeTask` 必须匿名包含 `pkg.handler.InternalBase` +3. `SomeTask` 必须实现 `pkg.handler.IHandler` +4. 调用 `pkg.handler.addInternalHandler` 注册 + +具体示例可以参考 `dbm-services/common/celery-service/pkg/handler/internalhandler/democounter` + +## 参数 +没有任何强制性要求, 可以随意实现并用 `post` 方法传入 + +# 环境变量 +1. 父进程的所有环境变量会传递到任务所属子进程 +2. [ ] ToDo 确定上报结果的数据库连接串环境变量 + +# 日志 + +1. 所有日志都收集到 _logs_ 目录 +2. 各任务有自己独立的日志文件 `$Name.log` + +# 结果入库 +1. 目前还没有统一接管结果入库的计划 +2. 考虑使用 _mongodb_ 存储结果 +3. 会通过环境变量的方式传入 _mongodb_ 的连接串 \ No newline at end of file diff --git a/dbm-services/common/db-config/assets/migrate.md b/dbm-services/common/db-config/assets/migrate.md index 72848ed50e..91f5d7edac 100644 --- a/dbm-services/common/db-config/assets/migrate.md +++ b/dbm-services/common/db-config/assets/migrate.md @@ -16,7 +16,7 @@ sed -i 's/CREATE TABLE /CREATE TABLE IF NOT EXISTS /g' 000002_create_table.up.sq dbuser=xx dbpass=xxx seqno=10 -namespaces="common es hdfs kafka PredixyTendisplusCluster rediscomm RedisInstance RedisMS tendb tendbcluster tendbha tendbsingle TendisCache TendisplusInstance TendisSSD TendisX TwemproxyRedisInstance TwemproxyTendisplusInstance TwemproxyTendisSSDInstance pulsar influxdb" +namespaces="common es hdfs kafka PredixyTendisplusCluster rediscomm RedisInstance RedisMS tendb tendbcluster tendbha tendbsingle TendisCache TendisplusInstance TendisSSD TendisX TwemproxyRedisInstance TwemproxyTendisplusInstance TwemproxyTendisSSDInstance pulsar influxdb riak" dbname=dbconfig_release exclude_sensitive="(flag_encrypt!=1 or value_default like '{{%')" @@ -33,6 +33,7 @@ DELETE FROM tb_config_name_def WHERE namespace='${namespace}' AND ${exclude_sens let seqno+=1 done +sed -i '/Dump completed on /d' 0000*.sql ``` migrates 文件名前缀一次保持递增 \ No newline at end of file diff --git a/dbm-services/common/db-config/assets/migrations/000010_common_data.up.sql b/dbm-services/common/db-config/assets/migrations/000010_common_data.up.sql index 1b432935ea..33fd2cb346 100644 --- a/dbm-services/common/db-config/assets/migrations/000010_common_data.up.sql +++ b/dbm-services/common/db-config/assets/migrations/000010_common_data.up.sql @@ -25,6 +25,8 @@ -- WHERE: namespace='common' INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (75,'common','actconf','redis','act任务配置','redis相关配置','pub,app',NULL,0,1,0,NULL,0,0,0,'redis相关配置','2022-09-16 14:34:33','2023-03-22 12:08:50',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (210,'common','backup_client','config.toml','','backup base config','plat,app','',1,1,1,'',0,0,0,'备份客户端','2023-03-09 17:40:06','2023-07-20 17:46:29',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (212,'common','backup_client','cosinfo.toml','','backup auth config','plat,app','',1,1,1,'',0,0,0,'备份客户端','2023-03-09 17:40:06','2023-07-20 17:46:33',''); INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (73,'common','osconf','os','操作系统配置',NULL,'plat,app,cluster',NULL,0,1,0,NULL,0,0,0,NULL,'2022-09-02 17:05:43','2023-03-20 21:40:05',''); /*!50112 SET @disable_bulk_load = IF (@is_rocksdb_supported, 'SET SESSION rocksdb_bulk_load = @old_rocksdb_bulk_load', 'SET @dummy_rocksdb_bulk_load = 0') */; /*!50112 PREPARE s FROM @disable_bulk_load */; @@ -37,7 +39,6 @@ INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:02 -- MySQL dump 10.13 Distrib 5.7.20, for Linux (x86_64) -- -- Host: localhost Database: bk_dbconfig @@ -66,6 +67,17 @@ INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (8872,'common','actconf','redis','delete_rate','INT','20000','[1,100000]','RANGE',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-16 12:01:44','2023-03-22 14:24:58',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (8873,'common','actconf','redis','tendisplus_delete_rate','INT','3000','[1,100000]','RANGE',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-16 12:09:00','2023-03-22 14:25:06',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16750,'common','backup_client','config.toml','cfg.file_tag_allowed','STRING','REDIS_BINLOG,INCREMENT_BACKUP,REDIS_FULL,MYSQL_FULL_BACKUP,BINLOG,OSDATA,MONGO_INCR_BACKUP,LOG,ORACLE,OTHER','','',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'If more than this many successive connection requests from a host are interrupted without a successful connection, the server blocks that host from further connections.','2022-04-25 10:00:47','2023-07-20 17:49:58',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16751,'common','backup_client','config.toml','cfg.net_addr','STRING','{{.NetAddr}}','','',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'If more than this many successive connection requests from a host are interrupted without a successful connection, the server blocks that host from further connections.','2022-04-25 10:00:47','2023-07-20 17:49:53',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16747,'common','backup_client','config.toml','coslimit.block_size','INT','100','[0, 9999999]','RANGE',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'If more than this many successive connection requests from a host are interrupted without a successful connection, the server blocks that host from further connections.','2022-04-25 10:00:47','2023-07-20 17:49:57',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16749,'common','backup_client','config.toml','coslimit.local_file_limit','INT','100','[0, 9999999]','RANGE',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'If more than this many successive connection requests from a host are interrupted without a successful connection, the server blocks that host from further connections.','2022-04-25 10:00:47','2023-07-20 17:49:54',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16748,'common','backup_client','config.toml','coslimit.local_total_limit','INT','100','[0, 9999999]','RANGE',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'If more than this many successive connection requests from a host are interrupted without a successful connection, the server blocks that host from further connections.','2022-04-25 10:00:47','2023-07-20 17:49:56',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16756,'common','backup_client','cosinfo.toml','app_attr.bk_biz_id','INT','{{.AppAttr.BkBizId}}','[0, 9999999]','RANGE',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'If more than this many successive connection requests from a host are interrupted without a successful connection, the server blocks that host from further connections.','2022-04-25 10:00:47','2023-07-20 17:49:59',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16757,'common','backup_client','cosinfo.toml','app_attr.bk_cloud_id','INT','{{.AppAttr.BkCloudId}}','[0, 9999999]','RANGE',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'If more than this many successive connection requests from a host are interrupted without a successful connection, the server blocks that host from further connections.','2022-04-25 10:00:47','2023-07-20 17:50:01',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16755,'common','backup_client','cosinfo.toml','cos_auth.bucket_name','STRING','{{.CosAuth.BucketName}}','','',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'If more than this many successive connection requests from a host are interrupted without a successful connection, the server blocks that host from further connections.','2022-04-25 10:00:47','2023-07-20 17:49:47',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16752,'common','backup_client','cosinfo.toml','cos_auth.region','STRING','{{.CosAuth.Region}}','','',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'If more than this many successive connection requests from a host are interrupted without a successful connection, the server blocks that host from further connections.','2022-04-25 10:00:47','2023-07-20 17:49:51',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16753,'common','backup_client','cosinfo.toml','cos_auth.secret_id','STRING','{{.CosAuth.SecretId}}','','',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'If more than this many successive connection requests from a host are interrupted without a successful connection, the server blocks that host from further connections.','2022-04-25 10:00:47','2023-07-20 17:49:50',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16754,'common','backup_client','cosinfo.toml','cos_auth.secret_key','STRING','{{.CosAuth.SecretKey}}','','',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'If more than this many successive connection requests from a host are interrupted without a successful connection, the server blocks that host from further connections.','2022-04-25 10:00:47','2023-07-20 17:49:49',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (8821,'common','osconf','os','user','STRING','mysql',NULL,'',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-02 17:21:18','2022-09-05 15:06:45',0); /*!50112 SET @disable_bulk_load = IF (@is_rocksdb_supported, 'SET SESSION rocksdb_bulk_load = @old_rocksdb_bulk_load', 'SET @dummy_rocksdb_bulk_load = 0') */; /*!50112 PREPARE s FROM @disable_bulk_load */; @@ -78,4 +90,3 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:02 diff --git a/dbm-services/common/db-config/assets/migrations/000011_es_data.up.sql b/dbm-services/common/db-config/assets/migrations/000011_es_data.up.sql index dc65b55d87..32b2f7ad09 100644 --- a/dbm-services/common/db-config/assets/migrations/000011_es_data.up.sql +++ b/dbm-services/common/db-config/assets/migrations/000011_es_data.up.sql @@ -24,7 +24,7 @@ -- -- WHERE: namespace='es' -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (74,'es','dbconf','7.10.2','elasticsearch.yml配置','es配置文件','plat,app,cluster','cluster',1,1,0,NULL,5,365,0,'es配置文件','2022-09-05 17:00:13','2023-03-22 12:08:50',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (74,'es','dbconf','7.10.2','elasticsearch.yml配置','ES-7.10','plat,app,cluster','cluster',1,1,0,NULL,5,365,0,'es配置文件','2022-09-05 17:00:13','2023-06-30 17:27:03',''); /*!50112 SET @disable_bulk_load = IF (@is_rocksdb_supported, 'SET SESSION rocksdb_bulk_load = @old_rocksdb_bulk_load', 'SET @dummy_rocksdb_bulk_load = 0') */; /*!50112 PREPARE s FROM @disable_bulk_load */; /*!50112 EXECUTE s */; @@ -36,7 +36,6 @@ INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:02 -- MySQL dump 10.13 Distrib 5.7.20, for Linux (x86_64) -- -- Host: localhost Database: bk_dbconfig @@ -103,4 +102,3 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:02 diff --git a/dbm-services/common/db-config/assets/migrations/000012_hdfs_data.up.sql b/dbm-services/common/db-config/assets/migrations/000012_hdfs_data.up.sql index 0e568933c3..81a6f4c655 100644 --- a/dbm-services/common/db-config/assets/migrations/000012_hdfs_data.up.sql +++ b/dbm-services/common/db-config/assets/migrations/000012_hdfs_data.up.sql @@ -24,12 +24,12 @@ -- -- WHERE: namespace='hdfs' -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (78,'hdfs','core-site','2.6.0-cdh5.4.11-tendataV0.2','core-site配置','core-site配置','plat,app,cluster','cluster',1,1,0,NULL,5,365,0,'core-site配置','2022-09-18 17:08:55','2023-03-22 12:08:50',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (101,'hdfs','dbconf','2.6.0-cdh5.4.11-tendataV0.2','hdfs集群配置','hdfs集群配置','plat,app,cluster','cluster',1,1,0,NULL,5,365,0,'hdfs集群配置','2022-10-18 16:00:03','2023-03-22 12:08:50',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (77,'hdfs','hdfs-site','2.6.0-cdh5.4.11-tendataV0.2','hdfs-site配置','hdfs-site配置','plat,app,cluster','cluster',1,1,0,NULL,5,365,0,'hdfs-site配置','2022-09-18 17:08:55','2023-03-22 12:08:50',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (76,'hdfs','hdfsconf','2.6.0-cdh5.4.11-tendataV0.2','hdfs配置','hdfs配置','plat,app,cluster','cluster',1,1,0,NULL,5,365,0,'hdfs配置','2022-09-18 16:53:04','2023-03-22 12:08:50',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (83,'hdfs','install','2.6.0-cdh5.4.11-tendataV0.2','hdfs安装配置','hdfs安装配置','plat,app,cluster','cluster',1,1,0,NULL,5,365,0,'hdfs安装配置','2022-09-19 17:02:17','2023-03-22 12:08:50',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (79,'hdfs','zoo.cfg','2.6.0-cdh5.4.11-tendataV0.2','zk配置','zk配置','plat,app,cluster','cluster',1,1,0,NULL,5,365,0,'zk配置','2022-09-18 17:08:55','2023-03-22 12:08:50',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (78,'hdfs','core-site','2.6.0-cdh5.4.11-tendataV0.2','core-site配置','core-site.xml','plat,app,cluster','cluster',1,1,0,NULL,5,365,0,'core-site配置','2022-09-18 17:08:55','2023-06-30 17:26:25',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (101,'hdfs','dbconf','2.6.0-cdh5.4.11-tendataV0.2','hdfs集群配置','2.6.0-cdh5.4.11','plat,app,cluster','cluster',1,1,0,NULL,5,365,0,'hdfs集群配置','2022-10-18 16:00:03','2023-06-29 10:36:47',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (77,'hdfs','hdfs-site','2.6.0-cdh5.4.11-tendataV0.2','hdfs-site配置','hdfs-site.xml','plat,app,cluster','cluster',1,1,0,NULL,5,365,0,'hdfs-site配置','2022-09-18 17:08:55','2023-06-30 17:26:19',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (76,'hdfs','hdfsconf','2.6.0-cdh5.4.11-tendataV0.2','hdfs配置','hdfs config','plat,app,cluster','cluster',1,1,0,NULL,5,365,0,'hdfs配置','2022-09-18 16:53:04','2023-06-30 17:26:37',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (83,'hdfs','install','2.6.0-cdh5.4.11-tendataV0.2','hdfs安装配置','2.6.0-cdh5.4.11','plat,app,cluster','cluster',1,1,0,NULL,5,365,0,'hdfs安装配置','2022-09-19 17:02:17','2023-06-29 10:37:49',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (79,'hdfs','zoo.cfg','2.6.0-cdh5.4.11-tendataV0.2','zk配置','zoo.cfg','plat,app,cluster','cluster',1,1,0,NULL,5,365,0,'zk配置','2022-09-18 17:08:55','2023-06-30 17:26:47',''); /*!50112 SET @disable_bulk_load = IF (@is_rocksdb_supported, 'SET SESSION rocksdb_bulk_load = @old_rocksdb_bulk_load', 'SET @dummy_rocksdb_bulk_load = 0') */; /*!50112 PREPARE s FROM @disable_bulk_load */; /*!50112 EXECUTE s */; @@ -41,7 +41,6 @@ INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:02 -- MySQL dump 10.13 Distrib 5.7.20, for Linux (x86_64) -- -- Host: localhost Database: bk_dbconfig @@ -163,4 +162,3 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:02 diff --git a/dbm-services/common/db-config/assets/migrations/000013_kafka_data.up.sql b/dbm-services/common/db-config/assets/migrations/000013_kafka_data.up.sql index fbab25b4b4..86dfeb94da 100644 --- a/dbm-services/common/db-config/assets/migrations/000013_kafka_data.up.sql +++ b/dbm-services/common/db-config/assets/migrations/000013_kafka_data.up.sql @@ -24,7 +24,7 @@ -- -- WHERE: namespace='kafka' -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (84,'kafka','dbconf','2.4.0','kafka配置','kafka配置文件','plat,app,cluster','cluster',1,1,0,NULL,5,365,0,'kafka配置文件','2022-09-20 15:17:36','2023-03-22 12:08:50',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (84,'kafka','dbconf','2.4.0','kafka配置','kafka-2.4','plat,app,cluster','cluster',1,1,0,NULL,5,365,0,'kafka配置文件','2022-09-20 15:17:36','2023-06-30 17:27:00',''); /*!50112 SET @disable_bulk_load = IF (@is_rocksdb_supported, 'SET SESSION rocksdb_bulk_load = @old_rocksdb_bulk_load', 'SET @dummy_rocksdb_bulk_load = 0') */; /*!50112 PREPARE s FROM @disable_bulk_load */; /*!50112 EXECUTE s */; @@ -36,7 +36,6 @@ INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:02 -- MySQL dump 10.13 Distrib 5.7.20, for Linux (x86_64) -- -- Host: localhost Database: bk_dbconfig @@ -66,6 +65,7 @@ INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (8891,'kafka','dbconf','2.4.0','adminUser','STRING','kafka','NULL','',1,0,0,0,1,'kafka','NULL',NULL,-1,NULL,'kafka配置','2022-09-20 15:28:01','2022-11-18 11:30:20',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (12972,'kafka','dbconf','2.4.0','factor','STRING','3','NULL','',1,0,0,0,1,'NULL','NULL','NULL',-1,'NULL','kafka配置','2022-11-24 11:46:20','2022-11-24 11:46:20',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14105,'kafka','dbconf','2.4.0','jmx_port','INT','9999',NULL,'',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,'kafka配置','2023-03-06 01:12:50','2023-03-06 11:27:24',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16877,'kafka','dbconf','2.4.0','no_security','INT','0','NULL','',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,'默认0,表示开启鉴权','2023-08-31 16:21:47','2023-09-04 15:38:49',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (12371,'kafka','dbconf','2.4.0','partition_num','STRING','1','NULL','',1,0,0,0,1,'NULL','NULL','NULL',-1,'NULL','kafka配置','2022-10-10 15:54:23','2022-11-18 11:30:20',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (12366,'kafka','dbconf','2.4.0','port','STRING','9092','NULL','',1,0,0,0,1,'NULL','NULL','NULL',-1,'NULL','kafka配置','2022-10-08 15:26:51','2022-11-18 11:30:20',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (12372,'kafka','dbconf','2.4.0','replication_num','STRING','1','NULL','',1,0,0,0,1,'NULL','NULL','NULL',-1,'NULL','kafka配置','2022-10-10 15:54:23','2022-11-18 11:30:20',0); @@ -83,4 +83,3 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:02 diff --git a/dbm-services/common/db-config/assets/migrations/000014_PredixyTendisplusCluster_data.up.sql b/dbm-services/common/db-config/assets/migrations/000014_PredixyTendisplusCluster_data.up.sql index 143c5b9563..d11a227b19 100644 --- a/dbm-services/common/db-config/assets/migrations/000014_PredixyTendisplusCluster_data.up.sql +++ b/dbm-services/common/db-config/assets/migrations/000014_PredixyTendisplusCluster_data.up.sql @@ -24,14 +24,14 @@ -- -- WHERE: namespace='PredixyTendisplusCluster' -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (99,'PredixyTendisplusCluster','config','backup','配置','备份相关的配置','plat,app,cluster','cluster',1,1,0,'Tendisplus',5,365,0,'备份相关的配置','2022-09-28 12:08:17','2023-03-22 12:08:50',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (121,'PredixyTendisplusCluster','config','binlogbackup','配置','binlog备份相关的配置','pub,app,module,cluster','cluster',1,1,0,'Tendisplus',5,365,0,'binlog备份相关的配置','2022-11-23 19:54:59','2023-03-22 12:08:50',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (120,'PredixyTendisplusCluster','config','fullbackup','配置','全备相关的配置','pub,app,module,cluster','cluster',1,1,0,'Tendisplus',5,365,0,'全备相关的配置','2022-11-23 19:54:59','2023-03-22 12:08:50',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (122,'PredixyTendisplusCluster','config','heartbeat','配置','心跳相关的配置','pub,app,module,cluster','cluster',1,1,0,'Tendisplus',5,365,0,'心跳相关的配置','2022-11-23 19:54:59','2023-03-22 12:08:50',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (100,'PredixyTendisplusCluster','config','monitor','配置','监控相关的配置','plat,app,cluster','cluster',1,1,0,'Tendisplus',5,365,0,'监控相关的配置','2022-09-28 12:08:17','2023-03-22 12:08:50',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (97,'PredixyTendisplusCluster','dbconf','Tendisplus-2.5','redis配置','Tendisplus-2.5的配置文件','plat,app,cluster','cluster',1,1,0,'Tendisplus',5,365,0,'Tendisplus-2.5的配置文件','2022-09-28 12:08:17','2023-03-22 12:08:50',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (202,'PredixyTendisplusCluster','dbconf','Tendisplus-2.6','redis配置','Tendisplus-2.6的配置文件','pub,plat,app,module,cluster','cluster',1,1,0,'Tendisplus',5,365,0,NULL,'2023-03-30 19:17:40','2023-04-07 15:40:18',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (98,'PredixyTendisplusCluster','proxyconf','Predixy-latest','redis配置','predixy配置文件','plat,app,cluster','cluster',1,1,0,'Tendisplus',5,365,0,'predixy配置文件','2022-09-28 12:08:17','2023-03-22 12:08:50',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (99,'PredixyTendisplusCluster','config','backup','备份相关的配置','backup','plat,app,cluster','cluster',1,1,0,'Tendisplus',5,365,0,'备份相关的配置','2022-09-28 12:08:17','2023-06-30 17:28:27',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (121,'PredixyTendisplusCluster','config','binlogbackup','binlog备份相关的配置','binlogbackup','pub,app,module,cluster','cluster',1,1,0,'Tendisplus',5,365,0,'binlog备份相关的配置','2022-11-23 19:54:59','2023-06-30 17:29:56',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (120,'PredixyTendisplusCluster','config','fullbackup','全备相关的配置','fullbackup','pub,app,module,cluster','cluster',1,1,0,'Tendisplus',5,365,0,'全备相关的配置','2022-11-23 19:54:59','2023-06-30 17:29:33',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (122,'PredixyTendisplusCluster','config','heartbeat','心跳相关的配置','heartbeat','pub,app,module,cluster','cluster',1,1,0,'Tendisplus',5,365,0,'心跳相关的配置','2022-11-23 19:54:59','2023-06-30 17:29:29',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (100,'PredixyTendisplusCluster','config','monitor','监控相关的配置','monitor','plat,app,cluster','cluster',1,1,0,'Tendisplus',5,365,0,'监控相关的配置','2022-09-28 12:08:17','2023-06-30 17:28:33',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (97,'PredixyTendisplusCluster','dbconf','Tendisplus-2.5','redis配置','Tendisplus-2.5','plat,app,cluster','cluster',1,1,0,'Tendisplus',5,365,0,'Tendisplus-2.5的配置文件','2022-09-28 12:08:17','2023-06-29 10:33:33',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (202,'PredixyTendisplusCluster','dbconf','Tendisplus-2.6','redis配置','Tendisplus-2.6','pub,plat,app,module,cluster','cluster',1,1,0,'Tendisplus',5,365,0,NULL,'2023-03-30 19:17:40','2023-06-29 10:35:33',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (98,'PredixyTendisplusCluster','proxyconf','Predixy-latest','redis配置','Predixy-latest','plat,app,cluster','cluster',1,1,0,'Tendisplus',5,365,0,'predixy配置文件','2022-09-28 12:08:17','2023-06-29 10:36:57',''); /*!50112 SET @disable_bulk_load = IF (@is_rocksdb_supported, 'SET SESSION rocksdb_bulk_load = @old_rocksdb_bulk_load', 'SET @dummy_rocksdb_bulk_load = 0') */; /*!50112 PREPARE s FROM @disable_bulk_load */; /*!50112 EXECUTE s */; @@ -43,7 +43,6 @@ INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:02 -- MySQL dump 10.13 Distrib 5.7.20, for Linux (x86_64) -- -- Host: localhost Database: bk_dbconfig @@ -184,4 +183,3 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:02 diff --git a/dbm-services/common/db-config/assets/migrations/000015_rediscomm_data.up.sql b/dbm-services/common/db-config/assets/migrations/000015_rediscomm_data.up.sql index 19bbcbb3ba..add40a3c17 100644 --- a/dbm-services/common/db-config/assets/migrations/000015_rediscomm_data.up.sql +++ b/dbm-services/common/db-config/assets/migrations/000015_rediscomm_data.up.sql @@ -24,13 +24,13 @@ -- -- WHERE: namespace='rediscomm' -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (124,'rediscomm','config','base','配置','运行时间','pub,plat,app,module,cluster','cluster',1,1,0,'rediscomm',5,365,0,'运行时间','2022-11-29 14:51:54','2023-04-06 11:59:44',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (126,'rediscomm','config','bigkey','配置','热key相关配置','pub,plat,app,module,cluster','cluster',1,1,0,'rediscomm',5,365,0,'热key相关配置','2022-11-29 14:51:54','2023-04-06 11:59:44',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (124,'rediscomm','config','base','运行时间','base','pub,plat,app,module,cluster','cluster',1,1,0,'rediscomm',5,365,0,'运行时间','2022-11-29 14:51:54','2023-06-30 17:29:26',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (126,'rediscomm','config','bigkey','热key相关配置','bigkey','pub,plat,app,module,cluster','cluster',1,1,0,'rediscomm',5,365,0,'热key相关配置','2022-11-29 14:51:54','2023-06-30 17:29:20',''); INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (171,'rediscomm','config','binlogbackup','配置','binlog备份相关的配置','pub,plat,app,module,cluster','cluster',1,1,0,'Tendisplus',5,365,0,'binlog备份相关的配置','2023-02-28 15:01:19','2023-04-06 11:59:44',''); INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (170,'rediscomm','config','fullbackup','配置','全备相关的配置','pub,plat,app,module,cluster','cluster',1,1,0,'Tendisplus',5,365,0,'全备相关的配置','2023-02-28 15:01:19','2023-04-06 11:59:44',''); INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (172,'rediscomm','config','heartbeat','配置','心跳相关的配置','pub,plat,app,module,cluster','cluster',1,1,0,'Tendisplus',5,365,0,'心跳相关的配置','2023-02-28 15:01:19','2023-04-06 11:59:44',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (125,'rediscomm','config','hotkey','配置','大key相关配置','pub,plat,app,module,cluster','cluster',1,1,0,'rediscomm',5,365,0,'大key相关配置','2022-11-29 14:51:54','2023-04-06 11:59:44',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (127,'rediscomm','config','keymod','配置','key模式相关配置','pub,plat,app,module,cluster','cluster',1,1,0,'rediscomm',5,365,0,'key模式相关配置','2022-11-29 14:51:54','2023-04-06 11:59:44',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (125,'rediscomm','config','hotkey','大key相关配置','hotkey','pub,plat,app,module,cluster','cluster',1,1,0,'rediscomm',5,365,0,'大key相关配置','2022-11-29 14:51:54','2023-06-30 17:29:23',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (127,'rediscomm','config','keymod','key模式相关配置','keymod','pub,plat,app,module,cluster','cluster',1,1,0,'rediscomm',5,365,0,'key模式相关配置','2022-11-29 14:51:54','2023-06-30 17:29:16',''); INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (173,'rediscomm','config','monitor','配置','监控相关的配置','pub,plat,app,module,cluster','cluster',1,1,0,'Tendisplus',5,365,0,'监控相关的配置','2023-02-28 15:01:19','2023-04-06 11:59:44',''); /*!50112 SET @disable_bulk_load = IF (@is_rocksdb_supported, 'SET SESSION rocksdb_bulk_load = @old_rocksdb_bulk_load', 'SET @dummy_rocksdb_bulk_load = 0') */; /*!50112 PREPARE s FROM @disable_bulk_load */; @@ -43,7 +43,6 @@ INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:02 -- MySQL dump 10.13 Distrib 5.7.20, for Linux (x86_64) -- -- Host: localhost Database: bk_dbconfig @@ -106,4 +105,3 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:02 diff --git a/dbm-services/common/db-config/assets/migrations/000016_RedisInstance_data.up.sql b/dbm-services/common/db-config/assets/migrations/000016_RedisInstance_data.up.sql index 129816abb9..b7fe89519e 100644 --- a/dbm-services/common/db-config/assets/migrations/000016_RedisInstance_data.up.sql +++ b/dbm-services/common/db-config/assets/migrations/000016_RedisInstance_data.up.sql @@ -24,7 +24,7 @@ -- -- WHERE: namespace='RedisInstance' -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (62,'RedisInstance','redisconf','Redis-6','Redis参数配置','redis-6版本_参数配置','plat,app,cluster','cluster',1,1,0,NULL,0,0,0,'redis-6版本_参数配置','2022-08-10 11:53:22','2023-03-22 12:08:50',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (62,'RedisInstance','redisconf','Redis-6','Redis参数配置','Redis-6','plat,app,cluster','cluster',1,1,0,NULL,0,0,0,'redis-6版本_参数配置','2022-08-10 11:53:22','2023-06-29 10:33:57',''); /*!50112 SET @disable_bulk_load = IF (@is_rocksdb_supported, 'SET SESSION rocksdb_bulk_load = @old_rocksdb_bulk_load', 'SET @dummy_rocksdb_bulk_load = 0') */; /*!50112 PREPARE s FROM @disable_bulk_load */; /*!50112 EXECUTE s */; @@ -36,7 +36,6 @@ INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:02 -- MySQL dump 10.13 Distrib 5.7.20, for Linux (x86_64) -- -- Host: localhost Database: bk_dbconfig @@ -131,4 +130,3 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:02 diff --git a/dbm-services/common/db-config/assets/migrations/000017_RedisMS_data.up.sql b/dbm-services/common/db-config/assets/migrations/000017_RedisMS_data.up.sql index d06bab156f..caec92d42a 100644 --- a/dbm-services/common/db-config/assets/migrations/000017_RedisMS_data.up.sql +++ b/dbm-services/common/db-config/assets/migrations/000017_RedisMS_data.up.sql @@ -24,8 +24,8 @@ -- -- WHERE: namespace='RedisMS' -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (52,'RedisMS','dbconf','RedisMS-2.8','DB参数配置','Redis主从版(2.8.17)','plat,app,cluster','cluster',1,1,0,NULL,5,365,0,'Redis主从版(2.8.17)','2022-08-02 14:29:01','2023-03-22 12:08:50',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (53,'RedisMS','dbconf','RedisMS-3.2','DB参数配置','Redis主从版(3.2.12)','plat,app,cluster','cluster',1,1,0,NULL,5,365,0,'Redis主从版(3.2.12)','2022-08-02 14:29:01','2023-03-22 12:08:50',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (52,'RedisMS','dbconf','RedisMS-2.8','DB参数配置','RedisMS-2.8','plat,app,cluster','cluster',1,1,0,NULL,5,365,0,'Redis主从版(2.8.17)','2022-08-02 14:29:01','2023-06-29 10:34:24',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (53,'RedisMS','dbconf','RedisMS-3.2','DB参数配置','RedisMS-3.2','plat,app,cluster','cluster',1,1,0,NULL,5,365,0,'Redis主从版(3.2.12)','2022-08-02 14:29:01','2023-06-29 10:34:19',''); /*!50112 SET @disable_bulk_load = IF (@is_rocksdb_supported, 'SET SESSION rocksdb_bulk_load = @old_rocksdb_bulk_load', 'SET @dummy_rocksdb_bulk_load = 0') */; /*!50112 PREPARE s FROM @disable_bulk_load */; /*!50112 EXECUTE s */; @@ -37,7 +37,6 @@ INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:02 -- MySQL dump 10.13 Distrib 5.7.20, for Linux (x86_64) -- -- Host: localhost Database: bk_dbconfig @@ -75,4 +74,3 @@ INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:02 diff --git a/dbm-services/common/db-config/assets/migrations/000018_tendb_data.up.sql b/dbm-services/common/db-config/assets/migrations/000018_tendb_data.up.sql index 66b4f15187..ea63851061 100644 --- a/dbm-services/common/db-config/assets/migrations/000018_tendb_data.up.sql +++ b/dbm-services/common/db-config/assets/migrations/000018_tendb_data.up.sql @@ -39,7 +39,6 @@ INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:02 -- MySQL dump 10.13 Distrib 5.7.20, for Linux (x86_64) -- -- Host: localhost Database: bk_dbconfig @@ -75,26 +74,31 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (8864,'tendb','init_user','mysql#user','yw_user','STRING','yw','','STRING',1,0,1,0,0,NULL,'','',-1,NULL,'','2022-05-23 15:54:15','2022-09-19 13:16:16',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (8860,'tendb','init_user','proxy#user','proxy_admin_user','STRING','proxy','','STRING',1,0,1,0,0,NULL,'','',-1,NULL,'','2022-05-23 15:54:15','2023-03-24 17:30:13',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15825,'tendb','init_user','spider#user','tdbctl_user','STRING','tdbctl','','STRING',1,0,1,0,0,NULL,'','',-1,NULL,'','2023-03-09 17:36:33','2023-03-24 17:30:47',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16465,'tendb','mysql_monitor','items-config.yaml','character-consistency','STRING','{\n \"enable\": true,\n \"schedule\": \"0 0 14 * * 1\",\n \"machine_type\": [\n \"backend\",\n \"remote\"\n ],\n \"role\": [\n \"slave\"\n ]\n}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-06-07 17:44:57',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16471,'tendb','mysql_monitor','items-config.yaml','ctl-replicate','STRING','{\n \"enable\": true,\n \"schedule\": \"@every 1m\",\n \"machine_type\": [\n \"spider\"\n ],\n \"role\": []\n}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-06-07 17:44:57',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16470,'tendb','mysql_monitor','items-config.yaml','engine','STRING','{\n \"enable\": true,\n \"schedule\": \"0 0 12 * * 1\",\n \"machine_type\": [\n \"backend\",\n \"remote\"\n ],\n \"role\": []\n}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-06-07 17:44:57',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16466,'tendb','mysql_monitor','items-config.yaml','ext3-check','STRING','{\n \"enable\": true,\n \"schedule\": \"0 0 16 * * 1\",\n \"machine_type\": [\n \"backend\",\n \"remote\"\n ],\n \"role\": []\n}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-06-07 17:44:57',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16458,'tendb','mysql_monitor','items-config.yaml','master-slave-heartbeat','STRING','{\n \"enable\": true,\n \"schedule\": \"@every 10s\",\n \"machine_type\": [\n \"backend\",\n \"remote\"\n ],\n \"role\": []\n}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-06-07 17:44:57',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16454,'tendb','mysql_monitor','items-config.yaml','mysql-config-diff','STRING','{\n \"enable\": true,\n \"schedule\": \"@every 10m\",\n \"machine_type\": [\n \"backend\",\n \"remote\",\n \"spider\"\n ],\n \"role\": []\n}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-06-07 17:44:57',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16459,'tendb','mysql_monitor','items-config.yaml','mysql-connlog-report','STRING','{\n \"enable\": true,\n \"schedule\": \"0 40 23 * * *\",\n \"machine_type\": [\n \"backend\",\n \"remote\",\n \"spider\"\n ],\n \"role\": []\n}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-06-07 17:44:57',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16457,'tendb','mysql_monitor','items-config.yaml','mysql-connlog-rotate','STRING','{\n \"enable\": true,\n \"schedule\": \"0 30 23 * * *\",\n \"machine_type\": [\n \"backend\",\n \"remote\",\n \"spider\"\n ],\n \"role\": []\n}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-06-07 17:44:57',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16467,'tendb','mysql_monitor','items-config.yaml','mysql-connlog-size','STRING','{\n \"enable\": true,\n \"schedule\": \"0 0 12 * * *\",\n \"machine_type\": [\n \"backend\",\n \"remote\",\n \"spider\"\n ],\n \"role\": []\n}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-06-07 17:44:57',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16451,'tendb','mysql_monitor','items-config.yaml','mysql-err-critical','STRING','{\n \"enable\": true,\n \"schedule\": \"@every 1m\",\n \"machine_type\": [\n \"backend\",\n \"remote\",\n \"spider\"\n ],\n \"role\": []\n}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-06-07 17:44:57',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16455,'tendb','mysql_monitor','items-config.yaml','mysql-err-notice','STRING','{\n \"enable\": true,\n \"schedule\": \"@every 1m\",\n \"machine_type\": [\n \"backend\",\n \"remote\",\n \"spider\"\n ],\n \"role\": []\n}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-06-07 17:44:57',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16460,'tendb','mysql_monitor','items-config.yaml','mysql-inject','STRING','{\n \"enable\": true,\n \"schedule\": \"@every 1m\",\n \"machine_type\": [\n \"backend\",\n \"remote\",\n \"spider\"\n ],\n \"role\": []\n}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-06-07 17:44:57',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16461,'tendb','mysql_monitor','items-config.yaml','mysql-lock','STRING','{\n \"enable\": true,\n \"schedule\": \"@every 1m\",\n \"machine_type\": [\n \"backend\",\n \"remote\",\n \"spider\"\n ],\n \"role\": []\n}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-06-07 17:44:57',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16462,'tendb','mysql_monitor','items-config.yaml','proxy-backend','STRING','{\n \"enable\": true,\n \"schedule\": \"0 0 14 * * 1\",\n \"machine_type\": [\n \"proxy\"\n ],\n \"role\": []\n}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-06-07 17:44:57',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16450,'tendb','mysql_monitor','items-config.yaml','proxy-user-list','STRING','{\n \"enable\": true,\n \"schedule\": \"0 0 14 * * 1\",\n \"machine_type\": [\n \"proxy\"\n ],\n \"role\": []\n}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-06-07 17:44:57',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16453,'tendb','mysql_monitor','items-config.yaml','rotate-slowlog','STRING','{\n \"enable\": true,\n \"schedule\": \"0 55 23 * * *\",\n \"machine_type\": [\n \"backend\",\n \"remote\",\n \"spider\"\n ],\n \"role\": []\n}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-06-07 17:44:57',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16469,'tendb','mysql_monitor','items-config.yaml','routine-definer','STRING','{\n \"enable\": true,\n \"schedule\": \"0 0 15 * * 1\",\n \"machine_type\": [\n \"backend\",\n \"remote\"\n ],\n \"role\": []\n}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-06-07 17:44:57',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16463,'tendb','mysql_monitor','items-config.yaml','slave-status','STRING','{\n \"enable\": true,\n \"schedule\": \"@every 1m\",\n \"machine_type\": [\n \"backend\",\n \"remote\"\n ],\n \"role\": [\n \"repeater\",\n \"slave\"\n ]\n}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-06-07 17:44:57',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16456,'tendb','mysql_monitor','items-config.yaml','trigger-definer','STRING','{\n \"enable\": true,\n \"schedule\": \"0 0 15 * * 1\",\n \"machine_type\": [\n \"backend\",\n \"remote\"\n ],\n \"role\": []\n}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-06-07 17:44:57',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16464,'tendb','mysql_monitor','items-config.yaml','view-definer','STRING','{\n \"enable\": true,\n \"schedule\": \"0 0 15 * * 1\",\n \"machine_type\": [\n \"backend\",\n \"remote\"\n ],\n \"role\": []\n}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-06-07 17:44:57',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16571,'tendb','mysql_monitor','items-config.yaml','character-consistency','STRING','{\"enable\":true,\"schedule\":\"0 0 14 * * 1\",\"machine_type\":[\"single\",\"backend\",\"remote\",\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:19:36','2023-06-30 17:19:36',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16594,'tendb','mysql_monitor','items-config.yaml','ctl-replicate','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:19:36','2023-06-30 17:19:36',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16575,'tendb','mysql_monitor','items-config.yaml','engine','STRING','{\"enable\":true,\"schedule\":\"0 0 12 * * *\",\"machine_type\":[\"single\",\"backend\",\"remote\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:19:36','2023-06-30 17:19:36',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16576,'tendb','mysql_monitor','items-config.yaml','ext3-check','STRING','{\"enable\":true,\"schedule\":\"0 0 16 * * 1\",\"machine_type\":[\"single\",\"backend\",\"remote\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:19:36','2023-06-30 17:19:36',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16577,'tendb','mysql_monitor','items-config.yaml','ibd-statistic','STRING','{\"enable\":true,\"schedule\":\"0 0 14 * * 1\",\"machine_type\":[\"single\",\"backend\",\"remote\"],\"role\":[\"slave\"]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:19:36','2023-06-30 17:19:36',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16578,'tendb','mysql_monitor','items-config.yaml','master-slave-heartbeat','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"backend\",\"remote\"],\"role\":[\"master\",\"repeater\",\"slave\"]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:19:36','2023-07-28 14:29:33',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16579,'tendb','mysql_monitor','items-config.yaml','mysql-config-diff','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"single\",\"backend\",\"remote\",\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:19:36','2023-06-30 17:19:36',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16582,'tendb','mysql_monitor','items-config.yaml','mysql-connlog-report','STRING','{\"enable\":true,\"schedule\":\"0 40 23 * * *\",\"machine_type\":[\"single\",\"backend\",\"remote\",\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:19:36','2023-06-30 17:19:36',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16581,'tendb','mysql_monitor','items-config.yaml','mysql-connlog-rotate','STRING','{\"enable\":true,\"schedule\":\"0 30 23 * * *\",\"machine_type\":[\"single\",\"backend\",\"remote\",\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:19:36','2023-06-30 17:19:36',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16580,'tendb','mysql_monitor','items-config.yaml','mysql-connlog-size','STRING','{\"enable\":true,\"schedule\":\"0 0 12 * * *\",\"machine_type\":[\"single\",\"backend\",\"remote\",\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:19:36','2023-06-30 17:19:36',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16584,'tendb','mysql_monitor','items-config.yaml','mysql-err-critical','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"single\",\"backend\",\"remote\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:19:36','2023-06-30 17:19:36',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16583,'tendb','mysql_monitor','items-config.yaml','mysql-err-notice','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"single\",\"backend\",\"remote\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:19:36','2023-06-30 17:19:36',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16589,'tendb','mysql_monitor','items-config.yaml','mysql-inject','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"single\",\"backend\",\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:19:36','2023-06-30 17:19:36',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16588,'tendb','mysql_monitor','items-config.yaml','mysql-lock','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"single\",\"backend\",\"remote\",\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:19:36','2023-06-30 17:19:36',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16590,'tendb','mysql_monitor','items-config.yaml','proxy-backend','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"proxy\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:19:36','2023-06-30 17:19:36',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16591,'tendb','mysql_monitor','items-config.yaml','proxy-user-list','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"proxy\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:19:36','2023-06-30 17:19:36',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16592,'tendb','mysql_monitor','items-config.yaml','rotate-slowlog','STRING','{\"enable\":true,\"schedule\":\"0 55 23 * * *\",\"machine_type\":[\"single\",\"backend\",\"remote\",\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:19:36','2023-06-30 17:19:36',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16572,'tendb','mysql_monitor','items-config.yaml','routine-definer','STRING','{\"enable\":true,\"schedule\":\"0 0 15 * * 1\",\"machine_type\":[\"single\",\"backend\",\"remote\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:19:36','2023-06-30 17:19:36',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16593,'tendb','mysql_monitor','items-config.yaml','slave-status','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"backend\",\"remote\"],\"role\":[\"slave\",\"repeater\"]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:19:36','2023-06-30 17:19:36',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16587,'tendb','mysql_monitor','items-config.yaml','spider-err-critical','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:19:36','2023-06-30 17:19:36',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16585,'tendb','mysql_monitor','items-config.yaml','spider-err-notice','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:19:36','2023-06-30 17:19:36',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16586,'tendb','mysql_monitor','items-config.yaml','spider-err-warn','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:19:36','2023-06-30 17:19:36',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16595,'tendb','mysql_monitor','items-config.yaml','spider-remote','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:19:36','2023-06-30 17:19:36',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16574,'tendb','mysql_monitor','items-config.yaml','trigger-definer','STRING','{\"enable\":true,\"schedule\":\"0 0 15 * * 1\",\"machine_type\":[\"single\",\"backend\",\"remote\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:19:36','2023-06-30 17:19:36',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16573,'tendb','mysql_monitor','items-config.yaml','view-definer','STRING','{\"enable\":true,\"schedule\":\"0 0 15 * * 1\",\"machine_type\":[\"single\",\"backend\",\"remote\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:19:36','2023-06-30 17:19:36',0); /*!50112 SET @disable_bulk_load = IF (@is_rocksdb_supported, 'SET SESSION rocksdb_bulk_load = @old_rocksdb_bulk_load', 'SET @dummy_rocksdb_bulk_load = 0') */; /*!50112 PREPARE s FROM @disable_bulk_load */; /*!50112 EXECUTE s */; @@ -106,4 +110,3 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:02 diff --git a/dbm-services/common/db-config/assets/migrations/000019_tendbcluster_data.up.sql b/dbm-services/common/db-config/assets/migrations/000019_tendbcluster_data.up.sql index d10aec1b89..f580b7a9a6 100644 --- a/dbm-services/common/db-config/assets/migrations/000019_tendbcluster_data.up.sql +++ b/dbm-services/common/db-config/assets/migrations/000019_tendbcluster_data.up.sql @@ -29,11 +29,11 @@ INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (189,'tendbcluster','backup','dbbackup.options','备份控制选项','dbbackup.ini控制选项','plat,app,module,cluster','',1,1,0,'',0,0,0,'dbbackup.ini控制选项','2023-03-09 17:40:06','2023-03-22 12:08:50',''); INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (190,'tendbcluster','checksum','checksum.option','checksum控制选项','checksum.option','plat,app,module,cluster','',1,1,0,'',0,0,0,'checksum.option','2023-03-09 17:40:06','2023-03-22 12:08:50',''); INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (191,'tendbcluster','checksum','checksum.yaml','checksum配置','checksum.yaml','plat,app,module,cluster','',1,1,0,'',0,0,0,'checksum.yaml','2023-03-09 17:40:06','2023-03-22 12:08:50',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (145,'tendbcluster','dbconf','MySQL-5.6','my.cnf配置','5.6_参数配置','plat,app,module,cluster','cluster',1,1,0,'',0,0,0,'5.6_参数配置','2022-04-25 10:19:22','2023-03-22 12:08:50',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (144,'tendbcluster','dbconf','MySQL-5.7','my.cnf配置','5.7_参数配置','plat,app,module,cluster','cluster',1,1,0,NULL,5,365,0,'5.7配置','2022-04-25 10:19:22','2023-03-28 21:40:30',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (143,'tendbcluster','dbconf','MySQL-8.0','','8.0_参数配置','plat,app,module,cluster','cluster',1,1,0,'',0,0,0,'MySQL8.0配置','2022-06-02 17:27:34','2023-03-28 21:40:19',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (147,'tendbcluster','dbconf','Spider-1','my.cnf配置','Spider 1.x 接入层','plat,app,module,cluster','cluster',1,1,0,NULL,5,365,0,'Spider 1.x 接入层','2022-04-25 10:19:22','2023-03-22 12:08:50',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (148,'tendbcluster','dbconf','Spider-3','my.cnf配置','Spider 3.x 接入层','plat,app,module,cluster','cluster',1,1,0,NULL,5,365,0,'Spider 3.x 接入层','2022-04-25 10:19:22','2023-03-22 12:08:50',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (145,'tendbcluster','dbconf','MySQL-5.6','my.cnf配置','MySQL-5.7','plat,app,module,cluster','cluster',1,1,0,'',0,0,0,'5.6_参数配置','2022-04-25 10:19:22','2023-06-29 10:32:57',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (144,'tendbcluster','dbconf','MySQL-5.7','my.cnf配置','MySQL-5.7','plat,app,module,cluster','cluster',1,1,0,NULL,5,365,0,'5.7配置','2022-04-25 10:19:22','2023-06-29 10:33:14',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (143,'tendbcluster','dbconf','MySQL-8.0','','MySQL-8.0','plat,app,module,cluster','cluster',1,1,0,'',0,0,0,'MySQL8.0配置','2022-06-02 17:27:34','2023-06-29 10:32:50',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (147,'tendbcluster','dbconf','Spider-1','my.cnf配置','Spider 1.x','plat,app,module,cluster','cluster',1,1,0,NULL,5,365,0,'Spider 1.x 接入层','2022-04-25 10:19:22','2023-06-29 10:33:02',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (148,'tendbcluster','dbconf','Spider-3','my.cnf配置','Spider 3.x','plat,app,module,cluster','cluster',1,1,0,NULL,5,365,0,'Spider 3.x 接入层','2022-04-25 10:19:22','2023-06-29 10:33:05',''); INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (177,'tendbcluster','dbconf','Tdbctl','my.cnf配置','tdbctl中控配置','plat,app,module,cluster','cluster',1,1,0,NULL,5,365,0,'tdbctl中控配置','2022-04-25 10:19:22','2023-05-10 19:35:47',''); INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (192,'tendbcluster','deploy','deploy_info','部署配置',NULL,'plat,app,module,cluster','',0,1,0,NULL,5,365,0,NULL,'2023-03-09 17:40:06','2023-03-20 21:40:05',''); INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (194,'tendbcluster','sys','sysfile','系统配置',NULL,'plat','',1,1,0,NULL,5,365,0,NULL,'2023-03-09 17:40:06','2023-03-20 21:40:05',''); @@ -48,7 +48,6 @@ INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:02 -- MySQL dump 10.13 Distrib 5.7.20, for Linux (x86_64) -- -- Host: localhost Database: bk_dbconfig @@ -75,11 +74,11 @@ INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` -- -- WHERE: namespace='tendbcluster' AND (flag_encrypt!=1 or value_default like '{{%') -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15918,'tendbcluster','backup','binlog_rotate.yaml','backup_client.cos','STRING','{\n \"enable\": true,\n \"with_md5\": true,\n \"file_tag\": \"INCREMENT_BACKUP\",\n \"tool_path\": \"cos-client\"\n}','','MAP',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2022-04-25 10:00:47','2023-03-22 12:23:33',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15918,'tendbcluster','backup','binlog_rotate.yaml','backup_client.cos','STRING','{\n \"enable\": true,\n \"with_md5\": true,\n \"file_tag\": \"INCREMENT_BACKUP\",\n \"tool_path\": \"/usr/local/backup_client/bin/backup_client\"\n}','','MAP',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2022-04-25 10:00:47','2023-09-06 10:53:00',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14143,'tendbcluster','backup','binlog_rotate.yaml','backup_client.ibs','STRING','{\n \"enable\": false,\n \"ibs_mode\": \"hdfs\",\n \"with_md5\": true,\n \"file_tag\": \"INCREMENT_BACKUP\",\n \"tool_path\": \"backup_client\"\n}','','MAP',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'','2023-03-09 17:36:33','2023-04-13 21:58:11',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14144,'tendbcluster','backup','binlog_rotate.yaml','crond.api_url','STRING','http://127.0.0.1:9999','','STRING',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'','2023-03-09 17:36:33','2023-03-22 12:27:39',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14145,'tendbcluster','backup','binlog_rotate.yaml','crond.command','STRING','cd /home/mysql/rotate_binlog && ./rotatebinlog -c config.yaml','','STRING',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'','2023-03-09 17:36:33','2023-03-22 12:27:40',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14146,'tendbcluster','backup','binlog_rotate.yaml','crond.item_name','STRING','rotate_binlog','','STRING',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'','2023-03-09 17:36:33','2023-03-22 12:27:41',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14145,'tendbcluster','backup','binlog_rotate.yaml','crond.command','STRING','cd /home/mysql/mysql-rotatebinlog && ./rotatebinlog -c config.yaml','','STRING',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'','2023-03-09 17:36:33','2023-07-04 22:06:05',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14146,'tendbcluster','backup','binlog_rotate.yaml','crond.item_name','STRING','mysql-rotatebinlog','','STRING',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'','2023-03-09 17:36:33','2023-07-04 22:16:02',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14147,'tendbcluster','backup','binlog_rotate.yaml','crond.schedule','STRING','*/5 * * * *','','STRING',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'','2023-03-09 17:36:33','2023-03-22 12:27:41',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14148,'tendbcluster','backup','binlog_rotate.yaml','encrypt.enable','BOOL','false','true | false','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'','2023-03-09 17:36:33','2023-03-22 12:30:40',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14150,'tendbcluster','backup','binlog_rotate.yaml','public.keep_policy','STRING','most','most | least','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'','2023-03-09 17:36:33','2023-03-22 12:27:45',0); @@ -87,15 +86,16 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14152,'tendbcluster','backup','binlog_rotate.yaml','public.max_disk_used_pct','INT','80','[1,99]','RANGE',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'','2023-03-09 17:36:33','2023-03-22 12:27:46',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14153,'tendbcluster','backup','binlog_rotate.yaml','public.max_keep_duration','STRING','61d','','DURATION',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'','2023-03-09 17:36:33','2023-03-22 12:27:47',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14154,'tendbcluster','backup','binlog_rotate.yaml','public.purge_interval','STRING','4h','','DURATION',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'','2023-03-09 17:36:33','2023-03-22 12:27:48',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14155,'tendbcluster','backup','binlog_rotate.yaml','public.rotate_interval','STRING','10m','','STRING',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'','2023-03-09 17:36:33','2023-03-22 12:27:49',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14155,'tendbcluster','backup','binlog_rotate.yaml','public.rotate_interval','STRING','20m','','STRING',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'','2023-03-09 17:36:33','2023-07-12 19:52:36',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14156,'tendbcluster','backup','binlog_rotate.yaml','report.enable','BOOL','true','true | false','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'','2023-03-09 17:36:33','2023-03-22 12:27:50',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14157,'tendbcluster','backup','binlog_rotate.yaml','report.filepath','STRING','/home/mysql/dbareport/mysql/binlog','','STRING',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'','2023-03-09 17:36:33','2023-03-22 12:27:51',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14158,'tendbcluster','backup','binlog_rotate.yaml','report.log_maxage','INT','30','[1, 60]','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'','2023-03-09 17:36:33','2023-03-22 12:27:52',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14159,'tendbcluster','backup','binlog_rotate.yaml','report.log_maxbackups','INT','10','[1, 30]','RANGE',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'','2023-03-09 17:36:33','2023-03-22 12:27:53',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14160,'tendbcluster','backup','binlog_rotate.yaml','report.log_maxsize','INT','5','[1, 10]','RANGE',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'','2023-03-09 17:36:33','2023-03-22 12:27:54',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14161,'tendbcluster','backup','dbbackup.ini','BackupClient.DoChecksum','STRING','true','true | false','ENUM',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-09 17:36:33','2023-03-22 12:23:33',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16933,'tendbcluster','backup','dbbackup.ini','BackupClient.Enable','STRING','true','true | false','ENUM',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-09-06 11:11:15',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14162,'tendbcluster','backup','dbbackup.ini','BackupClient.FileTag','STRING','MYSQL_FULL_BACKUP','','STRING',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-09 17:36:33','2023-03-22 12:23:33',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14163,'tendbcluster','backup','dbbackup.ini','BackupClient.RemoteFileSystem','STRING','hdfs','hdfs | cos','ENUM',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-09 17:36:33','2023-03-22 12:23:33',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14163,'tendbcluster','backup','dbbackup.ini','BackupClient.RemoteFileSystem','STRING','cos','hdfs | cos','ENUM',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-09 17:36:33','2023-09-06 11:11:31',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16361,'tendbcluster','backup','dbbackup.ini','LogicalBackup.ChunkFilesize','INT','2048','[512, 9999999]','RANGE',1,0,0,0,0,NULL,'','',-1,NULL,'MB','2022-05-26 20:11:23','2023-05-24 21:40:03',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16347,'tendbcluster','backup','dbbackup.ini','LogicalBackup.DefaultsFile','STRING','','','STRING',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-05-25 09:50:12',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16358,'tendbcluster','backup','dbbackup.ini','LogicalBackup.DisableCompress','STRING','false','false | true','BOOL',2,0,0,0,0,NULL,'','',-1,NULL,'','2023-05-24 21:45:24','2023-05-24 21:45:24',0); @@ -103,6 +103,8 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14164,'tendbcluster','backup','dbbackup.ini','LogicalBackup.FlushRetryCount','INT','3','','INT',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-09 17:36:33','2023-03-22 12:23:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14168,'tendbcluster','backup','dbbackup.ini','LogicalBackup.Regex','STRING','{{.LogicalBackup.Regex}}','','STRING',2,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-09 17:36:33','2023-04-17 17:10:41',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16359,'tendbcluster','backup','dbbackup.ini','LogicalBackup.Threads','INT','4','','INT',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-05-24 21:42:40',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (17761,'tendbcluster','backup','dbbackup.ini','LogicalLoad.CreateTableIfNotExists','STRING','false','false | true','BOOL',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-05-24 22:01:31',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (17764,'tendbcluster','backup','dbbackup.ini','LogicalLoad.DBListDropIfExists','STRING','','','STRING',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-09-07 21:05:47',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16349,'tendbcluster','backup','dbbackup.ini','LogicalLoad.EnableBinlog','STRING','false','false | true','BOOL',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-05-24 22:01:31',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16357,'tendbcluster','backup','dbbackup.ini','LogicalLoad.ExtraOpt','STRING','','','',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-05-24 21:46:16',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14170,'tendbcluster','backup','dbbackup.ini','LogicalLoad.IndexFilePath','STRING','/data/dbbak/xxxxx','','STRING',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-09 17:36:33','2023-03-22 12:23:33',0); @@ -144,8 +146,9 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14194,'tendbcluster','backup','dbbackup.ini','Public.MysqlUser','STRING','{{.Public.MysqlUser}}','','STRING',2,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-09 17:36:33','2023-04-17 17:10:41',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14195,'tendbcluster','backup','dbbackup.ini','Public.OldFileLeftDay','INT','2','','STRING',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-09 17:36:33','2023-03-22 12:23:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14196,'tendbcluster','backup','dbbackup.ini','Public.ResultReportPath','STRING','/home/mysql/dbareport/mysql/dbbackup/result','result log dir','STRING',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-09 17:36:33','2023-03-22 12:23:33',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16875,'tendbcluster','backup','dbbackup.ini','Public.ShardValue','INT','{{.Public.ShardValue}}','[0, 1024]','RANGE',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-09 17:36:33','2023-08-31 11:16:38',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14197,'tendbcluster','backup','dbbackup.ini','Public.StatusReportPath','STRING','/home/mysql/dbareport/mysql/dbbackup/status','status log dir','STRING',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-09 17:36:33','2023-03-22 12:23:33',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16360,'tendbcluster','backup','dbbackup.ini','Public.TarSizeThreshold','INT','8196','[128, 9999999]','RANGE',1,0,0,0,0,NULL,'','',-1,NULL,'MB','2022-05-26 20:11:23','2023-05-24 21:40:10',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16360,'tendbcluster','backup','dbbackup.ini','Public.TarSizeThreshold','INT','8192','[128, 9999999]','RANGE',1,0,0,0,0,NULL,'','',-1,NULL,'MB','2022-05-26 20:11:23','2023-07-13 10:09:41',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14199,'tendbcluster','backup','dbbackup.options','BackupType','STRING','logical','logical | physical','ENUM',1,0,0,0,0,NULL,'','备份类型',-1,NULL,'','2023-03-09 17:36:33','2023-03-22 12:23:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14200,'tendbcluster','backup','dbbackup.options','CrontabTime','STRING','3 5 * * *','','STRING',1,0,0,0,0,NULL,'','DB备份开始时间',-1,NULL,'','2023-03-09 17:36:33','2023-03-22 12:23:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14201,'tendbcluster','backup','dbbackup.options','Logical.IgnoreDatabases','STRING','mysql,test,infodba_schema,sys','','',1,0,0,0,0,NULL,'','主库备份数据',-1,NULL,'','2023-03-09 17:36:33','2023-03-22 12:23:33',0); @@ -229,6 +232,7 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14281,'tendbcluster','dbconf','MySQL-5.5','mysqld.log_warnings','STRING','0',NULL,'',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-03-09 17:36:33','2023-03-09 17:36:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14282,'tendbcluster','dbconf','MySQL-5.5','mysqld.long_query_time','FLOAT','1','[0,3600]','RANGE',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'If a query takes longer than this many seconds, the server increments the Slow_queries status variable','2023-03-09 17:36:33','2023-03-09 17:36:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14277,'tendbcluster','dbconf','MySQL-5.5','mysqld.loose_log_bin_compress','STRING','OFF','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-03-09 17:36:33','2023-05-11 12:35:45',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14298,'tendbcluster','dbconf','MySQL-5.5','mysqld.loose_query_response_time_stats','STRING','ON','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-03-09 17:36:33','2023-07-17 14:54:40',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14301,'tendbcluster','dbconf','MySQL-5.5','mysqld.loose_relay_log_uncompress','STRING','OFF','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-03-09 17:36:33','2023-05-11 12:35:45',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14283,'tendbcluster','dbconf','MySQL-5.5','mysqld.lower_case_table_names','INT','0','[0,1]','RANGE',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,'If set to 0, table names are stored as specified and comparisons are case sensitive. If set to 1, they are stored in lowercase on disk and comparisons are not case sensitive.','2023-03-09 17:36:33','2023-03-09 17:36:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14284,'tendbcluster','dbconf','MySQL-5.5','mysqld.low_priority_updates','STRING','OFF','ON| OFF ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'If set to true, all INSERT, UPDATE, DELETE, and LOCK TABLE WRITE statements wait until there is no pending SELECT or LOCK TABLE READ on the affected table','2023-03-09 17:36:33','2023-03-09 17:36:33',0); @@ -245,7 +249,6 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14295,'tendbcluster','dbconf','MySQL-5.5','mysqld.query_cache_size','INT','0','[0,104857600]','RANGE',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'The amount of memory allocated for caching query results. By default, the query cache is disabled.','2023-03-09 17:36:33','2023-03-09 17:36:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14296,'tendbcluster','dbconf','MySQL-5.5','mysqld.query_cache_type','STRING','OFF','OFF| ON| DEMAND','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,'Set the query cache type.','2023-03-09 17:36:33','2023-04-17 14:28:58',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14297,'tendbcluster','dbconf','MySQL-5.5','mysqld.query_cache_wlock_invalidate','STRING','OFF','ON| OFF ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Invalidate queries in query cache on LOCK for write','2023-03-09 17:36:33','2023-03-09 17:36:33',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14298,'tendbcluster','dbconf','MySQL-5.5','mysqld.query_response_time_stats','STRING','ON','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-03-09 17:36:33','2023-03-09 17:36:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14299,'tendbcluster','dbconf','MySQL-5.5','mysqld.relay-log','STRING','{{.Mysqld.Datadir}}/relay-log/relay-log.bin',NULL,'',2,0,0,0,1,'{{mysqld.datadir}}/relay-log/relay-log.bin',NULL,NULL,-1,NULL,NULL,'2023-03-09 17:36:33','2023-03-28 18:00:36',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14300,'tendbcluster','dbconf','MySQL-5.5','mysqld.relay_log_recovery','INT','1','1 | 0','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-03-09 17:36:33','2023-04-19 15:10:13',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14302,'tendbcluster','dbconf','MySQL-5.5','mysqld.replicate-wild-ignore-table','STRING','mysql.%,infodba_schema.conn_log',NULL,'STRING',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-03-09 17:36:33','2023-04-19 15:12:54',0); @@ -419,6 +422,7 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14475,'tendbcluster','dbconf','MySQL-5.6','mysqld.log_warnings','STRING','0','0| 1| 2 ','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2023-03-09 17:36:33','2023-03-09 17:36:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14476,'tendbcluster','dbconf','MySQL-5.6','mysqld.long_query_time','FLOAT','1','[0,3600]','RANGE',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'If a query takes longer than this many seconds, the server increments the Slow_queries status variable','2023-03-09 17:36:33','2023-03-09 17:36:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14468,'tendbcluster','dbconf','MySQL-5.6','mysqld.loose_log_bin_compress','STRING','OFF','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-03-09 17:36:33','2023-05-11 12:35:45',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14514,'tendbcluster','dbconf','MySQL-5.6','mysqld.loose_query_response_time_stats','STRING','ON','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-03-09 17:36:33','2023-07-17 14:53:37',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14519,'tendbcluster','dbconf','MySQL-5.6','mysqld.loose_relay_log_uncompress','STRING','OFF','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-03-09 17:36:33','2023-05-11 12:35:45',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14477,'tendbcluster','dbconf','MySQL-5.6','mysqld.lower_case_table_names','INT','0','[0,1]','RANGE',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,'If set to 0, table names are stored as specified and comparisons are case sensitive. If set to 1, they are stored in lowercase on disk and comparisons are not case sensitive.','2023-03-09 17:36:33','2023-03-09 17:36:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14478,'tendbcluster','dbconf','MySQL-5.6','mysqld.low_priority_updates','STRING','OFF','ON| OFF ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'If set to true, all INSERT, UPDATE, DELETE, and LOCK TABLE WRITE statements wait until there is no pending SELECT or LOCK TABLE READ on the affected table','2023-03-09 17:36:33','2023-03-09 17:36:33',0); @@ -457,7 +461,6 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14511,'tendbcluster','dbconf','MySQL-5.6','mysqld.query_cache_type','STRING','OFF','OFF| ON| DEMAND','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,'Set the query cache type.','2023-03-09 17:36:33','2023-04-17 14:28:58',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14512,'tendbcluster','dbconf','MySQL-5.6','mysqld.query_cache_wlock_invalidate','STRING','OFF','ON| OFF ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Invalidate queries in query cache on LOCK for write','2023-03-09 17:36:33','2023-03-09 17:36:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14513,'tendbcluster','dbconf','MySQL-5.6','mysqld.query_prealloc_size','INT','8192','[8192,1048576]','RANGE',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'The size of the persistent buffer used for statement parsing and execution. This buffer is not freed between statements.','2023-03-09 17:36:33','2023-03-09 17:36:33',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14514,'tendbcluster','dbconf','MySQL-5.6','mysqld.query_response_time_stats','STRING','ON','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-03-09 17:36:33','2023-03-09 17:36:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14515,'tendbcluster','dbconf','MySQL-5.6','mysqld.read_buffer_size','INT','262144','[8200,2147479552]','RANGE',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Each thread that does a sequential scan for a MyISAM table allocates a buffer of this size (in bytes) for each table it scans.','2023-03-09 17:36:33','2023-03-09 17:36:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14516,'tendbcluster','dbconf','MySQL-5.6','mysqld.read_rnd_buffer_size','INT','524288','[1,2147483647]','RANGE',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Used for reads from MyISAM tables, and, for any storage engine, for Multi-Range Read optimization.','2023-03-09 17:36:33','2023-03-09 17:36:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14517,'tendbcluster','dbconf','MySQL-5.6','mysqld.relay-log','STRING','{{.Mysqld.Datadir}}/relay-log/relay-log.bin',NULL,'',2,0,0,0,1,'{{mysqld.datadir}}/relay-log/relay-log.bin',NULL,NULL,-1,NULL,NULL,'2023-03-09 17:36:33','2023-03-28 18:00:36',0); @@ -655,6 +658,7 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14713,'tendbcluster','dbconf','MySQL-5.7','mysqld.log_warnings','STRING','0','0| 1| 2 ','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2023-03-09 17:36:33','2023-03-09 17:36:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14714,'tendbcluster','dbconf','MySQL-5.7','mysqld.long_query_time','FLOAT','1','[0,3600]','RANGE',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'If a query takes longer than this many seconds, the server increments the Slow_queries status variable','2023-03-09 17:36:33','2023-03-09 17:36:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14705,'tendbcluster','dbconf','MySQL-5.7','mysqld.loose_log_bin_compress','STRING','OFF','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-03-09 17:36:33','2023-05-11 12:35:45',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14756,'tendbcluster','dbconf','MySQL-5.7','mysqld.loose_query_response_time_stats','STRING','ON','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-03-09 17:36:33','2023-07-17 14:53:25',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14763,'tendbcluster','dbconf','MySQL-5.7','mysqld.loose_relay_log_uncompress','STRING','OFF','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-03-09 17:36:33','2023-05-11 12:35:45',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14715,'tendbcluster','dbconf','MySQL-5.7','mysqld.lower_case_table_names','INT','0','[0,1]','RANGE',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,'If set to 0, table names are stored as specified and comparisons are case sensitive. If set to 1, they are stored in lowercase on disk and comparisons are not case sensitive.','2023-03-09 17:36:33','2023-03-09 17:36:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14716,'tendbcluster','dbconf','MySQL-5.7','mysqld.low_priority_updates','STRING','OFF','ON| OFF ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'If set to true, all INSERT, UPDATE, DELETE, and LOCK TABLE WRITE statements wait until there is no pending SELECT or LOCK TABLE READ on the affected table','2023-03-09 17:36:33','2023-03-09 17:36:33',0); @@ -697,7 +701,6 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14753,'tendbcluster','dbconf','MySQL-5.7','mysqld.query_cache_type','STRING','OFF','OFF| ON| DEMAND','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,'Set the query cache type.','2023-03-09 17:36:33','2023-04-17 14:28:58',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14754,'tendbcluster','dbconf','MySQL-5.7','mysqld.query_cache_wlock_invalidate','STRING','OFF','ON| OFF ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Invalidate queries in query cache on LOCK for write','2023-03-09 17:36:33','2023-03-09 17:36:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14755,'tendbcluster','dbconf','MySQL-5.7','mysqld.query_prealloc_size','INT','8192','[8192,1048576]','RANGE',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'The size of the persistent buffer used for statement parsing and execution. This buffer is not freed between statements.','2023-03-09 17:36:33','2023-03-09 17:36:33',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14756,'tendbcluster','dbconf','MySQL-5.7','mysqld.query_response_time_stats','STRING','ON','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-03-09 17:36:33','2023-03-09 17:36:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14757,'tendbcluster','dbconf','MySQL-5.7','mysqld.range_alloc_block_size','INT','4096','[4096,4294967295]','RANGE',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'The size of blocks that are allocated when doing range optimization.','2023-03-09 17:36:33','2023-03-09 17:36:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14758,'tendbcluster','dbconf','MySQL-5.7','mysqld.range_optimizer_max_mem_size','INT','8388608','[0,17179869184]','RANGE',-1,0,0,0,1,NULL,NULL,NULL,-1,NULL,'The limit on memory consumption for the range optimizer. ','2023-03-09 17:36:33','2023-03-09 17:36:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14759,'tendbcluster','dbconf','MySQL-5.7','mysqld.read_buffer_size','INT','262144','[8200,2147479552]','RANGE',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Each thread that does a sequential scan for a MyISAM table allocates a buffer of this size (in bytes) for each table it scans.','2023-03-09 17:36:33','2023-03-09 17:36:33',0); @@ -899,7 +902,7 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14965,'tendbcluster','dbconf','MySQL-8.0','mysqld.log_bin_trust_function_creators','INT','1','1 | 0','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-03-09 17:36:33','2023-04-19 15:10:13',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14966,'tendbcluster','dbconf','MySQL-8.0','mysqld.log_output','STRING','FILE','FILE| TABLE| NONE ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'The destination or destinations for general query log and slow query log output. ','2023-03-09 17:36:33','2023-03-09 17:36:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14967,'tendbcluster','dbconf','MySQL-8.0','mysqld.log_queries_not_using_indexes','STRING','OFF','ON| OFF ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Whether queries that do not use indexes are logged to the slow query log','2023-03-09 17:36:33','2023-03-09 17:36:33',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14968,'tendbcluster','dbconf','MySQL-8.0','mysqld.log_slave_updates','INT','1','1 | 0','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-03-09 17:36:33','2023-04-19 15:10:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14968,'tendbcluster','dbconf','MySQL-8.0','mysqld.log_slave_updates','INT','1','1 | 0','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-03-09 17:36:33','2023-08-09 11:34:38',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14969,'tendbcluster','dbconf','MySQL-8.0','mysqld.log_slow_admin_statements','STRING','OFF','ON| OFF ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Whether include slow administrative statements in the statements written to the slow query log.','2023-03-09 17:36:33','2023-03-09 17:36:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14970,'tendbcluster','dbconf','MySQL-8.0','mysqld.log_throttle_queries_not_using_indexes','INT','0','[0,4294967295]','RANGE',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'If log_queries_not_using_indexes is enabled, the log_throttle_queries_not_using_indexes variable limits the number of such queries per minute that can be written to the slow query log. ','2023-03-09 17:36:33','2023-03-09 17:36:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14971,'tendbcluster','dbconf','MySQL-8.0','mysqld.log_timestamps','STRING','SYSTEM','UTC| SYSTEM ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'This variable controls the time zone of timestamps in messages written to the error log, and in general query log and slow query log messages written to files.','2023-03-09 17:36:33','2023-03-09 17:36:33',0); @@ -956,10 +959,10 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15028,'tendbcluster','dbconf','MySQL-8.0','mysqld.show_old_temporals','STRING','OFF','ON| OFF ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Whether SHOW CREATE TABLE output includes comments to flag temporal columns found to be in pre-5.6.4 format (TIME, DATETIME, and TIMESTAMP columns without support for fractional seconds precision).','2023-03-09 17:36:33','2023-03-09 17:36:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15029,'tendbcluster','dbconf','MySQL-8.0','mysqld.skip-name-resolve','STRING','true','true | false','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-03-09 17:36:33','2023-03-09 18:37:22',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15030,'tendbcluster','dbconf','MySQL-8.0','mysqld.slave_compressed_protocol','INT','1','1 | 0','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-03-09 17:36:33','2023-04-19 15:11:00',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15031,'tendbcluster','dbconf','MySQL-8.0','mysqld.slave_exec_mode','STRING','STRICT','|STRICT|IDEMPOTENT','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-03-09 17:36:33','2023-04-19 15:10:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15031,'tendbcluster','dbconf','MySQL-8.0','mysqld.slave_exec_mode','STRING','STRICT','|STRICT|IDEMPOTENT','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-03-09 17:36:33','2023-08-09 11:34:38',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15032,'tendbcluster','dbconf','MySQL-8.0','mysqld.slave_net_timeout','INT','120','[15,300]','RANGE',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'The number of seconds to wait for more data from the master before the slave considers the connection broken, aborts the read, and tries to reconnect','2023-03-09 17:36:33','2023-03-09 17:36:33',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15033,'tendbcluster','dbconf','MySQL-8.0','mysqld.slave_parallel_type','STRING','DATABASE','DATABASE| LOGICAL_CLOCK','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Specifies if the slave will use database partitioning or information from master to parallelize transactions.(Default: DATABASE).','2023-03-09 17:36:33','2023-04-17 14:55:08',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15034,'tendbcluster','dbconf','MySQL-8.0','mysqld.slave_parallel_workers','INT','4','0| 1| 2| 4| 8| 16| 32| 64','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Enable multithreading on the replica and set the number of application threads used to execute replicated transactions in parallel.','2023-03-09 17:36:33','2023-04-17 14:58:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15033,'tendbcluster','dbconf','MySQL-8.0','mysqld.slave_parallel_type','STRING','LOGICAL_CLOCK','DATABASE| LOGICAL_CLOCK','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Specifies if the slave will use database partitioning or information from master to parallelize transactions.(Default: DATABASE).','2023-03-09 17:36:33','2023-08-09 11:34:38',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15034,'tendbcluster','dbconf','MySQL-8.0','mysqld.slave_parallel_workers','INT','4','0| 1| 2| 4| 8| 16| 32| 64','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Enable multithreading on the replica and set the number of application threads used to execute replicated transactions in parallel.','2023-03-09 17:36:33','2023-08-09 11:34:38',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15035,'tendbcluster','dbconf','MySQL-8.0','mysqld.slave_rows_search_algorithms','STRING','TABLE_SCAN,INDEX_SCAN,HASH_SCAN','TABLE_SCAN,INDEX_SCAN| INDEX_SCAN,HASH_SCAN| TABLE_SCAN,HASH_SCAN| TABLE_SCAN,INDEX_SCAN,HASH_SCAN ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'When preparing batches of rows for row-based logging and replication, this variable controls how the rows are searched for matches.','2023-03-09 17:36:33','2023-03-09 17:36:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15036,'tendbcluster','dbconf','MySQL-8.0','mysqld.slow_launch_time','INT','2','[1,10]','RANGE',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'If creating a thread takes longer than this many seconds, the server increments the Slow_launch_threads status variable','2023-03-09 17:36:33','2023-03-09 17:36:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15037,'tendbcluster','dbconf','MySQL-8.0','mysqld.slow_query_log','STRING','ON','ON | OFF','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Specify the initial slow query log state.','2023-03-09 17:36:33','2023-04-17 14:35:44',0); @@ -1559,6 +1562,7 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15754,'tendbcluster','dbconf','Tdbctl','mysqld.read_rnd_buffer_size','INT','524288','[1,2147483647]','RANGE',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Used for reads from MyISAM tables, and, for any storage engine, for Multi-Range Read optimization.','2023-03-09 17:57:45','2023-05-10 19:35:56',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15755,'tendbcluster','dbconf','Tdbctl','mysqld.relay-log','STRING','{{.Mysqld.Datadir}}/relay-log/relay-log.bin',NULL,'',2,0,0,0,1,'{{mysqld.datadir}}/relay-log/relay-log.bin',NULL,NULL,-1,NULL,NULL,'2023-03-09 17:57:45','2023-05-10 19:35:56',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15756,'tendbcluster','dbconf','Tdbctl','mysqld.relay_log_recovery','INT','1','1 | 0','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-03-09 17:57:45','2023-05-10 19:35:56',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16746,'tendbcluster','dbconf','Tdbctl','mysqld.replicate-wild-ignore-table','STRING','mysql.servers',NULL,'STRING',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-03-09 17:36:33','2023-04-19 15:12:54',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15761,'tendbcluster','dbconf','Tdbctl','mysqld.server_id','STRING','{{.Mysqld.ServerId}}',NULL,'',2,0,0,0,1,'{{mysqld.server_id}}',NULL,NULL,-1,NULL,NULL,'2023-03-09 17:57:45','2023-05-10 19:35:56',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15762,'tendbcluster','dbconf','Tdbctl','mysqld.session_track_gtids','STRING','OFF','OFF| OWN_GTID| ALL_GTIDS ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Whether the server tracks GTIDs within the current session and returns them to the client. ','2023-03-09 17:57:45','2023-05-10 19:35:56',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15763,'tendbcluster','dbconf','Tdbctl','mysqld.session_track_schema','STRING','ON','ON| OFF ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Whether the server tracks changes to the default schema (database) name within the current session and makes this information available to the client when changes occur.','2023-03-09 17:57:45','2023-05-10 19:35:56',0); @@ -1584,7 +1588,7 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15786,'tendbcluster','dbconf','Tdbctl','mysqld.table_open_cache','INT','5120','[1, 524288]','RANGE',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'The number of open tables for all threads. Increasing this value increases the number of file descriptors that mysqld requires.','2023-03-09 17:57:45','2023-05-10 19:35:56',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15787,'tendbcluster','dbconf','Tdbctl','mysqld.table_open_cache_instances','STRING','{MIN(DBInitMemory/1000,16)}','','',-1,0,0,0,1,NULL,NULL,NULL,-1,NULL,'The number of table cache instances','2023-03-09 17:57:45','2023-05-10 19:35:56',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16472,'tendbcluster','dbconf','Tdbctl','mysqld.tc_admin','INT','1','1 | 0','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'When the value is ON, it means that the current node is the master node, allowing the execution of cluster-related DDL statements, management statements','2023-03-09 17:57:45','2023-06-12 10:37:08',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16473,'tendbcluster','dbconf','Tdbctl','mysqld.tc_skip_dump_db_list','STRING','performance_schema,information_schema,mysql,test,infodba_schema',NULL,'',2,0,0,0,1,'{{mysqld.server_id}}',NULL,NULL,-1,NULL,NULL,'2023-03-09 17:57:45','2023-05-10 19:35:56',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16473,'tendbcluster','dbconf','Tdbctl','mysqld.tc_skip_dump_db_list','STRING','performance_schema,information_schema,mysql,test,sys,infodba_schema',NULL,'',2,0,0,0,1,'{{mysqld.server_id}}',NULL,NULL,-1,NULL,NULL,'2023-03-09 17:57:45','2023-06-28 17:33:15',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15788,'tendbcluster','dbconf','Tdbctl','mysqld.thread_cache_size','INT','8','[4,64]','RANGE',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'How many threads we should keep in a cache for reuse','2023-03-09 17:57:45','2023-05-10 19:35:56',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15789,'tendbcluster','dbconf','Tdbctl','mysqld.thread_handling','STRING','one-thread-per-connection','one-thread-per-connection| pool-of-threads ','ENUM',-1,0,0,0,1,NULL,NULL,NULL,-1,NULL,'The thread-handling model used by the server for connection threads.','2023-03-09 17:57:45','2023-05-10 19:35:56',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15790,'tendbcluster','dbconf','Tdbctl','mysqld.thread_pool_oversubscribe','INT','3','[3,32]','RANGE',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Number of additional threads per group of thread pool.','2023-03-09 17:57:45','2023-05-10 19:35:56',0); @@ -1605,23 +1609,31 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16289,'tendbcluster','deploy','deploy_info','spider_version','STRING','Spider-3','Spider-3 | Spider-1','ENUM',1,0,0,0,1,NULL,NULL,'容灾级别',-1,NULL,NULL,'2023-03-09 17:36:33','2023-05-22 17:04:31',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15066,'tendbcluster','deploy','deploy_info','storage_engine','STRING','InnoDB','InnoDB','',1,0,0,0,1,NULL,NULL,'存储引擎',-1,NULL,NULL,'2023-03-09 17:36:33','2023-03-09 17:36:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15067,'tendbcluster','deploy','deploy_info','tolerance_level','STRING','compus','idc|compus|city','ENUM',-1,0,0,0,1,NULL,NULL,'容灾级别',-1,NULL,NULL,'2023-03-09 17:36:33','2023-03-09 17:36:33',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15881,'tendbcluster','mysql_monitor','items-config.yaml','character-consistency','STRING','{\"enable\":true, \"schedule\":\"0 0 14 * * 1\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-22 12:35:54','2023-03-22 12:35:54',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15882,'tendbcluster','mysql_monitor','items-config.yaml','engine','STRING','{\"enable\":true, \"schedule\":\"0 0 12 * * 1\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-22 12:35:54','2023-03-22 12:35:54',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15883,'tendbcluster','mysql_monitor','items-config.yaml','ext3-check','STRING','{\"enable\":true, \"schedule\":\"0 0 16 * * 1\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-22 12:35:54','2023-03-22 12:35:54',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15884,'tendbcluster','mysql_monitor','items-config.yaml','master-slave-heartbeat','STRING','{\"enable\":true, \"schedule\":\"@every 10s\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-22 12:35:54','2023-03-22 12:35:54',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15885,'tendbcluster','mysql_monitor','items-config.yaml','mysql-config-diff','STRING','{\"enable\":true, \"schedule\":\"@every 10m\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-22 12:35:54','2023-03-22 12:35:54',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15886,'tendbcluster','mysql_monitor','items-config.yaml','mysql-connlog-report','STRING','{\"enable\":true, \"schedule\":\"0 40 23 * * *\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-22 12:35:54','2023-03-22 12:35:54',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15887,'tendbcluster','mysql_monitor','items-config.yaml','mysql-connlog-rotate','STRING','{\"enable\":true, \"schedule\":\"0 30 23 * * *\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-22 12:35:54','2023-03-22 12:35:54',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15888,'tendbcluster','mysql_monitor','items-config.yaml','mysql-connlog-size','STRING','{\"enable\":true, \"schedule\":\"0 0 12 * * *\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-22 12:35:54','2023-03-22 12:35:54',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15889,'tendbcluster','mysql_monitor','items-config.yaml','mysql-err-critical','STRING','{\"enable\":true, \"schedule\":\"@every 1m\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-22 12:35:54','2023-03-22 12:35:54',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15890,'tendbcluster','mysql_monitor','items-config.yaml','mysql-err-notice','STRING','{\"enable\":true, \"schedule\":\"@every 1m\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-22 12:35:54','2023-03-22 12:35:54',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15891,'tendbcluster','mysql_monitor','items-config.yaml','mysql-inject','STRING','{\"enable\":true, \"schedule\":\"@every 1m\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-22 12:35:54','2023-03-22 12:35:54',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15892,'tendbcluster','mysql_monitor','items-config.yaml','mysql-lock','STRING','{\"enable\":true, \"schedule\":\"@every 1m\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-22 12:35:54','2023-03-22 12:35:54',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15893,'tendbcluster','mysql_monitor','items-config.yaml','rotate-slowlog','STRING','{\"enable\":true, \"schedule\":\"0 55 23 * * *\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-22 12:35:54','2023-03-22 12:35:54',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15894,'tendbcluster','mysql_monitor','items-config.yaml','routine-definer','STRING','{\"enable\":true, \"schedule\":\"0 0 15 * * 1\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-22 12:35:54','2023-03-22 12:35:54',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15895,'tendbcluster','mysql_monitor','items-config.yaml','slave-status','STRING','{\"enable\":true, \"schedule\":\"@every 1m\", \"machine_type\":\"backend\", \"role\": [\"repeater\", \"slave\"]}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-22 12:35:54','2023-03-22 12:35:54',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15896,'tendbcluster','mysql_monitor','items-config.yaml','trigger-definer','STRING','{\"enable\":true, \"schedule\":\"0 0 15 * * 1\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-22 12:35:54','2023-03-22 12:35:54',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15897,'tendbcluster','mysql_monitor','items-config.yaml','view-definer','STRING','{\"enable\":true, \"schedule\":\"0 0 15 * * 1\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-22 12:35:54','2023-03-22 12:35:54',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16721,'tendbcluster','mysql_monitor','items-config.yaml','character-consistency','STRING','{\"enable\":true,\"schedule\":\"0 0 14 * * 1\",\"machine_type\":[\"single\",\"backend\",\"remote\",\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16744,'tendbcluster','mysql_monitor','items-config.yaml','ctl-replicate','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16725,'tendbcluster','mysql_monitor','items-config.yaml','engine','STRING','{\"enable\":true,\"schedule\":\"0 0 12 * * *\",\"machine_type\":[\"single\",\"backend\",\"remote\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16726,'tendbcluster','mysql_monitor','items-config.yaml','ext3-check','STRING','{\"enable\":true,\"schedule\":\"0 0 16 * * 1\",\"machine_type\":[\"single\",\"backend\",\"remote\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16727,'tendbcluster','mysql_monitor','items-config.yaml','ibd-statistic','STRING','{\"enable\":true,\"schedule\":\"0 0 14 * * 1\",\"machine_type\":[\"single\",\"backend\",\"remote\"],\"role\":[\"slave\"]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16728,'tendbcluster','mysql_monitor','items-config.yaml','master-slave-heartbeat','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"backend\",\"remote\"],\"role\":[\"master\",\"repeater\",\"slave\"]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-07-28 14:29:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16729,'tendbcluster','mysql_monitor','items-config.yaml','mysql-config-diff','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"single\",\"backend\",\"remote\",\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16732,'tendbcluster','mysql_monitor','items-config.yaml','mysql-connlog-report','STRING','{\"enable\":true,\"schedule\":\"0 40 23 * * *\",\"machine_type\":[\"single\",\"backend\",\"remote\",\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16731,'tendbcluster','mysql_monitor','items-config.yaml','mysql-connlog-rotate','STRING','{\"enable\":true,\"schedule\":\"0 30 23 * * *\",\"machine_type\":[\"single\",\"backend\",\"remote\",\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16730,'tendbcluster','mysql_monitor','items-config.yaml','mysql-connlog-size','STRING','{\"enable\":true,\"schedule\":\"0 0 12 * * *\",\"machine_type\":[\"single\",\"backend\",\"remote\",\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16734,'tendbcluster','mysql_monitor','items-config.yaml','mysql-err-critical','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"single\",\"backend\",\"remote\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16733,'tendbcluster','mysql_monitor','items-config.yaml','mysql-err-notice','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"single\",\"backend\",\"remote\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16739,'tendbcluster','mysql_monitor','items-config.yaml','mysql-inject','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"single\",\"backend\",\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16738,'tendbcluster','mysql_monitor','items-config.yaml','mysql-lock','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"single\",\"backend\",\"remote\",\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16740,'tendbcluster','mysql_monitor','items-config.yaml','proxy-backend','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"proxy\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16741,'tendbcluster','mysql_monitor','items-config.yaml','proxy-user-list','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"proxy\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16742,'tendbcluster','mysql_monitor','items-config.yaml','rotate-slowlog','STRING','{\"enable\":true,\"schedule\":\"0 55 23 * * *\",\"machine_type\":[\"single\",\"backend\",\"remote\",\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16722,'tendbcluster','mysql_monitor','items-config.yaml','routine-definer','STRING','{\"enable\":true,\"schedule\":\"0 0 15 * * 1\",\"machine_type\":[\"single\",\"backend\",\"remote\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16743,'tendbcluster','mysql_monitor','items-config.yaml','slave-status','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"backend\",\"remote\"],\"role\":[\"slave\",\"repeater\"]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16737,'tendbcluster','mysql_monitor','items-config.yaml','spider-err-critical','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16735,'tendbcluster','mysql_monitor','items-config.yaml','spider-err-notice','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16736,'tendbcluster','mysql_monitor','items-config.yaml','spider-err-warn','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16745,'tendbcluster','mysql_monitor','items-config.yaml','spider-remote','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16724,'tendbcluster','mysql_monitor','items-config.yaml','trigger-definer','STRING','{\"enable\":true,\"schedule\":\"0 0 15 * * 1\",\"machine_type\":[\"single\",\"backend\",\"remote\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16723,'tendbcluster','mysql_monitor','items-config.yaml','view-definer','STRING','{\"enable\":true,\"schedule\":\"0 0 15 * * 1\",\"machine_type\":[\"single\",\"backend\",\"remote\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); /*!50112 SET @disable_bulk_load = IF (@is_rocksdb_supported, 'SET SESSION rocksdb_bulk_load = @old_rocksdb_bulk_load', 'SET @dummy_rocksdb_bulk_load = 0') */; /*!50112 PREPARE s FROM @disable_bulk_load */; /*!50112 EXECUTE s */; @@ -1633,4 +1645,3 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:02 diff --git a/dbm-services/common/db-config/assets/migrations/000020_tendbha_data.up.sql b/dbm-services/common/db-config/assets/migrations/000020_tendbha_data.up.sql index 47089a9731..d1ca1ac30e 100644 --- a/dbm-services/common/db-config/assets/migrations/000020_tendbha_data.up.sql +++ b/dbm-services/common/db-config/assets/migrations/000020_tendbha_data.up.sql @@ -29,14 +29,15 @@ INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (129,'tendbha','backup','dbbackup.options','备份控制选项','dbbackup.ini控制选项','plat,app,module,cluster','',1,1,0,'',0,0,0,'dbbackup.ini控制选项','2022-04-25 10:19:22','2023-03-22 12:08:50',''); INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (137,'tendbha','checksum','checksum.option','checksum控制选项','checksum.option','plat,app,module,cluster','',1,1,0,'',0,0,0,'checksum.option','2022-04-25 10:19:22','2023-03-22 12:08:50',''); INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (136,'tendbha','checksum','checksum.yaml','checksum配置','checksum.yaml','plat,app,module,cluster','',1,1,0,'',0,0,0,'checksum.yaml','2022-04-25 10:19:22','2023-03-22 12:08:50',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (9,'tendbha','dbconf','MySQL-5.6','my.cnf配置','5.6_参数配置','plat,app,module,cluster','cluster',0,0,0,'',0,0,0,'5.6_参数配置','2022-04-25 10:19:22','2023-03-22 12:08:50',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (10,'tendbha','dbconf','MySQL-5.7','my.cnf配置','5.7_参数配置','plat,app,module,cluster','cluster',1,1,0,NULL,5,365,0,'5.7_参数配置','2022-04-25 10:19:22','2023-03-22 12:08:50',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (40,'tendbha','dbconf','MySQL-8.0','','8.0_参数配置','plat,app,module,cluster','cluster',0,0,0,'',0,0,0,'MySQL8.0配置','2022-06-02 17:27:34','2023-03-28 21:40:07',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (9,'tendbha','dbconf','MySQL-5.6','my.cnf配置','MySQL-5.6','plat,app,module,cluster','cluster',1,1,0,'',0,0,0,'5.6_参数配置','2022-04-25 10:19:22','2023-08-11 13:12:14',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (10,'tendbha','dbconf','MySQL-5.7','my.cnf配置','MySQL-5.7','plat,app,module,cluster','cluster',1,1,0,NULL,5,365,0,'5.7_参数配置','2022-04-25 10:19:22','2023-06-29 10:34:49',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (40,'tendbha','dbconf','MySQL-8.0','','MySQL-8.0','plat,app,module,cluster','cluster',1,1,0,'',0,0,0,'MySQL8.0配置','2022-06-02 17:27:34','2023-08-11 13:12:18',''); INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (6,'tendbha','dbha','dbha','DBHA切换配置',NULL,'plat,app,city,module,cluster','',1,1,0,NULL,5,365,0,NULL,'2022-04-25 10:19:22','2023-03-20 21:40:05',''); INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (4,'tendbha','deploy','deploy_info','部署配置',NULL,'plat,app,module,cluster','',0,1,0,NULL,5,365,0,NULL,'2022-04-25 10:19:22','2023-03-20 21:40:05',''); INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (201,'tendbha','mysql_monitor','items-config.yaml','监控配置',NULL,'plat,app,module,cluster','',1,1,1,NULL,5,365,0,NULL,'2023-03-09 17:40:06','2023-03-20 21:40:05',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (39,'tendbha','proxyconf','default','','mysql-proxy配置','plat','',0,1,0,'',0,0,0,'mysql-proxy配置','2022-04-25 10:19:22','2023-03-22 12:08:50',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (39,'tendbha','proxyconf','default','','mysql-proxy','plat','',0,1,0,'',0,0,0,'mysql-proxy配置','2022-04-25 10:19:22','2023-06-30 17:24:45',''); INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (7,'tendbha','sys','sysfile','系统配置',NULL,'plat','',1,1,0,NULL,5,365,0,NULL,'2022-04-25 10:19:22','2023-03-20 21:40:05',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (213,'tendbha','tbinlogdumper','latest','','dumper config','plat,app,module,cluster','',1,1,0,'',0,0,0,'tbinlogdumper config','2022-06-02 17:27:34','2023-08-11 15:11:40',''); INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (1,'tendbha','user','tb_app_info','用户配置',NULL,'plat,app,module,cluster','',1,1,0,NULL,NULL,NULL,0,NULL,'2022-04-25 10:19:22','2023-03-20 21:40:05',''); /*!50112 SET @disable_bulk_load = IF (@is_rocksdb_supported, 'SET SESSION rocksdb_bulk_load = @old_rocksdb_bulk_load', 'SET @dummy_rocksdb_bulk_load = 0') */; /*!50112 PREPARE s FROM @disable_bulk_load */; @@ -49,7 +50,6 @@ INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:03 -- MySQL dump 10.13 Distrib 5.7.20, for Linux (x86_64) -- -- Host: localhost Database: bk_dbconfig @@ -76,11 +76,11 @@ INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` -- -- WHERE: namespace='tendbha' AND (flag_encrypt!=1 or value_default like '{{%') -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15916,'tendbha','backup','binlog_rotate.yaml','backup_client.cos','STRING','{\n \"enable\": true,\n \"with_md5\": true,\n \"file_tag\": \"INCREMENT_BACKUP\",\n \"tool_path\": \"cos-client\"\n}','','MAP',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2022-04-25 10:00:47','2023-03-22 12:23:33',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15916,'tendbha','backup','binlog_rotate.yaml','backup_client.cos','STRING','{\n \"enable\": true,\n \"with_md5\": true,\n \"file_tag\": \"INCREMENT_BACKUP\",\n \"tool_path\": \"/usr/local/backup_client/bin/backup_client\"\n}','','MAP',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2022-04-25 10:00:47','2023-09-06 10:52:38',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13141,'tendbha','backup','binlog_rotate.yaml','backup_client.ibs','STRING','{\n \"enable\": false,\n \"ibs_mode\": \"hdfs\",\n \"with_md5\": true,\n \"file_tag\": \"INCREMENT_BACKUP\",\n \"tool_path\": \"backup_client\"\n}','','MAP',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2022-04-25 10:00:47','2023-04-13 21:58:00',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14110,'tendbha','backup','binlog_rotate.yaml','crond.api_url','STRING','http://127.0.0.1:9999','','STRING',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2022-04-25 10:00:47','2023-03-22 12:23:33',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14111,'tendbha','backup','binlog_rotate.yaml','crond.command','STRING','cd /home/mysql/rotate_binlog && ./rotatebinlog -c config.yaml','','STRING',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2022-04-25 10:00:47','2023-03-22 12:23:33',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14106,'tendbha','backup','binlog_rotate.yaml','crond.item_name','STRING','rotate_binlog','','STRING',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2022-04-25 10:00:47','2023-03-22 12:23:33',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14111,'tendbha','backup','binlog_rotate.yaml','crond.command','STRING','cd /home/mysql/mysql-rotatebinlog && ./rotatebinlog -c config.yaml','','STRING',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2022-04-25 10:00:47','2023-07-04 22:06:10',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14106,'tendbha','backup','binlog_rotate.yaml','crond.item_name','STRING','mysql-rotatebinlog','','STRING',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2022-04-25 10:00:47','2023-07-04 22:16:16',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14108,'tendbha','backup','binlog_rotate.yaml','crond.schedule','STRING','*/5 * * * *','','STRING',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2022-04-25 10:00:47','2023-03-22 12:23:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13134,'tendbha','backup','binlog_rotate.yaml','encrypt.enable','BOOL','false','true | false','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2022-04-25 10:00:47','2023-03-22 12:30:57',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13119,'tendbha','backup','binlog_rotate.yaml','public.keep_policy','STRING','most','most | least','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2022-04-25 10:00:47','2023-03-22 12:23:33',0); @@ -88,15 +88,16 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13122,'tendbha','backup','binlog_rotate.yaml','public.max_disk_used_pct','INT','80','[1,99]','RANGE',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2022-04-25 10:00:47','2023-03-22 12:23:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13124,'tendbha','backup','binlog_rotate.yaml','public.max_keep_duration','STRING','61d','','DURATION',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2022-04-25 10:00:47','2023-03-22 12:23:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13128,'tendbha','backup','binlog_rotate.yaml','public.purge_interval','STRING','4h','','DURATION',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2022-04-25 10:00:47','2023-03-22 12:23:33',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13158,'tendbha','backup','binlog_rotate.yaml','public.rotate_interval','STRING','10m','','STRING',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2022-04-25 10:00:47','2023-03-22 12:23:33',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13158,'tendbha','backup','binlog_rotate.yaml','public.rotate_interval','STRING','20m','','STRING',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2022-04-25 10:00:47','2023-07-12 19:52:27',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13129,'tendbha','backup','binlog_rotate.yaml','report.enable','BOOL','true','true | false','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2022-04-25 10:00:47','2023-03-22 12:23:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13130,'tendbha','backup','binlog_rotate.yaml','report.filepath','STRING','/home/mysql/dbareport/mysql/binlog','','STRING',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2022-04-25 10:00:47','2023-03-22 12:23:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13133,'tendbha','backup','binlog_rotate.yaml','report.log_maxage','INT','30','[1, 60]','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2022-04-25 10:00:47','2023-03-22 12:23:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13132,'tendbha','backup','binlog_rotate.yaml','report.log_maxbackups','INT','10','[1, 30]','RANGE',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2022-04-25 10:00:47','2023-03-22 12:23:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13131,'tendbha','backup','binlog_rotate.yaml','report.log_maxsize','INT','5','[1, 10]','RANGE',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2022-04-25 10:00:47','2023-03-22 12:23:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13003,'tendbha','backup','dbbackup.ini','BackupClient.DoChecksum','STRING','true','true | false','ENUM',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-03-22 12:23:33',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16931,'tendbha','backup','dbbackup.ini','BackupClient.Enable','STRING','true','true | false','ENUM',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-09-06 11:11:15',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13001,'tendbha','backup','dbbackup.ini','BackupClient.FileTag','STRING','MYSQL_FULL_BACKUP','','STRING',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-03-22 12:23:33',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13002,'tendbha','backup','dbbackup.ini','BackupClient.RemoteFileSystem','STRING','hdfs','hdfs | cos','ENUM',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-03-22 12:23:33',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13002,'tendbha','backup','dbbackup.ini','BackupClient.RemoteFileSystem','STRING','cos','hdfs | cos','ENUM',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-09-06 11:11:35',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13006,'tendbha','backup','dbbackup.ini','LogicalBackup.ChunkFilesize','INT','2048','[512, 9999999]','RANGE',1,0,0,0,0,NULL,'','',-1,NULL,'MB','2022-05-26 20:11:23','2023-05-24 21:40:03',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13010,'tendbha','backup','dbbackup.ini','LogicalBackup.DefaultsFile','STRING','','','STRING',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-05-25 09:50:12',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16311,'tendbha','backup','dbbackup.ini','LogicalBackup.DisableCompress','STRING','false','false | true','BOOL',2,0,0,0,0,NULL,'','',-1,NULL,'','2023-05-24 21:45:24','2023-05-24 21:45:24',0); @@ -104,6 +105,8 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13009,'tendbha','backup','dbbackup.ini','LogicalBackup.FlushRetryCount','INT','3','','INT',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-03-22 12:23:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13007,'tendbha','backup','dbbackup.ini','LogicalBackup.Regex','STRING','{{.LogicalBackup.Regex}}','','STRING',2,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-04-17 17:10:41',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13008,'tendbha','backup','dbbackup.ini','LogicalBackup.Threads','INT','4','','INT',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-05-24 21:42:40',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (17762,'tendbha','backup','dbbackup.ini','LogicalLoad.CreateTableIfNotExists','STRING','false','false | true','BOOL',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-05-24 22:01:31',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (17765,'tendbha','backup','dbbackup.ini','LogicalLoad.DBListDropIfExists','STRING','','','STRING',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-09-07 21:05:51',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13020,'tendbha','backup','dbbackup.ini','LogicalLoad.EnableBinlog','STRING','false','false | true','BOOL',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-05-24 22:01:31',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16321,'tendbha','backup','dbbackup.ini','LogicalLoad.ExtraOpt','STRING','','','',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-05-24 21:46:16',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13157,'tendbha','backup','dbbackup.ini','LogicalLoad.IndexFilePath','STRING','/data/dbbak/xxxxx','','STRING',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-03-22 12:23:33',0); @@ -145,8 +148,9 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (12986,'tendbha','backup','dbbackup.ini','Public.MysqlUser','STRING','{{.Public.MysqlUser}}','','STRING',2,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-04-17 17:10:41',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (12995,'tendbha','backup','dbbackup.ini','Public.OldFileLeftDay','INT','2','','STRING',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-03-22 12:23:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13115,'tendbha','backup','dbbackup.ini','Public.ResultReportPath','STRING','/home/mysql/dbareport/mysql/dbbackup/result','result log dir','STRING',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-03-22 12:23:33',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16876,'tendbha','backup','dbbackup.ini','Public.ShardValue','INT','{{.Public.ShardValue}}','[0, 1024]','RANGE',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-09 17:36:33','2023-08-31 11:16:42',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13116,'tendbha','backup','dbbackup.ini','Public.StatusReportPath','STRING','/home/mysql/dbareport/mysql/dbbackup/status','status log dir','STRING',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-03-22 12:23:33',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (12996,'tendbha','backup','dbbackup.ini','Public.TarSizeThreshold','INT','8196','[128, 9999999]','RANGE',1,0,0,0,0,NULL,'','',-1,NULL,'MB','2022-05-26 20:11:23','2023-05-24 21:40:10',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (12996,'tendbha','backup','dbbackup.ini','Public.TarSizeThreshold','INT','8192','[128, 9999999]','RANGE',1,0,0,0,0,NULL,'','',-1,NULL,'MB','2022-05-26 20:11:23','2023-07-12 20:10:10',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13029,'tendbha','backup','dbbackup.options','BackupType','STRING','logical','logical | physical','ENUM',1,0,0,0,0,NULL,'','备份类型',-1,NULL,'','2022-05-26 20:11:23','2023-03-22 12:23:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13023,'tendbha','backup','dbbackup.options','CrontabTime','STRING','3 5 * * *','','STRING',1,0,0,0,0,NULL,'','DB备份开始时间',-1,NULL,'','2022-05-26 20:11:23','2023-03-22 12:23:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13032,'tendbha','backup','dbbackup.options','Logical.IgnoreDatabases','STRING','mysql,test,infodba_schema,sys','','',1,0,0,0,0,NULL,'','主库备份数据',-1,NULL,'','2022-05-26 20:11:23','2023-03-22 12:23:33',0); @@ -230,6 +234,7 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (236,'tendbha','dbconf','MySQL-5.5','mysqld.log_warnings','STRING','0',NULL,'',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2022-06-01 10:24:12',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (237,'tendbha','dbconf','MySQL-5.5','mysqld.long_query_time','FLOAT','1','[0,3600]','RANGE',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'If a query takes longer than this many seconds, the server increments the Slow_queries status variable','2022-04-25 10:00:47','2022-06-16 22:21:50',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (233,'tendbha','dbconf','MySQL-5.5','mysqld.loose_log_bin_compress','STRING','OFF','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-05-11 12:35:45',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (248,'tendbha','dbconf','MySQL-5.5','mysqld.loose_query_response_time_stats','STRING','ON','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-07-17 14:54:49',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (251,'tendbha','dbconf','MySQL-5.5','mysqld.loose_relay_log_uncompress','STRING','OFF','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-05-11 12:35:45',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (238,'tendbha','dbconf','MySQL-5.5','mysqld.lower_case_table_names','INT','0','[0,1]','RANGE',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,'If set to 0, table names are stored as specified and comparisons are case sensitive. If set to 1, they are stored in lowercase on disk and comparisons are not case sensitive.','2022-04-25 10:00:47','2022-06-16 21:33:04',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (5362,'tendbha','dbconf','MySQL-5.5','mysqld.low_priority_updates','STRING','OFF','ON| OFF ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'If set to true, all INSERT, UPDATE, DELETE, and LOCK TABLE WRITE statements wait until there is no pending SELECT or LOCK TABLE READ on the affected table','2022-06-16 21:39:26','2022-10-20 12:26:09',0); @@ -246,7 +251,6 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (246,'tendbha','dbconf','MySQL-5.5','mysqld.query_cache_size','INT','0','[0,104857600]','RANGE',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'The amount of memory allocated for caching query results. By default, the query cache is disabled.','2022-04-25 10:00:47','2022-06-16 22:21:50',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (247,'tendbha','dbconf','MySQL-5.5','mysqld.query_cache_type','STRING','OFF','OFF| ON| DEMAND','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,'Set the query cache type.','2022-04-25 10:00:47','2023-04-17 14:28:58',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (5366,'tendbha','dbconf','MySQL-5.5','mysqld.query_cache_wlock_invalidate','STRING','OFF','ON| OFF ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Invalidate queries in query cache on LOCK for write','2022-06-16 21:39:26','2022-10-20 12:26:09',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (248,'tendbha','dbconf','MySQL-5.5','mysqld.query_response_time_stats','STRING','ON','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2022-06-01 10:24:12',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (249,'tendbha','dbconf','MySQL-5.5','mysqld.relay-log','STRING','{{.Mysqld.Datadir}}/relay-log/relay-log.bin',NULL,'',2,0,0,0,1,'{{mysqld.datadir}}/relay-log/relay-log.bin',NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-03-28 18:00:36',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (250,'tendbha','dbconf','MySQL-5.5','mysqld.relay_log_recovery','INT','1','1 | 0','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-04-19 15:10:13',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (252,'tendbha','dbconf','MySQL-5.5','mysqld.replicate-wild-ignore-table','STRING','mysql.%,infodba_schema.conn_log',NULL,'STRING',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-04-19 15:12:54',0); @@ -421,6 +425,7 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (109,'tendbha','dbconf','MySQL-5.6','mysqld.log_warnings','STRING','0','0| 1| 2 ','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2022-04-25 10:00:47','2022-06-16 22:21:50',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (110,'tendbha','dbconf','MySQL-5.6','mysqld.long_query_time','FLOAT','1','[0,3600]','RANGE',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'If a query takes longer than this many seconds, the server increments the Slow_queries status variable','2022-04-25 10:00:47','2022-06-16 22:21:50',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (106,'tendbha','dbconf','MySQL-5.6','mysqld.loose_log_bin_compress','STRING','OFF','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-05-11 12:35:45',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (121,'tendbha','dbconf','MySQL-5.6','mysqld.loose_query_response_time_stats','STRING','ON','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-07-17 14:53:54',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (124,'tendbha','dbconf','MySQL-5.6','mysqld.loose_relay_log_uncompress','STRING','OFF','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-05-11 12:35:45',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (111,'tendbha','dbconf','MySQL-5.6','mysqld.lower_case_table_names','INT','0','[0,1]','RANGE',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,'If set to 0, table names are stored as specified and comparisons are case sensitive. If set to 1, they are stored in lowercase on disk and comparisons are not case sensitive.','2022-04-25 10:00:47','2022-06-16 21:32:58',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (5292,'tendbha','dbconf','MySQL-5.6','mysqld.low_priority_updates','STRING','OFF','ON| OFF ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'If set to true, all INSERT, UPDATE, DELETE, and LOCK TABLE WRITE statements wait until there is no pending SELECT or LOCK TABLE READ on the affected table','2022-06-16 21:39:26','2022-10-20 12:26:09',0); @@ -459,7 +464,6 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (120,'tendbha','dbconf','MySQL-5.6','mysqld.query_cache_type','STRING','OFF','OFF| ON| DEMAND','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,'Set the query cache type.','2022-04-25 10:00:47','2023-04-17 14:28:58',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (5317,'tendbha','dbconf','MySQL-5.6','mysqld.query_cache_wlock_invalidate','STRING','OFF','ON| OFF ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Invalidate queries in query cache on LOCK for write','2022-06-16 21:39:26','2022-10-20 12:26:09',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (5318,'tendbha','dbconf','MySQL-5.6','mysqld.query_prealloc_size','INT','8192','[8192,1048576]','RANGE',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'The size of the persistent buffer used for statement parsing and execution. This buffer is not freed between statements.','2022-06-16 21:39:26','2022-10-20 12:26:09',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (121,'tendbha','dbconf','MySQL-5.6','mysqld.query_response_time_stats','STRING','ON','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2022-06-01 09:56:29',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (5319,'tendbha','dbconf','MySQL-5.6','mysqld.read_buffer_size','INT','262144','[8200,2147479552]','RANGE',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Each thread that does a sequential scan for a MyISAM table allocates a buffer of this size (in bytes) for each table it scans.','2022-06-16 21:39:26','2022-10-20 12:26:09',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (5320,'tendbha','dbconf','MySQL-5.6','mysqld.read_rnd_buffer_size','INT','524288','[1,2147483647]','RANGE',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Used for reads from MyISAM tables, and, for any storage engine, for Multi-Range Read optimization.','2022-06-16 21:39:26','2022-10-20 12:26:09',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (122,'tendbha','dbconf','MySQL-5.6','mysqld.relay-log','STRING','{{.Mysqld.Datadir}}/relay-log/relay-log.bin',NULL,'',2,0,0,0,1,'{{mysqld.datadir}}/relay-log/relay-log.bin',NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-03-28 18:00:36',0); @@ -658,6 +662,7 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (35,'tendbha','dbconf','MySQL-5.7','mysqld.log_warnings','STRING','0','0| 1| 2 ','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2022-04-25 10:00:47','2022-06-16 22:21:50',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (36,'tendbha','dbconf','MySQL-5.7','mysqld.long_query_time','FLOAT','1','[0,3600]','RANGE',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'If a query takes longer than this many seconds, the server increments the Slow_queries status variable','2022-04-25 10:00:47','2022-06-16 22:21:50',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (32,'tendbha','dbconf','MySQL-5.7','mysqld.loose-log_bin_compress','STRING','OFF','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-05-11 12:16:54',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (47,'tendbha','dbconf','MySQL-5.7','mysqld.loose_query_response_time_stats','STRING','ON','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-07-17 14:53:48',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (50,'tendbha','dbconf','MySQL-5.7','mysqld.loose_relay_log_uncompress','STRING','OFF','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-05-11 12:35:45',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (37,'tendbha','dbconf','MySQL-5.7','mysqld.lower_case_table_names','INT','0','[0,1]','RANGE',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,'If set to 0, table names are stored as specified and comparisons are case sensitive. If set to 1, they are stored in lowercase on disk and comparisons are not case sensitive.','2022-04-25 10:00:47','2022-06-16 21:32:48',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (5122,'tendbha','dbconf','MySQL-5.7','mysqld.low_priority_updates','STRING','OFF','ON| OFF ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'If set to true, all INSERT, UPDATE, DELETE, and LOCK TABLE WRITE statements wait until there is no pending SELECT or LOCK TABLE READ on the affected table','2022-06-16 21:39:26','2022-10-20 12:26:09',0); @@ -700,7 +705,6 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (46,'tendbha','dbconf','MySQL-5.7','mysqld.query_cache_type','STRING','OFF','OFF| ON| DEMAND','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,'Set the query cache type.','2022-04-25 10:00:47','2023-04-17 14:28:58',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (5151,'tendbha','dbconf','MySQL-5.7','mysqld.query_cache_wlock_invalidate','STRING','OFF','ON| OFF ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Invalidate queries in query cache on LOCK for write','2022-06-16 21:39:26','2022-10-20 12:26:09',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (5152,'tendbha','dbconf','MySQL-5.7','mysqld.query_prealloc_size','INT','8192','[8192,1048576]','RANGE',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'The size of the persistent buffer used for statement parsing and execution. This buffer is not freed between statements.','2022-06-16 21:39:26','2022-10-20 12:26:09',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (47,'tendbha','dbconf','MySQL-5.7','mysqld.query_response_time_stats','STRING','ON','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2022-05-24 00:16:49',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (5153,'tendbha','dbconf','MySQL-5.7','mysqld.range_alloc_block_size','INT','4096','[4096,4294967295]','RANGE',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'The size of blocks that are allocated when doing range optimization.','2022-06-16 21:39:26','2022-10-20 12:26:09',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (5154,'tendbha','dbconf','MySQL-5.7','mysqld.range_optimizer_max_mem_size','INT','8388608','[0,17179869184]','RANGE',-1,0,0,0,1,NULL,NULL,NULL,-1,NULL,'The limit on memory consumption for the range optimizer. ','2022-06-16 21:39:26','2022-10-20 12:26:09',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (5155,'tendbha','dbconf','MySQL-5.7','mysqld.read_buffer_size','INT','262144','[8200,2147479552]','RANGE',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Each thread that does a sequential scan for a MyISAM table allocates a buffer of this size (in bytes) for each table it scans.','2022-06-16 21:39:26','2022-10-20 12:26:09',0); @@ -903,7 +907,7 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (361,'tendbha','dbconf','MySQL-8.0','mysqld.log_bin_trust_function_creators','INT','1','1 | 0','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-04-19 15:10:13',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (4939,'tendbha','dbconf','MySQL-8.0','mysqld.log_output','STRING','FILE','FILE| TABLE| NONE ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'The destination or destinations for general query log and slow query log output. ','2022-06-16 21:39:26','2022-10-20 12:26:09',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (4940,'tendbha','dbconf','MySQL-8.0','mysqld.log_queries_not_using_indexes','STRING','OFF','ON| OFF ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Whether queries that do not use indexes are logged to the slow query log','2022-06-16 21:39:26','2022-10-20 12:26:09',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (362,'tendbha','dbconf','MySQL-8.0','mysqld.log_slave_updates','INT','1','1 | 0','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-04-19 15:10:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (362,'tendbha','dbconf','MySQL-8.0','mysqld.log_slave_updates','INT','1','1 | 0','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-08-09 11:34:38',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (4941,'tendbha','dbconf','MySQL-8.0','mysqld.log_slow_admin_statements','STRING','OFF','ON| OFF ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Whether include slow administrative statements in the statements written to the slow query log.','2022-06-16 21:39:26','2022-10-20 12:26:09',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (4942,'tendbha','dbconf','MySQL-8.0','mysqld.log_throttle_queries_not_using_indexes','INT','0','[0,4294967295]','RANGE',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'If log_queries_not_using_indexes is enabled, the log_throttle_queries_not_using_indexes variable limits the number of such queries per minute that can be written to the slow query log. ','2022-06-16 21:39:26','2022-10-20 12:26:09',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (4943,'tendbha','dbconf','MySQL-8.0','mysqld.log_timestamps','STRING','SYSTEM','UTC| SYSTEM ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'This variable controls the time zone of timestamps in messages written to the error log, and in general query log and slow query log messages written to files.','2022-06-16 21:39:26','2022-10-20 12:26:09',0); @@ -960,10 +964,10 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (4979,'tendbha','dbconf','MySQL-8.0','mysqld.show_old_temporals','STRING','OFF','ON| OFF ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Whether SHOW CREATE TABLE output includes comments to flag temporal columns found to be in pre-5.6.4 format (TIME, DATETIME, and TIMESTAMP columns without support for fractional seconds precision).','2022-06-16 21:39:26','2022-10-20 12:26:09',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (385,'tendbha','dbconf','MySQL-8.0','mysqld.skip-name-resolve','STRING','true','true | false','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-03-09 18:37:22',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (386,'tendbha','dbconf','MySQL-8.0','mysqld.slave_compressed_protocol','INT','1','1 | 0','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-04-19 15:11:00',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (387,'tendbha','dbconf','MySQL-8.0','mysqld.slave_exec_mode','STRING','STRICT','|STRICT|IDEMPOTENT','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-04-19 15:10:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (387,'tendbha','dbconf','MySQL-8.0','mysqld.slave_exec_mode','STRING','STRICT','|STRICT|IDEMPOTENT','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-08-09 11:34:38',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (4980,'tendbha','dbconf','MySQL-8.0','mysqld.slave_net_timeout','INT','120','[15,300]','RANGE',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'The number of seconds to wait for more data from the master before the slave considers the connection broken, aborts the read, and tries to reconnect','2022-06-16 21:39:26','2022-10-20 12:26:09',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (388,'tendbha','dbconf','MySQL-8.0','mysqld.slave_parallel_type','STRING','DATABASE','DATABASE| LOGICAL_CLOCK','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Specifies if the slave will use database partitioning or information from master to parallelize transactions.(Default: DATABASE).','2022-04-25 10:00:47','2023-04-17 14:55:08',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (389,'tendbha','dbconf','MySQL-8.0','mysqld.slave_parallel_workers','INT','4','0| 1| 2| 4| 8| 16| 32| 64','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Enable multithreading on the replica and set the number of application threads used to execute replicated transactions in parallel.','2022-04-25 10:00:47','2023-04-17 14:58:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (388,'tendbha','dbconf','MySQL-8.0','mysqld.slave_parallel_type','STRING','LOGICAL_CLOCK','DATABASE| LOGICAL_CLOCK','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Specifies if the slave will use database partitioning or information from master to parallelize transactions.(Default: DATABASE).','2022-04-25 10:00:47','2023-08-09 11:34:38',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (389,'tendbha','dbconf','MySQL-8.0','mysqld.slave_parallel_workers','INT','4','0| 1| 2| 4| 8| 16| 32| 64','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Enable multithreading on the replica and set the number of application threads used to execute replicated transactions in parallel.','2022-04-25 10:00:47','2023-08-09 11:34:38',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (4981,'tendbha','dbconf','MySQL-8.0','mysqld.slave_rows_search_algorithms','STRING','TABLE_SCAN,INDEX_SCAN,HASH_SCAN','TABLE_SCAN,INDEX_SCAN| INDEX_SCAN,HASH_SCAN| TABLE_SCAN,HASH_SCAN| TABLE_SCAN,INDEX_SCAN,HASH_SCAN ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'When preparing batches of rows for row-based logging and replication, this variable controls how the rows are searched for matches.','2022-06-16 21:39:26','2022-10-20 12:26:09',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (4982,'tendbha','dbconf','MySQL-8.0','mysqld.slow_launch_time','INT','2','[1,10]','RANGE',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'If creating a thread takes longer than this many seconds, the server increments the Slow_launch_threads status variable','2022-06-16 21:39:26','2022-10-20 12:26:09',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (390,'tendbha','dbconf','MySQL-8.0','mysqld.slow_query_log','STRING','ON','ON | OFF','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Specify the initial slow query log state.','2022-04-25 10:00:47','2023-04-17 14:35:44',0); @@ -997,25 +1001,31 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (467,'tendbha','deploy','deploy_info','db_version','STRING','MySQL-5.7','MySQL-5.5 | MySQL-5.6 | MySQL-5.7 | MySQL-8.0','ENUM',1,0,0,0,1,NULL,NULL,'DB版本',-1,NULL,NULL,'2022-04-25 10:00:47','2022-09-13 17:28:25',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (468,'tendbha','deploy','deploy_info','storage_engine','STRING','InnoDB','InnoDB','',1,0,0,0,1,NULL,NULL,'存储引擎',-1,NULL,NULL,'2022-04-25 10:00:47','2022-09-13 15:03:55',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (470,'tendbha','deploy','deploy_info','tolerance_level','STRING','compus','idc|compus|city','ENUM',-1,0,0,0,1,NULL,NULL,'容灾级别',-1,NULL,NULL,'2022-04-25 10:00:47','2022-10-20 12:26:09',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15831,'tendbha','mysql_monitor','items-config.yaml','character-consistency','STRING','{\"enable\":true, \"schedule\":\"0 0 14 * * 1\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-03-20 17:11:26',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15835,'tendbha','mysql_monitor','items-config.yaml','engine','STRING','{\"enable\":true, \"schedule\":\"0 0 12 * * 1\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-03-20 17:11:30',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15836,'tendbha','mysql_monitor','items-config.yaml','ext3-check','STRING','{\"enable\":true, \"schedule\":\"0 0 16 * * 1\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-03-20 17:11:32',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15847,'tendbha','mysql_monitor','items-config.yaml','master-slave-heartbeat','STRING','{\"enable\":true, \"schedule\":\"@every 10s\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-03-20 17:33:13',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15846,'tendbha','mysql_monitor','items-config.yaml','mysql-config-diff','STRING','{\"enable\":true, \"schedule\":\"@every 10m\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-03-20 17:11:45',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15845,'tendbha','mysql_monitor','items-config.yaml','mysql-connlog-report','STRING','{\"enable\":true, \"schedule\":\"0 40 23 * * *\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-03-20 17:11:44',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15844,'tendbha','mysql_monitor','items-config.yaml','mysql-connlog-rotate','STRING','{\"enable\":true, \"schedule\":\"0 30 23 * * *\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-03-20 17:11:43',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15843,'tendbha','mysql_monitor','items-config.yaml','mysql-connlog-size','STRING','{\"enable\":true, \"schedule\":\"0 0 12 * * *\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-03-20 17:11:41',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15839,'tendbha','mysql_monitor','items-config.yaml','mysql-err-critical','STRING','{\"enable\":true, \"schedule\":\"@every 1m\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-03-20 17:11:36',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15838,'tendbha','mysql_monitor','items-config.yaml','mysql-err-notice','STRING','{\"enable\":true, \"schedule\":\"@every 1m\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-03-20 17:11:34',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15841,'tendbha','mysql_monitor','items-config.yaml','mysql-inject','STRING','{\"enable\":true, \"schedule\":\"@every 1m\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-03-20 17:11:38',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15840,'tendbha','mysql_monitor','items-config.yaml','mysql-lock','STRING','{\"enable\":true, \"schedule\":\"@every 1m\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-03-20 17:11:37',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15913,'tendbha','mysql_monitor','items-config.yaml','proxy-backend','STRING','{\"enable\":true, \"schedule\":\"0 0 14 * * 1\", \"machine_type\":\"proxy\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-03-20 17:33:13',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15912,'tendbha','mysql_monitor','items-config.yaml','proxy-user-list','STRING','{\"enable\":true, \"schedule\":\"0 0 14 * * 1\", \"machine_type\":\"proxy\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-03-20 17:33:13',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15842,'tendbha','mysql_monitor','items-config.yaml','rotate-slowlog','STRING','{\"enable\":true, \"schedule\":\"0 55 23 * * *\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-03-20 17:11:40',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15832,'tendbha','mysql_monitor','items-config.yaml','routine-definer','STRING','{\"enable\":true, \"schedule\":\"0 0 15 * * 1\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-03-20 17:11:27',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15837,'tendbha','mysql_monitor','items-config.yaml','slave-status','STRING','{\"enable\":true, \"schedule\":\"@every 1m\", \"machine_type\":\"backend\", \"role\": [\"repeater\", \"slave\"]}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-03-20 17:11:33',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15834,'tendbha','mysql_monitor','items-config.yaml','trigger-definer','STRING','{\"enable\":true, \"schedule\":\"0 0 15 * * 1\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-03-20 17:11:29',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15833,'tendbha','mysql_monitor','items-config.yaml','view-definer','STRING','{\"enable\":true, \"schedule\":\"0 0 15 * * 1\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-27 13:09:08','2023-03-20 17:11:28',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16671,'tendbha','mysql_monitor','items-config.yaml','character-consistency','STRING','{\"enable\":true,\"schedule\":\"0 0 14 * * 1\",\"machine_type\":[\"single\",\"backend\",\"remote\",\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-06-30 17:21:12',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16694,'tendbha','mysql_monitor','items-config.yaml','ctl-replicate','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-06-30 17:21:12',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16675,'tendbha','mysql_monitor','items-config.yaml','engine','STRING','{\"enable\":true,\"schedule\":\"0 0 12 * * *\",\"machine_type\":[\"single\",\"backend\",\"remote\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-06-30 17:21:12',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16676,'tendbha','mysql_monitor','items-config.yaml','ext3-check','STRING','{\"enable\":true,\"schedule\":\"0 0 16 * * 1\",\"machine_type\":[\"single\",\"backend\",\"remote\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-06-30 17:21:12',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16677,'tendbha','mysql_monitor','items-config.yaml','ibd-statistic','STRING','{\"enable\":true,\"schedule\":\"0 0 14 * * 1\",\"machine_type\":[\"single\",\"backend\",\"remote\"],\"role\":[\"slave\"]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-06-30 17:21:12',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16678,'tendbha','mysql_monitor','items-config.yaml','master-slave-heartbeat','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"backend\",\"remote\"],\"role\":[\"master\",\"repeater\",\"slave\"]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-07-28 14:29:22',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16679,'tendbha','mysql_monitor','items-config.yaml','mysql-config-diff','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"single\",\"backend\",\"remote\",\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-06-30 17:21:12',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16682,'tendbha','mysql_monitor','items-config.yaml','mysql-connlog-report','STRING','{\"enable\":true,\"schedule\":\"0 40 23 * * *\",\"machine_type\":[\"single\",\"backend\",\"remote\",\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-06-30 17:21:12',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16681,'tendbha','mysql_monitor','items-config.yaml','mysql-connlog-rotate','STRING','{\"enable\":true,\"schedule\":\"0 30 23 * * *\",\"machine_type\":[\"single\",\"backend\",\"remote\",\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-06-30 17:21:12',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16680,'tendbha','mysql_monitor','items-config.yaml','mysql-connlog-size','STRING','{\"enable\":true,\"schedule\":\"0 0 12 * * *\",\"machine_type\":[\"single\",\"backend\",\"remote\",\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-06-30 17:21:12',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16684,'tendbha','mysql_monitor','items-config.yaml','mysql-err-critical','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"single\",\"backend\",\"remote\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-06-30 17:21:12',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16683,'tendbha','mysql_monitor','items-config.yaml','mysql-err-notice','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"single\",\"backend\",\"remote\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-06-30 17:21:12',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16689,'tendbha','mysql_monitor','items-config.yaml','mysql-inject','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"single\",\"backend\",\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-06-30 17:21:12',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16688,'tendbha','mysql_monitor','items-config.yaml','mysql-lock','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"single\",\"backend\",\"remote\",\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-06-30 17:21:12',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16690,'tendbha','mysql_monitor','items-config.yaml','proxy-backend','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"proxy\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-06-30 17:21:12',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16691,'tendbha','mysql_monitor','items-config.yaml','proxy-user-list','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"proxy\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-06-30 17:21:12',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16692,'tendbha','mysql_monitor','items-config.yaml','rotate-slowlog','STRING','{\"enable\":true,\"schedule\":\"0 55 23 * * *\",\"machine_type\":[\"single\",\"backend\",\"remote\",\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-06-30 17:21:12',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16672,'tendbha','mysql_monitor','items-config.yaml','routine-definer','STRING','{\"enable\":true,\"schedule\":\"0 0 15 * * 1\",\"machine_type\":[\"single\",\"backend\",\"remote\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-06-30 17:21:12',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16693,'tendbha','mysql_monitor','items-config.yaml','slave-status','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"backend\",\"remote\"],\"role\":[\"slave\",\"repeater\"]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-06-30 17:21:12',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16687,'tendbha','mysql_monitor','items-config.yaml','spider-err-critical','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-06-30 17:21:12',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16685,'tendbha','mysql_monitor','items-config.yaml','spider-err-notice','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-06-30 17:21:12',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16686,'tendbha','mysql_monitor','items-config.yaml','spider-err-warn','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-06-30 17:21:12',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16695,'tendbha','mysql_monitor','items-config.yaml','spider-remote','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-06-30 17:21:12',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16674,'tendbha','mysql_monitor','items-config.yaml','trigger-definer','STRING','{\"enable\":true,\"schedule\":\"0 0 15 * * 1\",\"machine_type\":[\"single\",\"backend\",\"remote\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-06-30 17:21:12',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16673,'tendbha','mysql_monitor','items-config.yaml','view-definer','STRING','{\"enable\":true,\"schedule\":\"0 0 15 * * 1\",\"machine_type\":[\"single\",\"backend\",\"remote\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-06-30 17:21:12',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (715,'tendbha','proxyconf','default','conn_log','STRING','true','true|false','ENUM',1,0,1,0,0,NULL,'','',-1,NULL,'','2022-05-23 15:51:24','2022-05-23 15:55:10',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (716,'tendbha','proxyconf','default','daemon','STRING','true','true|false','ENUM',1,0,1,0,0,NULL,'','',-1,NULL,'','2022-05-23 15:51:24','2022-05-23 15:55:10',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (719,'tendbha','proxyconf','default','event-threads','INT','7','[1,10]','RANGE',1,0,1,0,0,NULL,'','',-1,NULL,'','2022-05-23 15:51:24','2022-05-23 15:55:10',0); @@ -1025,6 +1035,31 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (721,'tendbha','proxyconf','default','plugins','STRING','admin, proxy','','STRING',1,0,1,0,0,NULL,'','',-1,NULL,'','2022-05-23 15:51:24','2022-05-23 15:55:10',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (722,'tendbha','proxyconf','default','proxy-address','STRING','1.1.1.1:3306','','STRING',1,0,1,0,0,NULL,'','',-1,NULL,'','2022-05-23 15:51:24','2022-05-23 15:55:10',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (718,'tendbha','proxyconf','default','query_response_time_stats','STRING','true','true|false','ENUM',1,0,1,0,0,NULL,'','',-1,NULL,'','2022-05-23 15:51:24','2022-05-23 15:55:10',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16784,'tendbha','tbinlogdumper','latest','client.default-character-set','STRING','{{.Mysqld.CharacterSetServer}}','utf8 | utf8mb4 | latin1 | gbk','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-08-11 14:56:20',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16785,'tendbha','tbinlogdumper','latest','mysql.default-character-set','STRING','{{.Mysqld.CharacterSetServer}}','utf8 | utf8mb4 | latin1 | gbk','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-08-11 14:56:25',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16760,'tendbha','tbinlogdumper','latest','mysqld.bind-address','STRING','{{.Mysqld.BindAddress}}','','',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-08-11 15:01:23',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16762,'tendbha','tbinlogdumper','latest','mysqld.character_set_server','STRING','{{.Mysqld.CharacterSetServer}}','utf8 | utf8mb4 | latin1 | gbk','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-08-11 14:56:17',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16763,'tendbha','tbinlogdumper','latest','mysqld.datadir','STRING','{{.Mysqld.Datadir}}/data','','',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-08-11 15:01:19',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16761,'tendbha','tbinlogdumper','latest','mysqld.default-storage-engine','STRING','redis','redis','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-08-11 14:54:10',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16764,'tendbha','tbinlogdumper','latest','mysqld.lc_messages_dir','STRING','{{.Mysqld.Basedir}}/share','','',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-08-11 15:01:16',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16765,'tendbha','tbinlogdumper','latest','mysqld.port','INT','{{.Mysqld.Port}}','','',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-08-11 15:01:12',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16772,'tendbha','tbinlogdumper','latest','mysqld.redis_area_name','STRING','{{.Mysqld.AreaName}}','','',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-08-11 15:00:54',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16773,'tendbha','tbinlogdumper','latest','mysqld.redis_base_counter','INT','100000','[1000, 1000000]','RANGE',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-08-11 14:58:28',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16774,'tendbha','tbinlogdumper','latest','mysqld.redis_binlogdumpid','STRING','{{.Mysqld.Dumperid}}','','',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-08-11 15:00:51',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16775,'tendbha','tbinlogdumper','latest','mysqld.redis_l5_cmdid','STRING','{{.Mysqld.L5CmdId}}','','',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-08-11 15:04:04',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16776,'tendbha','tbinlogdumper','latest','mysqld.redis_l5_modid','STRING','{{.Mysqld.L5ModId}}','','',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-08-11 15:04:10',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16777,'tendbha','tbinlogdumper','latest','mysqld.redis_max_connections','INT','20','[10, 100]','RANGE',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-08-11 14:53:26',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16778,'tendbha','tbinlogdumper','latest','mysqld.redis_protocol_type','STRING','L5_AGENT','L5_AGENT','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-08-11 14:54:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16779,'tendbha','tbinlogdumper','latest','mysqld.redis_server_address','STRING','{{.Mysqld.ServerAddress}}','','',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-08-11 14:57:19',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16780,'tendbha','tbinlogdumper','latest','mysqld.redis_server_port','INT','{{.Mysqld.ServerPort}}','[10, 40000]','RANGE',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-08-11 14:55:21',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16781,'tendbha','tbinlogdumper','latest','mysqld.redis_write_log','INT','1','1 | 0','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-08-11 14:57:00',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16766,'tendbha','tbinlogdumper','latest','mysqld.relay-log','STRING','{{.Mysqld.Datadir}}/relay-log/relay-log.bin','','',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-08-11 15:01:09',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16767,'tendbha','tbinlogdumper','latest','mysqld.replicate_do_table','STRING','{.Mysqld.ReplicateDoTable}','','',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-08-11 14:55:36',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16768,'tendbha','tbinlogdumper','latest','mysqld.server_id','INT','{{.Mysqld.ServerId}}','','',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-08-11 15:01:06',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16769,'tendbha','tbinlogdumper','latest','mysqld.skip_blackhole','STRING','true','','',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-08-11 15:01:04',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16758,'tendbha','tbinlogdumper','latest','mysqld.skip_name_resolve','STRING','true','','',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-08-11 15:01:27',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16770,'tendbha','tbinlogdumper','latest','mysqld.socket','STRING','{{.Mysqld.Datadir}}/mysql.sock','','',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-08-11 15:01:00',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16771,'tendbha','tbinlogdumper','latest','mysqld.tmpdir','STRING','{{.Mysqld.Datadir}}/tmp','','',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-08-11 15:00:57',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (457,'tendbha','user','tb_app_info','dba','STRING','',NULL,'',-1,0,0,0,1,NULL,NULL,'DBA',-1,NULL,NULL,'2022-04-25 10:00:47','2022-10-20 12:26:09',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (462,'tendbha','user','tb_app_info','developer','STRING','',NULL,'',-1,0,0,0,1,NULL,NULL,'开发人员',-1,NULL,NULL,'2022-04-25 10:00:47','2022-10-20 12:26:09',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (460,'tendbha','user','tb_app_info','mongo_dba','STRING','',NULL,'',-1,0,0,0,1,NULL,NULL,'Mongo DBA',-1,NULL,NULL,'2022-04-25 10:00:47','2022-10-20 12:26:09',0); @@ -1045,4 +1080,3 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:03 diff --git a/dbm-services/common/db-config/assets/migrations/000021_tendbsingle_data.up.sql b/dbm-services/common/db-config/assets/migrations/000021_tendbsingle_data.up.sql index d21f9832f4..23aefaad13 100644 --- a/dbm-services/common/db-config/assets/migrations/000021_tendbsingle_data.up.sql +++ b/dbm-services/common/db-config/assets/migrations/000021_tendbsingle_data.up.sql @@ -27,10 +27,10 @@ INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (178,'tendbsingle','backup','binlog_rotate.yaml','binlog滚动与备份选项','binlog_rotate.yaml','plat,app,module,cluster','',1,1,1,'',0,0,0,'binlog_rotate.yaml','2023-03-09 17:34:12','2023-03-22 12:08:50',''); INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (131,'tendbsingle','backup','dbbackup.ini','备份配置','dbbackup.conf配置项','plat,app,module,cluster','',1,1,0,'',0,0,0,'dbbackup.conf配置项','2022-04-25 10:19:22','2023-03-22 12:08:50',''); INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (132,'tendbsingle','backup','dbbackup.options','备份控制选项','dbbackup.ini控制选项','plat,app,module,cluster','',1,1,0,'',0,0,0,'dbbackup.ini控制选项','2022-04-25 10:19:22','2023-03-22 12:08:50',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (47,'tendbsingle','dbconf','MySQL-5.6','my.cnf配置','5.6_参数配置','plat,app,module,cluster','cluster',1,1,0,NULL,5,365,0,'5.6_参数配置','2022-04-25 10:19:22','2023-03-22 12:08:50',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (42,'tendbsingle','dbconf','MySQL-5.7','my.cnf配置','5.7_参数配置','plat,app,module,cluster','cluster',1,1,0,NULL,5,365,0,'5.7_参数配置','2022-04-25 10:19:22','2023-03-22 12:08:50',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (48,'tendbsingle','dbconf','MySQL-8.0','my.cnf配置','8.0_参数配置','plat,app,module,cluster','cluster',1,1,0,NULL,5,365,0,'8.0_参数配置','2022-04-25 10:19:22','2023-03-22 12:08:50',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (105,'tendbsingle','deploy','deploy_info','部署配置',NULL,'plat,app,module,cluster','',0,1,0,NULL,5,365,0,NULL,'2022-04-25 10:19:22','2023-03-20 21:40:05',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (47,'tendbsingle','dbconf','MySQL-5.6','my.cnf配置','MySQL-5.6','plat,app,module,cluster','cluster',1,1,0,NULL,5,365,0,'5.6_参数配置','2022-04-25 10:19:22','2023-06-29 10:34:35',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (42,'tendbsingle','dbconf','MySQL-5.7','my.cnf配置','MySQL-5.7','plat,app,module,cluster','cluster',1,1,0,NULL,5,365,0,'5.7_参数配置','2022-04-25 10:19:22','2023-06-29 10:34:41',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (48,'tendbsingle','dbconf','MySQL-8.0','my.cnf配置','MySQL-8.0','plat,app,module,cluster','cluster',1,1,0,NULL,5,365,0,'8.0_参数配置','2022-04-25 10:19:22','2023-06-29 10:34:32',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (105,'tendbsingle','deploy','deploy_info','部署配置','deploy_info','plat,app,module,cluster','',0,1,0,NULL,5,365,0,NULL,'2022-04-25 10:19:22','2023-06-30 17:29:49',''); /*!50112 SET @disable_bulk_load = IF (@is_rocksdb_supported, 'SET SESSION rocksdb_bulk_load = @old_rocksdb_bulk_load', 'SET @dummy_rocksdb_bulk_load = 0') */; /*!50112 PREPARE s FROM @disable_bulk_load */; /*!50112 EXECUTE s */; @@ -42,7 +42,6 @@ INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:03 -- MySQL dump 10.13 Distrib 5.7.20, for Linux (x86_64) -- -- Host: localhost Database: bk_dbconfig @@ -69,11 +68,11 @@ INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` -- -- WHERE: namespace='tendbsingle' AND (flag_encrypt!=1 or value_default like '{{%') -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15917,'tendbsingle','backup','binlog_rotate.yaml','backup_client.cos','STRING','{\n \"enable\": true,\n \"with_md5\": true,\n \"file_tag\": \"INCREMENT_BACKUP\",\n \"tool_path\": \"cos-client\"\n}','','MAP',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2022-04-25 10:00:47','2023-03-22 12:23:33',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15917,'tendbsingle','backup','binlog_rotate.yaml','backup_client.cos','STRING','{\n \"enable\": true,\n \"with_md5\": true,\n \"file_tag\": \"INCREMENT_BACKUP\",\n \"tool_path\": \"/usr/local/backup_client/bin/backup_client\"\n}','','MAP',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2022-04-25 10:00:47','2023-09-06 10:52:49',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14112,'tendbsingle','backup','binlog_rotate.yaml','backup_client.ibs','STRING','{\n \"enable\": false,\n \"ibs_mode\": \"hdfs\",\n \"with_md5\": true,\n \"file_tag\": \"INCREMENT_BACKUP\",\n \"tool_path\": \"backup_client\"\n}','','MAP',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2023-03-09 17:34:12','2023-03-22 12:23:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14113,'tendbsingle','backup','binlog_rotate.yaml','crond.api_url','STRING','http://127.0.0.1:9999','','STRING',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2023-03-09 17:34:12','2023-03-22 12:23:33',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14114,'tendbsingle','backup','binlog_rotate.yaml','crond.command','STRING','cd /home/mysql/rotate_binlog && ./rotatebinlog -c config.yaml','','STRING',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2023-03-09 17:34:12','2023-03-22 12:23:33',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14115,'tendbsingle','backup','binlog_rotate.yaml','crond.item_name','STRING','rotate_binlog','','STRING',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2023-03-09 17:34:12','2023-03-22 12:23:33',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14114,'tendbsingle','backup','binlog_rotate.yaml','crond.command','STRING','cd /home/mysql/mysql-rotatebinlog && ./rotatebinlog -c config.yaml','','STRING',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2023-03-09 17:34:12','2023-07-04 22:06:15',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14115,'tendbsingle','backup','binlog_rotate.yaml','crond.item_name','STRING','mysql-rotatebinlog','','STRING',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2023-03-09 17:34:12','2023-07-04 22:16:19',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14116,'tendbsingle','backup','binlog_rotate.yaml','crond.schedule','STRING','*/5 * * * *','','STRING',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2023-03-09 17:34:12','2023-03-22 12:23:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14117,'tendbsingle','backup','binlog_rotate.yaml','encrypt.enable','BOOL','false','true | false','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2023-03-09 17:34:12','2023-03-22 12:23:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14119,'tendbsingle','backup','binlog_rotate.yaml','public.keep_policy','STRING','most','most | least','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2023-03-09 17:34:12','2023-03-22 12:23:33',0); @@ -81,15 +80,17 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14121,'tendbsingle','backup','binlog_rotate.yaml','public.max_disk_used_pct','INT','80','[1,99]','RANGE',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2023-03-09 17:34:12','2023-03-22 12:23:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14122,'tendbsingle','backup','binlog_rotate.yaml','public.max_keep_duration','STRING','61d','','DURATION',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2023-03-09 17:34:12','2023-03-22 12:23:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14123,'tendbsingle','backup','binlog_rotate.yaml','public.purge_interval','STRING','4h','','DURATION',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2023-03-09 17:34:12','2023-03-22 12:23:33',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14124,'tendbsingle','backup','binlog_rotate.yaml','public.rotate_interval','STRING','10m','','STRING',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2023-03-09 17:34:12','2023-03-22 12:23:33',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14124,'tendbsingle','backup','binlog_rotate.yaml','public.rotate_interval','STRING','20m','','STRING',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2023-03-09 17:34:12','2023-07-12 19:52:32',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14125,'tendbsingle','backup','binlog_rotate.yaml','report.enable','BOOL','true','true | false','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2023-03-09 17:34:12','2023-03-22 12:23:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14126,'tendbsingle','backup','binlog_rotate.yaml','report.filepath','STRING','/home/mysql/dbareport/mysql/binlog','','STRING',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2023-03-09 17:34:12','2023-03-22 12:23:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14127,'tendbsingle','backup','binlog_rotate.yaml','report.log_maxage','INT','30','[1, 60]','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2023-03-09 17:34:12','2023-03-22 12:23:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14128,'tendbsingle','backup','binlog_rotate.yaml','report.log_maxbackups','INT','10','[1, 30]','RANGE',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2023-03-09 17:34:12','2023-03-22 12:23:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (14129,'tendbsingle','backup','binlog_rotate.yaml','report.log_maxsize','INT','5','[1, 10]','RANGE',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,'Controls whether to produce additional warning messages.','2023-03-09 17:34:12','2023-03-22 12:23:33',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (18004,'tendbsingle','backup','dbbackup.ini','BackupClient.BackupClientBin','STRING','/usr/local/backup_client/bin/backup_client','','STRING',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-09-06 11:11:41',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13048,'tendbsingle','backup','dbbackup.ini','BackupClient.DoChecksum','STRING','true','true | false','ENUM',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-03-22 12:23:33',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16932,'tendbsingle','backup','dbbackup.ini','BackupClient.Enable','STRING','true','true | false','ENUM',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-09-06 11:11:15',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13049,'tendbsingle','backup','dbbackup.ini','BackupClient.FileTag','STRING','MYSQL_FULL_BACKUP','','STRING',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-03-22 12:23:33',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13050,'tendbsingle','backup','dbbackup.ini','BackupClient.RemoteFileSystem','STRING','hdfs','hdfs | cos','ENUM',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-03-22 12:23:33',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13050,'tendbsingle','backup','dbbackup.ini','BackupClient.RemoteFileSystem','STRING','cos','hdfs | cos','ENUM',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-09-06 11:11:41',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16403,'tendbsingle','backup','dbbackup.ini','LogicalBackup.ChunkFilesize','INT','2048','[512, 9999999]','RANGE',1,0,0,0,0,NULL,'','',-1,NULL,'MB','2022-05-26 20:11:23','2023-05-24 21:40:03',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16389,'tendbsingle','backup','dbbackup.ini','LogicalBackup.DefaultsFile','STRING','','','STRING',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-05-25 09:50:12',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16400,'tendbsingle','backup','dbbackup.ini','LogicalBackup.DisableCompress','STRING','false','false | true','BOOL',2,0,0,0,0,NULL,'','',-1,NULL,'','2023-05-24 21:45:24','2023-05-24 21:45:24',0); @@ -97,6 +98,8 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13051,'tendbsingle','backup','dbbackup.ini','LogicalBackup.FlushRetryCount','INT','3','','INT',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-03-22 12:23:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13055,'tendbsingle','backup','dbbackup.ini','LogicalBackup.Regex','STRING','{{.LogicalBackup.Regex}}','','STRING',2,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-04-17 17:10:41',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16401,'tendbsingle','backup','dbbackup.ini','LogicalBackup.Threads','INT','4','','INT',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-05-24 21:42:40',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (17763,'tendbsingle','backup','dbbackup.ini','LogicalLoad.CreateTableIfNotExists','STRING','false','false | true','BOOL',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-05-24 22:01:31',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (17766,'tendbsingle','backup','dbbackup.ini','LogicalLoad.DBListDropIfExists','STRING','','','STRING',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-09-07 21:05:54',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16391,'tendbsingle','backup','dbbackup.ini','LogicalLoad.EnableBinlog','STRING','false','false | true','BOOL',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-05-24 22:01:31',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16399,'tendbsingle','backup','dbbackup.ini','LogicalLoad.ExtraOpt','STRING','','','',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-05-24 21:46:16',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13156,'tendbsingle','backup','dbbackup.ini','LogicalLoad.IndexFilePath','STRING','/data/dbbak/xxxxx','','STRING',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-03-22 12:23:33',0); @@ -139,7 +142,7 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13081,'tendbsingle','backup','dbbackup.ini','Public.OldFileLeftDay','INT','2','','STRING',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-03-22 12:23:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13117,'tendbsingle','backup','dbbackup.ini','Public.ResultReportPath','STRING','/home/mysql/dbareport/mysql/dbbackup/result','result log dir','STRING',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-03-22 12:23:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13118,'tendbsingle','backup','dbbackup.ini','Public.StatusReportPath','STRING','/home/mysql/dbareport/mysql/dbbackup/status','status log dir','STRING',1,0,0,0,0,NULL,'','',-1,NULL,'','2022-05-26 20:11:23','2023-03-22 12:23:33',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16402,'tendbsingle','backup','dbbackup.ini','Public.TarSizeThreshold','INT','8196','[128, 9999999]','RANGE',1,0,0,0,0,NULL,'','',-1,NULL,'MB','2022-05-26 20:11:23','2023-05-24 21:40:10',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16402,'tendbsingle','backup','dbbackup.ini','Public.TarSizeThreshold','INT','8192','[128, 9999999]','RANGE',1,0,0,0,0,NULL,'','',-1,NULL,'MB','2022-05-26 20:11:23','2023-07-12 20:10:06',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13083,'tendbsingle','backup','dbbackup.options','BackupType','STRING','logical','logical | physical','ENUM',1,0,0,0,0,NULL,'','备份类型',-1,NULL,'','2022-05-26 20:11:23','2023-03-22 12:23:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13084,'tendbsingle','backup','dbbackup.options','CrontabTime','STRING','3 5 * * *','','STRING',1,0,0,0,0,NULL,'','DB备份开始时间',-1,NULL,'','2022-05-26 20:11:23','2023-03-22 12:23:33',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (13085,'tendbsingle','backup','dbbackup.options','Logical.IgnoreDatabases','STRING','mysql,test,infodba_schema,information_schema,performance_schema,sys','','',1,0,0,0,0,NULL,'','主库备份数据',-1,NULL,'','2022-05-26 20:11:23','2023-03-22 12:23:33',0); @@ -209,6 +212,7 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9025,'tendbsingle','dbconf','MySQL-5.5','mysqld.log_warnings','STRING','0',NULL,'',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-09-26 18:11:34',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9026,'tendbsingle','dbconf','MySQL-5.5','mysqld.long_query_time','FLOAT','1','[0,3600]','RANGE',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-09-26 18:11:34',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9021,'tendbsingle','dbconf','MySQL-5.5','mysqld.loose_log_bin_compress','STRING','OFF','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2023-05-11 12:35:45',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9042,'tendbsingle','dbconf','MySQL-5.5','mysqld.loose_query_response_time_stats','STRING','ON','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2023-07-17 14:54:45',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9045,'tendbsingle','dbconf','MySQL-5.5','mysqld.loose_relay_log_uncompress','STRING','OFF','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2023-05-11 12:35:45',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9027,'tendbsingle','dbconf','MySQL-5.5','mysqld.lower_case_table_names','INT','0','[0,1]','RANGE',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-09-26 18:11:34',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9028,'tendbsingle','dbconf','MySQL-5.5','mysqld.low_priority_updates','STRING','OFF','ON| OFF ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-10-20 12:26:09',0); @@ -225,7 +229,6 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9039,'tendbsingle','dbconf','MySQL-5.5','mysqld.query_cache_size','INT','0','[0,104857600]','RANGE',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-09-26 18:11:34',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9040,'tendbsingle','dbconf','MySQL-5.5','mysqld.query_cache_type','STRING','OFF','OFF| ON| DEMAND','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2023-04-17 14:28:58',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9041,'tendbsingle','dbconf','MySQL-5.5','mysqld.query_cache_wlock_invalidate','STRING','OFF','ON| OFF ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-10-20 12:26:09',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9042,'tendbsingle','dbconf','MySQL-5.5','mysqld.query_response_time_stats','STRING','ON','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-09-26 18:11:34',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9043,'tendbsingle','dbconf','MySQL-5.5','mysqld.relay-log','STRING','{{.Mysqld.Datadir}}/relay-log/relay-log.bin',NULL,'',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2023-03-28 18:00:36',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9044,'tendbsingle','dbconf','MySQL-5.5','mysqld.relay_log_recovery','INT','1','1 | 0','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2023-04-19 15:10:13',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9050,'tendbsingle','dbconf','MySQL-5.5','mysqld.server_id','STRING','{{.Mysqld.ServerId}}',NULL,'',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2023-03-28 18:01:00',0); @@ -399,6 +402,7 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9375,'tendbsingle','dbconf','MySQL-5.6','mysqld.log_warnings','STRING','0','0| 1| 2 ','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-09-26 18:11:34',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9376,'tendbsingle','dbconf','MySQL-5.6','mysqld.long_query_time','FLOAT','1','[0,3600]','RANGE',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-09-26 18:11:34',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9368,'tendbsingle','dbconf','MySQL-5.6','mysqld.loose_log_bin_compress','STRING','OFF','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2023-05-11 12:35:45',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9414,'tendbsingle','dbconf','MySQL-5.6','mysqld.loose_query_response_time_stats','STRING','ON','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2023-07-17 14:54:09',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9419,'tendbsingle','dbconf','MySQL-5.6','mysqld.loose_relay_log_uncompress','STRING','OFF','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2023-05-11 12:35:45',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9377,'tendbsingle','dbconf','MySQL-5.6','mysqld.lower_case_table_names','INT','0','[0,1]','RANGE',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-09-26 18:11:34',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9378,'tendbsingle','dbconf','MySQL-5.6','mysqld.low_priority_updates','STRING','OFF','ON| OFF ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-10-20 12:26:09',0); @@ -437,7 +441,6 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9411,'tendbsingle','dbconf','MySQL-5.6','mysqld.query_cache_type','STRING','OFF','OFF| ON| DEMAND','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2023-04-17 14:28:58',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9412,'tendbsingle','dbconf','MySQL-5.6','mysqld.query_cache_wlock_invalidate','STRING','OFF','ON| OFF ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-10-20 12:26:09',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9413,'tendbsingle','dbconf','MySQL-5.6','mysqld.query_prealloc_size','INT','8192','[8192,1048576]','RANGE',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-10-20 12:26:09',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9414,'tendbsingle','dbconf','MySQL-5.6','mysqld.query_response_time_stats','STRING','ON','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-09-26 18:11:34',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9415,'tendbsingle','dbconf','MySQL-5.6','mysqld.read_buffer_size','INT','262144','[8200,2147479552]','RANGE',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-10-20 12:26:09',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9416,'tendbsingle','dbconf','MySQL-5.6','mysqld.read_rnd_buffer_size','INT','524288','[1,2147483647]','RANGE',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-10-20 12:26:09',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9417,'tendbsingle','dbconf','MySQL-5.6','mysqld.relay-log','STRING','{{.Mysqld.Datadir}}/relay-log/relay-log.bin',NULL,'',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2023-03-28 18:00:36',0); @@ -633,6 +636,7 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9817,'tendbsingle','dbconf','MySQL-5.7','mysqld.log_warnings','STRING','0','0| 1| 2 ','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-09-26 18:11:34',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9818,'tendbsingle','dbconf','MySQL-5.7','mysqld.long_query_time','FLOAT','1','[0,3600]','RANGE',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-09-26 18:11:34',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9809,'tendbsingle','dbconf','MySQL-5.7','mysqld.loose_log_bin_compress','STRING','OFF','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2023-05-11 12:35:45',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9860,'tendbsingle','dbconf','MySQL-5.7','mysqld.loose_query_response_time_stats','STRING','ON','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2023-07-17 14:54:04',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9867,'tendbsingle','dbconf','MySQL-5.7','mysqld.loose_relay_log_uncompress','STRING','OFF','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2023-05-11 12:35:45',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9819,'tendbsingle','dbconf','MySQL-5.7','mysqld.lower_case_table_names','INT','0','[0,1]','RANGE',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-09-26 18:11:34',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9820,'tendbsingle','dbconf','MySQL-5.7','mysqld.low_priority_updates','STRING','OFF','ON| OFF ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-10-20 12:26:09',0); @@ -675,7 +679,6 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9857,'tendbsingle','dbconf','MySQL-5.7','mysqld.query_cache_type','STRING','OFF','OFF| ON| DEMAND','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2023-04-17 14:28:58',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9858,'tendbsingle','dbconf','MySQL-5.7','mysqld.query_cache_wlock_invalidate','STRING','OFF','ON| OFF ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-10-20 12:26:09',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9859,'tendbsingle','dbconf','MySQL-5.7','mysqld.query_prealloc_size','INT','8192','[8192,1048576]','RANGE',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-10-20 12:26:09',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9860,'tendbsingle','dbconf','MySQL-5.7','mysqld.query_response_time_stats','STRING','ON','ON|OFF','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-09-26 18:11:34',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9861,'tendbsingle','dbconf','MySQL-5.7','mysqld.range_alloc_block_size','INT','4096','[4096,4294967295]','RANGE',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-10-20 12:26:09',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9862,'tendbsingle','dbconf','MySQL-5.7','mysqld.range_optimizer_max_mem_size','INT','8388608','[0,17179869184]','RANGE',-1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-10-20 12:26:09',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (9863,'tendbsingle','dbconf','MySQL-5.7','mysqld.read_buffer_size','INT','262144','[8200,2147479552]','RANGE',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-10-20 12:26:09',0); @@ -875,7 +878,7 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (10279,'tendbsingle','dbconf','MySQL-8.0','mysqld.log_bin_trust_function_creators','INT','1','1 | 0','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2023-04-19 15:10:13',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (10280,'tendbsingle','dbconf','MySQL-8.0','mysqld.log_output','STRING','FILE','FILE| TABLE| NONE ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-10-20 12:26:09',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (10281,'tendbsingle','dbconf','MySQL-8.0','mysqld.log_queries_not_using_indexes','STRING','OFF','ON| OFF ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-10-20 12:26:09',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (10282,'tendbsingle','dbconf','MySQL-8.0','mysqld.log_slave_updates','INT','1','1 | 0','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2023-04-19 15:10:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (10282,'tendbsingle','dbconf','MySQL-8.0','mysqld.log_slave_updates','INT','1','1 | 0','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2023-08-09 11:34:38',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (10283,'tendbsingle','dbconf','MySQL-8.0','mysqld.log_slow_admin_statements','STRING','OFF','ON| OFF ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-10-20 12:26:09',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (10284,'tendbsingle','dbconf','MySQL-8.0','mysqld.log_throttle_queries_not_using_indexes','INT','0','[0,4294967295]','RANGE',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-10-20 12:26:09',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (10285,'tendbsingle','dbconf','MySQL-8.0','mysqld.log_timestamps','STRING','SYSTEM','UTC| SYSTEM ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-10-20 12:26:09',0); @@ -931,10 +934,10 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (10343,'tendbsingle','dbconf','MySQL-8.0','mysqld.show_old_temporals','STRING','OFF','ON| OFF ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-10-20 12:26:09',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (10344,'tendbsingle','dbconf','MySQL-8.0','mysqld.skip-name-resolve','STRING','true','true | false','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2023-03-09 18:37:22',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (10345,'tendbsingle','dbconf','MySQL-8.0','mysqld.slave_compressed_protocol','INT','1','1 | 0','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2023-04-19 15:11:00',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (10346,'tendbsingle','dbconf','MySQL-8.0','mysqld.slave_exec_mode','STRING','STRICT','|STRICT|IDEMPOTENT','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2023-04-19 15:10:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (10346,'tendbsingle','dbconf','MySQL-8.0','mysqld.slave_exec_mode','STRING','STRICT','|STRICT|IDEMPOTENT','ENUM',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2023-08-09 11:34:38',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (10347,'tendbsingle','dbconf','MySQL-8.0','mysqld.slave_net_timeout','INT','120','[15,300]','RANGE',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-10-20 12:26:09',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (10348,'tendbsingle','dbconf','MySQL-8.0','mysqld.slave_parallel_type','STRING','DATABASE','DATABASE| LOGICAL_CLOCK','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2023-04-17 14:55:08',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (10349,'tendbsingle','dbconf','MySQL-8.0','mysqld.slave_parallel_workers','INT','4','0| 1| 2| 4| 8| 16| 32| 64','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2023-04-17 14:58:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (10348,'tendbsingle','dbconf','MySQL-8.0','mysqld.slave_parallel_type','STRING','LOGICAL_CLOCK','DATABASE | LOGICAL_CLOCK','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2023-08-09 11:34:38',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (10349,'tendbsingle','dbconf','MySQL-8.0','mysqld.slave_parallel_workers','INT','4','0| 1| 2| 4| 8| 16| 32| 64','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2023-08-09 11:34:38',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (10350,'tendbsingle','dbconf','MySQL-8.0','mysqld.slave_rows_search_algorithms','STRING','TABLE_SCAN,INDEX_SCAN,HASH_SCAN','TABLE_SCAN,INDEX_SCAN| INDEX_SCAN,HASH_SCAN| TABLE_SCAN,HASH_SCAN| TABLE_SCAN,INDEX_SCAN,HASH_SCAN ','ENUM',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-10-20 12:26:09',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (10351,'tendbsingle','dbconf','MySQL-8.0','mysqld.slow_launch_time','INT','2','[1,10]','RANGE',-1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2022-10-20 12:26:09',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (10352,'tendbsingle','dbconf','MySQL-8.0','mysqld.slow_query_log','STRING','ON','ON | OFF','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-26 18:11:34','2023-04-17 14:35:44',0); @@ -968,23 +971,31 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (12418,'tendbsingle','deploy','deploy_info','db_version','STRING','MySQL-5.7','MySQL-5.5 | MySQL-5.6 | MySQL-5.7 | MySQL-8.0','ENUM',1,0,0,0,1,NULL,NULL,'DB版本',-1,NULL,NULL,'2022-10-27 11:03:11','2022-10-27 11:03:11',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (12419,'tendbsingle','deploy','deploy_info','storage_engine','STRING','InnoDB','InnoDB','',1,0,0,0,1,NULL,NULL,'存储引擎',-1,NULL,NULL,'2022-10-27 11:03:11','2022-10-27 11:03:11',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (12420,'tendbsingle','deploy','deploy_info','tolerance_level','STRING','compus','idc|compus|city','ENUM',-1,0,0,0,1,NULL,NULL,'容灾级别',-1,NULL,NULL,'2022-10-27 11:03:11','2022-10-27 11:03:11',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15850,'tendbsingle','mysql_monitor','items-config.yaml','character-consistency','STRING','{\"enable\":true, \"schedule\":\"0 0 14 * * 1\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-22 12:35:44','2023-03-22 12:35:44',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15851,'tendbsingle','mysql_monitor','items-config.yaml','engine','STRING','{\"enable\":true, \"schedule\":\"0 0 12 * * 1\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-22 12:35:44','2023-03-22 12:35:44',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15852,'tendbsingle','mysql_monitor','items-config.yaml','ext3-check','STRING','{\"enable\":true, \"schedule\":\"0 0 16 * * 1\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-22 12:35:44','2023-03-22 12:35:44',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15853,'tendbsingle','mysql_monitor','items-config.yaml','master-slave-heartbeat','STRING','{\"enable\":true, \"schedule\":\"@every 10s\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-22 12:35:44','2023-03-22 12:35:44',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15854,'tendbsingle','mysql_monitor','items-config.yaml','mysql-config-diff','STRING','{\"enable\":true, \"schedule\":\"@every 10m\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-22 12:35:44','2023-03-22 12:35:44',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15855,'tendbsingle','mysql_monitor','items-config.yaml','mysql-connlog-report','STRING','{\"enable\":true, \"schedule\":\"0 40 23 * * *\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-22 12:35:44','2023-03-22 12:35:44',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15856,'tendbsingle','mysql_monitor','items-config.yaml','mysql-connlog-rotate','STRING','{\"enable\":true, \"schedule\":\"0 30 23 * * *\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-22 12:35:44','2023-03-22 12:35:44',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15857,'tendbsingle','mysql_monitor','items-config.yaml','mysql-connlog-size','STRING','{\"enable\":true, \"schedule\":\"0 0 12 * * *\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-22 12:35:44','2023-03-22 12:35:44',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15858,'tendbsingle','mysql_monitor','items-config.yaml','mysql-err-critical','STRING','{\"enable\":true, \"schedule\":\"@every 1m\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-22 12:35:44','2023-03-22 12:35:44',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15859,'tendbsingle','mysql_monitor','items-config.yaml','mysql-err-notice','STRING','{\"enable\":true, \"schedule\":\"@every 1m\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-22 12:35:44','2023-03-22 12:35:44',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15860,'tendbsingle','mysql_monitor','items-config.yaml','mysql-inject','STRING','{\"enable\":true, \"schedule\":\"@every 1m\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-22 12:35:44','2023-03-22 12:35:44',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15861,'tendbsingle','mysql_monitor','items-config.yaml','mysql-lock','STRING','{\"enable\":true, \"schedule\":\"@every 1m\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-22 12:35:44','2023-03-22 12:35:44',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15862,'tendbsingle','mysql_monitor','items-config.yaml','rotate-slowlog','STRING','{\"enable\":true, \"schedule\":\"0 55 23 * * *\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-22 12:35:44','2023-03-22 12:35:44',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15863,'tendbsingle','mysql_monitor','items-config.yaml','routine-definer','STRING','{\"enable\":true, \"schedule\":\"0 0 15 * * 1\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-22 12:35:44','2023-03-22 12:35:44',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15864,'tendbsingle','mysql_monitor','items-config.yaml','slave-status','STRING','{\"enable\":true, \"schedule\":\"@every 1m\", \"machine_type\":\"backend\", \"role\": [\"repeater\", \"slave\"]}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-22 12:35:44','2023-03-22 12:35:44',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15865,'tendbsingle','mysql_monitor','items-config.yaml','trigger-definer','STRING','{\"enable\":true, \"schedule\":\"0 0 15 * * 1\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-22 12:35:44','2023-03-22 12:35:44',0); -INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (15866,'tendbsingle','mysql_monitor','items-config.yaml','view-definer','STRING','{\"enable\":true, \"schedule\":\"0 0 15 * * 1\", \"machine_type\":\"backend\", \"role\": []}','','MAP',1,0,0,0,0,NULL,'','',-1,NULL,'','2023-03-22 12:35:44','2023-03-22 12:35:44',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16696,'tendbsingle','mysql_monitor','items-config.yaml','character-consistency','STRING','{\"enable\":true,\"schedule\":\"0 0 14 * * 1\",\"machine_type\":[\"single\",\"backend\",\"remote\",\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-06-30 17:21:12',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16719,'tendbsingle','mysql_monitor','items-config.yaml','ctl-replicate','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16700,'tendbsingle','mysql_monitor','items-config.yaml','engine','STRING','{\"enable\":true,\"schedule\":\"0 0 12 * * *\",\"machine_type\":[\"single\",\"backend\",\"remote\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-06-30 17:21:12',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16701,'tendbsingle','mysql_monitor','items-config.yaml','ext3-check','STRING','{\"enable\":true,\"schedule\":\"0 0 16 * * 1\",\"machine_type\":[\"single\",\"backend\",\"remote\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-06-30 17:21:12',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16702,'tendbsingle','mysql_monitor','items-config.yaml','ibd-statistic','STRING','{\"enable\":true,\"schedule\":\"0 0 14 * * 1\",\"machine_type\":[\"single\",\"backend\",\"remote\"],\"role\":[\"slave\"]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-06-30 17:21:12',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16703,'tendbsingle','mysql_monitor','items-config.yaml','master-slave-heartbeat','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"backend\",\"remote\"],\"role\":[\"master\",\"repeater\",\"slave\"]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-07-28 14:29:47',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16704,'tendbsingle','mysql_monitor','items-config.yaml','mysql-config-diff','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"single\",\"backend\",\"remote\",\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-06-30 17:21:12',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16707,'tendbsingle','mysql_monitor','items-config.yaml','mysql-connlog-report','STRING','{\"enable\":true,\"schedule\":\"0 40 23 * * *\",\"machine_type\":[\"single\",\"backend\",\"remote\",\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16706,'tendbsingle','mysql_monitor','items-config.yaml','mysql-connlog-rotate','STRING','{\"enable\":true,\"schedule\":\"0 30 23 * * *\",\"machine_type\":[\"single\",\"backend\",\"remote\",\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16705,'tendbsingle','mysql_monitor','items-config.yaml','mysql-connlog-size','STRING','{\"enable\":true,\"schedule\":\"0 0 12 * * *\",\"machine_type\":[\"single\",\"backend\",\"remote\",\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-06-30 17:21:12',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16709,'tendbsingle','mysql_monitor','items-config.yaml','mysql-err-critical','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"single\",\"backend\",\"remote\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16708,'tendbsingle','mysql_monitor','items-config.yaml','mysql-err-notice','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"single\",\"backend\",\"remote\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16714,'tendbsingle','mysql_monitor','items-config.yaml','mysql-inject','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"single\",\"backend\",\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16713,'tendbsingle','mysql_monitor','items-config.yaml','mysql-lock','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"single\",\"backend\",\"remote\",\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16715,'tendbsingle','mysql_monitor','items-config.yaml','proxy-backend','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"proxy\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16716,'tendbsingle','mysql_monitor','items-config.yaml','proxy-user-list','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"proxy\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16717,'tendbsingle','mysql_monitor','items-config.yaml','rotate-slowlog','STRING','{\"enable\":true,\"schedule\":\"0 55 23 * * *\",\"machine_type\":[\"single\",\"backend\",\"remote\",\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16697,'tendbsingle','mysql_monitor','items-config.yaml','routine-definer','STRING','{\"enable\":true,\"schedule\":\"0 0 15 * * 1\",\"machine_type\":[\"single\",\"backend\",\"remote\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-06-30 17:21:12',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16718,'tendbsingle','mysql_monitor','items-config.yaml','slave-status','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"backend\",\"remote\"],\"role\":[\"slave\",\"repeater\"]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16712,'tendbsingle','mysql_monitor','items-config.yaml','spider-err-critical','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16710,'tendbsingle','mysql_monitor','items-config.yaml','spider-err-notice','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16711,'tendbsingle','mysql_monitor','items-config.yaml','spider-err-warn','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16720,'tendbsingle','mysql_monitor','items-config.yaml','spider-remote','STRING','{\"enable\":true,\"schedule\":\"@every 1m\",\"machine_type\":[\"spider\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:13','2023-06-30 17:21:13',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16699,'tendbsingle','mysql_monitor','items-config.yaml','trigger-definer','STRING','{\"enable\":true,\"schedule\":\"0 0 15 * * 1\",\"machine_type\":[\"single\",\"backend\",\"remote\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-06-30 17:21:12',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16698,'tendbsingle','mysql_monitor','items-config.yaml','view-definer','STRING','{\"enable\":true,\"schedule\":\"0 0 15 * * 1\",\"machine_type\":[\"single\",\"backend\",\"remote\"],\"role\":[]}','','MAP',1,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2023-06-30 17:21:12','2023-06-30 17:21:12',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (11726,'tendbsingle','proxyconf','default','conn_log','STRING','true','true|false','ENUM',1,0,1,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-27 11:52:55','2022-09-27 11:52:55',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (11727,'tendbsingle','proxyconf','default','daemon','STRING','true','true|false','ENUM',1,0,1,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-27 11:52:55','2022-09-27 11:52:55',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (11728,'tendbsingle','proxyconf','default','event-threads','INT','7','[1,10]','RANGE',1,0,1,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-09-27 11:52:55','2022-09-27 11:52:55',0); @@ -1014,4 +1025,3 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:03 diff --git a/dbm-services/common/db-config/assets/migrations/000022_TendisCache_data.up.sql b/dbm-services/common/db-config/assets/migrations/000022_TendisCache_data.up.sql index 53b6440071..7d89f4e055 100644 --- a/dbm-services/common/db-config/assets/migrations/000022_TendisCache_data.up.sql +++ b/dbm-services/common/db-config/assets/migrations/000022_TendisCache_data.up.sql @@ -24,9 +24,9 @@ -- -- WHERE: namespace='TendisCache' -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (60,'TendisCache','dbconf','TendisCache-2.8','DB参数配置','5.8_参数配置','','',0,0,0,'',0,0,0,'5.8_参数配置','2022-08-04 15:28:35','2023-03-22 12:08:50',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (49,'TendisCache','dbconf','TendisCache-3.2','DB参数配置','TendisCache-3.2配置','plat,app,cluster','cluster',1,1,0,NULL,5,365,0,'5.8_参数配置','2022-08-02 14:27:14','2023-03-28 21:40:02',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (51,'TendisCache','dbconf','TendisCache-4.0','DB参数配置','TendisCache-4.0配置','plat,app,cluster','cluster',1,1,0,NULL,5,365,0,'TendisCache-4.0配置','2022-08-02 14:27:14','2023-03-22 12:08:50',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (60,'TendisCache','dbconf','TendisCache-2.8','DB参数配置','TendisCache-2.8','','',0,0,0,'',0,0,0,'5.8_参数配置','2022-08-04 15:28:35','2023-06-29 10:34:03',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (49,'TendisCache','dbconf','TendisCache-3.2','DB参数配置','TendisCache-3.2','plat,app,cluster','cluster',1,1,0,NULL,5,365,0,'5.8_参数配置','2022-08-02 14:27:14','2023-06-29 10:31:00',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (51,'TendisCache','dbconf','TendisCache-4.0','DB参数配置','TendisCache-4.0','plat,app,cluster','cluster',1,1,0,NULL,5,365,0,'TendisCache-4.0配置','2022-08-02 14:27:14','2023-06-29 10:31:09',''); /*!50112 SET @disable_bulk_load = IF (@is_rocksdb_supported, 'SET SESSION rocksdb_bulk_load = @old_rocksdb_bulk_load', 'SET @dummy_rocksdb_bulk_load = 0') */; /*!50112 PREPARE s FROM @disable_bulk_load */; /*!50112 EXECUTE s */; @@ -38,7 +38,6 @@ INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:03 -- MySQL dump 10.13 Distrib 5.7.20, for Linux (x86_64) -- -- Host: localhost Database: bk_dbconfig @@ -96,4 +95,3 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:03 diff --git a/dbm-services/common/db-config/assets/migrations/000023_TendisplusInstance_data.up.sql b/dbm-services/common/db-config/assets/migrations/000023_TendisplusInstance_data.up.sql index d626f83e22..faf668a90e 100644 --- a/dbm-services/common/db-config/assets/migrations/000023_TendisplusInstance_data.up.sql +++ b/dbm-services/common/db-config/assets/migrations/000023_TendisplusInstance_data.up.sql @@ -24,7 +24,7 @@ -- -- WHERE: namespace='TendisplusInstance' -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (63,'TendisplusInstance','redisconf','tendisplus-2.5','Tendisplus参数配置','tendisplus-2.5版本_参数配置','plat,app,cluster','cluster',1,1,0,NULL,0,0,0,'tendisplus-2.5版本_参数配置','2022-08-11 20:22:50','2023-03-22 12:08:50',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (63,'TendisplusInstance','redisconf','tendisplus-2.5','Tendisplus参数配置','tendisplus-2.5','plat,app,cluster','cluster',1,1,0,NULL,0,0,0,'tendisplus-2.5版本_参数配置','2022-08-11 20:22:50','2023-06-29 10:33:49',''); /*!50112 SET @disable_bulk_load = IF (@is_rocksdb_supported, 'SET SESSION rocksdb_bulk_load = @old_rocksdb_bulk_load', 'SET @dummy_rocksdb_bulk_load = 0') */; /*!50112 PREPARE s FROM @disable_bulk_load */; /*!50112 EXECUTE s */; @@ -36,7 +36,6 @@ INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:03 -- MySQL dump 10.13 Distrib 5.7.20, for Linux (x86_64) -- -- Host: localhost Database: bk_dbconfig @@ -116,4 +115,3 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:03 diff --git a/dbm-services/common/db-config/assets/migrations/000024_TendisSSD_data.up.sql b/dbm-services/common/db-config/assets/migrations/000024_TendisSSD_data.up.sql index 9e3daa6dd9..a2bd6ecc42 100644 --- a/dbm-services/common/db-config/assets/migrations/000024_TendisSSD_data.up.sql +++ b/dbm-services/common/db-config/assets/migrations/000024_TendisSSD_data.up.sql @@ -24,7 +24,7 @@ -- -- WHERE: namespace='TendisSSD' -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (50,'TendisSSD','dbconf','TendisSSD-2.8','DB参数配置','TendisSSD-2.8配置','plat,app,cluster','cluster',1,1,0,NULL,5,365,0,'TendisSSD-2.8配置','2022-08-02 14:29:01','2023-03-22 12:08:50',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (50,'TendisSSD','dbconf','TendisSSD-2.8','DB参数配置','TendisSSD-2.8','plat,app,cluster','cluster',1,1,0,NULL,5,365,0,'TendisSSD-2.8配置','2022-08-02 14:29:01','2023-06-29 10:31:03',''); /*!50112 SET @disable_bulk_load = IF (@is_rocksdb_supported, 'SET SESSION rocksdb_bulk_load = @old_rocksdb_bulk_load', 'SET @dummy_rocksdb_bulk_load = 0') */; /*!50112 PREPARE s FROM @disable_bulk_load */; /*!50112 EXECUTE s */; @@ -36,7 +36,6 @@ INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:03 -- MySQL dump 10.13 Distrib 5.7.20, for Linux (x86_64) -- -- Host: localhost Database: bk_dbconfig @@ -74,4 +73,3 @@ INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:03 diff --git a/dbm-services/common/db-config/assets/migrations/000025_TendisX_data.up.sql b/dbm-services/common/db-config/assets/migrations/000025_TendisX_data.up.sql index 569d3b691f..c4116c0d26 100644 --- a/dbm-services/common/db-config/assets/migrations/000025_TendisX_data.up.sql +++ b/dbm-services/common/db-config/assets/migrations/000025_TendisX_data.up.sql @@ -24,7 +24,7 @@ -- -- WHERE: namespace='TendisX' -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (54,'TendisX','dbconf','TendisX-4.0','DB参数配置','TendisX集群(4.0.10)','plat,app,cluster','cluster',1,1,0,NULL,5,365,0,'TendisX集群(4.0.10)','2022-08-02 14:29:01','2023-03-22 12:08:50',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (54,'TendisX','dbconf','TendisX-4.0','DB参数配置','TendisX-4.0','plat,app,cluster','cluster',1,1,0,NULL,5,365,0,'TendisX集群(4.0.10)','2022-08-02 14:29:01','2023-06-29 10:34:12',''); /*!50112 SET @disable_bulk_load = IF (@is_rocksdb_supported, 'SET SESSION rocksdb_bulk_load = @old_rocksdb_bulk_load', 'SET @dummy_rocksdb_bulk_load = 0') */; /*!50112 PREPARE s FROM @disable_bulk_load */; /*!50112 EXECUTE s */; @@ -36,7 +36,6 @@ INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:03 -- MySQL dump 10.13 Distrib 5.7.20, for Linux (x86_64) -- -- Host: localhost Database: bk_dbconfig @@ -74,4 +73,3 @@ INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:03 diff --git a/dbm-services/common/db-config/assets/migrations/000026_TwemproxyRedisInstance_data.up.sql b/dbm-services/common/db-config/assets/migrations/000026_TwemproxyRedisInstance_data.up.sql index 0c028d659d..ab676444e8 100644 --- a/dbm-services/common/db-config/assets/migrations/000026_TwemproxyRedisInstance_data.up.sql +++ b/dbm-services/common/db-config/assets/migrations/000026_TwemproxyRedisInstance_data.up.sql @@ -24,12 +24,12 @@ -- -- WHERE: namespace='TwemproxyRedisInstance' -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (117,'TwemproxyRedisInstance','config','binlogbackup','配置','binlog备份相关的配置','plat,app,cluster','cluster',1,1,0,'tendisCache',5,365,0,'binlog备份相关的配置','2022-11-22 08:55:34','2023-03-22 12:08:50',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (71,'TwemproxyRedisInstance','config','fullbackup','配置','备份相关的配置','plat,app,cluster','cluster',1,1,0,'tendisCache',5,365,0,'备份相关的配置','2022-09-01 17:00:08','2023-03-22 12:08:50',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (118,'TwemproxyRedisInstance','config','heartbeat','配置','心跳相关的配置','plat,app,cluster','cluster',1,1,0,'tendisCache',5,365,0,'心跳相关的配置','2022-11-22 08:55:34','2023-03-22 12:08:50',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (72,'TwemproxyRedisInstance','config','monitor','配置','监控相关的配置','plat,app,cluster','cluster',1,1,0,'tendisCache',5,365,0,'监控相关的配置','2022-09-01 17:00:08','2023-03-22 12:08:50',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (69,'TwemproxyRedisInstance','dbconf','Redis-6','redis配置','redis6.0的配置文件','plat,app,cluster,host,instance','cluster',1,1,0,'tendisCache',5,365,0,'redis6.0的配置文件','2022-09-01 16:58:02','2023-03-22 12:08:50',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (70,'TwemproxyRedisInstance','proxyconf','Twemproxy-latest','redis配置','twemproxy配置文件','plat,app,cluster','cluster',1,1,0,'tendisCache',5,365,0,'twemproxy配置文件','2022-09-01 16:59:02','2023-03-22 12:08:50',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (117,'TwemproxyRedisInstance','config','binlogbackup','binlog备份相关的配置','binlogbackup','plat,app,cluster','cluster',1,1,0,'tendisCache',5,365,0,'binlog备份相关的配置','2022-11-22 08:55:34','2023-06-30 17:29:39',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (71,'TwemproxyRedisInstance','config','fullbackup','全备配置','fullbackup','plat,app,cluster','cluster',1,1,0,'tendisCache',5,365,0,'备份相关的配置','2022-09-01 17:00:08','2023-06-30 17:30:00',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (118,'TwemproxyRedisInstance','config','heartbeat','心跳相关的配置','heartbeat','plat,app,cluster','cluster',1,1,0,'tendisCache',5,365,0,'心跳相关的配置','2022-11-22 08:55:34','2023-06-30 17:29:36',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (72,'TwemproxyRedisInstance','config','monitor','监控配置','monitor','plat,app,cluster','cluster',1,1,0,'tendisCache',5,365,0,'监控相关的配置','2022-09-01 17:00:08','2023-06-30 17:30:03',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (69,'TwemproxyRedisInstance','dbconf','Redis-6','redis配置','Redis-6','plat,app,cluster,host,instance','cluster',1,1,0,'tendisCache',5,365,0,'redis6.0的配置文件','2022-09-01 16:58:02','2023-06-30 17:24:33',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (70,'TwemproxyRedisInstance','proxyconf','Twemproxy-latest','redis配置','Twemproxy-latest','plat,app,cluster','cluster',1,1,0,'tendisCache',5,365,0,'twemproxy配置文件','2022-09-01 16:59:02','2023-06-30 17:24:36',''); /*!50112 SET @disable_bulk_load = IF (@is_rocksdb_supported, 'SET SESSION rocksdb_bulk_load = @old_rocksdb_bulk_load', 'SET @dummy_rocksdb_bulk_load = 0') */; /*!50112 PREPARE s FROM @disable_bulk_load */; /*!50112 EXECUTE s */; @@ -41,7 +41,6 @@ INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:03 -- MySQL dump 10.13 Distrib 5.7.20, for Linux (x86_64) -- -- Host: localhost Database: bk_dbconfig @@ -71,6 +70,7 @@ INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (12936,'TwemproxyRedisInstance','config','binlogbackup','cron','STRING','@every 10m','','',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-11-22 08:55:44','2022-11-22 08:55:44',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (12935,'TwemproxyRedisInstance','config','binlogbackup','old_file_left_day','INT','2','[0,365]','RANGE',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-11-22 08:55:43','2022-11-22 08:55:43',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (12934,'TwemproxyRedisInstance','config','binlogbackup','to_backup_system','STRING','yes','yes|no','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-11-22 08:55:43','2022-11-22 08:55:43',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (17767,'TwemproxyRedisInstance','config','fullbackup','cache_backup_mode','STRING','aof','aof|rdb','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2023-09-09 11:03:50','2023-09-09 11:03:50',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (12931,'TwemproxyRedisInstance','config','fullbackup','cron','STRING','0 5,13,21 * * *','','',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-11-22 08:55:43','2022-11-22 08:55:43',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (12930,'TwemproxyRedisInstance','config','fullbackup','old_file_left_day','INT','2','[0,365]','RANGE',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-11-22 08:55:43','2022-11-22 08:55:43',0); INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (12932,'TwemproxyRedisInstance','config','fullbackup','tar_split','BOOL','true','true|false','ENUM',1,0,0,0,0,NULL,NULL,NULL,-1,NULL,NULL,'2022-11-22 08:55:43','2022-11-22 08:55:43',0); @@ -164,4 +164,3 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:03 diff --git a/dbm-services/common/db-config/assets/migrations/000027_TwemproxyTendisplusInstance_data.up.sql b/dbm-services/common/db-config/assets/migrations/000027_TwemproxyTendisplusInstance_data.up.sql index 8b1173a7bd..10d2e38064 100644 --- a/dbm-services/common/db-config/assets/migrations/000027_TwemproxyTendisplusInstance_data.up.sql +++ b/dbm-services/common/db-config/assets/migrations/000027_TwemproxyTendisplusInstance_data.up.sql @@ -24,10 +24,10 @@ -- -- WHERE: namespace='TwemproxyTendisplusInstance' -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (87,'TwemproxyTendisplusInstance','config','backup','配置','备份相关的配置','plat,app,cluster','cluster',1,1,0,'Tendisplus',5,365,0,'备份相关的配置','2022-09-27 17:48:51','2023-03-22 12:08:50',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (88,'TwemproxyTendisplusInstance','config','monitor','配置','监控相关的配置','plat,app,cluster','cluster',1,1,0,'Tendisplus',5,365,0,'监控相关的配置','2022-09-27 17:49:02','2023-03-22 12:08:50',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (85,'TwemproxyTendisplusInstance','dbconf','Tendisplus-2.5','redis配置','Tendisplus-2.5的配置文件','plat,app,cluster','cluster',1,1,0,'Tendisplus',5,365,0,'Tendisplus-2.5的配置文件','2022-09-27 17:48:42','2023-03-22 12:08:50',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (86,'TwemproxyTendisplusInstance','proxyconf','Twemproxy-latest','redis配置','twemproxy配置文件','plat,app,cluster','cluster',1,1,0,'Tendisplus',5,365,0,'twemproxy配置文件','2022-09-27 17:48:46','2023-03-22 12:08:50',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (87,'TwemproxyTendisplusInstance','config','backup','备份相关的配置','backup','plat,app,cluster','cluster',1,1,0,'Tendisplus',5,365,0,'备份相关的配置','2022-09-27 17:48:51','2023-06-30 17:27:33',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (88,'TwemproxyTendisplusInstance','config','monitor','监控相关的配置','monitor','plat,app,cluster','cluster',1,1,0,'Tendisplus',5,365,0,'监控相关的配置','2022-09-27 17:49:02','2023-06-30 17:27:28',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (85,'TwemproxyTendisplusInstance','dbconf','Tendisplus-2.5','redis配置','Tendisplus-2.5','plat,app,cluster','cluster',1,1,0,'Tendisplus',5,365,0,'Tendisplus-2.5的配置文件','2022-09-27 17:48:42','2023-06-29 10:33:39',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (86,'TwemproxyTendisplusInstance','proxyconf','Twemproxy-latest','redis配置','Twemproxy-latest','plat,app,cluster','cluster',1,1,0,'Tendisplus',5,365,0,'twemproxy配置文件','2022-09-27 17:48:46','2023-06-29 10:37:01',''); /*!50112 SET @disable_bulk_load = IF (@is_rocksdb_supported, 'SET SESSION rocksdb_bulk_load = @old_rocksdb_bulk_load', 'SET @dummy_rocksdb_bulk_load = 0') */; /*!50112 PREPARE s FROM @disable_bulk_load */; /*!50112 EXECUTE s */; @@ -39,7 +39,6 @@ INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:03 -- MySQL dump 10.13 Distrib 5.7.20, for Linux (x86_64) -- -- Host: localhost Database: bk_dbconfig @@ -133,4 +132,3 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:03 diff --git a/dbm-services/common/db-config/assets/migrations/000028_TwemproxyTendisSSDInstance_data.up.sql b/dbm-services/common/db-config/assets/migrations/000028_TwemproxyTendisSSDInstance_data.up.sql index 3f7e09cae9..86a1e4986b 100644 --- a/dbm-services/common/db-config/assets/migrations/000028_TwemproxyTendisSSDInstance_data.up.sql +++ b/dbm-services/common/db-config/assets/migrations/000028_TwemproxyTendisSSDInstance_data.up.sql @@ -24,8 +24,8 @@ -- -- WHERE: namespace='TwemproxyTendisSSDInstance' -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (174,'TwemproxyTendisSSDInstance','dbconf','TendisSSD-1.2','redis配置','TendisSSD的配置文件','plat,app,module,cluster','cluster',1,1,0,'Tendisplus',5,365,0,'TendisSSD的配置文件','2023-03-01 10:27:53','2023-03-22 12:08:50',''); -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (175,'TwemproxyTendisSSDInstance','dbconf','TendisSSD-1.3','redis配置','TendisSSD的配置文件','plat,app,module,cluster','cluster',1,1,0,'Tendisplus',5,365,0,'TendisSSD的配置文件','2023-03-01 10:27:53','2023-03-22 12:08:50',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (174,'TwemproxyTendisSSDInstance','dbconf','TendisSSD-1.2','redis配置','TendisSSD-1.2','plat,app,module,cluster','cluster',1,1,0,'Tendisplus',5,365,0,'TendisSSD的配置文件','2023-03-01 10:27:53','2023-06-29 10:35:22',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (175,'TwemproxyTendisSSDInstance','dbconf','TendisSSD-1.3','redis配置','TendisSSD-1.3','plat,app,module,cluster','cluster',1,1,0,'Tendisplus',5,365,0,'TendisSSD的配置文件','2023-03-01 10:27:53','2023-06-29 10:35:27',''); INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (176,'TwemproxyTendisSSDInstance','proxyconf','Twemproxy-latest','redis配置','twemproxy配置文件','plat,app,module,cluster','cluster',1,1,0,'Tendisplus',5,365,0,'twemproxy配置文件','2023-03-01 10:27:53','2023-03-22 12:08:50',''); /*!50112 SET @disable_bulk_load = IF (@is_rocksdb_supported, 'SET SESSION rocksdb_bulk_load = @old_rocksdb_bulk_load', 'SET @dummy_rocksdb_bulk_load = 0') */; /*!50112 PREPARE s FROM @disable_bulk_load */; @@ -38,7 +38,6 @@ INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:03 -- MySQL dump 10.13 Distrib 5.7.20, for Linux (x86_64) -- -- Host: localhost Database: bk_dbconfig @@ -222,4 +221,3 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:03 diff --git a/dbm-services/common/db-config/assets/migrations/000029_pulsar_data.up.sql b/dbm-services/common/db-config/assets/migrations/000029_pulsar_data.up.sql index 6d78f6bcf2..a933b5ad22 100644 --- a/dbm-services/common/db-config/assets/migrations/000029_pulsar_data.up.sql +++ b/dbm-services/common/db-config/assets/migrations/000029_pulsar_data.up.sql @@ -24,7 +24,7 @@ -- -- WHERE: namespace='pulsar' -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (142,'pulsar','dbconf','2.10.1','pulsar集群配置','pulsar集群配置','plat,app,cluster','cluster',1,1,0,NULL,5,365,0,'pulsar集群配置','2023-01-13 10:37:29','2023-03-22 12:08:50',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (142,'pulsar','dbconf','2.10.1','pulsar集群配置','pulsar-2.10','plat,app,cluster','cluster',1,1,0,NULL,5,365,0,'pulsar集群配置','2023-01-13 10:37:29','2023-06-29 10:36:17',''); /*!50112 SET @disable_bulk_load = IF (@is_rocksdb_supported, 'SET SESSION rocksdb_bulk_load = @old_rocksdb_bulk_load', 'SET @dummy_rocksdb_bulk_load = 0') */; /*!50112 PREPARE s FROM @disable_bulk_load */; /*!50112 EXECUTE s */; @@ -36,7 +36,6 @@ INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:03 -- MySQL dump 10.13 Distrib 5.7.20, for Linux (x86_64) -- -- Host: localhost Database: bk_dbconfig @@ -547,4 +546,3 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:03 diff --git a/dbm-services/common/db-config/assets/migrations/000030_influxdb_data.up.sql b/dbm-services/common/db-config/assets/migrations/000030_influxdb_data.up.sql index 163b343a46..41a76d8792 100644 --- a/dbm-services/common/db-config/assets/migrations/000030_influxdb_data.up.sql +++ b/dbm-services/common/db-config/assets/migrations/000030_influxdb_data.up.sql @@ -24,7 +24,7 @@ -- -- WHERE: namespace='influxdb' -INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (149,'influxdb','dbconf','1.8.4','influxdb配置','influxdb配置文件','plat,app,instance','instance',1,1,0,'NULL',5,365,0,'influxdb配置文件','2022-09-20 15:17:36','2023-03-22 12:08:50',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (149,'influxdb','dbconf','1.8.4','influxdb配置','influxdb-1.8','plat,app,instance','instance',1,1,0,'NULL',5,365,0,'influxdb配置文件','2022-09-20 15:17:36','2023-06-29 10:36:04',''); /*!50112 SET @disable_bulk_load = IF (@is_rocksdb_supported, 'SET SESSION rocksdb_bulk_load = @old_rocksdb_bulk_load', 'SET @dummy_rocksdb_bulk_load = 0') */; /*!50112 PREPARE s FROM @disable_bulk_load */; /*!50112 EXECUTE s */; @@ -36,7 +36,6 @@ INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:03 -- MySQL dump 10.13 Distrib 5.7.20, for Linux (x86_64) -- -- Host: localhost Database: bk_dbconfig @@ -75,4 +74,3 @@ INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, ` /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-06-26 14:30:03 diff --git a/dbm-services/common/db-config/assets/migrations/000031_riak_data.down.sql b/dbm-services/common/db-config/assets/migrations/000031_riak_data.down.sql new file mode 100644 index 0000000000..91f36bce00 --- /dev/null +++ b/dbm-services/common/db-config/assets/migrations/000031_riak_data.down.sql @@ -0,0 +1,2 @@ +DELETE FROM tb_config_file_def WHERE namespace='riak'; +DELETE FROM tb_config_name_def WHERE namespace='riak' AND (flag_encrypt!=1 or value_default like '{{%'); diff --git a/dbm-services/common/db-config/assets/migrations/000031_riak_data.up.sql b/dbm-services/common/db-config/assets/migrations/000031_riak_data.up.sql new file mode 100644 index 0000000000..27dd8e932c --- /dev/null +++ b/dbm-services/common/db-config/assets/migrations/000031_riak_data.up.sql @@ -0,0 +1,109 @@ +-- MySQL dump 10.13 Distrib 5.7.20, for Linux (x86_64) +-- +-- Host: localhost Database: bk_dbconfig +-- ------------------------------------------------------ +-- Server version 5.7.20-tmysql-3.3-log +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; +/*!50717 SELECT COUNT(*) INTO @rocksdb_has_p_s_session_variables FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'performance_schema' AND TABLE_NAME = 'session_variables' */; +/*!50717 SET @rocksdb_get_is_supported = IF (@rocksdb_has_p_s_session_variables, 'SELECT COUNT(*) INTO @rocksdb_is_supported FROM performance_schema.session_variables WHERE VARIABLE_NAME=\'rocksdb_bulk_load\'', 'SELECT 0') */; +/*!50717 PREPARE s FROM @rocksdb_get_is_supported */; +/*!50717 EXECUTE s */; +/*!50717 DEALLOCATE PREPARE s */; +/*!50717 SET @rocksdb_enable_bulk_load = IF (@rocksdb_is_supported, 'SET SESSION rocksdb_bulk_load = 1', 'SET @rocksdb_dummy_bulk_load = 0') */; +/*!50717 PREPARE s FROM @rocksdb_enable_bulk_load */; +/*!50717 EXECUTE s */; +/*!50717 DEALLOCATE PREPARE s */; + +-- +-- Dumping data for table `tb_config_file_def` +-- +-- WHERE: namespace='riak' + +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (206,'riak','dbconf','riak-2.2-legs','riak-2.2','riak-2.2-legs','plat,app,cluster','cluster',1,1,0,'Tendisplus',5,365,0,NULL,'2023-03-30 19:17:40','2023-06-30 16:01:57',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (203,'riak','dbconf','riak-2.2-mhs','riak-2.2','riak-2.2-mhs','plat,app,cluster','cluster',1,1,0,'Tendisplus',5,365,0,NULL,'2023-03-30 19:17:40','2023-06-30 16:02:24',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (209,'riak','dbconf','riak-2.2-mixed','riak-2.2','riak-2.2-mixedt','plat,app,cluster','cluster',1,1,0,'Tendisplus',5,365,0,NULL,'2023-03-30 19:17:40','2023-06-30 16:03:19',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (207,'riak','dbconf','riak-2.2-pp','riak-2.2','riak-2.2-pp','plat,app,cluster','cluster',1,1,0,'Tendisplus',5,365,0,NULL,'2023-03-30 19:17:40','2023-06-30 16:02:01',''); +INSERT INTO `tb_config_file_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_type_lc`, `conf_file_lc`, `level_names`, `level_versioned`, `conf_name_validate`, `conf_value_validate`, `value_type_strict`, `namespace_info`, `version_keep_limit`, `version_keep_days`, `conf_name_order`, `description`, `created_at`, `updated_at`, `updated_by`) VALUES (208,'riak','dbconf','riak-2.2-test','riak-2.2','riak-2.2-test','plat,app,cluster','cluster',1,1,0,'Tendisplus',5,365,0,NULL,'2023-03-30 19:17:40','2023-06-30 16:02:04',''); +/*!50112 SET @disable_bulk_load = IF (@is_rocksdb_supported, 'SET SESSION rocksdb_bulk_load = @old_rocksdb_bulk_load', 'SET @dummy_rocksdb_bulk_load = 0') */; +/*!50112 PREPARE s FROM @disable_bulk_load */; +/*!50112 EXECUTE s */; +/*!50112 DEALLOCATE PREPARE s */; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- MySQL dump 10.13 Distrib 5.7.20, for Linux (x86_64) +-- +-- Host: localhost Database: bk_dbconfig +-- ------------------------------------------------------ +-- Server version 5.7.20-tmysql-3.3-log +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; +/*!50717 SELECT COUNT(*) INTO @rocksdb_has_p_s_session_variables FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'performance_schema' AND TABLE_NAME = 'session_variables' */; +/*!50717 SET @rocksdb_get_is_supported = IF (@rocksdb_has_p_s_session_variables, 'SELECT COUNT(*) INTO @rocksdb_is_supported FROM performance_schema.session_variables WHERE VARIABLE_NAME=\'rocksdb_bulk_load\'', 'SELECT 0') */; +/*!50717 PREPARE s FROM @rocksdb_get_is_supported */; +/*!50717 EXECUTE s */; +/*!50717 DEALLOCATE PREPARE s */; +/*!50717 SET @rocksdb_enable_bulk_load = IF (@rocksdb_is_supported, 'SET SESSION rocksdb_bulk_load = 1', 'SET @rocksdb_dummy_bulk_load = 0') */; +/*!50717 PREPARE s FROM @rocksdb_enable_bulk_load */; +/*!50717 EXECUTE s */; +/*!50717 DEALLOCATE PREPARE s */; + +-- +-- Dumping data for table `tb_config_name_def` +-- +-- WHERE: namespace='riak' AND (flag_encrypt!=1 or value_default like '{{%') + +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16498,'riak','dbconf','riak-2.2-legs','bucket_types','STRING','','','STRING',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-06-30 15:59:34',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16499,'riak','dbconf','riak-2.2-legs','distributed_cookie','STRING','legsriak','','',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-06-30 15:46:53',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16494,'riak','dbconf','riak-2.2-legs','leveldb_expiration','STRING','on','off | on','ENUM',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-06-30 15:59:54',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16495,'riak','dbconf','riak-2.2-legs','leveldb_expiration_mode','STRING','whole_file','','',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-06-30 15:46:58',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16496,'riak','dbconf','riak-2.2-legs','leveldb_expiration_retention_time','STRING','365d','unlimited | 365d','ENUM',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-06-30 16:00:03',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16497,'riak','dbconf','riak-2.2-legs','ring_size','INT','256','[256,256]','RANGE',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-06-30 15:47:01',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16491,'riak','dbconf','riak-2.2-mhs','bucket_types','STRING','','','STRING',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-06-30 15:59:18',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16492,'riak','dbconf','riak-2.2-mhs','distributed_cookie','STRING','mhsriak','','',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-06-30 15:46:53',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16487,'riak','dbconf','riak-2.2-mhs','leveldb_expiration','STRING','off','off | on','ENUM',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-06-30 15:46:50',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16488,'riak','dbconf','riak-2.2-mhs','leveldb_expiration_mode','STRING','whole_file','','',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-06-30 15:46:58',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16489,'riak','dbconf','riak-2.2-mhs','leveldb_expiration_retention_time','STRING','unlimited','unlimited | 365d','ENUM',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-06-30 15:46:37',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16490,'riak','dbconf','riak-2.2-mhs','ring_size','INT','256','[256,256]','RANGE',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-06-30 15:47:01',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16516,'riak','dbconf','riak-2.2-mixied','bucket_types','STRING','player_preferences','','STRING',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-06-30 16:03:35',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16517,'riak','dbconf','riak-2.2-mixied','distributed_cookie','STRING','mixedriak','','',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-06-30 16:03:43',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16512,'riak','dbconf','riak-2.2-mixied','leveldb_expiration','STRING','off','off | on','ENUM',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-06-30 16:03:29',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16513,'riak','dbconf','riak-2.2-mixied','leveldb_expiration_mode','STRING','whole_file','','',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-06-30 16:03:31',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16514,'riak','dbconf','riak-2.2-mixied','leveldb_expiration_retention_time','STRING','unlimited','unlimited | 365d','ENUM',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-06-30 16:03:32',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16515,'riak','dbconf','riak-2.2-mixied','ring_size','INT','256','[256,256]','RANGE',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-06-30 16:03:34',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16504,'riak','dbconf','riak-2.2-pp','bucket_types','STRING','player_preferences','','STRING',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-06-30 15:49:55',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16505,'riak','dbconf','riak-2.2-pp','distributed_cookie','STRING','ppriak','','',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-06-30 15:46:53',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16500,'riak','dbconf','riak-2.2-pp','leveldb_expiration','STRING','off','off | on','ENUM',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-06-30 15:46:50',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16501,'riak','dbconf','riak-2.2-pp','leveldb_expiration_mode','STRING','whole_file','','',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-06-30 15:46:58',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16502,'riak','dbconf','riak-2.2-pp','leveldb_expiration_retention_time','STRING','unlimited','unlimited | 365d','ENUM',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-06-30 15:46:37',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16503,'riak','dbconf','riak-2.2-pp','ring_size','INT','256','[256,256]','RANGE',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-06-30 15:47:01',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16510,'riak','dbconf','riak-2.2-test','bucket_types','STRING','player_preferences','','STRING',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-06-30 15:49:55',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16511,'riak','dbconf','riak-2.2-test','distributed_cookie','STRING','testriak','','',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-06-30 15:46:53',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16506,'riak','dbconf','riak-2.2-test','leveldb_expiration','STRING','off','off | on','ENUM',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-06-30 15:46:50',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16507,'riak','dbconf','riak-2.2-test','leveldb_expiration_mode','STRING','whole_file','','',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-06-30 15:46:58',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16508,'riak','dbconf','riak-2.2-test','leveldb_expiration_retention_time','STRING','unlimited','unlimited | 365d','ENUM',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-06-30 15:46:37',0); +INSERT INTO `tb_config_name_def` (`id`, `namespace`, `conf_type`, `conf_file`, `conf_name`, `value_type`, `value_default`, `value_allowed`, `value_type_sub`, `flag_status`, `flag_disable`, `flag_locked`, `flag_encrypt`, `need_restart`, `value_formula`, `extra_info`, `conf_name_lc`, `order_index`, `since_version`, `description`, `created_at`, `updated_at`, `stage`) VALUES (16509,'riak','dbconf','riak-2.2-test','ring_size','INT','256','[256,256]','RANGE',2,0,0,0,1,NULL,NULL,NULL,-1,NULL,NULL,'2022-04-25 10:00:47','2023-06-30 15:47:01',0); +/*!50112 SET @disable_bulk_load = IF (@is_rocksdb_supported, 'SET SESSION rocksdb_bulk_load = @old_rocksdb_bulk_load', 'SET @dummy_rocksdb_bulk_load = 0') */; +/*!50112 PREPARE s FROM @disable_bulk_load */; +/*!50112 EXECUTE s */; +/*!50112 DEALLOCATE PREPARE s */; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + diff --git a/dbm-services/common/db-config/docs/swagger.json b/dbm-services/common/db-config/docs/swagger.json index 0c0f0c7755..6332dbb578 100644 --- a/dbm-services/common/db-config/docs/swagger.json +++ b/dbm-services/common/db-config/docs/swagger.json @@ -4,8 +4,8 @@ ], "swagger": "2.0", "info": { - "description": "This is a bkconfigsvr celler server.", - "title": "bkconfigsvr API", + "description": "This is a dbactuator command collection.", + "title": "dbactuator API", "termsOfService": "http://swagger.io/terms/", "contact": { "name": "API Support", @@ -18,201 +18,98 @@ }, "version": "0.0.1" }, - "host": "localhost:8080", + "host": "./dbactuator", "basePath": "/", "paths": { - "/bkconfig/v1/conffile/add": { + "/common/file-server": { "post": { - "description": "新增平台级配置文件,定义允许的配置名。指定 req_type 为 `SaveOnly` 仅保存, `SaveAndPublish` 保存并发布。保存并发布 也必须提供全量,而不能是前面保存基础上的增量\nreq_type=`SaveOnly` 已废弃\n第一次保存时,会返回 `file_id`,下次 保存/发布 需传入 `file_id`\nnamespace,conf_type,conf_file 唯一确定一个配置文件,不同DB版本信息体现在 conf_file 里 (如MySQL-5.7), namespace_info 可以存前端传入的 数据库版本,只用于在展示\nHTTP Header 指定 `X-Bkapi-User-Name` 请求的操作人员", + "description": "通过 http 暴露指定目录可用于下载,可用于在重建备库时,从其它机器下载备份\n在 OS 不允许 ssh 登录(scp/sftp)时,可以临时启动该服务来获取备份文件", "consumes": [ "application/json" ], - "produces": [ - "application/json" - ], "tags": [ - "plat_config" + "common" ], - "summary": "新增平台级配置文件", + "summary": "简单文件服务", "parameters": [ { - "description": "ConfName for ConfType", + "description": "short description", "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/api.UpsertConfFilePlatReq" + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_fileserver.FileServerComp" } } ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/api.UpsertConfFilePlatResp" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/api.HTTPClientErrResp" - } - } - } + "responses": {} } }, - "/bkconfig/v1/conffile/list": { - "get": { - "description": "查询配置文件模板列表。只有平台和业务才有配置文件列表\n返回的 updated_by 代表操作人", - "produces": [ + "/common/rm-file": { + "post": { + "consumes": [ "application/json" ], "tags": [ - "plat_config" + "common" ], - "summary": "查询配置文件列表", + "summary": "限速删除大文件", "parameters": [ { - "type": "string", - "description": "业务id, bk_biz_id=0 代表平台配置", - "name": "bk_biz_id", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "如果指定了 conf_file 则只查这一个文件信息", - "name": "conf_file", - "in": "query" - }, - { - "type": "string", - "example": "dbconf", - "name": "conf_type", - "in": "query", - "required": true - }, - { - "enum": [ - "plat", - "app", - "module", - "cluster" - ], - "type": "string", - "description": "配置层级名,当前允许值 `app`,`module`,`cluster`\n配合 flag_locked 锁定标记,可以知道 锁定级别", - "name": "level_name", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "配置层级值", - "name": "level_value", - "in": "query" - }, - { - "type": "string", - "description": "命名空间,一般指DB类型", - "name": "namespace", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/api.ListConfFileResp" - } - } - }, - "400": { - "description": "Bad Request", + "description": "short description", + "name": "body", + "in": "body", + "required": true, "schema": { - "$ref": "#/definitions/api.HTTPClientErrResp" + "$ref": "#/definitions/internal_subcmd_commoncmd.RMLargeFileParam" } } - } + ], + "responses": {} } }, - "/bkconfig/v1/conffile/query": { - "get": { - "description": "查询 平台配置 某个配置类型/配置文件的所有配置名列表", - "produces": [ + "/download/http": { + "post": { + "description": "支持限速、basicAuth 认证. 一般配合 common fileserver 使用\n# server1\n./dbactuator common file-server \\\n--payload-format raw \\\n--payload '{\"extend\":{\"bind_address\":\":8082\",\"mount_path\":\"/data/dbbak\",\"user\":\"xiaog\",\"password\":\"xxxx\",\"proc_maxidle_duration\":\"60s\"}}'\n\n# server2\ncurl -u 'xiaog:xxxx' 'http://server1:8082/datadbbak8082/dbactuator' -o dbactuator.bin --limit-rate 10k", + "consumes": [ "application/json" ], "tags": [ - "plat_config" + "download" ], - "summary": "查询平台配置项列表", + "summary": "http下载文件", "parameters": [ { - "type": "string", - "example": "MySQL-5.7", - "name": "conf_file", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "如果设置,会根据前缀模糊匹配搜索", - "name": "conf_name", - "in": "query" - }, - { - "type": "string", - "example": "dbconf", - "name": "conf_type", - "in": "query", - "required": true - }, - { - "type": "string", - "example": "tendbha", - "name": "namespace", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/QueryConfigNamesResp" - } - }, - "400": { - "description": "Bad Request", + "description": "short description", + "name": "body", + "in": "body", + "required": true, "schema": { - "$ref": "#/definitions/api.HTTPClientErrResp" + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.DFHttpParam" } } - } + ], + "responses": {} } }, - "/bkconfig/v1/conffile/update": { + "/download/ibs-query": { "post": { - "description": "编辑平台级配置文件。指定 req_type 为 `SaveOnly` 仅保存, `SaveAndPublish` 保存并发布\nHTTP Header 指定 `X-Bkapi-User-Name` 请求的操作人员\n编辑平台配置时,如果设置 flag_disable=1 时,该配置不会显示在平台配置项列表,相当于管理 所有允许的配置项列表\n保存时会校验输入的 value_default, value_type, value_allowed\n1. value_type 目前允许 STRING, INT, FLOAT, NUMBER\n2. value_type_sub 允许 ENUM, ENUMS, RANGE, STRING, JSON, REGEX(一种特殊的STRING,会验证 value_default 是否满足 value_allowed 正则)\n3. value_allowed 允许 枚举: 例如`0|1|2`, `ON|OFF` 格式, 范围: 例如`(0, 1000]`", + "description": "filename 会进行模糊匹配,返回 task_id 用于下载", "consumes": [ "application/json" ], - "produces": [ - "application/json" - ], "tags": [ - "plat_config" + "download" ], - "summary": "编辑平台级配置文件", + "summary": "从 ieg 备份系统查询文件", "parameters": [ { - "description": "ConfName for ConfType", + "description": "short description", "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/api.UpsertConfFilePlatReq" + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSQueryComp" } } ], @@ -220,39 +117,30 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/api.UpsertConfFilePlatResp" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/api.HTTPClientErrResp" + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSQueryResult" } } } } }, - "/bkconfig/v1/confitem/query": { + "/download/ibs-recover": { "post": { - "description": "根据业务/模块/集群的信息,获取某个配置文件的配置项。一般用户前端请求、再编辑的场景,后端服务直接获取配置文件使用 /version/generate 接口\nconf_file 可以是,号分隔的多个文件名,返回结果是一个按照配置文件名组合的一个 list\n需要指定返回格式 format, 可选值 map, list.\nmap 格式会丢弃 conf_item 的其它信息,只保留 conf_name=conf_value, 一般用于后台服务\nlist 格式会保留 conf_items 的其它信息,conf_name=conf_item,一般用于前端展示\n获取cluster级别配置时,需要提供 level_info:{\"module\":\"xxx\"} 模块信息", + "description": "提供 task_id,从 ieg 备份系统下载文件\ntask_files_wild: 模糊搜索文件并下载, task_files: 精确文件查询并下载\ntask_files_wild, task_files 二选一\n启用 skip_local_exists=true 时,如果目标目录已存在要下载的文件,会自动跳过", "consumes": [ "application/json" ], - "produces": [ - "application/json" - ], "tags": [ - "config_item" + "download" ], - "summary": "获取配置文件配置项列表", + "summary": "从 ieg 备份系统下载文件", "parameters": [ { - "description": "GetConfigItemsReq", + "description": "short description", "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/GetConfigItemsReq" + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSRecoverComp" } } ], @@ -260,64 +148,63 @@ "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/GetConfigItemsResp" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/api.HTTPClientErrResp" + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSRecoverTask" } } } } }, - "/bkconfig/v1/confitem/save": { + "/download/scp": { "post": { - "description": "编辑层级配置,层级包括业务app、模块module、集群cluster,需要指定修改哪个级别的配置,通过 level_name, level_value 来区分\n针对编辑的配置类型 conf_type 无版本化的概念,即保存生效,无需发布\n保存 cluster级别配置时,需要提供 level_info:{\"module\":\"xxx\"} 模块信息", + "description": "支持限速", "consumes": [ "application/json" ], - "produces": [ - "application/json" - ], "tags": [ - "config_item" + "download" ], - "summary": "编辑配置(无版本概念)", + "summary": "scp下载文件", "parameters": [ { - "description": "SaveConfItemsReq", + "description": "short description", "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/api.SaveConfItemsReq" + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.DFScpParam" } } ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/api.UpsertConfItemsResp" - } - }, - "400": { - "description": "Bad Request", + "responses": {} + } + }, + "/mysql/change-master": { + "post": { + "description": "执行 change master to", + "consumes": [ + "application/json" + ], + "tags": [ + "mysql" + ], + "summary": "建立主从关系", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, "schema": { - "$ref": "#/definitions/api.HTTPClientErrResp" + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BuildMSRelationComp" } } - } + ], + "responses": {} } }, - "/bkconfig/v1/confitem/upsert": { + "/mysql/clean-mysql": { "post": { - "description": "编辑层级配置,层级包括业务app、模块module、集群cluster,需要指定修改哪个级别的配置,通过 level_name, level_value 来区分\n例1: level_name=app, level_value=testapp 表示修改业务 bk_biz_id=testapp 的配置\n例2: level_name=module, level_value=account 表示某业务 bk_biz_id 的模块 module=account 的配置\nHTTP Header 指定 `X-Bkapi-User-Name` 请求的操作人员\n获取cluster级别配置时,需要提供 level_info:{\"module\":\"xxx\"} 模块信息", + "description": "清空本地实例,保留系统库", "consumes": [ "application/json" ], @@ -325,1501 +212,3075 @@ "application/json" ], "tags": [ - "config_item" + "mysql" ], - "summary": "编辑发布层级配置", + "summary": "清空实例,高危", "parameters": [ { - "description": "UpsertConfItemsReq", + "description": "description", "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/api.UpsertConfItemsReq" + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.CleanMysqlComp" } } ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/api.UpsertConfItemsResp" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/api.HTTPClientErrResp" - } - } - } + "responses": {} } }, - "/bkconfig/v1/confname/list": { - "get": { - "description": "查询某个配置类型/配置文件的配置名列表,会排除 已锁定的平台配置", - "produces": [ + "/mysql/deploy": { + "post": { + "description": "部署 mysql 实例说明", + "consumes": [ "application/json" ], "tags": [ - "config_meta" + "mysql" ], - "summary": "查询预定义的配置名列表", + "summary": "部署 mysql 实例", "parameters": [ { - "type": "string", - "example": "MySQL-5.7", - "name": "conf_file", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "如果设置,会根据前缀模糊匹配搜索", - "name": "conf_name", - "in": "query" - }, - { - "type": "string", - "example": "dbconf", - "name": "conf_type", - "in": "query", - "required": true - }, - { - "type": "string", - "example": "tendbha", - "name": "namespace", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/QueryConfigNamesResp" - } - }, - "400": { - "description": "Bad Request", + "description": "short description", + "name": "body", + "in": "body", + "required": true, "schema": { - "$ref": "#/definitions/api.HTTPClientErrResp" + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLComp" } } - } + ], + "responses": {} } }, - "/bkconfig/v1/simpleitem/list": { - "get": { - "description": "请勿使用", - "produces": [ + "/mysql/deploy-dbbackup": { + "post": { + "description": "部署GO版本备份程序", + "consumes": [ "application/json" ], "tags": [ - "simple_item" + "mysql" ], - "summary": "查询配置项列表通用接口", + "summary": "部署备份程序", "parameters": [ { - "type": "string", - "name": "bk_biz_id", - "in": "query" - }, - { - "type": "string", - "name": "cluster", - "in": "query" - }, - { - "type": "string", - "name": "conf_file", - "in": "query" - }, - { - "type": "string", - "name": "conf_name", - "in": "query" - }, - { - "type": "string", - "name": "conf_type", - "in": "query" - }, - { - "type": "string", - "name": "conf_value", - "in": "query" - }, - { - "type": "string", - "name": "created_at", - "in": "query" - }, - { - "type": "string", - "name": "created_by", - "in": "query" - }, - { - "type": "string", - "name": "description", - "in": "query" - }, - { - "type": "string", - "name": "format", - "in": "query" - }, - { - "type": "string", - "name": "inherit_from", - "in": "query" - }, - { - "type": "string", - "name": "level_name", - "in": "query" - }, - { - "type": "string", - "name": "level_value", - "in": "query" - }, - { - "type": "string", - "name": "module", - "in": "query" - }, - { - "type": "string", - "name": "namespace", - "in": "query" - }, - { - "type": "string", - "name": "revision", - "in": "query" - }, - { - "type": "string", - "name": "updated_at", - "in": "query" - }, - { - "type": "string", - "name": "updated_by", - "in": "query" - }, - { - "type": "string", - "name": "view", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/model.ConfigModel" - } - } - }, - "400": { - "description": "Bad Request", + "description": "short description", + "name": "body", + "in": "body", + "required": true, "schema": { - "$ref": "#/definitions/api.HTTPClientErrResp" + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallNewDbBackupComp" } } - } + ], + "responses": {} } }, - "/bkconfig/v1/version/detail": { - "get": { - "description": "查询历史配置版本的详情", - "produces": [ + "/mysql/find-local-backup": { + "post": { + "description": "查找本地备份", + "consumes": [ "application/json" ], "tags": [ - "config_version" + "mysql" ], - "summary": "查询版本的详细信息", + "summary": "查找本地备份", "parameters": [ { - "type": "string", - "example": "testapp", - "description": "业务ID,必选项", - "name": "bk_biz_id", - "in": "query", - "required": true - }, - { - "type": "string", - "example": "MySQL-5.7", - "description": "配置文件名,一般配置类型与配置文件一一对应,但如 mysql 5.6, 5.7 两个版本同属 dbconf 配置,所以有 MySQL-5.5, MySQL-5.6 两个配置文件", - "name": "conf_file", - "in": "query", - "required": true - }, - { - "type": "string", - "example": "dbconf", - "description": "配置类型,如 dbconf,backup", - "name": "conf_type", - "in": "query", - "required": true - }, - { - "enum": [ - "plat", - "app", - "module", - "cluster" - ], - "type": "string", - "description": "配置层级名,当前允许值 `app`,`module`,`cluster`\n配合 flag_locked 锁定标记,可以知道 锁定级别", - "name": "level_name", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "配置层级值", - "name": "level_value", - "in": "query" - }, - { - "type": "string", - "example": "tendbha", - "description": "命名空间,一般指DB类型", - "name": "namespace", - "in": "query", - "required": true - }, - { - "type": "string", - "example": "v_20220309215824", - "name": "revision", - "in": "query" + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.FindLocalBackupParam" + } } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/api.GetVersionedDetailResp" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/api.HTTPClientErrResp" + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.FindLocalBackupResp" } } } } }, - "/bkconfig/v1/version/generate": { + "/mysql/flashback-binlog": { "post": { - "description": "从现有配置项直接生成配置文件并返回,每次调用会生成一个新版本,可以选择是否直接发布。这个接口一般用户后台服务查询配置\n修改配置并发布,使用 /confitem/upsert 接口\n直接查询配置文件内容,使用 /confitem/query 接口\n根据 `method` 生成方式不同,可以生成配置并存储 `GenerateAndSave`、生成配置并存储且发布`GenerateAndPublish`\n使用 `GenerateAndSave` 方式需要进一步调用 PublishConfigFile 接口进行发布", + "description": "通过 `mysqlbinlog --flashback xxx | mysql` 导入 binlog", "consumes": [ "application/json" ], - "produces": [ - "application/json" - ], "tags": [ - "config_version" + "mysql" ], - "summary": "生成并获取配置文件新版本", + "summary": "导入 binlog", "parameters": [ { - "description": "Generate config file versioned", + "description": "short description", "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/GenerateConfigReq" + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_rollback.FlashbackComp" } } ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/GenerateConfigResp" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/api.HTTPClientErrResp" - } - } - } + "responses": {} } }, - "/bkconfig/v1/version/list": { - "get": { - "description": "Get config file versions list", - "produces": [ + "/mysql/grant-repl": { + "post": { + "description": "在目标机器新建 repl 账号", + "consumes": [ "application/json" ], "tags": [ - "config_version" + "mysql" ], - "summary": "查询历史配置版本名列表", + "summary": "建立复制账号", "parameters": [ { - "type": "string", - "example": "testapp", - "description": "业务ID,必选项", - "name": "bk_biz_id", - "in": "query", - "required": true - }, - { - "type": "string", - "example": "MySQL-5.7", - "description": "配置文件名,一般配置类型与配置文件一一对应,但如 mysql 5.6, 5.7 两个版本同属 dbconf 配置,所以有 MySQL-5.5, MySQL-5.6 两个配置文件", - "name": "conf_file", - "in": "query", - "required": true - }, - { - "type": "string", - "example": "dbconf", - "description": "配置类型,如 dbconf,backup", - "name": "conf_type", - "in": "query", - "required": true - }, - { - "enum": [ - "plat", - "app", - "module", - "cluster" - ], - "type": "string", - "description": "配置层级名,当前允许值 `app`,`module`,`cluster`\n配合 flag_locked 锁定标记,可以知道 锁定级别", - "name": "level_name", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "配置层级值", - "name": "level_value", - "in": "query" - }, - { - "type": "string", - "example": "tendbha", - "description": "命名空间,一般指DB类型", - "name": "namespace", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/ListConfigVersionsResp" - } - }, - "400": { - "description": "Bad Request", + "description": "short description", + "name": "body", + "in": "body", + "required": true, "schema": { - "$ref": "#/definitions/api.HTTPClientErrResp" + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_grant.GrantReplComp" } } - } + ], + "responses": {} } }, - "/bkconfig/v1/version/publish": { + "/mysql/init-cluster-routing": { "post": { - "description": "发布指定版本的配置文件,未发布状态的配置文件是不能使用的\n发布操作会把已有 published 状态的配置文件下线;同一个 revision 版本的配置无法重复发布\n发布时带上 patch 参数可以覆盖配置中心该版本的配置项(只有配置项值是`{{`开头的才能被覆盖)", + "description": "初始化tendb cluster 集群的路由关系说明", "consumes": [ "application/json" ], - "produces": [ - "application/json" - ], "tags": [ - "config_version" + "spiderctl" ], - "summary": "直接发布一个版本[废弃]", + "summary": "初始化tendb cluster 集群的路由关系", "parameters": [ { - "description": "Publish config file versioned", + "description": "short description", "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/PublishConfigFileReq" + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_spiderctl.InitClusterRoutingComp" } } ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/api.HTTPOkNilResp" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/api.HTTPClientErrResp" - } - } - } + "responses": {} + } + }, + "/mysql/install-checksum": { + "post": { + "description": "安装mysql校验", + "consumes": [ + "application/json" + ], + "tags": [ + "mysql" + ], + "summary": "安装mysql校验", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLChecksumComp" + } + } + ], + "responses": {} + } + }, + "/mysql/install-dbatoolkit": { + "post": { + "description": "部署 /home/mysql/dba_toolkit,覆盖", + "consumes": [ + "application/json" + ], + "tags": [ + "mysql" + ], + "summary": "部署DBA工具箱", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallDBAToolkitComp" + } + } + ], + "responses": {} + } + }, + "/mysql/mycnf-change": { + "post": { + "description": "修改mysql配置", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "mysql" + ], + "summary": "修改mysql配置", + "parameters": [ + { + "description": "description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.MycnfChangeComp" + } + } + ], + "responses": {} + } + }, + "/mysql/mycnf-clone": { + "post": { + "description": "用于 slave 重建或迁移,保持新实例与 my.cnf 实例关键参数相同的场景\n默认 clone 参数:", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "mysql" + ], + "summary": "从源实例克隆 my.cnf 部分参数到目标实例", + "parameters": [ + { + "description": "description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.MycnfCloneComp" + } + } + ], + "responses": {} + } + }, + "/mysql/oscmd-run": { + "post": { + "description": "执行os简单命令", + "consumes": [ + "application/json" + ], + "tags": [ + "mysql" + ], + "summary": "执行os简单命令", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.OSCmds" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.OSCmdRunResp" + } + } + } + } + }, + "/mysql/parse-binlog-time": { + "post": { + "description": "获取 binlog FileDescriptionFormat 和 RotateEvent 事件", + "consumes": [ + "application/json" + ], + "tags": [ + "mysql" + ], + "summary": "获取 binlog 的开始和结束时间", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BinlogTimeComp" + } + } + ], + "responses": {} + } + }, + "/mysql/pt-table-checksum": { + "post": { + "description": "数据校验", + "consumes": [ + "application/json" + ], + "tags": [ + "mysql" + ], + "summary": "数据校验", + "parameters": [ + { + "description": "description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.PtTableChecksumComp" + } + } + ], + "responses": {} + } + }, + "/mysql/recover-binlog": { + "post": { + "description": "通过 `mysqlbinlog xxx | mysql` 导入 binlog", + "consumes": [ + "application/json" + ], + "tags": [ + "mysql" + ], + "summary": "导入 binlog", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RecoverBinlogComp" + } + } + ], + "responses": {} + } + }, + "/mysql/restore-dr": { + "post": { + "description": "物理备份、逻辑备份恢复", + "consumes": [ + "application/json" + ], + "tags": [ + "mysql" + ], + "summary": "备份恢复", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RestoreDRComp" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_util_mysqlutil.ChangeMaster" + } + } + } + } + }, + "/mysql/semantic-dumpschema": { + "post": { + "description": "运行语义检查", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "mysql" + ], + "summary": "运行语义检查", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SemanticDumpSchemaComp" + } + } + ], + "responses": {} + } + }, + "/spdierctl/deploy": { + "post": { + "description": "部署 spider ctl 实例说明", + "consumes": [ + "application/json" + ], + "tags": [ + "spiderctl" + ], + "summary": "部署 spider ctl 实例", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLComp" + } + } + ], + "responses": {} + } + }, + "/spider/deploy": { + "post": { + "description": "部署 spider 实例说明", + "consumes": [ + "application/json" + ], + "tags": [ + "spider" + ], + "summary": "部署 spider 实例", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLComp" + } + } + ], + "responses": {} + } + }, + "/tbinlogdumper/deploy": { + "post": { + "description": "部署 tbinlogdumper 实例说明", + "consumes": [ + "application/json" + ], + "tags": [ + "tbinlogdumper" + ], + "summary": "部署 tbinlogdumper 实例", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_tbinlogdumper.InstallTbinlogDumperComp" + } + } + ], + "responses": {} + } + }, + "/tbinlogdumper/semantic-dumpschema": { + "post": { + "description": "备份表结构并导入", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "tbinlogdumper" + ], + "summary": "备份表结构并导入", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_tbinlogdumper.DumpSchemaComp" + } + } + ], + "responses": {} + } + }, + "/tbinlogdumper/uninstall": { + "post": { + "description": "卸载 tbinlogdumper 实例说明", + "consumes": [ + "application/json" + ], + "tags": [ + "tbinlogdumper" + ], + "summary": "卸载 tbinlogdumper 实例", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_tbinlogdumper.UnInstallTbinlogDumperComp" + } + } + ], + "responses": {} } } }, "definitions": { - "GenerateConfigReq": { + "dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam": { + "type": "object", + "properties": { + "runtime_account": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.RuntimeAccountParam" + }, + "runtime_extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.RuntimeExtend" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components.RuntimeAccountParam": { + "type": "object", + "properties": { + "admin_pwd": { + "description": "mysql admin 密码,环境变量 GENERAL_ACCOUNT_admin_pwd", + "type": "string" + }, + "admin_user": { + "description": "mysql admin 账户,环境变量 GENERAL_ACCOUNT_admin_user", + "type": "string" + }, + "backup_pwd": { + "description": "dbbackup pwd", + "type": "string" + }, + "backup_user": { + "description": "dbbackup user", + "type": "string" + }, + "monitor_access_all_pwd": { + "description": "mysql monitor@% 密码", + "type": "string" + }, + "monitor_access_all_user": { + "description": "mysql monitor@%", + "type": "string" + }, + "monitor_pwd": { + "description": "mysql monitor 密码,环境变量 GENERAL_ACCOUNT_monitor_pwd", + "type": "string" + }, + "monitor_user": { + "description": "mysql monitor 账户,环境变量 GENERAL_ACCOUNT_monitor_user", + "type": "string" + }, + "proxy_admin_pwd": { + "description": "proxy admin pwd", + "type": "string" + }, + "proxy_admin_user": { + "description": "proxy admin user", + "type": "string" + }, + "repl_pwd": { + "description": "repl pwd, 环境变量 GENERAL_ACCOUNT_repl_pwd", + "type": "string" + }, + "repl_user": { + "description": "repl user, 环境变量 GENERAL_ACCOUNT_repl_user", + "type": "string" + }, + "tdbctl_pwd": { + "type": "string" + }, + "tdbctl_user": { + "type": "string" + }, + "yw_pwd": { + "description": "yw pwd", + "type": "string" + }, + "yw_user": { + "description": "yw user", + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components.RuntimeExtend": { + "type": "object", + "properties": { + "mysql_sys_users": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.DFHttpParam": { + "type": "object", + "required": [ + "file_list", + "path_tgt", + "server" + ], + "properties": { + "auth_pass": { + "description": "http url basic auth pass", + "type": "string" + }, + "auth_user": { + "description": "http url basic auth user", + "type": "string" + }, + "bk_biz_id": { + "type": "integer" + }, + "bwlimit_mb": { + "description": "单文件下载限速,单位 MB/s", + "type": "integer" + }, + "curl_options": { + "type": "array", + "items": { + "type": "string" + } + }, + "curl_path": { + "description": "curl 命令路径,默认留空. 目前只用于测试 url", + "type": "string" + }, + "file_list": { + "description": "下载哪些文件", + "type": "array", + "items": { + "type": "string" + } + }, + "max_concurrency": { + "description": "并发下载数", + "type": "integer" + }, + "path_tgt": { + "description": "文件存放到本机哪个目录", + "type": "string" + }, + "server": { + "description": "下载 url", + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.DFScpParam": { + "type": "object", + "required": [ + "file_src", + "file_tgt" + ], + "properties": { + "bk_biz_id": { + "type": "integer" + }, + "bwlimit_mb": { + "description": "单文件下载限速,单位 MB/s", + "type": "integer" + }, + "file_src": { + "description": "下载源", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.FileSrc" + } + ] + }, + "file_tgt": { + "description": "下载目标", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.FileTgt" + } + ] + }, + "max_concurrency": { + "description": "并发下载数", + "type": "integer" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.FileSrc": { + "type": "object", + "required": [ + "file_list", + "path", + "ssh_host", + "ssh_port", + "ssh_user" + ], + "properties": { + "file_list": { + "description": "源文件名列表,相对上面的 path", + "type": "array", + "items": { + "type": "string" + } + }, + "match": { + "type": "string" + }, + "path": { + "description": "源文件所在目录", + "type": "string" + }, + "ssh_host": { + "type": "string" + }, + "ssh_pass": { + "type": "string" + }, + "ssh_port": { + "type": "string" + }, + "ssh_user": { + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.FileTgt": { + "type": "object", + "required": [ + "path" + ], + "properties": { + "path": { + "description": "文件下载目标目录", + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSBaseInfo": { + "type": "object", + "required": [ + "key", + "sys_id", + "url" + ], + "properties": { + "key": { + "description": "16位字串,由备份系统分配,可从环境变量获取 IBS_INFO__key", + "type": "string" + }, + "sys_id": { + "description": "application标识,亦即哪个系统需要访问本接口,可从环境变量获取 IBS_INFO_sys_id", + "type": "string" + }, + "ticket": { + "description": "OA验证的ticket,一个长串,通常附加在访问内网应用的URL上,主要用来验证用户身份,可以留空", + "type": "string" + }, + "url": { + "description": "ieg 备份系统 api url 地址,会在后面拼接 /query /recover 后缀进行请求\n可从环境变量获取 IBS_INFO_url", + "type": "string", + "example": "http://127.0.0.1/backupApi" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSQueryComp": { + "type": "object", + "properties": { + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSQueryParam" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSQueryForRecover": { + "type": "object", + "properties": { + "begin_date": { + "description": "查询文件起始时间,备份系统以 file_last_mtime 为条件", + "type": "string" + }, + "end_date": { + "description": "哪一天提交,结束时间,与begin_date形成一个时间范围,建议begin_date与end_date形成的时间范围不要超过3天", + "type": "string" + }, + "source_ip": { + "description": "来源IP,即提交备份任务的机器IP", + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSQueryParam": { + "type": "object", + "required": [ + "begin_date", + "end_date", + "filename", + "ibs_info", + "source_ip" + ], + "properties": { + "begin_date": { + "description": "哪一天提交,起始时间", + "type": "string" + }, + "end_date": { + "description": "哪一天提交,结束时间,与begin_date形成一个时间范围,建议begin_date与end_date形成的时间范围不要超过3天", + "type": "string" + }, + "filename": { + "description": "文件名", + "type": "string" + }, + "ibs_info": { + "description": "ieg backup system url and auth params", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSBaseInfo" + } + ] + }, + "source_ip": { + "description": "来源IP,即提交备份任务的机器IP", + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSQueryResult": { + "type": "object", + "properties": { + "bkstif": { + "description": "备份状态信息, 'done, success', 'Fail: bad md5' 等", + "type": "string" + }, + "createTime": { + "description": "非备份系统字段,全备(截取文件名中的字段),binlog 打开文件读取", + "type": "string" + }, + "expire_time": { + "type": "string" + }, + "expired": { + "type": "string" + }, + "file_last_mtime": { + "description": "文件最后修改时间", + "type": "string" + }, + "file_name": { + "type": "string" + }, + "file_tag": { + "type": "string" + }, + "md5": { + "type": "string" + }, + "path": { + "description": "非备份系统字段", + "type": "string" + }, + "size": { + "description": "文件大小", + "type": "string" + }, + "source_ip": { + "description": "上报该备份任务的IP", + "type": "string" + }, + "source_port": { + "description": "非备份系统字段", + "type": "string" + }, + "status": { + "description": "文件状态", + "type": "string" + }, + "task_id": { + "description": "任务ID,用于下载", + "type": "string" + }, + "uptime": { + "description": "备份任务上报时间", + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSRecoverComp": { + "type": "object", + "properties": { + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSRecoverParam" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSRecoverParam": { + "type": "object", + "required": [ + "dest_ip", + "diretory", + "ibs_info", + "login_user" + ], + "properties": { + "dest_ip": { + "description": "目标IP,文件恢复到哪一台机器上的", + "type": "string", + "example": "1.1.1.1" + }, + "diretory": { + "description": "diretory 是备份系统参数错误拼写", + "type": "string", + "example": "/data/dbbak" + }, + "ibs_info": { + "description": "ieg backup system url and auth params", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSBaseInfo" + } + ] + }, + "ibs_query": { + "description": "根据文件名下载,或者判断是否跳过下载时,需要提供 ibs_query 参数用于查询", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSQueryForRecover" + } + ] + }, + "login_passwd": { + "description": "登录 dest_ip 的用户名的密码, ieg 传统scp 方式下载才需要。如果是 cos 下载则不需要", + "type": "string" + }, + "login_user": { + "description": "登录 dest_ip 的用户名,下载后的文件属组是该用户", + "type": "string" + }, + "reason": { + "description": "恢复原因(备注用途)", + "type": "string" + }, + "skip_local_exists": { + "description": "如果本地目标目录已经存在对应文件,是否保留(即跳过下载). 默认 false", + "type": "boolean", + "example": false + }, + "task_files": { + "description": "如果是精确文件名下载,用 task_files。提供需要下载的文件列表,提供 task_id 或者完整的 file_name 即可\n如果顺便提供了 size 信息则不用请求备份系统获取大小 来决定文件是否需要重新下载", + "type": "array", + "items": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.TaskFilesExact" + } + }, + "task_files_wild": { + "description": "如果是模糊匹配搜索并下载,用 task_files_wild", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.TaskFilesWild" + } + ] + }, + "taskid_list": { + "description": "taskid 列表,,逗号分隔。会根据 task_files 里的信息,追加到这里。这里一般不传值,在 task_files 里提供 task_id 或者 file_name", + "type": "string", + "example": "10000,100001" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSRecoverTask": { + "type": "object", + "properties": { + "file_last_mtime": { + "description": "文件最后修改时间", + "type": "string" + }, + "file_name": { + "type": "string" + }, + "file_tag": { + "type": "string" + }, + "md5": { + "type": "string" + }, + "size": { + "description": "文件大小", + "type": "string" + }, + "source_ip": { + "description": "上报该备份任务的IP", + "type": "string" + }, + "status": { + "description": "文件状态", + "type": "string" + }, + "task_id": { + "description": "任务ID,用于下载", + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.TaskFilesExact": { + "type": "object", + "properties": { + "file_name": { + "type": "string" + }, + "md5": { + "type": "string" + }, + "size": { + "description": "文件大小", + "type": "string" + }, + "task_id": { + "description": "任务ID,用于下载", + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.TaskFilesWild": { + "type": "object", + "properties": { + "file_tag": { + "type": "string" + }, + "name_regex": { + "description": "在搜索的结果里面,应用该正则进行过滤", + "type": "string" + }, + "name_search": { + "description": "搜索的模糊条件,不用带 *", + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_fileserver.FileServer": { + "type": "object", + "required": [ + "auth_user", + "bind_address", + "mount_path" + ], + "properties": { + "acls": { + "description": "访问来源限制,从前往后匹配。格式 `[\"allow 1.1.1.1/32\", \"deny all\"]`", + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "allow all" + ] + }, + "auth_pass": { + "description": "http basic auth pass,为空时会随机生成密码", + "type": "string" + }, + "auth_user": { + "description": "http basic auth user", + "type": "string" + }, + "bind_address": { + "description": "http file-server 监听地址. 不提供端口,会在 12000-19999 之间随机选择一个端口,不提供 ip 时默认 localhost", + "type": "string" + }, + "enable_tls": { + "description": "暂不支持", + "type": "boolean" + }, + "max_connections": { + "description": "限制最大连接数,超过需要等待. 为 0 时表示不限制", + "type": "integer" + }, + "mount_path": { + "description": "将本地哪个目录通过 http 分享", + "type": "string" + }, + "path_prefix": { + "description": "path_prefix 用在生成 url 时的路径前缀. 可留空", + "type": "string" + }, + "print_download": { + "description": "输出 download http 的信息,方便使用", + "type": "boolean" + }, + "proc_maxidle_duration": { + "description": "超过最大空闲时间,自动退出. 示例 3600s, 60m, 1h", + "type": "string", + "example": "1h" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_fileserver.FileServerComp": { + "type": "object", + "properties": { + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_fileserver.FileServer" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BackupOptions": { + "type": "object", + "required": [ + "BackupType", + "CrontabTime", + "Master" + ], + "properties": { + "BackupType": { + "type": "string" + }, + "CrontabTime": { + "type": "string" + }, + "Logical": { + "type": "object", + "properties": { + "ExcludeDatabases": { + "description": "\"mysql,test,db_infobase,information_schema,performance_schema,sys\"", + "type": "string" + }, + "ExcludeTables": { + "type": "string" + } + } + }, + "Master": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.logicBackupDataOption" + }, + "Slave": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.logicBackupDataOption" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BinlogTimeComp": { + "type": "object", + "properties": { + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BinlogTimeParam" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BinlogTimeParam": { + "type": "object", + "required": [ + "binlog_dir", + "binlog_files" + ], + "properties": { + "binlog_dir": { + "type": "string" + }, + "binlog_files": { + "type": "array", + "items": { + "type": "string" + } + }, + "format": { + "type": "string", + "enum": [ + "", + "json", + "dump" + ] + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BuildMSRelationComp": { + "type": "object", + "properties": { + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BuildMSRelationParam" + }, + "general": { + "description": "本地使用 ADMIN, change master 使用 repl", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam" + } + ] + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BuildMSRelationParam": { + "type": "object", + "required": [ + "bin_file", + "bin_position", + "host", + "master_host", + "master_port", + "port" + ], + "properties": { + "bin_file": { + "description": "binlog 文件名称", + "type": "string" + }, + "bin_position": { + "description": "binlog 位点信息", + "type": "integer", + "minimum": 0 + }, + "force": { + "description": "如果当前实例存在主从关系是否直接reset slave后,强制change master", + "type": "boolean", + "example": false + }, + "host": { + "description": "具体操作内容需要操作的参数", + "type": "string" + }, + "is_gtid": { + "description": "是否启动GID方式进行建立主从", + "type": "boolean" + }, + "master_host": { + "description": "change master to 主库ip", + "type": "string" + }, + "master_port": { + "description": "change master to 主库端口", + "type": "integer", + "maximum": 65535, + "minimum": 3306 + }, + "max_tolerate_delay": { + "description": "最大容忍延迟, 当 主从延迟 小于 该值, 认为建立主从关系成功. 不传或者为 0 时,表示不检查", + "type": "integer" + }, + "not_start_io_thread": { + "description": "不启动 io_thread。默认false 表示启动 io_thread", + "type": "boolean", + "example": false + }, + "not_start_sql_thread": { + "description": "不启动 sql_thread。默认false 表示启动 sql_thread", + "type": "boolean", + "example": false + }, + "port": { + "description": "当前实例的端口", + "type": "integer", + "maximum": 65535, + "minimum": 3306 + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.CleanMysqlComp": { + "type": "object", + "properties": { + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.CleanMysqlParam" + }, + "general": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.CleanMysqlParam": { + "type": "object", + "required": [ + "tgt_instance" + ], + "properties": { + "check_interval_sec": { + "type": "integer" + }, + "drop_database": { + "description": "是否执行 drop database,这里是确认行为. 如果 false 则只把 drop 命令打印到输出", + "type": "boolean" + }, + "force": { + "description": "当实例不空闲时是否强制清空", + "type": "boolean" + }, + "reset_slave": { + "description": "是否执行 reset slave all", + "type": "boolean" + }, + "restart": { + "description": "drop_database 之后是否重启实例", + "type": "boolean" + }, + "stop_slave": { + "description": "是否执行 stop slave", + "type": "boolean" + }, + "tgt_instance": { + "description": "清空目标实例", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.Instance" + } + ] + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.ConfItemOp": { + "type": "object", + "required": [ + "op_type" + ], + "properties": { + "conf_value": { + "description": "ConfName string `json:\"conf_name\" validate:\"required\"`", + "type": "string" + }, + "need_restart": { + "type": "boolean" + }, + "op_type": { + "description": "配置项修改动作,允许值 `upsert`,`remove`", + "type": "string", + "enum": [ + "upsert", + "remove" + ] + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.DBHAAccount": { + "type": "object", + "required": [ + "pwd", + "user" + ], + "properties": { + "access_hosts": { + "type": "array", + "items": { + "type": "string" + } + }, + "pwd": { + "type": "string" + }, + "user": { + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.DumpSchemaParam": { + "type": "object", + "required": [ + "charset", + "host", + "port" + ], + "properties": { + "backup_dir": { + "type": "string" + }, + "backup_file_name": { + "type": "string" + }, + "bk_cloud_id": { + "description": "所在的云区域", + "type": "integer" + }, + "charset": { + "description": "字符集参数", + "type": "string" + }, + "db_cloud_token": { + "description": "云区域token", + "type": "string" + }, + "fileserver": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.FileServer" + }, + "host": { + "description": "当前实例的主机地址", + "type": "string" + }, + "port": { + "description": "当前实例的端口", + "type": "integer", + "minimum": 3306 + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.FileServer": { + "type": "object", + "properties": { + "bucket": { + "description": "目标bucket", + "type": "string" + }, + "password": { + "description": "制品库 password", + "type": "string" + }, + "project": { + "description": "制品库 project", + "type": "string" + }, + "upload_path": { + "description": "上传路径", + "type": "string" + }, + "url": { + "description": "制品库地址", + "type": "string" + }, + "username": { + "description": "制品库 username", + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.FindLocalBackupParam": { + "type": "object", + "required": [ + "backup_dirs", + "tgt_instance" + ], + "properties": { + "backup_dirs": { + "type": "array", + "items": { + "type": "string" + } + }, + "cluster_id": { + "description": "指定查询哪个 cluster_id 的备份,如果不指定可能查询到其它非法的备份", + "type": "integer" + }, + "file_server": { + "type": "boolean" + }, + "tgt_instance": { + "description": "查找哪个实例的备份", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.Instance" + } + ] + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.FindLocalBackupResp": { + "type": "object", + "properties": { + "backups": { + "description": "backups key 是 .info 文件", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.LocalBackupObj" + } + }, + "latest": { + "description": "记录上面 backups 最近的一次备份", + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallDBAToolkitComp": { + "type": "object", + "properties": { + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallDBAToolkitParam" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallDBAToolkitParam": { + "type": "object", + "required": [ + "pkg", + "pkg_md5" + ], + "properties": { + "exec_user": { + "description": "发起执行actor的用户,仅用于审计", + "type": "string" + }, + "pkg": { + "description": "安装包名", + "type": "string" + }, + "pkg_md5": { + "description": "安装包MD5", + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLChecksumComp": { + "type": "object", + "properties": { + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLChecksumParam" + }, + "general": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLChecksumParam": { + "type": "object", + "required": [ + "pkg", + "pkg_md5" + ], + "properties": { + "api_url": { + "type": "string" + }, + "exec_user": { + "type": "string" + }, + "instances_info": { + "type": "array", + "items": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstanceInfo" + } + }, + "pkg": { + "description": "安装包名", + "type": "string" + }, + "pkg_md5": { + "description": "安装包MD5", + "type": "string" + }, + "schedule": { + "type": "string" + }, + "system_dbs": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLComp": { + "type": "object", + "properties": { + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLParams" + }, + "general": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam" + }, + "timeZone": { + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLParams": { + "type": "object", + "required": [ + "charset", + "host", + "mycnf_configs", + "mysql_version", + "pkg", + "pkg_md5", + "ports" + ], + "properties": { + "allowDiskFileSystemTypes": { + "type": "array", + "items": { + "type": "string" + } + }, + "charset": { + "description": "字符集参数", + "type": "string" + }, + "dbha_account": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.DBHAAccount" + }, + "host": { + "type": "string" + }, + "inst_mem": { + "description": "安装实例的内存大小,可以不指定,会自动计算", + "type": "integer" + }, + "mycnf_configs": { + "description": "map[port]my.cnf", + "type": "array", + "items": { + "type": "integer" + } + }, + "mysql_version": { + "description": "MySQLVerion 只需5.6 5.7 这样的大版本号", + "type": "string" + }, + "pkg": { + "description": "安装包名", + "type": "string" + }, + "pkg_md5": { + "description": "安装包MD5", + "type": "string" + }, + "ports": { + "description": "Ports", + "type": "array", + "items": { + "type": "integer" + } + }, + "spider_auto_incr_mode_map": { + "type": "array", + "items": { + "type": "integer" + } + }, + "super_account": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SuperAccount" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallNewDbBackupComp": { + "type": "object", + "properties": { + "generalParam": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam" + }, + "params": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallNewDbBackupParam" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallNewDbBackupParam": { "type": "object", "required": [ "bk_biz_id", - "conf_file", - "conf_type", - "format", - "level_name", - "method", - "namespace" + "configs", + "host", + "options", + "pkg", + "pkg_md5", + "ports", + "role" ], "properties": { "bk_biz_id": { - "description": "业务ID,必选项", - "type": "string", - "example": "testapp" + "description": "bkbizid", + "type": "integer" }, - "conf_file": { - "description": "配置文件名,一般配置类型与配置文件一一对应,但如 mysql 5.6, 5.7 两个版本同属 dbconf 配置,所以有 MySQL-5.5, MySQL-5.6 两个配置文件", - "type": "string", - "example": "MySQL-5.7" + "bk_cloud_id": { + "description": "bk_cloud_id", + "type": "integer" }, - "conf_type": { - "description": "配置类型,如 dbconf,backup", - "type": "string", - "example": "dbconf" + "cluster_address": { + "description": "cluster addresss", + "type": "object", + "additionalProperties": { + "type": "string" + } }, - "format": { - "description": "`map.`, `map#`, `map|` 是特殊的map格式,返回结果会以 . 或者 # 或者 | 拆分 conf_name", - "type": "string", + "cluster_id": { + "description": "cluster id", + "type": "object", + "additionalProperties": { + "type": "integer" + } + }, + "cluster_type": { + "type": "string" + }, + "configs": { + "description": "Configs BackupConfig", + "type": "object", + "additionalProperties": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "exec_user": { + "description": "执行Job的用户", + "type": "string" + }, + "host": { + "description": "当前实例的主机地址", + "type": "string" + }, + "options": { + "description": "选项参数配置", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BackupOptions" + } + ] + }, + "pkg": { + "description": "安装包名", + "type": "string" + }, + "pkg_md5": { + "description": "安装包MD5", + "type": "string" + }, + "ports": { + "description": "被监控机器的上所有需要监控的端口", + "type": "array", + "items": { + "type": "integer" + } + }, + "role": { + "description": "当前主机安装的mysqld的角色", + "type": "string" + }, + "shard_value": { + "description": "shard value for spider", + "type": "object", + "additionalProperties": { + "type": "integer" + } + }, + "untar_only": { + "description": "只解压,不校验不渲染配置,不连接 db", + "type": "boolean" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstanceInfo": { + "type": "object", + "properties": { + "bk_biz_id": { + "type": "integer" + }, + "bk_instance_id": { + "description": "0 被视为空, 不序列化", + "type": "integer" + }, + "cluster_id": { + "type": "integer" + }, + "immute_domain": { + "type": "string" + }, + "ip": { + "type": "string" + }, + "port": { + "type": "integer" + }, + "role": { + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.LocalBackupObj": { + "type": "object", + "properties": { + "backup_dir": { + "description": "备份所在目录", + "type": "string" + }, + "backup_id": { + "type": "string" + }, + "backup_time": { + "description": "备份时间,目前是备份开始时间", + "type": "string" + }, + "backup_type": { + "type": "string" + }, + "bill_id": { + "type": "string" + }, + "bk_biz_id": { + "type": "integer" + }, + "cluster_id": { + "type": "integer" + }, + "data_schema_grant": { + "type": "string" + }, + "db_role": { + "type": "string" + }, + "file_list": { + "description": "InfoFile common.InfoFileDetail `json:\"info_file\"`\n备份文件列表", + "type": "array", + "items": { + "type": "string" + } + }, + "index_file": { + "type": "string" + }, + "inst_host": { + "description": "备份所属 host", + "type": "string" + }, + "inst_port": { + "description": "备份所属 port", + "type": "integer" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.MycnfChangeComp": { + "type": "object", + "properties": { + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.MycnfChangeParam" + }, + "general": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.MycnfChangeParam": { + "type": "object", + "required": [ + "items", + "persistent", + "restart", + "tgt_instance" + ], + "properties": { + "items": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.ConfItemOp" + } + }, + "persistent": { + "description": "是否持久化到 my.cnf 文件,-1: 不持久化,1: 持久化, 2: 仅持久化但不修改运行时", + "type": "integer", "enum": [ - "list", - "map", - "map.", - "map#", - "map|" + -1, + 1, + 2 ] }, - "level_info": { - "description": "上层级信息,如获取当前层级 cluster=c1 的配置,需要设置 level_info: {\"module\": \"m1\"} 提供cluster所属上级 module 的信息\n非必选项,目前只在查询 cluster 级别配置时需要指定模块信息有用\ntodo 将来可能本配置中心,直接请求dbmeta元数据来获取 可能的 app-module-cluster-host-instance 关系", - "type": "object", - "additionalProperties": { + "restart": { + "description": "指定是否 允许重启, -1:不重启, 1: 重启, 2:根据 items need_restart 自动判断是否重启", + "type": "integer", + "enum": [ + -1, + 1, + 2 + ] + }, + "tgt_instance": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.InsObject" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.MycnfCloneComp": { + "type": "object", + "properties": { + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.MycnfCloneParam" + }, + "general": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.MycnfCloneParam": { + "type": "object", + "required": [ + "persistent", + "restart", + "src_instance", + "tgt_instance" + ], + "properties": { + "items": { + "description": "需要克隆哪些变量, 考虑到不同版本参数不一样,这里不要求指定一定存在; 只修改 mysqld 区。即失败忽略\n有些参数是 readonly 的,只会保存到 my.cnf 中,如果与运行值不一样需要用户重启\n默认值见 MycnfCloneItemsDefault", + "type": "array", + "items": { + "type": "string" + } + }, + "persistent": { + "description": "是否持久化到 my.cnf 文件,0: 不持久化,1: 持久化, 2: 仅持久化但不修改运行时", + "type": "integer", + "enum": [ + 0, + 1, + 2 + ], + "example": 1 + }, + "restart": { + "description": "指定是否 允许重启, 0:不重启, 1: 重启, 2:根据 items need_restart 自动判断是否重启", + "type": "integer", + "enum": [ + 0, + 1, + 2 + ], + "example": 2 + }, + "src_instance": { + "description": "参数克隆,获取源实例,可以提供 repl 账号权限", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.InsObject" + } + ] + }, + "tgt_instance": { + "description": "应用到本地目标实例,需要有 ADMIN 权限", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.InsObject" + } + ] + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.OSCmdRunResp": { + "type": "object", + "properties": { + "code": { + "type": "integer" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SimpleCmdResult" + } + }, + "message": { + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.OSCmds": { + "type": "object", + "properties": { + "cmds": { + "type": "array", + "items": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SimpleCmd" + } + }, + "run_user": { + "type": "string" + }, + "work_dir": { + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.PtTableChecksumComp": { + "type": "object", + "properties": { + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.PtTableChecksumParam" + }, + "general": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.PtTableChecksumParam": { + "type": "object", + "properties": { + "bk_biz_id": { + "description": "业务 id", + "type": "integer" + }, + "cluster_id": { + "description": "集群 id", + "type": "integer" + }, + "db_patterns": { + "description": "库表过滤选项", + "type": "array", + "items": { + "type": "string" + } + }, + "ignore_dbs": { + "description": "库表过滤选项", + "type": "array", + "items": { + "type": "string" + } + }, + "ignore_tables": { + "description": "库表过滤选项", + "type": "array", + "items": { + "type": "string" + } + }, + "immute_domain": { + "description": "集群域名", + "type": "string" + }, + "inner_role": { + "description": "执行校验的 db inner role, 应该是[master, repeater]", + "type": "string" + }, + "master_access_slave_password": { + "description": "从 db 访问 slave 的密码", + "type": "string" + }, + "master_access_slave_user": { + "description": "从 db 访问 slave 的用户名", + "type": "string" + }, + "master_ip": { + "description": "执行校验的 db ip", + "type": "string" + }, + "master_port": { + "description": "执行校验的 db port", + "type": "integer" + }, + "replicate_table": { + "description": "结果表, 带库前缀", + "type": "string" + }, + "runtime_hour": { + "description": "校验运行时长", + "type": "integer" + }, + "slaves": { + "description": "slave 列表", + "type": "array", + "items": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SlaveInfo" + } + }, + "system_dbs": { + "description": "系统表", + "type": "array", + "items": { + "type": "string" + } + }, + "table_patterns": { + "description": "库表过滤选项", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SemanticDumpSchemaComp": { + "type": "object", + "properties": { + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.DumpSchemaParam" + }, + "general": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SimpleCmd": { + "type": "object", + "properties": { + "cmd_args": { + "type": "array", + "items": { "type": "string" } }, - "level_name": { - "description": "配置层级名,当前允许值 `app`,`module`,`cluster`\n配合 flag_locked 锁定标记,可以知道 锁定级别", - "type": "string", - "enum": [ - "plat", - "app", - "module", - "cluster" - ] - }, - "level_value": { - "description": "配置层级值", + "cmd_name": { "type": "string" - }, - "method": { - "description": "method must be one of GenerateOnly|GenerateAndSave|GenerateAndPublish\n`GenerateOnly`: generate merged config\n`GenerateAndSave`: generate and save the merged config to db (snapshot).\n`GenerateAndPublish`: generate and save the merged config to db, and mark it as published (release)", - "type": "string", - "enum": [ - "GenerateAndSave", - "GenerateAndPublish" - ] - }, - "namespace": { - "description": "命名空间,一般指DB类型", - "type": "string", - "example": "tendbha" } } }, - "GenerateConfigResp": { + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SimpleCmdResult": { "type": "object", - "required": [ - "level_name" - ], "properties": { - "bk_biz_id": { + "cmd_stderr": { "type": "string" }, - "conf_file": { + "cmd_stdout": { "type": "string" }, - "content": { - "description": "content is a {conf_name:conf_type} dict like {\"a\":1, \"b\":\"string\"}", - "type": "object", - "additionalProperties": true - }, - "level_name": { - "description": "配置层级名,当前允许值 `app`,`module`,`cluster`\n配合 flag_locked 锁定标记,可以知道 锁定级别", - "type": "string", - "enum": [ - "plat", - "app", - "module", - "cluster" - ] - }, - "level_value": { - "description": "配置层级值", + "err_msg": { "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SlaveInfo": { + "type": "object", + "properties": { + "id": { + "description": "slave id", + "type": "integer" }, - "revision": { - "description": "version name for this config_file generation", + "ip": { + "description": "slave ip", "type": "string" + }, + "port": { + "description": "slave port", + "type": "integer" } } }, - "GetConfigItemsReq": { + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SuperAccount": { "type": "object", "required": [ - "bk_biz_id", - "conf_file", - "conf_type", - "format", - "level_name", - "namespace" + "pwd", + "user" ], "properties": { - "bk_biz_id": { - "description": "业务ID,必选项", - "type": "string", - "example": "testapp" - }, - "conf_file": { - "description": "配置文件名,一般配置类型与配置文件一一对应,但如 mysql 5.6, 5.7 两个版本同属 dbconf 配置,所以有 MySQL-5.5, MySQL-5.6 两个配置文件", - "type": "string", - "example": "MySQL-5.7" - }, - "conf_name": { - "description": "指定要查询的 conf_name, 多个值以,分隔,为空表示查询该 conf_file 的所有conf_name", - "type": "string" - }, - "conf_type": { - "description": "配置类型,如 dbconf,backup", - "type": "string", - "example": "dbconf" - }, - "format": { - "description": "`map.`, `map#`, `map|` 是特殊的map格式,返回结果会以 . 或者 # 或者 | 拆分 conf_name", - "type": "string", - "enum": [ - "list", - "map", - "map.", - "map#", - "map|" - ] - }, - "level_info": { - "description": "上层级信息,如获取当前层级 cluster=c1 的配置,需要设置 level_info: {\"module\": \"m1\"} 提供cluster所属上级 module 的信息\n非必选项,目前只在查询 cluster 级别配置时需要指定模块信息有用\ntodo 将来可能本配置中心,直接请求dbmeta元数据来获取 可能的 app-module-cluster-host-instance 关系", - "type": "object", - "additionalProperties": { + "access_hosts": { + "type": "array", + "items": { "type": "string" } }, - "level_name": { - "description": "配置层级名,当前允许值 `app`,`module`,`cluster`\n配合 flag_locked 锁定标记,可以知道 锁定级别", - "type": "string", - "enum": [ - "plat", - "app", - "module", - "cluster" - ] - }, - "level_value": { - "description": "配置层级值", + "pwd": { "type": "string" }, - "namespace": { - "description": "命名空间,一般指DB类型", - "type": "string", - "example": "tendbha" + "user": { + "type": "string" } } }, - "GetConfigItemsResp": { + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.UnInstallMySQLParam": { "type": "object", "required": [ - "conf_file", - "conf_type", - "level_name", - "namespace" + "host", + "ports" ], "properties": { - "bk_biz_id": { - "type": "string" + "force": { + "description": "是否强制下架mysqld", + "type": "boolean" }, - "conf_file": { - "description": "配置文件名,一般配置类型与配置文件一一对应,但如 mysql 5.6, 5.7 两个版本同属 dbconf 配置,所以有 MySQL-5.5, MySQL-5.6 两个配置文件", - "type": "string", - "example": "MySQL-5.7" - }, - "conf_file_lc": { - "description": "配置文件中文名,也可以是其它 locale 语言类型", - "type": "string", - "example": "5.7_参数配置" - }, - "conf_type": { - "description": "配置类型,如 dbconf,backup", - "type": "string", - "example": "dbconf" - }, - "conf_type_lc": { - "description": "配置类型中文名", - "type": "string", - "example": "DB参数配置" - }, - "content": { - "description": "content is a {conf_name:conf_type} dict like {\"a\":1, \"b\":\"string\"}", - "type": "object", - "additionalProperties": true - }, - "created_at": { + "host": { "type": "string" }, - "description": { - "description": "配置文件的描述", + "ports": { + "description": "被监控机器的上所有需要监控的端口", + "type": "array", + "items": { + "type": "integer" + } + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.logicBackupDataOption": { + "type": "object", + "properties": { + "DataSchemaGrant": { + "description": "\"grant,schema,data\"", "type": "string" - }, - "level_name": { - "description": "配置层级名,当前允许值 `app`,`module`,`cluster`\n配合 flag_locked 锁定标记,可以知道 锁定级别", - "type": "string", - "enum": [ - "plat", - "app", - "module", - "cluster" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_grant.GrantReplComp": { + "type": "object", + "properties": { + "db": { + "description": "本地db链接", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.DbWorker" + } ] }, - "level_value": { - "description": "配置层级值", - "type": "string" - }, - "namespace": { - "description": "命名空间,一般指DB类型", - "type": "string", - "example": "tendbha" - }, - "namespace_info": { - "description": "namespace信息,比如数据库版本,与 conf_file 对应", - "type": "string", - "example": "MySQL 5.7" + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_grant.GrantReplParam" }, - "updated_at": { + "general": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_grant.GrantReplParam": { + "type": "object", + "properties": { + "host": { + "description": "当前实例的主机地址", "type": "string" }, - "updated_by": { - "type": "string" + "port": { + "description": "当前实例的端口", + "type": "integer" + }, + "repl_hosts": { + "description": "slave host", + "type": "array", + "items": { + "type": "string" + } } } }, - "ListConfigVersionsResp": { + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.MySQLBinlogUtil": { "type": "object", - "required": [ - "level_name" - ], "properties": { - "bk_biz_id": { + "databases": { + "description": "row event 解析指定 databases", + "type": "array", + "items": { + "type": "string" + } + }, + "databases_ignore": { + "description": "row event 解析指定 忽略 databases", + "type": "array", + "items": { + "type": "string" + } + }, + "filter_statement_match_error": { + "description": "匹配字符串成功,则解析 binlog 报错", + "type": "string" + }, + "filter_statement_match_ignore": { + "description": "匹配字符串成功,则忽略语句,加入注释中", "type": "string" }, - "conf_file": { + "filter_statement_match_ignore_force": { + "description": "匹配字符串成功,强制忽略语句,加入注释中。当与 filter_statement_match_error 都匹配时,ignore_force会优先生效\n默认 infodba_schema", "type": "string" }, - "level_name": { - "description": "配置层级名,当前允许值 `app`,`module`,`cluster`\n配合 flag_locked 锁定标记,可以知道 锁定级别", + "flashback": { + "description": "是否启用 flashback", + "type": "boolean" + }, + "idempotent_mode": { + "description": "是否开启幂等模式, mysql --slave-exec-mode=idempotent or mysqlbinlog --idempotent", + "type": "boolean" + }, + "mysql_client_opt": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.MySQLClientOpt" + }, + "not_write_binlog": { + "description": "导入时是否记录 binlog, mysql sql_log_bin=0 or mysqlbinlog --disable-log-bin. true表示不写", + "type": "boolean" + }, + "query_event_handler": { + "description": "query event 默认处理策略。keep:保留解析出的query event 语句, ignore:注释(丢弃)该 query event, error:认为是不接受的语句,报错\n默认 keep", "type": "string", "enum": [ - "plat", - "app", - "module", - "cluster" + "keep", + "ignore", + "safe", + "error" ] }, - "level_value": { - "description": "配置层级值", + "rewrite_db": { + "description": "--rewrite_db=\"db1-\u003exx_db1,db2-\u003exx_db2\"", "type": "string" }, - "namespace": { + "start_pos": { + "description": "--start-position", + "type": "integer" + }, + "start_time": { + "description": "--start-datetime", "type": "string" }, - "published": { - "description": "version published. empty when published version is not in versions", + "stop_pos": { + "description": "--stop-position", + "type": "integer" + }, + "stop_time": { + "description": "--stop-datetime", "type": "string" }, - "versions": { - "description": "版本列表,格式 [{\"revision\":\"v1\", \"rows_affected\":1},{\"revision\":\"v2\", \"rows_affected\":2}]", + "tables": { + "description": "row event 解析指定 tables", "type": "array", "items": { - "type": "object", - "additionalProperties": true + "type": "string" + } + }, + "tables_ignore": { + "description": "row event 解析指定 忽略 tables", + "type": "array", + "items": { + "type": "string" } } } }, - "PublishConfigFileReq": { + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.MySQLClientOpt": { + "type": "object", + "properties": { + "binary_mode": { + "description": "是否启用 --binary-mode", + "type": "boolean" + }, + "max_allowed_packet": { + "type": "integer" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RecoverBinlog": { "type": "object", "required": [ - "bk_biz_id", - "conf_file", - "conf_type", - "namespace", - "revision" + "binlog_dir", + "binlog_files", + "recover_opt", + "tgt_instance", + "work_dir" ], "properties": { - "bk_biz_id": { - "description": "业务ID,必选项", + "binlog_dir": { + "description": "恢复时 binlog 存放目录,一般是下载目录", "type": "string", - "example": "testapp" + "example": "/data/dbbak/123456/binlog" + }, + "binlog_files": { + "description": "binlog列表", + "type": "array", + "items": { + "type": "string" + } }, - "cluster": { + "binlog_start_file": { + "description": "指定要开始应用的第 1 个 binlog。如果指定,一般要设置 start_pos,如果不指定则使用 start_time", "type": "string" }, - "conf_file": { - "description": "配置文件名,一般配置类型与配置文件一一对应,但如 mysql 5.6, 5.7 两个版本同属 dbconf 配置,所以有 MySQL-5.5, MySQL-5.6 两个配置文件", - "type": "string", - "example": "MySQL-5.7" + "parse_concurrency": { + "description": "解析的并发度,默认 1", + "type": "integer" }, - "conf_type": { - "description": "配置类型,如 dbconf,backup", - "type": "string", - "example": "dbconf" + "parse_only": { + "description": "仅解析 binlog,不做导入", + "type": "boolean" + }, + "quick_mode": { + "description": "如果启用 quick_mode,解析 binlog 时根据 filter databases 等选项过滤 row event,对 query event 会全部保留 。需要 mysqlbinlog 工具支持 --tables 选项,可以指定参数的 tools\n当 quick_mode=false 时,recover_opt 里的 databases 等选项无效,会应用全部 binlog", + "type": "boolean" }, - "namespace": { - "description": "命名空间,一般指DB类型", + "recover_opt": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.MySQLBinlogUtil" + }, + "source_binlog_format": { "type": "string", - "example": "tendbha" + "enum": [ + "", + "ROW", + "STATEMENT", + "MIXED" + ] }, - "patch": { - "description": "patch will overwrite conf_value to versioned config_file. it's a key-value dict", + "tgt_instance": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.InsObject" + }, + "tools": { + "description": "外部指定工具路径", "type": "object", "additionalProperties": { "type": "string" } }, - "revision": { - "description": "the version you want to publish", + "work_dir": { + "description": "binlog 解析所在目录,存放运行日志", + "type": "string", + "example": "/data/dbbak/" + }, + "work_id": { "type": "string", - "example": "v_20220309161928" + "example": "123456" } } }, - "QueryConfigNamesResp": { + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RecoverBinlogComp": { "type": "object", "properties": { - "conf_file": { - "type": "string" + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RecoverBinlog" }, - "conf_names": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/api.ConfNameDef" - } + "general": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam" } } }, - "api.ConfFileDef": { + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RestoreDRComp": { "type": "object", - "required": [ - "conf_file", - "conf_type", - "namespace" - ], "properties": { - "conf_file": { - "description": "配置文件名,一般配置类型与配置文件一一对应,但如 mysql 5.6, 5.7 两个版本同属 dbconf 配置,所以有 MySQL-5.5, MySQL-5.6 两个配置文件", - "type": "string", - "example": "MySQL-5.7" - }, - "conf_file_lc": { - "description": "配置文件中文名,也可以是其它 locale 语言类型", - "type": "string", - "example": "5.7_参数配置" - }, - "conf_type": { - "description": "配置类型,如 dbconf,backup", - "type": "string", - "example": "dbconf" - }, - "conf_type_lc": { - "description": "配置类型中文名", - "type": "string", - "example": "DB参数配置" - }, - "description": { - "description": "配置文件的描述", - "type": "string" + "extend": { + "description": "恢复参数,会复制给具体的 Restore 实现. 见 ChooseType 方法", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RestoreParam" + } + ] }, - "namespace": { - "description": "命名空间,一般指DB类型", - "type": "string", - "example": "tendbha" + "general": { + "description": "通用参数", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam" + } + ] }, - "namespace_info": { - "description": "namespace信息,比如数据库版本,与 conf_file 对应", - "type": "string", - "example": "MySQL 5.7" + "resume": { + "description": "是否是中断后继续执行", + "type": "boolean" } } }, - "api.ConfNameDef": { + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RestoreOpt": { "type": "object", - "required": [ - "conf_name", - "value_type" - ], "properties": { - "conf_name": { - "description": "配置项,也叫参数项", - "type": "string" - }, - "conf_name_lc": { - "description": "配置项中文名,可不填", - "type": "string" - }, - "description": { - "description": "配置项说明", - "type": "string" - }, - "flag_disable": { - "description": "是否禁用,代表该配置项状态. 默认0启用", - "type": "integer", - "example": 0 + "databases": { + "description": "恢复哪些 db,当前只对 逻辑恢复有效", + "type": "array", + "items": { + "type": "string" + } }, - "flag_locked": { - "description": "是否锁定. 默认0", - "type": "integer", - "example": 0 + "enable_binlog": { + "description": "EnableBinlog 导入数据时是否写binlog,默认不启用", + "type": "boolean" }, - "need_restart": { - "description": "是否需要重启生效. 默认1", - "type": "integer", - "example": 1 + "ignore_databases": { + "type": "array", + "items": { + "type": "string" + } }, - "value_allowed": { - "description": "允许设定值,如枚举/范围等,为空时表示不限制范围\n当 value_type_sub=ENUM 时,value_allowed 格式 0|1 或者 ON|OFF 或者 aaa|bbb|ccc , 会校验value的合法性\n当 value_type_sub=REGEX 时,会根据 value_allowed 进行正则校验\n当 value_type_sub=RANGE 时,也会校验value 范围的合法性.\n - BYTES 是一种特殊的RANGE,value允许1mm 但value_allowed 必须是数字的range", - "type": "string" + "ignore_tables": { + "type": "array", + "items": { + "type": "string" + } }, - "value_default": { - "description": "配置项默认值", - "type": "string", - "example": "1" + "recover_binlog": { + "description": "在指定时间点回档场景才需要,是否恢复 binlog。在 doSlave 场景,是不需要 recover_binlog。这个选项是控制下一步恢复binlog的行为\n当 recover_binlog 时,要确保实例的所有库表结构都恢复。在逻辑回档场景,只回档部分库表数据时,依然要恢复所有表结构", + "type": "boolean" }, - "value_type": { - "description": "配置项的值类型,如 `STRING`,`INT`,`FLOAT`,`NUMBER`", - "type": "string", - "enum": [ - "STRING", - "INT", - "FLOAT", - "NUMBER" - ], - "example": "STRING" + "recover_privs": { + "type": "boolean" }, - "value_type_sub": { - "description": "value_type 的子类型,如果设置则用于校验 value_type 的具体类型,或者返回用于告知前端控件类型,例如 ENUM,RANGE", + "source_binlog_format": { + "description": "在库表级定点回档时有用,如果是 statement/mixed 格式,导入数据时需要全部导入;\n如果是 row,可只导入指定库表数据, 在 recover-binlog 时可指定 quick_mode=true 也恢复指定库表 binlog", "type": "string", "enum": [ "", - "STRING", - "ENUM", - "RANGE", - "BYTES", - "REGEX", - "JSON", - "COMPLEX" - ], - "example": "ENUM" + "ROW", + "STATEMENT", + "MIXED" + ] + }, + "tables": { + "type": "array", + "items": { + "type": "string" + } } } }, - "api.GetVersionedDetailResp": { + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RestoreParam": { "type": "object", + "required": [ + "backup_dir", + "backup_files", + "work_dir" + ], "properties": { - "configs": { - "description": "配置项", - "type": "object", - "additionalProperties": true + "backup_dir": { + "description": "备份文件所在本地目录,理论上doDr不会对该目录写入,而是写入 targetDir", + "type": "string", + "example": "/data/dbbak" }, - "configs_diff": { - "description": "与上一个版本的差异", + "backup_files": { + "description": "备份文件名列表,key 是 info|full|priv|index, value 是是相对于 backup_dir 的文件名列表", "type": "object", - "additionalProperties": true - }, - "content": { - "type": "string" - }, - "created_at": { - "description": "发布时间", - "type": "string" - }, - "created_by": { - "description": "发布人", - "type": "string" - }, - "description": { - "type": "string" - }, - "id": { - "type": "integer" + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } }, - "is_published": { - "type": "integer" + "change_master": { + "description": "恢复完成后是否执行 change master,会 change master 到 src_instance", + "type": "boolean" }, - "pre_revision": { - "description": "上一个版本好", - "type": "string" + "restore_opts": { + "description": "恢复选项,比如恢复库表、是否导入binlog等。目前只对逻辑恢复有效", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RestoreOpt" + } + ] }, - "revision": { - "description": "版本号", - "type": "string" + "src_instance": { + "description": "备份实例的 ip port,用于生产 change master 语句。如果 host 为空,表示不检查、不生成change master,恢复spider节点时使用", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.Instance" + } + ] }, - "rows_affected": { - "description": "相对上一个版本 影响行数", - "type": "integer" - } - } - }, - "api.HTTPClientErrResp": { - "type": "object", - "properties": { - "code": { - "type": "integer", - "example": 400 + "tgt_instance": { + "description": "恢复本地的目标实例", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.InsObject" + } + ] }, - "data": {}, - "message": { - "description": "status bad request", + "tools": { + "description": "恢复用到的客户端工具,不提供时会有默认值", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_tools.ToolSet" + } + ] + }, + "work_dir": { + "description": "备份恢复目录,工作目录", "type": "string", - "example": "输入参数错误" - } - } - }, - "api.HTTPOkNilResp": { - "type": "object", - "properties": { - "code": { - "type": "integer", - "example": 200 + "example": "/data1/dbbak" }, - "data": {}, - "message": { + "work_id": { + "description": "work_id 标识本次恢复,若为0则为当前时间戳", "type": "string" } } }, - "api.ListConfFileResp": { + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_rollback.Flashback": { "type": "object", "required": [ - "conf_file", - "conf_type", - "namespace" + "recover_opt", + "target_time", + "tgt_instance", + "work_dir" ], "properties": { - "conf_file": { - "description": "配置文件名,一般配置类型与配置文件一一对应,但如 mysql 5.6, 5.7 两个版本同属 dbconf 配置,所以有 MySQL-5.5, MySQL-5.6 两个配置文件", - "type": "string", - "example": "MySQL-5.7" + "binlog_dir": { + "description": "当 binlog_dir 不为空,表示 binlog 已下载;当为空时,目前只从本地软连接", + "type": "string" }, - "conf_file_lc": { - "description": "配置文件中文名,也可以是其它 locale 语言类型", - "type": "string", - "example": "5.7_参数配置" + "binlog_files": { + "description": "binlog列表,如果不提供,则自动从本地查找符合时间范围的 binlog", + "type": "array", + "items": { + "type": "string" + } }, - "conf_type": { - "description": "配置类型,如 dbconf,backup", - "type": "string", - "example": "dbconf" + "parse_concurrency": { + "description": "解析binlog并发度", + "type": "integer" }, - "conf_type_lc": { - "description": "配置类型中文名", - "type": "string", - "example": "DB参数配置" + "recover_opt": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_rollback.RecoverOpt" }, - "created_at": { - "description": "创建时间", + "stop_time": { "type": "string" }, - "description": { - "description": "配置文件的描述", + "target_time": { + "description": "闪回的目标时间点,对应 recover-binlog 的 start_time, 精确到秒。目标实例的时区", "type": "string" }, - "namespace": { - "description": "命名空间,一般指DB类型", - "type": "string", - "example": "tendbha" + "tgt_instance": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.InsObject" }, - "namespace_info": { - "description": "namespace信息,比如数据库版本,与 conf_file 对应", - "type": "string", - "example": "MySQL 5.7" + "tools": { + "description": "外部指定工具路径", + "type": "object", + "additionalProperties": { + "type": "string" + } }, - "updated_at": { - "description": "更新时间", + "work_dir": { + "description": "binlog 解析所在目录,存放运行日志", "type": "string" }, - "updated_by": { - "description": "更新人", + "work_id": { "type": "string" } } }, - "api.SaveConfItemsReq": { + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_rollback.FlashbackComp": { "type": "object", - "required": [ - "bk_biz_id", - "level_name" - ], "properties": { - "bk_biz_id": { - "description": "业务ID,必选项", - "type": "string", - "example": "testapp" + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_rollback.Flashback" }, - "conf_file_info": { - "$ref": "#/definitions/api.ConfFileDef" - }, - "conf_items": { + "general": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_rollback.RecoverOpt": { + "type": "object", + "properties": { + "databases": { + "description": "row event 解析指定 databases", "type": "array", "items": { - "$ref": "#/definitions/api.UpsertConfItem" + "type": "string" } }, - "confirm": { - "description": "保存时如果与下层级存在冲突,提示确认,用 confirm=1 重新请求", - "type": "integer" + "databases_ignore": { + "description": "row event 解析指定 忽略 databases", + "type": "array", + "items": { + "type": "string" + } }, - "description": { - "description": "发布描述", + "filter_rows": { + "description": "暂不支持行级闪回", "type": "string" }, - "level_info": { - "description": "上层级信息,如获取当前层级 cluster=c1 的配置,需要设置 level_info: {\"module\": \"m1\"} 提供cluster所属上级 module 的信息\n非必选项,目前只在查询 cluster 级别配置时需要指定模块信息有用\ntodo 将来可能本配置中心,直接请求dbmeta元数据来获取 可能的 app-module-cluster-host-instance 关系", - "type": "object", - "additionalProperties": { + "tables": { + "description": "row event 解析指定 tables", + "type": "array", + "items": { "type": "string" } }, - "level_name": { - "description": "配置层级名,当前允许值 `app`,`module`,`cluster`\n配合 flag_locked 锁定标记,可以知道 锁定级别", - "type": "string", - "enum": [ - "plat", - "app", - "module", - "cluster" - ] + "tables_ignore": { + "description": "row event 解析指定 忽略 tables", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_spiderctl.InitClusterRoutingComp": { + "type": "object", + "properties": { + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_spiderctl.InitClusterRoutingParam" }, - "level_value": { - "description": "配置层级值", - "type": "string" + "general": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam" } } }, - "api.UpsertConfFilePlatReq": { + "dbm-services_mysql_db-tools_dbactuator_pkg_components_spiderctl.InitClusterRoutingParam": { "type": "object", "required": [ - "conf_file", - "conf_type", - "namespace", - "req_type" + "ctl_instances", + "host", + "mysql_instance_tuples", + "port", + "spider_instances", + "tdbctl_pass", + "tdbctl_user" ], "properties": { - "conf_file": { - "description": "配置文件名,一般配置类型与配置文件一一对应,但如 mysql 5.6, 5.7 两个版本同属 dbconf 配置,所以有 MySQL-5.5, MySQL-5.6 两个配置文件", - "type": "string", - "example": "MySQL-5.7" + "ctl_instances": { + "type": "array", + "items": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_spiderctl.Instance" + } }, - "conf_file_lc": { - "description": "配置文件中文名,也可以是其它 locale 语言类型", - "type": "string", - "example": "5.7_参数配置" + "host": { + "type": "string" }, - "conf_names": { - "description": "如果revision为空,表示第一次保存。每次 update 操作都会返回 revision,确保在这一轮编辑操作下都是操作这个revision\n已发布的 revision 不能编辑\nRevision string `json:\"revision\" form:\"revision\"`", + "mysql_instance_tuples": { "type": "array", "items": { - "$ref": "#/definitions/api.UpsertConfNames" + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_spiderctl.InstanceTuple" } }, - "conf_type": { - "description": "配置类型,如 dbconf,backup", - "type": "string", - "example": "dbconf" - }, - "conf_type_lc": { - "description": "配置类型中文名", - "type": "string", - "example": "DB参数配置" + "port": { + "type": "integer", + "minimum": 3306 }, - "confirm": { - "description": "保存时如果与下层级存在冲突,提示确认,用 confirm=1 重新请求", - "type": "integer" + "spider_instances": { + "type": "array", + "items": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_spiderctl.Instance" + } }, - "description": { - "description": "配置文件的描述", + "tdbctl_pass": { "type": "string" }, - "file_id": { - "description": "新建配置文件,第一次保存返回 file_id, 后续保存/发布 需传入 file_id", - "type": "integer" - }, - "namespace": { - "description": "命名空间,一般指DB类型", - "type": "string", - "example": "tendbha" - }, - "namespace_info": { - "description": "namespace信息,比如数据库版本,与 conf_file 对应", - "type": "string", - "example": "MySQL 5.7" - }, - "req_type": { - "description": "配置文件修改动作的请求类型,`SaveOnly`: 仅保存, `SaveAndPublish`保存并发布", - "type": "string", - "enum": [ - "SaveOnly", - "SaveAndPublish" - ] + "tdbctl_user": { + "type": "string" } } }, - "api.UpsertConfFilePlatResp": { + "dbm-services_mysql_db-tools_dbactuator_pkg_components_spiderctl.Instance": { "type": "object", "required": [ - "conf_file", - "conf_type", - "namespace" + "host", + "port" ], "properties": { - "conf_file": { - "description": "配置文件名,一般配置类型与配置文件一一对应,但如 mysql 5.6, 5.7 两个版本同属 dbconf 配置,所以有 MySQL-5.5, MySQL-5.6 两个配置文件", - "type": "string", - "example": "MySQL-5.7" - }, - "conf_type": { - "description": "配置类型,如 dbconf,backup", - "type": "string", - "example": "dbconf" + "host": { + "type": "string" }, - "file_id": { - "type": "integer" + "port": { + "type": "integer", + "minimum": 3306 }, - "is_published": { + "shard_id": { "type": "integer" - }, - "namespace": { - "description": "命名空间,一般指DB类型", - "type": "string", - "example": "tendbha" - }, - "revision": { - "description": "编辑配置文件,仅保存时不会产生 revision,保存并发布时才返回", - "type": "string" } } }, - "api.UpsertConfItem": { + "dbm-services_mysql_db-tools_dbactuator_pkg_components_spiderctl.InstanceTuple": { "type": "object", "required": [ - "conf_name", - "op_type" + "host", + "port", + "shard_id", + "slave_host" ], "properties": { - "conf_name": { - "description": "配置项名称", + "host": { "type": "string" }, - "conf_value": { - "type": "string" + "port": { + "type": "integer" }, - "description": { - "type": "string" + "shard_id": { + "type": "integer" }, - "extra_info": { + "slave_host": { "type": "string" - }, - "flag_disable": { - "description": "是否禁用,默认 0 表示启用. 1表示禁用", - "type": "integer", - "example": 0 - }, - "flag_locked": { - "description": "是否锁定,默认 0 表上不锁定", - "type": "integer", - "example": 0 - }, - "op_type": { - "description": "配置项修改动作,需提供操作类型字段,允许值 `add`,`update`,`remove`", - "type": "string", - "enum": [ - "add", - "update", - "remove" - ] } } }, - "api.UpsertConfItemsReq": { + "dbm-services_mysql_db-tools_dbactuator_pkg_components_tbinlogdumper.Configs": { "type": "object", "required": [ - "bk_biz_id", - "level_name", - "req_type" + "dumper_configs" ], "properties": { - "bk_biz_id": { - "description": "业务ID,必选项", - "type": "string", - "example": "testapp" - }, - "conf_file_info": { - "$ref": "#/definitions/api.ConfFileDef" - }, - "conf_items": { + "dumper_configs": { "type": "array", "items": { - "$ref": "#/definitions/api.UpsertConfItem" + "type": "integer" } + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_tbinlogdumper.DumpSchemaComp": { + "type": "object", + "properties": { + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_tbinlogdumper.DumpSchemaParam" }, - "confirm": { - "description": "保存时如果与下层级存在冲突,提示确认,用 confirm=1 重新请求", - "type": "integer" - }, - "description": { - "description": "发布描述", + "general": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_tbinlogdumper.DumpSchemaParam": { + "type": "object", + "required": [ + "charset", + "host", + "port", + "tbinlogdumper_port" + ], + "properties": { + "charset": { + "description": "字符集参数", "type": "string" }, - "level_info": { - "description": "上层级信息,如获取当前层级 cluster=c1 的配置,需要设置 level_info: {\"module\": \"m1\"} 提供cluster所属上级 module 的信息\n非必选项,目前只在查询 cluster 级别配置时需要指定模块信息有用\ntodo 将来可能本配置中心,直接请求dbmeta元数据来获取 可能的 app-module-cluster-host-instance 关系", - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "level_name": { - "description": "配置层级名,当前允许值 `app`,`module`,`cluster`\n配合 flag_locked 锁定标记,可以知道 锁定级别", - "type": "string", - "enum": [ - "plat", - "app", - "module", - "cluster" - ] - }, - "level_value": { - "description": "配置层级值", + "host": { + "description": "当前实例的主机地址", "type": "string" }, - "req_type": { - "description": "配置文件修改动作的请求类型,`SaveOnly`: 仅保存, `SaveAndPublish`保存并发布", - "type": "string", - "enum": [ - "SaveOnly", - "SaveAndPublish" - ] + "port": { + "description": "当前实例的端口", + "type": "integer", + "minimum": 3306 }, - "revision": { - "type": "string" + "tbinlogdumper_port": { + "description": "当前TBinlogdumperPort实例的端口", + "type": "integer", + "minimum": 3306 } } }, - "api.UpsertConfItemsResp": { + "dbm-services_mysql_db-tools_dbactuator_pkg_components_tbinlogdumper.DumperParams": { "type": "object", - "required": [ - "conf_file", - "conf_type", - "namespace" - ], "properties": { - "bk_biz_id": { + "area_name": { "type": "string" }, - "conf_file": { - "description": "配置文件名,一般配置类型与配置文件一一对应,但如 mysql 5.6, 5.7 两个版本同属 dbconf 配置,所以有 MySQL-5.5, MySQL-5.6 两个配置文件", - "type": "string", - "example": "MySQL-5.7" + "dumper_id": { + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_tbinlogdumper.InstallTbinlogDumperComp": { + "type": "object", + "properties": { + "dumperConfigs": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_tbinlogdumper.DumperParams" + } }, - "conf_type": { - "description": "配置类型,如 dbconf,backup", - "type": "string", - "example": "dbconf" + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_tbinlogdumper.Configs" }, - "is_published": { - "type": "integer" + "general": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam" }, - "namespace": { - "description": "命名空间,一般指DB类型", - "type": "string", - "example": "tendbha" + "renderConfigs": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_tbinlogdumper.renderDumperConfigs" + } }, - "revision": { - "description": "编辑配置文件,第一次保存返回 revision, 后续保存/发布 需传入 revision", + "timeZone": { "type": "string" } } }, - "api.UpsertConfNames": { + "dbm-services_mysql_db-tools_dbactuator_pkg_components_tbinlogdumper.Mysqld": { "type": "object", - "required": [ - "conf_name", - "op_type", - "value_type" - ], "properties": { - "conf_name": { - "description": "配置项,也叫参数项", + "area_name": { "type": "string" }, - "conf_name_lc": { - "description": "配置项中文名,可不填", + "basedir": { "type": "string" }, - "description": { - "description": "配置项说明", + "bind-address": { "type": "string" }, - "flag_disable": { - "description": "是否禁用,代表该配置项状态. 默认0启用", - "type": "integer", - "example": 0 + "character_set_server": { + "type": "string" }, - "flag_locked": { - "description": "是否锁定. 默认0", - "type": "integer", - "example": 0 + "datadir": { + "type": "string" }, - "need_restart": { - "description": "是否需要重启生效. 默认1", - "type": "integer", - "example": 1 + "dumper_id": { + "type": "string" }, - "op_type": { - "description": "配置项修改动作,需提供操作类型字段,允许值 `add`,`update`,`remove`", - "type": "string", - "enum": [ - "add", - "update", - "remove" - ] + "logdir": { + "type": "string" }, - "value_allowed": { - "description": "允许设定值,如枚举/范围等,为空时表示不限制范围\n当 value_type_sub=ENUM 时,value_allowed 格式 0|1 或者 ON|OFF 或者 aaa|bbb|ccc , 会校验value的合法性\n当 value_type_sub=REGEX 时,会根据 value_allowed 进行正则校验\n当 value_type_sub=RANGE 时,也会校验value 范围的合法性.\n - BYTES 是一种特殊的RANGE,value允许1mm 但value_allowed 必须是数字的range", + "port": { "type": "string" }, - "value_default": { - "description": "配置项默认值", - "type": "string", - "example": "1" + "server_id": { + "type": "integer" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_tbinlogdumper.UnInstallTbinlogDumperComp": { + "type": "object", + "properties": { + "generalParam": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam" }, - "value_type": { - "description": "配置项的值类型,如 `STRING`,`INT`,`FLOAT`,`NUMBER`", - "type": "string", - "enum": [ - "STRING", - "INT", - "FLOAT", - "NUMBER" - ], - "example": "STRING" + "params": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.UnInstallMySQLParam" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_tbinlogdumper.renderDumperConfigs": { + "type": "object", + "properties": { + "mysqld": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_tbinlogdumper.Mysqld" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_native.DbWorker": { + "type": "object", + "properties": { + "db": { + "$ref": "#/definitions/sql.DB" }, - "value_type_sub": { - "description": "value_type 的子类型,如果设置则用于校验 value_type 的具体类型,或者返回用于告知前端控件类型,例如 ENUM,RANGE", - "type": "string", - "enum": [ - "", - "STRING", - "ENUM", - "RANGE", - "BYTES", - "REGEX", - "JSON", - "COMPLEX" - ], - "example": "ENUM" + "dsn": { + "type": "string" } } }, - "model.ConfigModel": { + "dbm-services_mysql_db-tools_dbactuator_pkg_native.InsObject": { "type": "object", "properties": { - "bk_biz_id": { + "charset": { + "description": "连接字符集", "type": "string" }, - "conf_file": { + "host": { + "description": "当前实例的主机地址", "type": "string" }, - "conf_name": { + "options": { + "description": "其它选项", "type": "string" }, - "conf_type": { + "port": { + "description": "当前实例的端口", + "type": "integer" + }, + "pwd": { + "description": "连接当前实例的User Pwd", "type": "string" }, - "conf_value": { + "socket": { + "description": "连接socket", + "type": "string" + }, + "user": { + "description": "连接当前实例的User", "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_native.Instance": { + "type": "object", + "properties": { + "host": { + "description": "当前实例的主机地址", + "type": "string", + "example": "1.1.1.1" }, - "created_at": { + "port": { + "description": "当前实例的端口", + "type": "integer", + "example": 33060 + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_tools.ToolSet": { + "type": "object", + "properties": { + "tools": { + "description": "外部指定工具路径", + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_util_mysqlutil.ChangeMaster": { + "type": "object", + "required": [ + "master_host", + "master_password", + "master_port", + "master_user" + ], + "properties": { + "change_sql": { "type": "string" }, - "description": { + "channel": { "type": "string" }, - "extra_info": { + "executed_gtid_set": { "type": "string" }, - "flag_disable": { - "type": "integer" + "force": { + "description": "如果当前实例存在主从关系是否直接reset slave后,强制change master", + "type": "boolean" }, - "flag_locked": { - "type": "integer" + "is_gtid": { + "description": "是否启动GID方式进行建立主从", + "type": "boolean" }, - "id": { + "master_auto_position": { "type": "integer" }, - "level_locked": { + "master_host": { + "description": "主库ip", "type": "string" }, - "level_name": { + "master_log_file": { + "description": "binlog 文件名称", "type": "string" }, - "level_value": { + "master_log_pos": { + "description": "binlog 位点信息", + "type": "integer" + }, + "master_password": { "type": "string" }, - "namespace": { + "master_port": { + "description": "主库端口", + "type": "integer", + "minimum": 3306 + }, + "master_user": { "type": "string" }, - "updated_at": { + "max_tolerate_delay": { + "description": "最大容忍延迟,即主从延迟小于该值,认为建立主从关系成功", + "type": "integer" + } + } + }, + "internal_subcmd_commoncmd.RMLargeFileParam": { + "type": "object", + "required": [ + "bw_limit_mb", + "filename" + ], + "properties": { + "bw_limit_mb": { + "description": "删除速度,MB/s,默认 30", + "type": "integer", + "default": 30, + "maximum": 1000, + "minimum": 1 + }, + "filename": { "type": "string" } } - } - }, - "securityDefinitions": { - "BasicAuth": { - "type": "basic" + }, + "sql.DB": { + "type": "object" } } } \ No newline at end of file diff --git a/dbm-services/common/db-config/docs/swagger.yaml b/dbm-services/common/db-config/docs/swagger.yaml index 16711f952a..93b6ddd41d 100644 --- a/dbm-services/common/db-config/docs/swagger.yaml +++ b/dbm-services/common/db-config/docs/swagger.yaml @@ -1,1402 +1,2295 @@ basePath: / definitions: - GenerateConfigReq: + dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam: properties: - bk_biz_id: - description: 业务ID,必选项 - example: testapp + runtime_account: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.RuntimeAccountParam' + runtime_extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.RuntimeExtend' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components.RuntimeAccountParam: + properties: + admin_pwd: + description: mysql admin 密码,环境变量 GENERAL_ACCOUNT_admin_pwd type: string - conf_file: - description: 配置文件名,一般配置类型与配置文件一一对应,但如 mysql 5.6, 5.7 两个版本同属 dbconf 配置,所以有 - MySQL-5.5, MySQL-5.6 两个配置文件 - example: MySQL-5.7 + admin_user: + description: mysql admin 账户,环境变量 GENERAL_ACCOUNT_admin_user type: string - conf_type: - description: 配置类型,如 dbconf,backup - example: dbconf + backup_pwd: + description: dbbackup pwd type: string - format: - description: '`map.`, `map#`, `map|` 是特殊的map格式,返回结果会以 . 或者 # 或者 | 拆分 conf_name' - enum: - - list - - map - - map. - - map# - - map| + backup_user: + description: dbbackup user type: string - level_info: - additionalProperties: - type: string - description: |- - 上层级信息,如获取当前层级 cluster=c1 的配置,需要设置 level_info: {"module": "m1"} 提供cluster所属上级 module 的信息 - 非必选项,目前只在查询 cluster 级别配置时需要指定模块信息有用 - todo 将来可能本配置中心,直接请求dbmeta元数据来获取 可能的 app-module-cluster-host-instance 关系 - type: object - level_name: - description: |- - 配置层级名,当前允许值 `app`,`module`,`cluster` - 配合 flag_locked 锁定标记,可以知道 锁定级别 - enum: - - plat - - app - - module - - cluster + monitor_access_all_pwd: + description: mysql monitor@% 密码 type: string - level_value: - description: 配置层级值 + monitor_access_all_user: + description: mysql monitor@% type: string - method: - description: |- - method must be one of GenerateOnly|GenerateAndSave|GenerateAndPublish - `GenerateOnly`: generate merged config - `GenerateAndSave`: generate and save the merged config to db (snapshot). - `GenerateAndPublish`: generate and save the merged config to db, and mark it as published (release) - enum: - - GenerateAndSave - - GenerateAndPublish + monitor_pwd: + description: mysql monitor 密码,环境变量 GENERAL_ACCOUNT_monitor_pwd type: string - namespace: - description: 命名空间,一般指DB类型 - example: tendbha + monitor_user: + description: mysql monitor 账户,环境变量 GENERAL_ACCOUNT_monitor_user type: string - required: - - bk_biz_id - - conf_file - - conf_type - - format - - level_name - - method - - namespace - type: object - GenerateConfigResp: - properties: - bk_biz_id: + proxy_admin_pwd: + description: proxy admin pwd type: string - conf_file: + proxy_admin_user: + description: proxy admin user type: string - content: - additionalProperties: true - description: content is a {conf_name:conf_type} dict like {"a":1, "b":"string"} - type: object - level_name: - description: |- - 配置层级名,当前允许值 `app`,`module`,`cluster` - 配合 flag_locked 锁定标记,可以知道 锁定级别 - enum: - - plat - - app - - module - - cluster + repl_pwd: + description: repl pwd, 环境变量 GENERAL_ACCOUNT_repl_pwd type: string - level_value: - description: 配置层级值 + repl_user: + description: repl user, 环境变量 GENERAL_ACCOUNT_repl_user type: string - revision: - description: version name for this config_file generation + tdbctl_pwd: type: string - required: - - level_name - type: object - GetConfigItemsReq: - properties: - bk_biz_id: - description: 业务ID,必选项 - example: testapp + tdbctl_user: type: string - conf_file: - description: 配置文件名,一般配置类型与配置文件一一对应,但如 mysql 5.6, 5.7 两个版本同属 dbconf 配置,所以有 - MySQL-5.5, MySQL-5.6 两个配置文件 - example: MySQL-5.7 + yw_pwd: + description: yw pwd type: string - conf_name: - description: 指定要查询的 conf_name, 多个值以,分隔,为空表示查询该 conf_file 的所有conf_name + yw_user: + description: yw user type: string - conf_type: - description: 配置类型,如 dbconf,backup - example: dbconf + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components.RuntimeExtend: + properties: + mysql_sys_users: + items: + type: string + type: array + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.DFHttpParam: + properties: + auth_pass: + description: http url basic auth pass type: string - format: - description: '`map.`, `map#`, `map|` 是特殊的map格式,返回结果会以 . 或者 # 或者 | 拆分 conf_name' - enum: - - list - - map - - map. - - map# - - map| + auth_user: + description: http url basic auth user type: string - level_info: - additionalProperties: + bk_biz_id: + type: integer + bwlimit_mb: + description: 单文件下载限速,单位 MB/s + type: integer + curl_options: + items: type: string - description: |- - 上层级信息,如获取当前层级 cluster=c1 的配置,需要设置 level_info: {"module": "m1"} 提供cluster所属上级 module 的信息 - 非必选项,目前只在查询 cluster 级别配置时需要指定模块信息有用 - todo 将来可能本配置中心,直接请求dbmeta元数据来获取 可能的 app-module-cluster-host-instance 关系 - type: object - level_name: - description: |- - 配置层级名,当前允许值 `app`,`module`,`cluster` - 配合 flag_locked 锁定标记,可以知道 锁定级别 - enum: - - plat - - app - - module - - cluster + type: array + curl_path: + description: curl 命令路径,默认留空. 目前只用于测试 url type: string - level_value: - description: 配置层级值 + file_list: + description: 下载哪些文件 + items: + type: string + type: array + max_concurrency: + description: 并发下载数 + type: integer + path_tgt: + description: 文件存放到本机哪个目录 type: string - namespace: - description: 命名空间,一般指DB类型 - example: tendbha + server: + description: 下载 url type: string required: - - bk_biz_id - - conf_file - - conf_type - - format - - level_name - - namespace + - file_list + - path_tgt + - server type: object - GetConfigItemsResp: + dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.DFScpParam: properties: bk_biz_id: + type: integer + bwlimit_mb: + description: 单文件下载限速,单位 MB/s + type: integer + file_src: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.FileSrc' + description: 下载源 + file_tgt: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.FileTgt' + description: 下载目标 + max_concurrency: + description: 并发下载数 + type: integer + required: + - file_src + - file_tgt + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.FileSrc: + properties: + file_list: + description: 源文件名列表,相对上面的 path + items: + type: string + type: array + match: type: string - conf_file: - description: 配置文件名,一般配置类型与配置文件一一对应,但如 mysql 5.6, 5.7 两个版本同属 dbconf 配置,所以有 - MySQL-5.5, MySQL-5.6 两个配置文件 - example: MySQL-5.7 - type: string - conf_file_lc: - description: 配置文件中文名,也可以是其它 locale 语言类型 - example: 5.7_参数配置 - type: string - conf_type: - description: 配置类型,如 dbconf,backup - example: dbconf + path: + description: 源文件所在目录 type: string - conf_type_lc: - description: 配置类型中文名 - example: DB参数配置 + ssh_host: type: string - content: - additionalProperties: true - description: content is a {conf_name:conf_type} dict like {"a":1, "b":"string"} - type: object - created_at: + ssh_pass: type: string - description: - description: 配置文件的描述 + ssh_port: type: string - level_name: - description: |- - 配置层级名,当前允许值 `app`,`module`,`cluster` - 配合 flag_locked 锁定标记,可以知道 锁定级别 - enum: - - plat - - app - - module - - cluster + ssh_user: type: string - level_value: - description: 配置层级值 + required: + - file_list + - path + - ssh_host + - ssh_port + - ssh_user + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.FileTgt: + properties: + path: + description: 文件下载目标目录 type: string - namespace: - description: 命名空间,一般指DB类型 - example: tendbha + required: + - path + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSBaseInfo: + properties: + key: + description: 16位字串,由备份系统分配,可从环境变量获取 IBS_INFO__key type: string - namespace_info: - description: namespace信息,比如数据库版本,与 conf_file 对应 - example: MySQL 5.7 + sys_id: + description: application标识,亦即哪个系统需要访问本接口,可从环境变量获取 IBS_INFO_sys_id type: string - updated_at: + ticket: + description: OA验证的ticket,一个长串,通常附加在访问内网应用的URL上,主要用来验证用户身份,可以留空 type: string - updated_by: + url: + description: |- + ieg 备份系统 api url 地址,会在后面拼接 /query /recover 后缀进行请求 + 可从环境变量获取 IBS_INFO_url + example: http://127.0.0.1/backupApi type: string required: - - conf_file - - conf_type - - level_name - - namespace + - key + - sys_id + - url type: object - ListConfigVersionsResp: + dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSQueryComp: properties: - bk_biz_id: + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSQueryParam' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSQueryForRecover: + properties: + begin_date: + description: 查询文件起始时间,备份系统以 file_last_mtime 为条件 type: string - conf_file: + end_date: + description: 哪一天提交,结束时间,与begin_date形成一个时间范围,建议begin_date与end_date形成的时间范围不要超过3天 type: string - level_name: - description: |- - 配置层级名,当前允许值 `app`,`module`,`cluster` - 配合 flag_locked 锁定标记,可以知道 锁定级别 - enum: - - plat - - app - - module - - cluster + source_ip: + description: 来源IP,即提交备份任务的机器IP type: string - level_value: - description: 配置层级值 + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSQueryParam: + properties: + begin_date: + description: 哪一天提交,起始时间 type: string - namespace: + end_date: + description: 哪一天提交,结束时间,与begin_date形成一个时间范围,建议begin_date与end_date形成的时间范围不要超过3天 type: string - published: - description: version published. empty when published version is not in versions + filename: + description: 文件名 + type: string + ibs_info: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSBaseInfo' + description: ieg backup system url and auth params + source_ip: + description: 来源IP,即提交备份任务的机器IP type: string - versions: - description: 版本列表,格式 [{"revision":"v1", "rows_affected":1},{"revision":"v2", - "rows_affected":2}] - items: - additionalProperties: true - type: object - type: array required: - - level_name + - begin_date + - end_date + - filename + - ibs_info + - source_ip type: object - PublishConfigFileReq: + dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSQueryResult: properties: - bk_biz_id: - description: 业务ID,必选项 - example: testapp + bkstif: + description: '备份状态信息, ''done, success'', ''Fail: bad md5'' 等' type: string - cluster: + createTime: + description: 非备份系统字段,全备(截取文件名中的字段),binlog 打开文件读取 type: string - conf_file: - description: 配置文件名,一般配置类型与配置文件一一对应,但如 mysql 5.6, 5.7 两个版本同属 dbconf 配置,所以有 - MySQL-5.5, MySQL-5.6 两个配置文件 - example: MySQL-5.7 + expire_time: type: string - conf_type: - description: 配置类型,如 dbconf,backup - example: dbconf + expired: type: string - namespace: - description: 命名空间,一般指DB类型 - example: tendbha + file_last_mtime: + description: 文件最后修改时间 type: string - patch: - additionalProperties: - type: string - description: patch will overwrite conf_value to versioned config_file. it's - a key-value dict - type: object - revision: - description: the version you want to publish - example: v_20220309161928 + file_name: type: string - required: - - bk_biz_id - - conf_file - - conf_type - - namespace - - revision - type: object - QueryConfigNamesResp: - properties: - conf_file: + file_tag: type: string - conf_names: - additionalProperties: - $ref: '#/definitions/api.ConfNameDef' - type: object - type: object - api.ConfFileDef: - properties: - conf_file: - description: 配置文件名,一般配置类型与配置文件一一对应,但如 mysql 5.6, 5.7 两个版本同属 dbconf 配置,所以有 - MySQL-5.5, MySQL-5.6 两个配置文件 - example: MySQL-5.7 + md5: type: string - conf_file_lc: - description: 配置文件中文名,也可以是其它 locale 语言类型 - example: 5.7_参数配置 + path: + description: 非备份系统字段 type: string - conf_type: - description: 配置类型,如 dbconf,backup - example: dbconf + size: + description: 文件大小 type: string - conf_type_lc: - description: 配置类型中文名 - example: DB参数配置 + source_ip: + description: 上报该备份任务的IP type: string - description: - description: 配置文件的描述 + source_port: + description: 非备份系统字段 type: string - namespace: - description: 命名空间,一般指DB类型 - example: tendbha + status: + description: 文件状态 type: string - namespace_info: - description: namespace信息,比如数据库版本,与 conf_file 对应 - example: MySQL 5.7 + task_id: + description: 任务ID,用于下载 + type: string + uptime: + description: 备份任务上报时间 type: string - required: - - conf_file - - conf_type - - namespace type: object - api.ConfNameDef: + dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSRecoverComp: properties: - conf_name: - description: 配置项,也叫参数项 - type: string - conf_name_lc: - description: 配置项中文名,可不填 - type: string - description: - description: 配置项说明 - type: string - flag_disable: - description: 是否禁用,代表该配置项状态. 默认0启用 - example: 0 - type: integer - flag_locked: - description: 是否锁定. 默认0 - example: 0 - type: integer - need_restart: - description: 是否需要重启生效. 默认1 - example: 1 - type: integer - value_allowed: + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSRecoverParam' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSRecoverParam: + properties: + dest_ip: + description: 目标IP,文件恢复到哪一台机器上的 + example: 1.1.1.1 + type: string + diretory: + description: diretory 是备份系统参数错误拼写 + example: /data/dbbak + type: string + ibs_info: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSBaseInfo' + description: ieg backup system url and auth params + ibs_query: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSQueryForRecover' + description: 根据文件名下载,或者判断是否跳过下载时,需要提供 ibs_query 参数用于查询 + login_passwd: + description: 登录 dest_ip 的用户名的密码, ieg 传统scp 方式下载才需要。如果是 cos 下载则不需要 + type: string + login_user: + description: 登录 dest_ip 的用户名,下载后的文件属组是该用户 + type: string + reason: + description: 恢复原因(备注用途) + type: string + skip_local_exists: + description: 如果本地目标目录已经存在对应文件,是否保留(即跳过下载). 默认 false + example: false + type: boolean + task_files: description: |- - 允许设定值,如枚举/范围等,为空时表示不限制范围 - 当 value_type_sub=ENUM 时,value_allowed 格式 0|1 或者 ON|OFF 或者 aaa|bbb|ccc , 会校验value的合法性 - 当 value_type_sub=REGEX 时,会根据 value_allowed 进行正则校验 - 当 value_type_sub=RANGE 时,也会校验value 范围的合法性. - - BYTES 是一种特殊的RANGE,value允许1mm 但value_allowed 必须是数字的range - type: string - value_default: - description: 配置项默认值 - example: "1" - type: string - value_type: - description: 配置项的值类型,如 `STRING`,`INT`,`FLOAT`,`NUMBER` - enum: - - STRING - - INT - - FLOAT - - NUMBER - example: STRING - type: string - value_type_sub: - description: value_type 的子类型,如果设置则用于校验 value_type 的具体类型,或者返回用于告知前端控件类型,例如 - ENUM,RANGE - enum: - - "" - - STRING - - ENUM - - RANGE - - BYTES - - REGEX - - JSON - - COMPLEX - example: ENUM + 如果是精确文件名下载,用 task_files。提供需要下载的文件列表,提供 task_id 或者完整的 file_name 即可 + 如果顺便提供了 size 信息则不用请求备份系统获取大小 来决定文件是否需要重新下载 + items: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.TaskFilesExact' + type: array + task_files_wild: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.TaskFilesWild' + description: 如果是模糊匹配搜索并下载,用 task_files_wild + taskid_list: + description: taskid 列表,,逗号分隔。会根据 task_files 里的信息,追加到这里。这里一般不传值,在 task_files + 里提供 task_id 或者 file_name + example: 10000,100001 type: string required: - - conf_name - - value_type + - dest_ip + - diretory + - ibs_info + - login_user type: object - api.GetVersionedDetailResp: + dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSRecoverTask: properties: - configs: - additionalProperties: true - description: 配置项 - type: object - configs_diff: - additionalProperties: true - description: 与上一个版本的差异 - type: object - content: + file_last_mtime: + description: 文件最后修改时间 type: string - created_at: - description: 发布时间 + file_name: type: string - created_by: - description: 发布人 + file_tag: type: string - description: + md5: type: string - id: - type: integer - is_published: - type: integer - pre_revision: - description: 上一个版本好 + size: + description: 文件大小 type: string - revision: - description: 版本号 + source_ip: + description: 上报该备份任务的IP type: string - rows_affected: - description: 相对上一个版本 影响行数 - type: integer - type: object - api.HTTPClientErrResp: - properties: - code: - example: 400 - type: integer - data: {} - message: - description: status bad request - example: 输入参数错误 + status: + description: 文件状态 + type: string + task_id: + description: 任务ID,用于下载 type: string type: object - api.HTTPOkNilResp: + dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.TaskFilesExact: properties: - code: - example: 200 - type: integer - data: {} - message: + file_name: + type: string + md5: + type: string + size: + description: 文件大小 + type: string + task_id: + description: 任务ID,用于下载 type: string type: object - api.ListConfFileResp: + dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.TaskFilesWild: properties: - conf_file: - description: 配置文件名,一般配置类型与配置文件一一对应,但如 mysql 5.6, 5.7 两个版本同属 dbconf 配置,所以有 - MySQL-5.5, MySQL-5.6 两个配置文件 - example: MySQL-5.7 - type: string - conf_file_lc: - description: 配置文件中文名,也可以是其它 locale 语言类型 - example: 5.7_参数配置 + file_tag: type: string - conf_type: - description: 配置类型,如 dbconf,backup - example: dbconf + name_regex: + description: 在搜索的结果里面,应用该正则进行过滤 type: string - conf_type_lc: - description: 配置类型中文名 - example: DB参数配置 + name_search: + description: 搜索的模糊条件,不用带 * type: string - created_at: - description: 创建时间 + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_fileserver.FileServer: + properties: + acls: + description: 访问来源限制,从前往后匹配。格式 `["allow 1.1.1.1/32", "deny all"]` + example: + - allow all + items: + type: string + type: array + auth_pass: + description: http basic auth pass,为空时会随机生成密码 type: string - description: - description: 配置文件的描述 + auth_user: + description: http basic auth user type: string - namespace: - description: 命名空间,一般指DB类型 - example: tendbha + bind_address: + description: http file-server 监听地址. 不提供端口,会在 12000-19999 之间随机选择一个端口,不提供 ip + 时默认 localhost type: string - namespace_info: - description: namespace信息,比如数据库版本,与 conf_file 对应 - example: MySQL 5.7 + enable_tls: + description: 暂不支持 + type: boolean + max_connections: + description: 限制最大连接数,超过需要等待. 为 0 时表示不限制 + type: integer + mount_path: + description: 将本地哪个目录通过 http 分享 type: string - updated_at: - description: 更新时间 + path_prefix: + description: path_prefix 用在生成 url 时的路径前缀. 可留空 type: string - updated_by: - description: 更新人 + print_download: + description: 输出 download http 的信息,方便使用 + type: boolean + proc_maxidle_duration: + description: 超过最大空闲时间,自动退出. 示例 3600s, 60m, 1h + example: 1h type: string required: - - conf_file - - conf_type - - namespace + - auth_user + - bind_address + - mount_path type: object - api.SaveConfItemsReq: + dbm-services_mysql_db-tools_dbactuator_pkg_components_fileserver.FileServerComp: properties: - bk_biz_id: - description: 业务ID,必选项 - example: testapp + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_fileserver.FileServer' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BackupOptions: + properties: + BackupType: type: string - conf_file_info: - $ref: '#/definitions/api.ConfFileDef' - conf_items: - items: - $ref: '#/definitions/api.UpsertConfItem' - type: array - confirm: - description: 保存时如果与下层级存在冲突,提示确认,用 confirm=1 重新请求 - type: integer - description: - description: 发布描述 + CrontabTime: type: string - level_info: - additionalProperties: - type: string - description: |- - 上层级信息,如获取当前层级 cluster=c1 的配置,需要设置 level_info: {"module": "m1"} 提供cluster所属上级 module 的信息 - 非必选项,目前只在查询 cluster 级别配置时需要指定模块信息有用 - todo 将来可能本配置中心,直接请求dbmeta元数据来获取 可能的 app-module-cluster-host-instance 关系 + Logical: + properties: + ExcludeDatabases: + description: '"mysql,test,db_infobase,information_schema,performance_schema,sys"' + type: string + ExcludeTables: + type: string type: object - level_name: - description: |- - 配置层级名,当前允许值 `app`,`module`,`cluster` - 配合 flag_locked 锁定标记,可以知道 锁定级别 - enum: - - plat - - app - - module - - cluster - type: string - level_value: - description: 配置层级值 - type: string + Master: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.logicBackupDataOption' + Slave: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.logicBackupDataOption' required: - - bk_biz_id - - level_name + - BackupType + - CrontabTime + - Master type: object - api.UpsertConfFilePlatReq: + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BinlogTimeComp: properties: - conf_file: - description: 配置文件名,一般配置类型与配置文件一一对应,但如 mysql 5.6, 5.7 两个版本同属 dbconf 配置,所以有 - MySQL-5.5, MySQL-5.6 两个配置文件 - example: MySQL-5.7 - type: string - conf_file_lc: - description: 配置文件中文名,也可以是其它 locale 语言类型 - example: 5.7_参数配置 + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BinlogTimeParam' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BinlogTimeParam: + properties: + binlog_dir: type: string - conf_names: - description: |- - 如果revision为空,表示第一次保存。每次 update 操作都会返回 revision,确保在这一轮编辑操作下都是操作这个revision - 已发布的 revision 不能编辑 - Revision string `json:"revision" form:"revision"` + binlog_files: items: - $ref: '#/definitions/api.UpsertConfNames' + type: string type: array - conf_type: - description: 配置类型,如 dbconf,backup - example: dbconf - type: string - conf_type_lc: - description: 配置类型中文名 - example: DB参数配置 - type: string - confirm: - description: 保存时如果与下层级存在冲突,提示确认,用 confirm=1 重新请求 - type: integer - description: - description: 配置文件的描述 - type: string - file_id: - description: 新建配置文件,第一次保存返回 file_id, 后续保存/发布 需传入 file_id - type: integer - namespace: - description: 命名空间,一般指DB类型 - example: tendbha - type: string - namespace_info: - description: namespace信息,比如数据库版本,与 conf_file 对应 - example: MySQL 5.7 - type: string - req_type: - description: '配置文件修改动作的请求类型,`SaveOnly`: 仅保存, `SaveAndPublish`保存并发布' + format: enum: - - SaveOnly - - SaveAndPublish + - "" + - json + - dump type: string required: - - conf_file - - conf_type - - namespace - - req_type + - binlog_dir + - binlog_files type: object - api.UpsertConfFilePlatResp: + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BuildMSRelationComp: properties: - conf_file: - description: 配置文件名,一般配置类型与配置文件一一对应,但如 mysql 5.6, 5.7 两个版本同属 dbconf 配置,所以有 - MySQL-5.5, MySQL-5.6 两个配置文件 - example: MySQL-5.7 - type: string - conf_type: - description: 配置类型,如 dbconf,backup - example: dbconf + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BuildMSRelationParam' + general: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam' + description: 本地使用 ADMIN, change master 使用 repl + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BuildMSRelationParam: + properties: + bin_file: + description: binlog 文件名称 type: string - file_id: + bin_position: + description: binlog 位点信息 + minimum: 0 type: integer - is_published: + force: + description: 如果当前实例存在主从关系是否直接reset slave后,强制change master + example: false + type: boolean + host: + description: 具体操作内容需要操作的参数 + type: string + is_gtid: + description: 是否启动GID方式进行建立主从 + type: boolean + master_host: + description: change master to 主库ip + type: string + master_port: + description: change master to 主库端口 + maximum: 65535 + minimum: 3306 + type: integer + max_tolerate_delay: + description: 最大容忍延迟, 当 主从延迟 小于 该值, 认为建立主从关系成功. 不传或者为 0 时,表示不检查 + type: integer + not_start_io_thread: + description: 不启动 io_thread。默认false 表示启动 io_thread + example: false + type: boolean + not_start_sql_thread: + description: 不启动 sql_thread。默认false 表示启动 sql_thread + example: false + type: boolean + port: + description: 当前实例的端口 + maximum: 65535 + minimum: 3306 type: integer - namespace: - description: 命名空间,一般指DB类型 - example: tendbha - type: string - revision: - description: 编辑配置文件,仅保存时不会产生 revision,保存并发布时才返回 - type: string required: - - conf_file - - conf_type - - namespace + - bin_file + - bin_position + - host + - master_host + - master_port + - port type: object - api.UpsertConfItem: + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.CleanMysqlComp: + properties: + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.CleanMysqlParam' + general: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.CleanMysqlParam: + properties: + check_interval_sec: + type: integer + drop_database: + description: 是否执行 drop database,这里是确认行为. 如果 false 则只把 drop 命令打印到输出 + type: boolean + force: + description: 当实例不空闲时是否强制清空 + type: boolean + reset_slave: + description: 是否执行 reset slave all + type: boolean + restart: + description: drop_database 之后是否重启实例 + type: boolean + stop_slave: + description: 是否执行 stop slave + type: boolean + tgt_instance: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.Instance' + description: 清空目标实例 + required: + - tgt_instance + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.ConfItemOp: properties: - conf_name: - description: 配置项名称 - type: string conf_value: + description: ConfName string `json:"conf_name" validate:"required"` type: string - description: - type: string - extra_info: - type: string - flag_disable: - description: 是否禁用,默认 0 表示启用. 1表示禁用 - example: 0 - type: integer - flag_locked: - description: 是否锁定,默认 0 表上不锁定 - example: 0 - type: integer + need_restart: + type: boolean op_type: - description: 配置项修改动作,需提供操作类型字段,允许值 `add`,`update`,`remove` + description: 配置项修改动作,允许值 `upsert`,`remove` enum: - - add - - update + - upsert - remove type: string required: - - conf_name - op_type type: object - api.UpsertConfItemsReq: + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.DBHAAccount: properties: - bk_biz_id: - description: 业务ID,必选项 - example: testapp - type: string - conf_file_info: - $ref: '#/definitions/api.ConfFileDef' - conf_items: + access_hosts: items: - $ref: '#/definitions/api.UpsertConfItem' + type: string type: array - confirm: - description: 保存时如果与下层级存在冲突,提示确认,用 confirm=1 重新请求 - type: integer - description: - description: 发布描述 + pwd: type: string - level_info: - additionalProperties: - type: string - description: |- - 上层级信息,如获取当前层级 cluster=c1 的配置,需要设置 level_info: {"module": "m1"} 提供cluster所属上级 module 的信息 - 非必选项,目前只在查询 cluster 级别配置时需要指定模块信息有用 - todo 将来可能本配置中心,直接请求dbmeta元数据来获取 可能的 app-module-cluster-host-instance 关系 - type: object - level_name: - description: |- - 配置层级名,当前允许值 `app`,`module`,`cluster` - 配合 flag_locked 锁定标记,可以知道 锁定级别 - enum: - - plat - - app - - module - - cluster + user: + type: string + required: + - pwd + - user + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.DumpSchemaParam: + properties: + backup_dir: type: string - level_value: - description: 配置层级值 + backup_file_name: type: string - req_type: - description: '配置文件修改动作的请求类型,`SaveOnly`: 仅保存, `SaveAndPublish`保存并发布' - enum: - - SaveOnly - - SaveAndPublish + bk_cloud_id: + description: 所在的云区域 + type: integer + charset: + description: 字符集参数 + type: string + db_cloud_token: + description: 云区域token type: string - revision: + fileserver: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.FileServer' + host: + description: 当前实例的主机地址 type: string + port: + description: 当前实例的端口 + minimum: 3306 + type: integer required: - - bk_biz_id - - level_name - - req_type + - charset + - host + - port type: object - api.UpsertConfItemsResp: + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.FileServer: properties: - bk_biz_id: + bucket: + description: 目标bucket type: string - conf_file: - description: 配置文件名,一般配置类型与配置文件一一对应,但如 mysql 5.6, 5.7 两个版本同属 dbconf 配置,所以有 - MySQL-5.5, MySQL-5.6 两个配置文件 - example: MySQL-5.7 + password: + description: 制品库 password type: string - conf_type: - description: 配置类型,如 dbconf,backup - example: dbconf + project: + description: 制品库 project type: string - is_published: - type: integer - namespace: - description: 命名空间,一般指DB类型 - example: tendbha + upload_path: + description: 上传路径 type: string - revision: - description: 编辑配置文件,第一次保存返回 revision, 后续保存/发布 需传入 revision + url: + description: 制品库地址 type: string + username: + description: 制品库 username + type: string + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.FindLocalBackupParam: + properties: + backup_dirs: + items: + type: string + type: array + cluster_id: + description: 指定查询哪个 cluster_id 的备份,如果不指定可能查询到其它非法的备份 + type: integer + file_server: + type: boolean + tgt_instance: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.Instance' + description: 查找哪个实例的备份 required: - - conf_file - - conf_type - - namespace + - backup_dirs + - tgt_instance type: object - api.UpsertConfNames: + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.FindLocalBackupResp: properties: - conf_name: - description: 配置项,也叫参数项 - type: string - conf_name_lc: - description: 配置项中文名,可不填 + backups: + additionalProperties: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.LocalBackupObj' + description: backups key 是 .info 文件 + type: object + latest: + description: 记录上面 backups 最近的一次备份 type: string - description: - description: 配置项说明 + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallDBAToolkitComp: + properties: + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallDBAToolkitParam' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallDBAToolkitParam: + properties: + exec_user: + description: 发起执行actor的用户,仅用于审计 type: string - flag_disable: - description: 是否禁用,代表该配置项状态. 默认0启用 - example: 0 - type: integer - flag_locked: - description: 是否锁定. 默认0 - example: 0 - type: integer - need_restart: - description: 是否需要重启生效. 默认1 - example: 1 - type: integer - op_type: - description: 配置项修改动作,需提供操作类型字段,允许值 `add`,`update`,`remove` - enum: - - add - - update - - remove + pkg: + description: 安装包名 type: string - value_allowed: - description: |- - 允许设定值,如枚举/范围等,为空时表示不限制范围 - 当 value_type_sub=ENUM 时,value_allowed 格式 0|1 或者 ON|OFF 或者 aaa|bbb|ccc , 会校验value的合法性 - 当 value_type_sub=REGEX 时,会根据 value_allowed 进行正则校验 - 当 value_type_sub=RANGE 时,也会校验value 范围的合法性. - - BYTES 是一种特殊的RANGE,value允许1mm 但value_allowed 必须是数字的range - type: string - value_default: - description: 配置项默认值 - example: "1" - type: string - value_type: - description: 配置项的值类型,如 `STRING`,`INT`,`FLOAT`,`NUMBER` - enum: - - STRING - - INT - - FLOAT - - NUMBER - example: STRING - type: string - value_type_sub: - description: value_type 的子类型,如果设置则用于校验 value_type 的具体类型,或者返回用于告知前端控件类型,例如 - ENUM,RANGE - enum: - - "" - - STRING - - ENUM - - RANGE - - BYTES - - REGEX - - JSON - - COMPLEX - example: ENUM + pkg_md5: + description: 安装包MD5 type: string required: - - conf_name - - op_type - - value_type + - pkg + - pkg_md5 type: object - model.ConfigModel: + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLChecksumComp: properties: - bk_biz_id: + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLChecksumParam' + general: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLChecksumParam: + properties: + api_url: type: string - conf_file: + exec_user: type: string - conf_name: + instances_info: + items: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstanceInfo' + type: array + pkg: + description: 安装包名 type: string - conf_type: + pkg_md5: + description: 安装包MD5 type: string - conf_value: + schedule: type: string - created_at: + system_dbs: + items: + type: string + type: array + required: + - pkg + - pkg_md5 + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLComp: + properties: + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLParams' + general: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam' + timeZone: type: string - description: + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLParams: + properties: + allowDiskFileSystemTypes: + items: + type: string + type: array + charset: + description: 字符集参数 type: string - extra_info: + dbha_account: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.DBHAAccount' + host: type: string - flag_disable: + inst_mem: + description: 安装实例的内存大小,可以不指定,会自动计算 type: integer - flag_locked: - type: integer - id: - type: integer - level_locked: - type: string - level_name: - type: string - level_value: + mycnf_configs: + description: map[port]my.cnf + items: + type: integer + type: array + mysql_version: + description: MySQLVerion 只需5.6 5.7 这样的大版本号 type: string - namespace: + pkg: + description: 安装包名 type: string - updated_at: + pkg_md5: + description: 安装包MD5 type: string - type: object -host: localhost:8080 + ports: + description: Ports + items: + type: integer + type: array + spider_auto_incr_mode_map: + items: + type: integer + type: array + super_account: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SuperAccount' + required: + - charset + - host + - mycnf_configs + - mysql_version + - pkg + - pkg_md5 + - ports + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallNewDbBackupComp: + properties: + generalParam: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam' + params: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallNewDbBackupParam' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallNewDbBackupParam: + properties: + bk_biz_id: + description: bkbizid + type: integer + bk_cloud_id: + description: bk_cloud_id + type: integer + cluster_address: + additionalProperties: + type: string + description: cluster addresss + type: object + cluster_id: + additionalProperties: + type: integer + description: cluster id + type: object + cluster_type: + type: string + configs: + additionalProperties: + additionalProperties: + type: string + type: object + description: Configs BackupConfig + type: object + exec_user: + description: 执行Job的用户 + type: string + host: + description: 当前实例的主机地址 + type: string + options: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BackupOptions' + description: 选项参数配置 + pkg: + description: 安装包名 + type: string + pkg_md5: + description: 安装包MD5 + type: string + ports: + description: 被监控机器的上所有需要监控的端口 + items: + type: integer + type: array + role: + description: 当前主机安装的mysqld的角色 + type: string + shard_value: + additionalProperties: + type: integer + description: shard value for spider + type: object + untar_only: + description: 只解压,不校验不渲染配置,不连接 db + type: boolean + required: + - bk_biz_id + - configs + - host + - options + - pkg + - pkg_md5 + - ports + - role + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstanceInfo: + properties: + bk_biz_id: + type: integer + bk_instance_id: + description: 0 被视为空, 不序列化 + type: integer + cluster_id: + type: integer + immute_domain: + type: string + ip: + type: string + port: + type: integer + role: + type: string + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.LocalBackupObj: + properties: + backup_dir: + description: 备份所在目录 + type: string + backup_id: + type: string + backup_time: + description: 备份时间,目前是备份开始时间 + type: string + backup_type: + type: string + bill_id: + type: string + bk_biz_id: + type: integer + cluster_id: + type: integer + data_schema_grant: + type: string + db_role: + type: string + file_list: + description: |- + InfoFile common.InfoFileDetail `json:"info_file"` + 备份文件列表 + items: + type: string + type: array + index_file: + type: string + inst_host: + description: 备份所属 host + type: string + inst_port: + description: 备份所属 port + type: integer + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.MycnfChangeComp: + properties: + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.MycnfChangeParam' + general: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.MycnfChangeParam: + properties: + items: + additionalProperties: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.ConfItemOp' + type: object + persistent: + description: '是否持久化到 my.cnf 文件,-1: 不持久化,1: 持久化, 2: 仅持久化但不修改运行时' + enum: + - -1 + - 1 + - 2 + type: integer + restart: + description: '指定是否 允许重启, -1:不重启, 1: 重启, 2:根据 items need_restart 自动判断是否重启' + enum: + - -1 + - 1 + - 2 + type: integer + tgt_instance: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.InsObject' + required: + - items + - persistent + - restart + - tgt_instance + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.MycnfCloneComp: + properties: + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.MycnfCloneParam' + general: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.MycnfCloneParam: + properties: + items: + description: |- + 需要克隆哪些变量, 考虑到不同版本参数不一样,这里不要求指定一定存在; 只修改 mysqld 区。即失败忽略 + 有些参数是 readonly 的,只会保存到 my.cnf 中,如果与运行值不一样需要用户重启 + 默认值见 MycnfCloneItemsDefault + items: + type: string + type: array + persistent: + description: '是否持久化到 my.cnf 文件,0: 不持久化,1: 持久化, 2: 仅持久化但不修改运行时' + enum: + - 0 + - 1 + - 2 + example: 1 + type: integer + restart: + description: '指定是否 允许重启, 0:不重启, 1: 重启, 2:根据 items need_restart 自动判断是否重启' + enum: + - 0 + - 1 + - 2 + example: 2 + type: integer + src_instance: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.InsObject' + description: 参数克隆,获取源实例,可以提供 repl 账号权限 + tgt_instance: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.InsObject' + description: 应用到本地目标实例,需要有 ADMIN 权限 + required: + - persistent + - restart + - src_instance + - tgt_instance + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.OSCmdRunResp: + properties: + code: + type: integer + data: + items: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SimpleCmdResult' + type: array + message: + type: string + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.OSCmds: + properties: + cmds: + items: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SimpleCmd' + type: array + run_user: + type: string + work_dir: + type: string + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.PtTableChecksumComp: + properties: + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.PtTableChecksumParam' + general: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.PtTableChecksumParam: + properties: + bk_biz_id: + description: 业务 id + type: integer + cluster_id: + description: 集群 id + type: integer + db_patterns: + description: 库表过滤选项 + items: + type: string + type: array + ignore_dbs: + description: 库表过滤选项 + items: + type: string + type: array + ignore_tables: + description: 库表过滤选项 + items: + type: string + type: array + immute_domain: + description: 集群域名 + type: string + inner_role: + description: 执行校验的 db inner role, 应该是[master, repeater] + type: string + master_access_slave_password: + description: 从 db 访问 slave 的密码 + type: string + master_access_slave_user: + description: 从 db 访问 slave 的用户名 + type: string + master_ip: + description: 执行校验的 db ip + type: string + master_port: + description: 执行校验的 db port + type: integer + replicate_table: + description: 结果表, 带库前缀 + type: string + runtime_hour: + description: 校验运行时长 + type: integer + slaves: + description: slave 列表 + items: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SlaveInfo' + type: array + system_dbs: + description: 系统表 + items: + type: string + type: array + table_patterns: + description: 库表过滤选项 + items: + type: string + type: array + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SemanticDumpSchemaComp: + properties: + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.DumpSchemaParam' + general: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SimpleCmd: + properties: + cmd_args: + items: + type: string + type: array + cmd_name: + type: string + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SimpleCmdResult: + properties: + cmd_stderr: + type: string + cmd_stdout: + type: string + err_msg: + type: string + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SlaveInfo: + properties: + id: + description: slave id + type: integer + ip: + description: slave ip + type: string + port: + description: slave port + type: integer + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SuperAccount: + properties: + access_hosts: + items: + type: string + type: array + pwd: + type: string + user: + type: string + required: + - pwd + - user + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.UnInstallMySQLParam: + properties: + force: + description: 是否强制下架mysqld + type: boolean + host: + type: string + ports: + description: 被监控机器的上所有需要监控的端口 + items: + type: integer + type: array + required: + - host + - ports + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.logicBackupDataOption: + properties: + DataSchemaGrant: + description: '"grant,schema,data"' + type: string + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_grant.GrantReplComp: + properties: + db: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.DbWorker' + description: 本地db链接 + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_grant.GrantReplParam' + general: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_grant.GrantReplParam: + properties: + host: + description: 当前实例的主机地址 + type: string + port: + description: 当前实例的端口 + type: integer + repl_hosts: + description: slave host + items: + type: string + type: array + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.MySQLBinlogUtil: + properties: + databases: + description: row event 解析指定 databases + items: + type: string + type: array + databases_ignore: + description: row event 解析指定 忽略 databases + items: + type: string + type: array + filter_statement_match_error: + description: 匹配字符串成功,则解析 binlog 报错 + type: string + filter_statement_match_ignore: + description: 匹配字符串成功,则忽略语句,加入注释中 + type: string + filter_statement_match_ignore_force: + description: |- + 匹配字符串成功,强制忽略语句,加入注释中。当与 filter_statement_match_error 都匹配时,ignore_force会优先生效 + 默认 infodba_schema + type: string + flashback: + description: 是否启用 flashback + type: boolean + idempotent_mode: + description: 是否开启幂等模式, mysql --slave-exec-mode=idempotent or mysqlbinlog --idempotent + type: boolean + mysql_client_opt: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.MySQLClientOpt' + not_write_binlog: + description: 导入时是否记录 binlog, mysql sql_log_bin=0 or mysqlbinlog --disable-log-bin. + true表示不写 + type: boolean + query_event_handler: + description: |- + query event 默认处理策略。keep:保留解析出的query event 语句, ignore:注释(丢弃)该 query event, error:认为是不接受的语句,报错 + 默认 keep + enum: + - keep + - ignore + - safe + - error + type: string + rewrite_db: + description: --rewrite_db="db1->xx_db1,db2->xx_db2" + type: string + start_pos: + description: --start-position + type: integer + start_time: + description: --start-datetime + type: string + stop_pos: + description: --stop-position + type: integer + stop_time: + description: --stop-datetime + type: string + tables: + description: row event 解析指定 tables + items: + type: string + type: array + tables_ignore: + description: row event 解析指定 忽略 tables + items: + type: string + type: array + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.MySQLClientOpt: + properties: + binary_mode: + description: 是否启用 --binary-mode + type: boolean + max_allowed_packet: + type: integer + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RecoverBinlog: + properties: + binlog_dir: + description: 恢复时 binlog 存放目录,一般是下载目录 + example: /data/dbbak/123456/binlog + type: string + binlog_files: + description: binlog列表 + items: + type: string + type: array + binlog_start_file: + description: 指定要开始应用的第 1 个 binlog。如果指定,一般要设置 start_pos,如果不指定则使用 start_time + type: string + parse_concurrency: + description: 解析的并发度,默认 1 + type: integer + parse_only: + description: 仅解析 binlog,不做导入 + type: boolean + quick_mode: + description: |- + 如果启用 quick_mode,解析 binlog 时根据 filter databases 等选项过滤 row event,对 query event 会全部保留 。需要 mysqlbinlog 工具支持 --tables 选项,可以指定参数的 tools + 当 quick_mode=false 时,recover_opt 里的 databases 等选项无效,会应用全部 binlog + type: boolean + recover_opt: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.MySQLBinlogUtil' + source_binlog_format: + enum: + - "" + - ROW + - STATEMENT + - MIXED + type: string + tgt_instance: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.InsObject' + tools: + additionalProperties: + type: string + description: 外部指定工具路径 + type: object + work_dir: + description: binlog 解析所在目录,存放运行日志 + example: /data/dbbak/ + type: string + work_id: + example: "123456" + type: string + required: + - binlog_dir + - binlog_files + - recover_opt + - tgt_instance + - work_dir + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RecoverBinlogComp: + properties: + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RecoverBinlog' + general: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RestoreDRComp: + properties: + extend: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RestoreParam' + description: 恢复参数,会复制给具体的 Restore 实现. 见 ChooseType 方法 + general: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam' + description: 通用参数 + resume: + description: 是否是中断后继续执行 + type: boolean + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RestoreOpt: + properties: + databases: + description: 恢复哪些 db,当前只对 逻辑恢复有效 + items: + type: string + type: array + enable_binlog: + description: EnableBinlog 导入数据时是否写binlog,默认不启用 + type: boolean + ignore_databases: + items: + type: string + type: array + ignore_tables: + items: + type: string + type: array + recover_binlog: + description: |- + 在指定时间点回档场景才需要,是否恢复 binlog。在 doSlave 场景,是不需要 recover_binlog。这个选项是控制下一步恢复binlog的行为 + 当 recover_binlog 时,要确保实例的所有库表结构都恢复。在逻辑回档场景,只回档部分库表数据时,依然要恢复所有表结构 + type: boolean + recover_privs: + type: boolean + source_binlog_format: + description: |- + 在库表级定点回档时有用,如果是 statement/mixed 格式,导入数据时需要全部导入; + 如果是 row,可只导入指定库表数据, 在 recover-binlog 时可指定 quick_mode=true 也恢复指定库表 binlog + enum: + - "" + - ROW + - STATEMENT + - MIXED + type: string + tables: + items: + type: string + type: array + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RestoreParam: + properties: + backup_dir: + description: 备份文件所在本地目录,理论上doDr不会对该目录写入,而是写入 targetDir + example: /data/dbbak + type: string + backup_files: + additionalProperties: + items: + type: string + type: array + description: 备份文件名列表,key 是 info|full|priv|index, value 是是相对于 backup_dir 的文件名列表 + type: object + change_master: + description: 恢复完成后是否执行 change master,会 change master 到 src_instance + type: boolean + restore_opts: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RestoreOpt' + description: 恢复选项,比如恢复库表、是否导入binlog等。目前只对逻辑恢复有效 + src_instance: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.Instance' + description: 备份实例的 ip port,用于生产 change master 语句。如果 host 为空,表示不检查、不生成change + master,恢复spider节点时使用 + tgt_instance: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.InsObject' + description: 恢复本地的目标实例 + tools: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_tools.ToolSet' + description: 恢复用到的客户端工具,不提供时会有默认值 + work_dir: + description: 备份恢复目录,工作目录 + example: /data1/dbbak + type: string + work_id: + description: work_id 标识本次恢复,若为0则为当前时间戳 + type: string + required: + - backup_dir + - backup_files + - work_dir + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_rollback.Flashback: + properties: + binlog_dir: + description: 当 binlog_dir 不为空,表示 binlog 已下载;当为空时,目前只从本地软连接 + type: string + binlog_files: + description: binlog列表,如果不提供,则自动从本地查找符合时间范围的 binlog + items: + type: string + type: array + parse_concurrency: + description: 解析binlog并发度 + type: integer + recover_opt: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_rollback.RecoverOpt' + stop_time: + type: string + target_time: + description: 闪回的目标时间点,对应 recover-binlog 的 start_time, 精确到秒。目标实例的时区 + type: string + tgt_instance: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.InsObject' + tools: + additionalProperties: + type: string + description: 外部指定工具路径 + type: object + work_dir: + description: binlog 解析所在目录,存放运行日志 + type: string + work_id: + type: string + required: + - recover_opt + - target_time + - tgt_instance + - work_dir + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_rollback.FlashbackComp: + properties: + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_rollback.Flashback' + general: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_rollback.RecoverOpt: + properties: + databases: + description: row event 解析指定 databases + items: + type: string + type: array + databases_ignore: + description: row event 解析指定 忽略 databases + items: + type: string + type: array + filter_rows: + description: 暂不支持行级闪回 + type: string + tables: + description: row event 解析指定 tables + items: + type: string + type: array + tables_ignore: + description: row event 解析指定 忽略 tables + items: + type: string + type: array + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_spiderctl.InitClusterRoutingComp: + properties: + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_spiderctl.InitClusterRoutingParam' + general: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_spiderctl.InitClusterRoutingParam: + properties: + ctl_instances: + items: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_spiderctl.Instance' + type: array + host: + type: string + mysql_instance_tuples: + items: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_spiderctl.InstanceTuple' + type: array + port: + minimum: 3306 + type: integer + spider_instances: + items: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_spiderctl.Instance' + type: array + tdbctl_pass: + type: string + tdbctl_user: + type: string + required: + - ctl_instances + - host + - mysql_instance_tuples + - port + - spider_instances + - tdbctl_pass + - tdbctl_user + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_spiderctl.Instance: + properties: + host: + type: string + port: + minimum: 3306 + type: integer + shard_id: + type: integer + required: + - host + - port + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_spiderctl.InstanceTuple: + properties: + host: + type: string + port: + type: integer + shard_id: + type: integer + slave_host: + type: string + required: + - host + - port + - shard_id + - slave_host + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_tbinlogdumper.Configs: + properties: + dumper_configs: + items: + type: integer + type: array + required: + - dumper_configs + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_tbinlogdumper.DumpSchemaComp: + properties: + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_tbinlogdumper.DumpSchemaParam' + general: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_tbinlogdumper.DumpSchemaParam: + properties: + charset: + description: 字符集参数 + type: string + host: + description: 当前实例的主机地址 + type: string + port: + description: 当前实例的端口 + minimum: 3306 + type: integer + tbinlogdumper_port: + description: 当前TBinlogdumperPort实例的端口 + minimum: 3306 + type: integer + required: + - charset + - host + - port + - tbinlogdumper_port + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_tbinlogdumper.DumperParams: + properties: + area_name: + type: string + dumper_id: + type: string + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_tbinlogdumper.InstallTbinlogDumperComp: + properties: + dumperConfigs: + additionalProperties: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_tbinlogdumper.DumperParams' + type: object + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_tbinlogdumper.Configs' + general: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam' + renderConfigs: + additionalProperties: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_tbinlogdumper.renderDumperConfigs' + type: object + timeZone: + type: string + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_tbinlogdumper.Mysqld: + properties: + area_name: + type: string + basedir: + type: string + bind-address: + type: string + character_set_server: + type: string + datadir: + type: string + dumper_id: + type: string + logdir: + type: string + port: + type: string + server_id: + type: integer + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_tbinlogdumper.UnInstallTbinlogDumperComp: + properties: + generalParam: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam' + params: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.UnInstallMySQLParam' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_tbinlogdumper.renderDumperConfigs: + properties: + mysqld: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_tbinlogdumper.Mysqld' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_native.DbWorker: + properties: + db: + $ref: '#/definitions/sql.DB' + dsn: + type: string + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_native.InsObject: + properties: + charset: + description: 连接字符集 + type: string + host: + description: 当前实例的主机地址 + type: string + options: + description: 其它选项 + type: string + port: + description: 当前实例的端口 + type: integer + pwd: + description: 连接当前实例的User Pwd + type: string + socket: + description: 连接socket + type: string + user: + description: 连接当前实例的User + type: string + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_native.Instance: + properties: + host: + description: 当前实例的主机地址 + example: 1.1.1.1 + type: string + port: + description: 当前实例的端口 + example: 33060 + type: integer + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_tools.ToolSet: + properties: + tools: + additionalProperties: + type: string + description: 外部指定工具路径 + type: object + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_util_mysqlutil.ChangeMaster: + properties: + change_sql: + type: string + channel: + type: string + executed_gtid_set: + type: string + force: + description: 如果当前实例存在主从关系是否直接reset slave后,强制change master + type: boolean + is_gtid: + description: 是否启动GID方式进行建立主从 + type: boolean + master_auto_position: + type: integer + master_host: + description: 主库ip + type: string + master_log_file: + description: binlog 文件名称 + type: string + master_log_pos: + description: binlog 位点信息 + type: integer + master_password: + type: string + master_port: + description: 主库端口 + minimum: 3306 + type: integer + master_user: + type: string + max_tolerate_delay: + description: 最大容忍延迟,即主从延迟小于该值,认为建立主从关系成功 + type: integer + required: + - master_host + - master_password + - master_port + - master_user + type: object + internal_subcmd_commoncmd.RMLargeFileParam: + properties: + bw_limit_mb: + default: 30 + description: 删除速度,MB/s,默认 30 + maximum: 1000 + minimum: 1 + type: integer + filename: + type: string + required: + - bw_limit_mb + - filename + type: object + sql.DB: + type: object +host: ./dbactuator info: contact: email: support@swagger.io name: API Support url: http://www.swagger.io/support - description: This is a bkconfigsvr celler server. + description: This is a dbactuator command collection. license: name: Apache 2.0 url: http://www.apache.org/licenses/LICENSE-2.0.html termsOfService: http://swagger.io/terms/ - title: bkconfigsvr API + title: dbactuator API version: 0.0.1 paths: - /bkconfig/v1/conffile/add: + /common/file-server: + post: + consumes: + - application/json + description: |- + 通过 http 暴露指定目录可用于下载,可用于在重建备库时,从其它机器下载备份 + 在 OS 不允许 ssh 登录(scp/sftp)时,可以临时启动该服务来获取备份文件 + parameters: + - description: short description + in: body + name: body + required: true + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_fileserver.FileServerComp' + responses: {} + summary: 简单文件服务 + tags: + - common + /common/rm-file: + post: + consumes: + - application/json + parameters: + - description: short description + in: body + name: body + required: true + schema: + $ref: '#/definitions/internal_subcmd_commoncmd.RMLargeFileParam' + responses: {} + summary: 限速删除大文件 + tags: + - common + /download/http: + post: + consumes: + - application/json + description: |- + 支持限速、basicAuth 认证. 一般配合 common fileserver 使用 + # server1 + ./dbactuator common file-server \ + --payload-format raw \ + --payload '{"extend":{"bind_address":":8082","mount_path":"/data/dbbak","user":"xiaog","password":"xxxx","proc_maxidle_duration":"60s"}}' + + # server2 + curl -u 'xiaog:xxxx' 'http://server1:8082/datadbbak8082/dbactuator' -o dbactuator.bin --limit-rate 10k + parameters: + - description: short description + in: body + name: body + required: true + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.DFHttpParam' + responses: {} + summary: http下载文件 + tags: + - download + /download/ibs-query: + post: + consumes: + - application/json + description: filename 会进行模糊匹配,返回 task_id 用于下载 + parameters: + - description: short description + in: body + name: body + required: true + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSQueryComp' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSQueryResult' + summary: 从 ieg 备份系统查询文件 + tags: + - download + /download/ibs-recover: post: consumes: - application/json description: |- - 新增平台级配置文件,定义允许的配置名。指定 req_type 为 `SaveOnly` 仅保存, `SaveAndPublish` 保存并发布。保存并发布 也必须提供全量,而不能是前面保存基础上的增量 - req_type=`SaveOnly` 已废弃 - 第一次保存时,会返回 `file_id`,下次 保存/发布 需传入 `file_id` - namespace,conf_type,conf_file 唯一确定一个配置文件,不同DB版本信息体现在 conf_file 里 (如MySQL-5.7), namespace_info 可以存前端传入的 数据库版本,只用于在展示 - HTTP Header 指定 `X-Bkapi-User-Name` 请求的操作人员 + 提供 task_id,从 ieg 备份系统下载文件 + task_files_wild: 模糊搜索文件并下载, task_files: 精确文件查询并下载 + task_files_wild, task_files 二选一 + 启用 skip_local_exists=true 时,如果目标目录已存在要下载的文件,会自动跳过 parameters: - - description: ConfName for ConfType + - description: short description in: body name: body required: true schema: - $ref: '#/definitions/api.UpsertConfFilePlatReq' - produces: - - application/json + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSRecoverComp' responses: "200": description: OK schema: - $ref: '#/definitions/api.UpsertConfFilePlatResp' - "400": - description: Bad Request - schema: - $ref: '#/definitions/api.HTTPClientErrResp' - summary: 新增平台级配置文件 + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSRecoverTask' + summary: 从 ieg 备份系统下载文件 tags: - - plat_config - /bkconfig/v1/conffile/list: - get: - description: |- - 查询配置文件模板列表。只有平台和业务才有配置文件列表 - 返回的 updated_by 代表操作人 + - download + /download/scp: + post: + consumes: + - application/json + description: 支持限速 parameters: - - description: 业务id, bk_biz_id=0 代表平台配置 - in: query - name: bk_biz_id - required: true - type: string - - description: 如果指定了 conf_file 则只查这一个文件信息 - in: query - name: conf_file - type: string - - example: dbconf - in: query - name: conf_type + - description: short description + in: body + name: body required: true - type: string - - description: |- - 配置层级名,当前允许值 `app`,`module`,`cluster` - 配合 flag_locked 锁定标记,可以知道 锁定级别 - enum: - - plat - - app - - module - - cluster - in: query - name: level_name + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.DFScpParam' + responses: {} + summary: scp下载文件 + tags: + - download + /mysql/change-master: + post: + consumes: + - application/json + description: 执行 change master to + parameters: + - description: short description + in: body + name: body required: true - type: string - - description: 配置层级值 - in: query - name: level_value - type: string - - description: 命名空间,一般指DB类型 - in: query - name: namespace + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BuildMSRelationComp' + responses: {} + summary: 建立主从关系 + tags: + - mysql + /mysql/clean-mysql: + post: + consumes: + - application/json + description: 清空本地实例,保留系统库 + parameters: + - description: description + in: body + name: body required: true - type: string + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.CleanMysqlComp' produces: - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/api.ListConfFileResp' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/api.HTTPClientErrResp' - summary: 查询配置文件列表 + responses: {} + summary: 清空实例,高危 tags: - - plat_config - /bkconfig/v1/conffile/query: - get: - description: 查询 平台配置 某个配置类型/配置文件的所有配置名列表 + - mysql + /mysql/deploy: + post: + consumes: + - application/json + description: 部署 mysql 实例说明 parameters: - - example: MySQL-5.7 - in: query - name: conf_file + - description: short description + in: body + name: body required: true - type: string - - description: 如果设置,会根据前缀模糊匹配搜索 - in: query - name: conf_name - type: string - - example: dbconf - in: query - name: conf_type + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLComp' + responses: {} + summary: 部署 mysql 实例 + tags: + - mysql + /mysql/deploy-dbbackup: + post: + consumes: + - application/json + description: 部署GO版本备份程序 + parameters: + - description: short description + in: body + name: body required: true - type: string - - example: tendbha - in: query - name: namespace - type: string - produces: + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallNewDbBackupComp' + responses: {} + summary: 部署备份程序 + tags: + - mysql + /mysql/find-local-backup: + post: + consumes: - application/json + description: 查找本地备份 + parameters: + - description: short description + in: body + name: body + required: true + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.FindLocalBackupParam' responses: "200": description: OK schema: - $ref: '#/definitions/QueryConfigNamesResp' - "400": - description: Bad Request - schema: - $ref: '#/definitions/api.HTTPClientErrResp' - summary: 查询平台配置项列表 + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.FindLocalBackupResp' + summary: 查找本地备份 tags: - - plat_config - /bkconfig/v1/conffile/update: + - mysql + /mysql/flashback-binlog: post: consumes: - application/json - description: |- - 编辑平台级配置文件。指定 req_type 为 `SaveOnly` 仅保存, `SaveAndPublish` 保存并发布 - HTTP Header 指定 `X-Bkapi-User-Name` 请求的操作人员 - 编辑平台配置时,如果设置 flag_disable=1 时,该配置不会显示在平台配置项列表,相当于管理 所有允许的配置项列表 - 保存时会校验输入的 value_default, value_type, value_allowed - 1. value_type 目前允许 STRING, INT, FLOAT, NUMBER - 2. value_type_sub 允许 ENUM, ENUMS, RANGE, STRING, JSON, REGEX(一种特殊的STRING,会验证 value_default 是否满足 value_allowed 正则) - 3. value_allowed 允许 枚举: 例如`0|1|2`, `ON|OFF` 格式, 范围: 例如`(0, 1000]` + description: 通过 `mysqlbinlog --flashback xxx | mysql` 导入 binlog parameters: - - description: ConfName for ConfType + - description: short description in: body name: body required: true schema: - $ref: '#/definitions/api.UpsertConfFilePlatReq' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/api.UpsertConfFilePlatResp' - "400": - description: Bad Request - schema: - $ref: '#/definitions/api.HTTPClientErrResp' - summary: 编辑平台级配置文件 + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_rollback.FlashbackComp' + responses: {} + summary: 导入 binlog tags: - - plat_config - /bkconfig/v1/confitem/query: + - mysql + /mysql/grant-repl: post: consumes: - application/json - description: |- - 根据业务/模块/集群的信息,获取某个配置文件的配置项。一般用户前端请求、再编辑的场景,后端服务直接获取配置文件使用 /version/generate 接口 - conf_file 可以是,号分隔的多个文件名,返回结果是一个按照配置文件名组合的一个 list - 需要指定返回格式 format, 可选值 map, list. - map 格式会丢弃 conf_item 的其它信息,只保留 conf_name=conf_value, 一般用于后台服务 - list 格式会保留 conf_items 的其它信息,conf_name=conf_item,一般用于前端展示 - 获取cluster级别配置时,需要提供 level_info:{"module":"xxx"} 模块信息 + description: 在目标机器新建 repl 账号 parameters: - - description: GetConfigItemsReq + - description: short description in: body name: body required: true schema: - $ref: '#/definitions/GetConfigItemsReq' - produces: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_grant.GrantReplComp' + responses: {} + summary: 建立复制账号 + tags: + - mysql + /mysql/init-cluster-routing: + post: + consumes: - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/GetConfigItemsResp' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/api.HTTPClientErrResp' - summary: 获取配置文件配置项列表 + description: 初始化tendb cluster 集群的路由关系说明 + parameters: + - description: short description + in: body + name: body + required: true + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_spiderctl.InitClusterRoutingComp' + responses: {} + summary: 初始化tendb cluster 集群的路由关系 tags: - - config_item - /bkconfig/v1/confitem/save: + - spiderctl + /mysql/install-checksum: post: consumes: - application/json - description: |- - 编辑层级配置,层级包括业务app、模块module、集群cluster,需要指定修改哪个级别的配置,通过 level_name, level_value 来区分 - 针对编辑的配置类型 conf_type 无版本化的概念,即保存生效,无需发布 - 保存 cluster级别配置时,需要提供 level_info:{"module":"xxx"} 模块信息 + description: 安装mysql校验 parameters: - - description: SaveConfItemsReq + - description: short description in: body name: body required: true schema: - $ref: '#/definitions/api.SaveConfItemsReq' - produces: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLChecksumComp' + responses: {} + summary: 安装mysql校验 + tags: + - mysql + /mysql/install-dbatoolkit: + post: + consumes: - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/api.UpsertConfItemsResp' - "400": - description: Bad Request - schema: - $ref: '#/definitions/api.HTTPClientErrResp' - summary: 编辑配置(无版本概念) + description: 部署 /home/mysql/dba_toolkit,覆盖 + parameters: + - description: short description + in: body + name: body + required: true + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallDBAToolkitComp' + responses: {} + summary: 部署DBA工具箱 tags: - - config_item - /bkconfig/v1/confitem/upsert: + - mysql + /mysql/mycnf-change: post: consumes: - application/json - description: |- - 编辑层级配置,层级包括业务app、模块module、集群cluster,需要指定修改哪个级别的配置,通过 level_name, level_value 来区分 - 例1: level_name=app, level_value=testapp 表示修改业务 bk_biz_id=testapp 的配置 - 例2: level_name=module, level_value=account 表示某业务 bk_biz_id 的模块 module=account 的配置 - HTTP Header 指定 `X-Bkapi-User-Name` 请求的操作人员 - 获取cluster级别配置时,需要提供 level_info:{"module":"xxx"} 模块信息 + description: 修改mysql配置 parameters: - - description: UpsertConfItemsReq + - description: description in: body name: body required: true schema: - $ref: '#/definitions/api.UpsertConfItemsReq' + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.MycnfChangeComp' produces: - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/api.UpsertConfItemsResp' - "400": - description: Bad Request - schema: - $ref: '#/definitions/api.HTTPClientErrResp' - summary: 编辑发布层级配置 + responses: {} + summary: 修改mysql配置 tags: - - config_item - /bkconfig/v1/confname/list: - get: - description: 查询某个配置类型/配置文件的配置名列表,会排除 已锁定的平台配置 + - mysql + /mysql/mycnf-clone: + post: + consumes: + - application/json + description: |- + 用于 slave 重建或迁移,保持新实例与 my.cnf 实例关键参数相同的场景 + 默认 clone 参数: parameters: - - example: MySQL-5.7 - in: query - name: conf_file - required: true - type: string - - description: 如果设置,会根据前缀模糊匹配搜索 - in: query - name: conf_name - type: string - - example: dbconf - in: query - name: conf_type + - description: description + in: body + name: body required: true - type: string - - example: tendbha - in: query - name: namespace - type: string + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.MycnfCloneComp' produces: - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/QueryConfigNamesResp' - "400": - description: Bad Request - schema: - $ref: '#/definitions/api.HTTPClientErrResp' - summary: 查询预定义的配置名列表 + responses: {} + summary: 从源实例克隆 my.cnf 部分参数到目标实例 tags: - - config_meta - /bkconfig/v1/simpleitem/list: - get: - description: 请勿使用 - parameters: - - in: query - name: bk_biz_id - type: string - - in: query - name: cluster - type: string - - in: query - name: conf_file - type: string - - in: query - name: conf_name - type: string - - in: query - name: conf_type - type: string - - in: query - name: conf_value - type: string - - in: query - name: created_at - type: string - - in: query - name: created_by - type: string - - in: query - name: description - type: string - - in: query - name: format - type: string - - in: query - name: inherit_from - type: string - - in: query - name: level_name - type: string - - in: query - name: level_value - type: string - - in: query - name: module - type: string - - in: query - name: namespace - type: string - - in: query - name: revision - type: string - - in: query - name: updated_at - type: string - - in: query - name: updated_by - type: string - - in: query - name: view - type: string - produces: + - mysql + /mysql/oscmd-run: + post: + consumes: - application/json + description: 执行os简单命令 + parameters: + - description: short description + in: body + name: body + required: true + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.OSCmds' responses: "200": description: OK schema: - items: - $ref: '#/definitions/model.ConfigModel' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/api.HTTPClientErrResp' - summary: 查询配置项列表通用接口 + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.OSCmdRunResp' + summary: 执行os简单命令 tags: - - simple_item - /bkconfig/v1/version/detail: - get: - description: 查询历史配置版本的详情 + - mysql + /mysql/parse-binlog-time: + post: + consumes: + - application/json + description: 获取 binlog FileDescriptionFormat 和 RotateEvent 事件 parameters: - - description: 业务ID,必选项 - example: testapp - in: query - name: bk_biz_id - required: true - type: string - - description: 配置文件名,一般配置类型与配置文件一一对应,但如 mysql 5.6, 5.7 两个版本同属 dbconf 配置,所以有 - MySQL-5.5, MySQL-5.6 两个配置文件 - example: MySQL-5.7 - in: query - name: conf_file - required: true - type: string - - description: 配置类型,如 dbconf,backup - example: dbconf - in: query - name: conf_type + - description: short description + in: body + name: body required: true - type: string - - description: |- - 配置层级名,当前允许值 `app`,`module`,`cluster` - 配合 flag_locked 锁定标记,可以知道 锁定级别 - enum: - - plat - - app - - module - - cluster - in: query - name: level_name + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BinlogTimeComp' + responses: {} + summary: 获取 binlog 的开始和结束时间 + tags: + - mysql + /mysql/pt-table-checksum: + post: + consumes: + - application/json + description: 数据校验 + parameters: + - description: description + in: body + name: body required: true - type: string - - description: 配置层级值 - in: query - name: level_value - type: string - - description: 命名空间,一般指DB类型 - example: tendbha - in: query - name: namespace + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.PtTableChecksumComp' + responses: {} + summary: 数据校验 + tags: + - mysql + /mysql/recover-binlog: + post: + consumes: + - application/json + description: 通过 `mysqlbinlog xxx | mysql` 导入 binlog + parameters: + - description: short description + in: body + name: body required: true - type: string - - example: v_20220309215824 - in: query - name: revision - type: string - produces: + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RecoverBinlogComp' + responses: {} + summary: 导入 binlog + tags: + - mysql + /mysql/restore-dr: + post: + consumes: - application/json + description: 物理备份、逻辑备份恢复 + parameters: + - description: short description + in: body + name: body + required: true + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RestoreDRComp' responses: "200": description: OK schema: - $ref: '#/definitions/api.GetVersionedDetailResp' - "400": - description: Bad Request - schema: - $ref: '#/definitions/api.HTTPClientErrResp' - summary: 查询版本的详细信息 + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_util_mysqlutil.ChangeMaster' + summary: 备份恢复 tags: - - config_version - /bkconfig/v1/version/generate: + - mysql + /mysql/semantic-dumpschema: post: consumes: - application/json - description: |- - 从现有配置项直接生成配置文件并返回,每次调用会生成一个新版本,可以选择是否直接发布。这个接口一般用户后台服务查询配置 - 修改配置并发布,使用 /confitem/upsert 接口 - 直接查询配置文件内容,使用 /confitem/query 接口 - 根据 `method` 生成方式不同,可以生成配置并存储 `GenerateAndSave`、生成配置并存储且发布`GenerateAndPublish` - 使用 `GenerateAndSave` 方式需要进一步调用 PublishConfigFile 接口进行发布 + description: 运行语义检查 parameters: - - description: Generate config file versioned + - description: short description in: body name: body required: true schema: - $ref: '#/definitions/GenerateConfigReq' + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SemanticDumpSchemaComp' produces: - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/GenerateConfigResp' - "400": - description: Bad Request - schema: - $ref: '#/definitions/api.HTTPClientErrResp' - summary: 生成并获取配置文件新版本 + responses: {} + summary: 运行语义检查 tags: - - config_version - /bkconfig/v1/version/list: - get: - description: Get config file versions list + - mysql + /spdierctl/deploy: + post: + consumes: + - application/json + description: 部署 spider ctl 实例说明 parameters: - - description: 业务ID,必选项 - example: testapp - in: query - name: bk_biz_id - required: true - type: string - - description: 配置文件名,一般配置类型与配置文件一一对应,但如 mysql 5.6, 5.7 两个版本同属 dbconf 配置,所以有 - MySQL-5.5, MySQL-5.6 两个配置文件 - example: MySQL-5.7 - in: query - name: conf_file + - description: short description + in: body + name: body required: true - type: string - - description: 配置类型,如 dbconf,backup - example: dbconf - in: query - name: conf_type + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLComp' + responses: {} + summary: 部署 spider ctl 实例 + tags: + - spiderctl + /spider/deploy: + post: + consumes: + - application/json + description: 部署 spider 实例说明 + parameters: + - description: short description + in: body + name: body required: true - type: string - - description: |- - 配置层级名,当前允许值 `app`,`module`,`cluster` - 配合 flag_locked 锁定标记,可以知道 锁定级别 - enum: - - plat - - app - - module - - cluster - in: query - name: level_name + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLComp' + responses: {} + summary: 部署 spider 实例 + tags: + - spider + /tbinlogdumper/deploy: + post: + consumes: + - application/json + description: 部署 tbinlogdumper 实例说明 + parameters: + - description: short description + in: body + name: body required: true - type: string - - description: 配置层级值 - in: query - name: level_value - type: string - - description: 命名空间,一般指DB类型 - example: tendbha - in: query - name: namespace + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_tbinlogdumper.InstallTbinlogDumperComp' + responses: {} + summary: 部署 tbinlogdumper 实例 + tags: + - tbinlogdumper + /tbinlogdumper/semantic-dumpschema: + post: + consumes: + - application/json + description: 备份表结构并导入 + parameters: + - description: short description + in: body + name: body required: true - type: string + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_tbinlogdumper.DumpSchemaComp' produces: - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/ListConfigVersionsResp' - "400": - description: Bad Request - schema: - $ref: '#/definitions/api.HTTPClientErrResp' - summary: 查询历史配置版本名列表 + responses: {} + summary: 备份表结构并导入 tags: - - config_version - /bkconfig/v1/version/publish: + - tbinlogdumper + /tbinlogdumper/uninstall: post: consumes: - application/json - description: |- - 发布指定版本的配置文件,未发布状态的配置文件是不能使用的 - 发布操作会把已有 published 状态的配置文件下线;同一个 revision 版本的配置无法重复发布 - 发布时带上 patch 参数可以覆盖配置中心该版本的配置项(只有配置项值是`{{`开头的才能被覆盖) + description: 卸载 tbinlogdumper 实例说明 parameters: - - description: Publish config file versioned + - description: short description in: body name: body required: true schema: - $ref: '#/definitions/PublishConfigFileReq' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/api.HTTPOkNilResp' - "400": - description: Bad Request - schema: - $ref: '#/definitions/api.HTTPClientErrResp' - summary: 直接发布一个版本[废弃] + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_tbinlogdumper.UnInstallTbinlogDumperComp' + responses: {} + summary: 卸载 tbinlogdumper 实例 tags: - - config_version + - tbinlogdumper schemes: - http -securityDefinitions: - BasicAuth: - type: basic swagger: "2.0" diff --git a/dbm-services/common/db-config/internal/repository/migrate.go b/dbm-services/common/db-config/internal/repository/migrate.go index 94e21d5a81..ea5857c669 100644 --- a/dbm-services/common/db-config/internal/repository/migrate.go +++ b/dbm-services/common/db-config/internal/repository/migrate.go @@ -2,6 +2,7 @@ package repository import ( "fmt" + "time" "bk-dbconfig/assets" "bk-dbconfig/internal/repository/migratespec" @@ -77,6 +78,9 @@ func DoMigrateFromEmbed() error { return nil } else { logger.Errorf("migrate data from embed failed: %s", err.Error()) + logger.Warn("sleep 120s to return. " + + "you may need ./bkconfigsvr --migrate --migrate-force=VersionNo after you fix it") + time.Sleep(120 * time.Second) return err } } diff --git a/dbm-services/common/db-config/internal/repository/migratespec/sensitive.go b/dbm-services/common/db-config/internal/repository/migratespec/sensitive.go index 80b1674b3d..ce9efc6954 100644 --- a/dbm-services/common/db-config/internal/repository/migratespec/sensitive.go +++ b/dbm-services/common/db-config/internal/repository/migratespec/sensitive.go @@ -99,7 +99,8 @@ func MigrateSensitive(db *gorm.DB) error { ValueDefault: "", FlagEncrypt: 1, }, } - for _, c := range confNames { + confNameIdStart := 1000000 + for i, c := range confNames { if c.ValueDefault == "" { c.ValueDefault = password.MustGenerate(12, 3, 0, false, true) logger.Info("sensitive: {Namespace:%s ConfType:%s ConfFile:%s ConfName:%s ValueDefault:%s}", @@ -109,14 +110,15 @@ func MigrateSensitive(db *gorm.DB) error { c.ValueDefault, _ = crypt.EncryptString(c.ValueDefault, key, constvar.EncryptEnableZip) } } - c.ConfType = "STRING" + c.ID = uint64(confNameIdStart + i) + c.ValueType = "STRING" c.ValueTypeSub = "" c.ValueAllowed = "" c.FlagStatus = 1 } err := db.Transaction(func(tx *gorm.DB) error { - if err1 := tx.Omit("id", "value_formula", "order_index", "since_version", "stage"). + if err1 := tx.Omit("value_formula", "order_index", "since_version", "stage"). Create(confNames).Error; err1 != nil { return errors.WithMessage(err1, "init sensitive conf_name") } @@ -126,7 +128,6 @@ func MigrateSensitive(db *gorm.DB) error { return errors.WithMessage(err1, c.ConfName) } */ - return nil }) return err diff --git a/dbm-services/common/db-config/internal/service/simpleconfig/config_meta.go b/dbm-services/common/db-config/internal/service/simpleconfig/config_meta.go index 15055e9794..4471faee7d 100644 --- a/dbm-services/common/db-config/internal/service/simpleconfig/config_meta.go +++ b/dbm-services/common/db-config/internal/service/simpleconfig/config_meta.go @@ -1,12 +1,13 @@ package simpleconfig import ( + "fmt" + "bk-dbconfig/internal/api" "bk-dbconfig/internal/pkg/errno" "bk-dbconfig/internal/repository/model" "bk-dbconfig/pkg/util" "bk-dbconfig/pkg/validate" - "fmt" "github.com/pkg/errors" "gorm.io/gorm" @@ -40,7 +41,7 @@ func CheckConfNameAndValue(c *model.ConfigModel, checkValue bool, valueType, val if checkName { if sqlRes.Error != nil { if errors.Is(sqlRes.Error, gorm.ErrRecordNotFound) { - return errors.Errorf("illegal conf_name [%s] for conf_type=%s", c.ConfName, c.ConfType) + return errors.Errorf("illegal conf_name [%s] for %s %s", c.ConfName, c.Namespace, c.ConfType) } return sqlRes.Error } diff --git a/dbm-services/common/db-dns/dns-api/internal/domain/entity/base.go b/dbm-services/common/db-dns/dns-api/internal/domain/entity/base.go index 5e43799e74..239a1fbb3f 100644 --- a/dbm-services/common/db-dns/dns-api/internal/domain/entity/base.go +++ b/dbm-services/common/db-dns/dns-api/internal/domain/entity/base.go @@ -24,7 +24,7 @@ import ( type TbDnsBase struct { Uid int64 `gorm:"column:uid;size:11;primary_key;AUTO_INCREMENT" json:"uid"` App string `gorm:"size:32;column:app" json:"app"` - DomainName string `gorm:"size:64;column:domain_name" json:"domain_name"` + DomainName string `gorm:"size:255;column:domain_name" json:"domain_name"` Ip string `gorm:"size:20;column:ip" json:"ip"` Port int `gorm:"size:11;column:port" json:"port"` StartTime time.Time `gorm:"column:start_time" json:"start_time"` @@ -37,12 +37,12 @@ type TbDnsBase struct { BkCloudId int64 `gorm:"size:32;column:bk_cloud_id" json:"bk_cloud_id"` } -// TableName TODO +// TableName tb_dns_base func (t *TbDnsBase) TableName() string { return "tb_dns_base" } -// Columns TODO +// Columns tb_dns_base col func (t *TbDnsBase) Columns() []string { return []string{"uid", "app", "domain_name", "ip", "port", "start_time", "last_change_time", "manager", "remark", "dns_str", "status", "domain_type", "bk_cloud_id"} @@ -68,7 +68,7 @@ func (t *TbDnsBase) TableUnique() [][]string { type TbDnsServer struct { Uid int64 `gorm:"column:uid;size:11;primary_key;AUTO_INCREMENT" json:"uid"` Ip string `gorm:"column:ip;size:20" json:"ip"` - ForwardIp string `gorm:"column:forward_ip;size:100" json:"forward_ip"` + ForwardIp string `gorm:"column:forward_ip;size:255" json:"forward_ip"` Idc string `gorm:"column:idc;size:64" json:"idc"` StartTime time.Time `gorm:"column:start_time" json:"start_time"` LastConfigTime time.Time `gorm:"column:last_config_time" json:"last_config_time"` @@ -80,12 +80,12 @@ type TbDnsServer struct { BkCloudId int64 `gorm:"size:32;column:bk_cloud_id" json:"bk_cloud_id"` } -// TableName TODO +// TableName tb_dns_server func (t *TbDnsServer) TableName() string { return "tb_dns_server" } -// Columns TODO +// Columns tb_dns_server col func (t *TbDnsServer) Columns() []string { return []string{"uid", "ip", "forward_ip", "idc", "start_time", "last_config_time", "last_alived", "remark", "update_counter", "type", "status"} @@ -100,7 +100,7 @@ type TbDnsIdcMap struct { BkCloudId int64 `gorm:"size:32;column:bk_cloud_id" json:"bk_cloud_id"` } -// TableName TODO +// TableName tb_dns_idc_map func (t *TbDnsIdcMap) TableName() string { return "tb_dns_idc_map" } diff --git a/dbm-services/common/db-resource/.golangci.yml b/dbm-services/common/db-resource/.golangci.yml index 023e934a2f..2f733f72d8 100644 --- a/dbm-services/common/db-resource/.golangci.yml +++ b/dbm-services/common/db-resource/.golangci.yml @@ -4,6 +4,8 @@ linters-settings: funlen: lines: 80 statements: 80 + gocyclo: + min-complexity: 20 gocritic: enabled-checks: - nestingReduce @@ -54,5 +56,5 @@ linters: # - wsl - funlen # - golint - - cyclop + - gocyclo fast: false \ No newline at end of file diff --git a/dbm-services/common/db-resource/go.mod b/dbm-services/common/db-resource/go.mod index f15cdae184..13c642430f 100644 --- a/dbm-services/common/db-resource/go.mod +++ b/dbm-services/common/db-resource/go.mod @@ -6,8 +6,9 @@ require ( github.com/deckarep/golang-set/v2 v2.3.0 github.com/gin-contrib/pprof v1.4.0 github.com/gin-contrib/requestid v0.0.6 - github.com/gin-gonic/gin v1.9.0 + github.com/gin-gonic/gin v1.9.1 github.com/go-redis/redis/v8 v8.11.5 + github.com/robfig/cron/v3 v3.0.1 github.com/spf13/viper v1.15.0 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cbs v1.0.604 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.604 @@ -16,15 +17,17 @@ require ( ) require ( - github.com/bytedance/sonic v1.8.8 // indirect + github.com/bytedance/sonic v1.10.0-rc3 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect - github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect + github.com/chenzhuoyu/iasm v0.9.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.12.0 // indirect + github.com/go-playground/validator/v10 v10.14.1 // indirect github.com/go-sql-driver/mysql v1.7.1 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/google/uuid v1.3.0 // indirect @@ -32,14 +35,14 @@ require ( github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/cpuid/v2 v2.2.4 // indirect - github.com/leodido/go-urn v1.2.3 // indirect + github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/leodido/go-urn v1.2.4 // indirect github.com/magiconair/properties v1.8.7 // indirect - github.com/mattn/go-isatty v0.0.18 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.0.7 // indirect + github.com/pelletier/go-toml/v2 v2.0.9 // indirect github.com/spf13/afero v1.9.5 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect @@ -47,12 +50,12 @@ require ( github.com/subosito/gotenv v1.4.2 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect - golang.org/x/arch v0.3.0 // indirect - golang.org/x/crypto v0.8.0 // indirect - golang.org/x/net v0.9.0 // indirect - golang.org/x/sys v0.7.0 // indirect - golang.org/x/text v0.9.0 // indirect - google.golang.org/protobuf v1.30.0 // indirect + golang.org/x/arch v0.4.0 // indirect + golang.org/x/crypto v0.11.0 // indirect + golang.org/x/net v0.13.0 // indirect + golang.org/x/sys v0.10.0 // indirect + golang.org/x/text v0.11.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/dbm-services/common/db-resource/go.sum b/dbm-services/common/db-resource/go.sum index f8d79fa566..5535e2230f 100644 --- a/dbm-services/common/db-resource/go.sum +++ b/dbm-services/common/db-resource/go.sum @@ -39,14 +39,18 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.8.8 h1:Kj4AYbZSeENfyXicsYppYKO0K2YWab+i2UTSY7Ukz9Q= -github.com/bytedance/sonic v1.8.8/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= +github.com/bytedance/sonic v1.10.0-rc3 h1:uNSnscRapXTwUgTyOF0GVljYD08p9X/Lbr9MweSV3V0= +github.com/bytedance/sonic v1.10.0-rc3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= +github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo= +github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -71,6 +75,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/gin-contrib/pprof v1.4.0 h1:XxiBSf5jWZ5i16lNOPbMTVdgHBdhfGRD5PZ1LWazzvg= github.com/gin-contrib/pprof v1.4.0/go.mod h1:RrehPJasUVBPK6yTUwOl8/NP6i0vbUgmxtis+Z5KE90= github.com/gin-contrib/requestid v0.0.6 h1:mGcxTnHQ45F6QU5HQRgQUDsAfHprD3P7g2uZ4cSZo9o= @@ -78,8 +84,8 @@ github.com/gin-contrib/requestid v0.0.6/go.mod h1:9i4vKATX/CdggbkY252dPVasgVucy/ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= -github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8= -github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k= +github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= +github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -92,8 +98,8 @@ github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= -github.com/go-playground/validator/v10 v10.12.0 h1:E4gtWgxWxp8YSxExrQFv5BpCahla0PVF2oTTEYaWQGI= -github.com/go-playground/validator/v10 v10.12.0/go.mod h1:hCAPuzYvKdP33pxWa+2+6AIKXEKqjIUyqsNCtbsSJrA= +github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k= +github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= @@ -178,8 +184,9 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= -github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= +github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -190,13 +197,13 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= -github.com/leodido/go-urn v1.2.3 h1:6BE2vPT0lqoz3fmOesHZiaiFh7889ssCo2GMvLCfiuA= -github.com/leodido/go-urn v1.2.3/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= -github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -208,14 +215,16 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= -github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us= -github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= +github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0= +github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= +github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= @@ -242,8 +251,9 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cbs v1.0.604 h1:GTPOz3rke4PitOgPZbQ5DaEK3e7zLzm2+r8XzAQgIF4= @@ -267,8 +277,8 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= -golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc= +golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -277,8 +287,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= -golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -344,8 +354,8 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/net v0.13.0 h1:Nvo8UFsZ8X3BhAC9699Z1j7XQ3rsZnUUm7jfBEk1ueY= +golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -402,11 +412,11 @@ golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -416,8 +426,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -562,8 +572,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= @@ -591,6 +601,7 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= diff --git a/dbm-services/common/db-resource/internal/config/config.go b/dbm-services/common/db-resource/internal/config/config.go index ccd74a58ca..7720685255 100644 --- a/dbm-services/common/db-resource/internal/config/config.go +++ b/dbm-services/common/db-resource/internal/config/config.go @@ -20,7 +20,7 @@ type Config struct { CmdbDb Db `yaml:"cmdb_db" mapstructure:"cmdb_db"` LoggerConfig LoggerConfig `yaml:"loggerConfig"` BkSecretConfig BkSecretConfig `yaml:"bkSecretConfig"` - RedisDb RedisDb `yaml:"redis"` + Redis Redis `yaml:"redis"` CloudCertificate *CloudCertificate `yaml:"cloudCertificate"` // dbmeta: http://bk-dbm DbMeta string `json:"dbmeta"` @@ -47,12 +47,13 @@ type BkSecretConfig struct { BKAppSecret string `yaml:"bk_app_secret" mapstructure:"bk_app_secret"` BkUserName string `yaml:"bk_username" mapstructure:"bk_username"` BkBaseUrl string `yaml:"bk_base_url" mapstructure:"bk_base_url"` + GseBaseUrl string `yaml:"gse_base_url" mapstructure:"gse_base_url"` } -// RedisDb TODO -type RedisDb struct { - Addr string `yaml:"addr"` - Pwd string `yaml:"password"` +// Redis TODO +type Redis struct { + Addr string `yaml:"addr"` + Password string `yaml:"password"` } // CloudCertificate TODO diff --git a/dbm-services/common/db-resource/internal/controller/apply/apply.go b/dbm-services/common/db-resource/internal/controller/apply/apply.go index f14a6491ad..dc422da85e 100644 --- a/dbm-services/common/db-resource/internal/controller/apply/apply.go +++ b/dbm-services/common/db-resource/internal/controller/apply/apply.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + // Package apply TODO package apply @@ -7,15 +17,23 @@ import ( "dbm-services/common/db-resource/internal/controller" "dbm-services/common/db-resource/internal/lock" + "dbm-services/common/db-resource/internal/middleware" "dbm-services/common/db-resource/internal/model" "dbm-services/common/db-resource/internal/svr/apply" "dbm-services/common/db-resource/internal/svr/task" "dbm-services/common/go-pubpkg/cmutil" + "dbm-services/common/go-pubpkg/errno" "dbm-services/common/go-pubpkg/logger" "github.com/gin-gonic/gin" ) +func init() { + middleware.RequestLoggerFilter.Add("/resource/apply") + middleware.RequestLoggerFilter.Add("/resource/pre-apply") + middleware.RequestLoggerFilter.Add("/resource/confirm/apply") +} + // ApplyHandler TODO type ApplyHandler struct { controller.BaseHandler @@ -34,11 +52,6 @@ func (c *ApplyHandler) RegisterRouter(engine *gin.Engine) { } } -func newLocker(key string, requestId string) *lock.SpinLock { - return lock.NewSpinLock(&lock.RedisLock{Name: key, RandKey: requestId, Expiry: 120 * time.Second}, 60, - 350*time.Millisecond) -} - // ConfirmApplyParam TODO type ConfirmApplyParam struct { RequestId string `json:"request_id" binding:"required"` @@ -52,12 +65,12 @@ func (c *ApplyHandler) ConfirmApply(r *gin.Context) { return } requestId := r.GetString("request_id") - hostIds := cmutil.RemoveDuplicateIntElement(param.HostIds) + hostIds := cmutil.RemoveDuplicate(param.HostIds) var cnt int64 err := model.DB.Self.Table(model.TbRpApplyDetailLogName()).Where("request_id = ?", param.RequestId).Count(&cnt).Error if err != nil { logger.Error("use request id %s,query apply resouece failed %s", param.RequestId, err.Error()) - c.SendResponse(r, fmt.Errorf("%w", err), requestId, "use request id search applyed resource failed") + c.SendResponse(r, fmt.Errorf("%w", err), "use request id search applyed resource failed", requestId) return } if len(hostIds) != int(cnt) { @@ -69,7 +82,7 @@ func (c *ApplyHandler) ConfirmApply(r *gin.Context) { err = model.DB.Self.Table(model.TbRpDetailName()).Where(" bk_host_id in (?) and status != ? ", hostIds, model.Prepoccupied).Find(&rs).Error if err != nil { - c.SendResponse(r, err, requestId, err.Error()) + c.SendResponse(r, err, err.Error(), requestId) return } if len(rs) > 0 { @@ -77,7 +90,7 @@ func (c *ApplyHandler) ConfirmApply(r *gin.Context) { for _, v := range rs { errMsg += fmt.Sprintf("%s:%s\n", v.IP, v.Status) } - c.SendResponse(r, fmt.Errorf("the following example:%s,abnormal state", errMsg), requestId, "") + c.SendResponse(r, fmt.Errorf("the following example:%s,abnormal state", errMsg), "", requestId) return } // update to used status @@ -89,10 +102,29 @@ func (c *ApplyHandler) ConfirmApply(r *gin.Context) { }, ) if err != nil { - c.SendResponse(r, err, requestId, err.Error()) + c.SendResponse(r, err, err.Error(), requestId) return } - c.SendResponse(r, nil, requestId, "successful") + uerr := model.DB.Self.Table(model.TbRpOperationInfoTableName()).Where("request_id = ?", + param.RequestId).Update("status", model.Used).Error + if uerr != nil { + logger.Warn("update tb_rp_operation_info failed %s ", uerr.Error()) + } + archive(hostIds) + c.SendResponse(r, nil, "successful", requestId) +} + +func archive(bkHostIds []int) { + var rs []model.TbRpDetail + err := model.DB.Self.Table(model.TbRpDetailName()).Where(" bk_host_id in (?) and status = ? ", bkHostIds, + model.Used).Find(&rs).Error + if err != nil { + logger.Error("query used resource failed %s", err.Error()) + return + } + for _, v := range rs { + task.ArchiverResourceChan <- v.ID + } } // ApplyResource TODO @@ -105,10 +137,16 @@ func (c *ApplyHandler) PreApplyResource(r *gin.Context) { c.ApplyBase(r, model.Prepoccupied) } +func newLocker(key string, requestId string) *lock.SpinLock { + return lock.NewSpinLock(&lock.RedisLock{Name: key, RandKey: requestId, Expiry: 120 * time.Second}, 60, + 350*time.Millisecond) +} + // ApplyBase TODO func (c *ApplyHandler) ApplyBase(r *gin.Context, mode string) { task.RuningTask <- struct{}{} defer func() { <-task.RuningTask }() + logger.Info("start apply resource ... ") var param apply.ApplyRequestInputParam var pickers []*apply.PickerObject var err error @@ -118,14 +156,14 @@ func (c *ApplyHandler) ApplyBase(r *gin.Context, mode string) { } requestId = r.GetString("request_id") if err := param.ParamCheck(); err != nil { - c.SendResponse(r, err, requestId, err.Error()) + c.SendResponse(r, errno.ErrApplyResourceParamCheck.AddErr(err), err.Error(), requestId) return } // get the resource lock if it is dry run you do not need to acquire it if !param.DryRun { lock := newLocker(param.LockKey(), requestId) if err := lock.Lock(); err != nil { - c.SendResponse(r, err, requestId, err.Error()) + c.SendResponse(r, errno.ErrResourceLock.AddErr(err), err.Error(), requestId) return } defer func() { @@ -140,24 +178,24 @@ func (c *ApplyHandler) ApplyBase(r *gin.Context, mode string) { }() pickers, err = apply.CycleApply(param) if err != nil { - logger.Error("apply machine failed %s", err.Error()) - c.SendResponse(r, err, requestId, err.Error()) + c.SendResponse(r, err, "", requestId) return } - if !param.DryRun { - data, err := apply.LockReturnPickers(pickers, mode) - if err != nil { - c.SendResponse(r, err, nil, requestId) - return - } - logger.Info(fmt.Sprintf("The %s, will return %d machines", requestId, len(data))) - task.ApplyResponeLogChan <- task.ApplyResponeLogItem{ - RequestId: requestId, - Data: data, - } - task.RecordRsOperatorInfoChan <- param.GetOperationInfo(requestId) - c.SendResponse(r, nil, data, requestId) + if param.DryRun { + c.SendResponse(r, nil, map[string]interface{}{"check_success": true}, requestId) return } - c.SendResponse(r, nil, map[string]interface{}{"check_success": true}, requestId) + data, err := apply.LockReturnPickers(pickers, mode) + if err != nil { + c.SendResponse(r, errno.ErresourceLockReturn.AddErr(err), nil, requestId) + return + } + logger.Info(fmt.Sprintf("The %s, will return %d machines", requestId, len(data))) + task.ApplyResponeLogChan <- task.ApplyResponeLogItem{ + RequestId: requestId, + Data: data, + } + task.RecordRsOperatorInfoChan <- param.GetOperationInfo(requestId, mode, data) + c.SendResponse(r, nil, data, requestId) + return } diff --git a/dbm-services/common/db-resource/internal/controller/controller.go b/dbm-services/common/db-resource/internal/controller/controller.go index 88b7294477..e3c47780ce 100644 --- a/dbm-services/common/db-resource/internal/controller/controller.go +++ b/dbm-services/common/db-resource/internal/controller/controller.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + // Package controller TODO package controller @@ -5,8 +15,8 @@ import ( "fmt" "net/http" - "dbm-services/common/db-resource/pkg/errno" "dbm-services/common/go-pubpkg/cmutil" + "dbm-services/common/go-pubpkg/errno" "dbm-services/common/go-pubpkg/logger" "github.com/gin-gonic/gin" @@ -30,7 +40,6 @@ func (c *BaseHandler) Prepare(r *gin.Context, schema interface{}) error { err := fmt.Errorf("get request id error ~") c.SendResponse(r, err, nil, requestId) return err - } if err := r.ShouldBind(&schema); err != nil { logger.Error("ShouldBind Failed %s", err.Error()) @@ -42,7 +51,6 @@ func (c *BaseHandler) Prepare(r *gin.Context, schema interface{}) error { } // SendResponse TODO -// SendResponseT TODO func (c *BaseHandler) SendResponse(r *gin.Context, err error, data interface{}, requestId string) { code, message := errno.DecodeErr(err) r.JSON(http.StatusOK, Response{ diff --git a/dbm-services/common/db-resource/internal/controller/manage/import.go b/dbm-services/common/db-resource/internal/controller/manage/import.go new file mode 100644 index 0000000000..bd2b50f0e1 --- /dev/null +++ b/dbm-services/common/db-resource/internal/controller/manage/import.go @@ -0,0 +1,302 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package manage + +import ( + "encoding/json" + "fmt" + "sync" + "time" + + "dbm-services/common/db-resource/internal/model" + "dbm-services/common/db-resource/internal/svr/apply" + "dbm-services/common/db-resource/internal/svr/bk" + "dbm-services/common/db-resource/internal/svr/cloud" + "dbm-services/common/db-resource/internal/svr/task" + "dbm-services/common/go-pubpkg/cc.v3" + "dbm-services/common/go-pubpkg/cmutil" + "dbm-services/common/go-pubpkg/errno" + "dbm-services/common/go-pubpkg/logger" + + rf "github.com/gin-gonic/gin" +) + +// ImportMachParam TODO +type ImportMachParam struct { + BkCloudId int `json:"bk_cloud_id"` + // ForBizs 业务标签,表示这个资源将来给ForBizs这个业务使用 + ForBizs []int `json:"for_bizs"` + RsTypes []string `json:"resource_types"` + BkBizId int `json:"bk_biz_id" binding:"number"` + Hosts []HostBase `json:"hosts" binding:"gt=0,dive,required"` + Labels map[string]string `json:"labels"` + apply.ActionInfo +} + +// HostBase TODO +type HostBase struct { + Ip string `json:"ip" binding:"required,ip"` + HostId int `json:"host_id" binding:"required"` +} + +func (p ImportMachParam) getOperationInfo(requestId string, hostIds json.RawMessage, + iplist json.RawMessage) model.TbRpOperationInfo { + return model.TbRpOperationInfo{ + RequestID: requestId, + OperationType: model.Imported, + TotalCount: len(p.getIps()), + TaskId: p.TaskId, + BillId: p.BillId, + Operator: p.Operator, + CreateTime: time.Now(), + BkHostIds: hostIds, + IpList: iplist, + UpdateTime: time.Now(), + } +} + +func (p ImportMachParam) getIps() (ips []string) { + for _, v := range p.Hosts { + if !cmutil.IsEmpty(v.Ip) { + ips = append(ips, v.Ip) + } + } + return +} + +func (p ImportMachParam) getHostIds() (hostIds []int) { + for _, v := range p.Hosts { + if v.HostId > 0 { + hostIds = append(hostIds, v.HostId) + } + } + return +} + +func (p *ImportMachParam) existCheck() (err error) { + var alreadyExistRs []model.TbRpDetail + err = model.DB.Self.Table(model.TbRpDetailName()).Where("bk_cloud_id = ? and ip in (?)", p.BkCloudId, p.getIps()). + Scan(&alreadyExistRs).Error + if err != nil { + return errno.ErrDBQuery.Add(err.Error()) + } + if len(alreadyExistRs) > 0 { + errMsg := "already exist:\n " + for _, r := range alreadyExistRs { + errMsg += fmt.Sprintf(" bk_cloud_id:%d,ip:%s \n", r.BkCloudID, r.IP) + } + return fmt.Errorf(errMsg) + } + return nil +} + +// Import TODO +func (c *MachineResourceHandler) Import(r *rf.Context) { + var input ImportMachParam + if err := c.Prepare(r, &input); err != nil { + logger.Error(fmt.Sprintf("Preare Error %s", err.Error())) + return + } + requestId := r.GetString("request_id") + if err := input.existCheck(); err != nil { + c.SendResponse(r, errno.RepeatedIpExistSystem.Add(err.Error()), requestId, err.Error()) + return + } + resp, err := Doimport(input) + if err != nil { + logger.Error(fmt.Sprintf("ImportByIps failed %s", err.Error())) + c.SendResponse(r, err, requestId, err.Error()) + return + } + if len(resp.NotFoundInCCHosts) == len(input.Hosts) { + c.SendResponse(r, fmt.Errorf("all machines failed to query cmdb information"), resp, requestId) + return + } + hostIds, err := json.Marshal(input.getHostIds()) + if err != nil { + c.SendResponse(r, errno.ErrJSONMarshal.Add("input bkhostIds"), resp, requestId) + return + } + iplist, err := json.Marshal(input.getIps()) + if err != nil { + c.SendResponse(r, errno.ErrJSONMarshal.Add("input ips"), resp, requestId) + return + } + task.RecordRsOperatorInfoChan <- input.getOperationInfo(requestId, hostIds, iplist) + c.SendResponse(r, err, resp, requestId) +} + +// ImportHostResp TODO +type ImportHostResp struct { + GetDiskInfoJobErrMsg string `json:"get_disk_job_errmsg"` + SearchDiskErrInfo map[string]string `json:"search_disk_err_info"` + NotFoundInCCHosts []string `json:"not_found_in_cc_hosts"` +} + +func (p ImportMachParam) transParamToBytes() (lableJson, bizJson, rstypes json.RawMessage, err error) { + lableJson = []byte("{}") + lableJson, err = json.Marshal(cmutil.CleanStrMap(p.Labels)) + if err != nil { + logger.Error(fmt.Sprintf("ConverLableToJsonStr Failed,Error:%s", err.Error())) + return + } + bizJson = []byte("[]") + if len(p.ForBizs) > 0 { + bizJson, err = json.Marshal(cmutil.IntSliceToStrSlice(p.ForBizs)) + if err != nil { + logger.Error(fmt.Sprintf("conver biz json Failed,Error:%s", err.Error())) + return + } + } + rstypes = []byte("[]") + if len(p.RsTypes) > 0 { + rstypes, err = json.Marshal(cmutil.CleanStrElems(p.RsTypes)) + if err != nil { + logger.Error(fmt.Sprintf("conver resource types Failed,Error:%s", err.Error())) + return + } + } + return +} + +// Doimport TODO +func Doimport(param ImportMachParam) (resp *ImportHostResp, err error) { + var ccHostsInfo []*cc.Host + var derr error + var diskResp bk.GetDiskResp + var notFoundHosts, gseAgentIds []string + var elems []model.TbRpDetail + resp = &ImportHostResp{} + wg := sync.WaitGroup{} + targetHosts := cmutil.RemoveDuplicate(param.getIps()) + + wg.Add(1) + go func() { + defer wg.Done() + ccHostsInfo, notFoundHosts, derr = bk.BatchQueryHostsInfo(param.BkBizId, targetHosts) + }() + // get disk information in batch + diskResp, err = bk.GetDiskInfo(targetHosts, param.BkCloudId, param.BkBizId) + if err != nil { + logger.Error("query host cc info failed %s", err.Error()) + return resp, err + } + wg.Wait() + resp.SearchDiskErrInfo = diskResp.IpFailedLogMap + resp.NotFoundInCCHosts = notFoundHosts + if derr != nil { + logger.Error("search disk info by job failed %s", derr.Error()) + resp.GetDiskInfoJobErrMsg = derr.Error() + // return + } + if len(notFoundHosts) >= len(param.Hosts) { + return resp, fmt.Errorf("all hosts query empty in cc") + } + + lableJson, bizJson, rstypes, err := param.transParamToBytes() + if err != nil { + return resp, err + } + hostsMap := make(map[string]struct{}) + for _, host := range targetHosts { + hostsMap[host] = struct{}{} + } + for _, emptyhost := range notFoundHosts { + delete(hostsMap, emptyhost) + } + // further probe disk specific information + probeFromCloud(diskResp.IpLogContentMap) + logger.Info("more info %v", ccHostsInfo) + for _, h := range ccHostsInfo { + delete(hostsMap, h.InnerIP) + el := transHostInfoToDbModule(h, param.BkCloudId, param.BkBizId, rstypes, bizJson, lableJson) + el.SetMore(h.InnerIP, diskResp.IpLogContentMap) + // gse agent 1.0的 agent 是用 cloudid:ip + gseAgentId := h.BkAgentId + if cmutil.IsEmpty(gseAgentId) { + gseAgentId = fmt.Sprintf("%d:%s", param.BkCloudId, h.InnerIP) + } + gseAgentIds = append(gseAgentIds, gseAgentId) + el.BkAgentId = gseAgentId + elems = append(elems, el) + } + if err := model.DB.Self.Table(model.TbRpDetailName()).Create(elems).Error; err != nil { + logger.Error("failed to save resource: %s", err.Error()) + return resp, err + } + task.SyncRsGseAgentStatusChan <- gseAgentIds + return resp, err +} + +func transHostInfoToDbModule(h *cc.Host, bkCloudId, bkBizId int, rstp, biz, label []byte) model.TbRpDetail { + return model.TbRpDetail{ + RsTypes: rstp, + DedicatedBizs: biz, + BkCloudID: bkCloudId, + BkBizId: bkBizId, + AssetID: h.AssetID, + BkHostID: h.BKHostId, + IP: h.InnerIP, + Label: label, + DeviceClass: h.DeviceClass, + DramCap: h.BkMem, + CPUNum: h.BkCpu, + City: h.IdcCityName, + CityID: h.IdcCityId, + SubZone: h.SZone, + SubZoneID: h.SZoneID, + RackID: h.Equipment, + SvrTypeName: h.SvrTypeName, + Status: model.Unused, + NetDeviceID: h.LinkNetdeviceId, + StorageDevice: []byte("{}"), + TotalStorageCap: h.BkDisk, + BkAgentId: h.BkAgentId, + AgentStatusCode: 2, + UpdateTime: time.Now(), + CreateTime: time.Now(), + } +} + +// probeFromCloud Detect The Disk Type Again Through The Cloud Interface +func probeFromCloud(diskMap map[string]*bk.ShellResCollection) { + var clouder cloud.Disker + var err error + if clouder, err = cloud.NewDisker(); err != nil { + return + } + ctr := make(chan struct{}, 5) + wg := sync.WaitGroup{} + for ip := range diskMap { + // if the disk id and region obtained by job are all empty skip the request for cloud api + ctr <- struct{}{} + wg.Add(1) + go func(ip string) { + defer func() { wg.Done(); <-ctr }() + dkinfo := diskMap[ip] + diskIds := bk.GetAllDiskIds(dkinfo.Disk) + if cmutil.IsEmpty(dkinfo.TxRegion) || len(diskIds) <= 0 { + return + } + cloudInfo, err := clouder.DescribeDisk(diskIds, dkinfo.TxRegion) + if err != nil { + logger.Error("call clouder describe disk info failed %s", err.Error()) + return + } + for _, dk := range dkinfo.Disk { + if v, ok := cloudInfo[dk.DiskId]; ok { + dk.DiskType = v + } + } + }(ip) + } + wg.Wait() +} diff --git a/dbm-services/common/db-resource/internal/controller/manage/list.go b/dbm-services/common/db-resource/internal/controller/manage/list.go new file mode 100644 index 0000000000..dc6fcee46d --- /dev/null +++ b/dbm-services/common/db-resource/internal/controller/manage/list.go @@ -0,0 +1,190 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package manage + +import ( + "fmt" + "path" + "strings" + + rf "github.com/gin-gonic/gin" + "gorm.io/gorm" + + "dbm-services/common/db-resource/internal/model" + "dbm-services/common/db-resource/internal/svr/apply" + "dbm-services/common/db-resource/internal/svr/bk" + "dbm-services/common/go-pubpkg/cmutil" + "dbm-services/common/go-pubpkg/errno" + "dbm-services/common/go-pubpkg/logger" +) + +// MachineResourceGetterInputParam TODO +type MachineResourceGetterInputParam struct { + // 专用业务Ids + ForBizs []int `json:"for_bizs"` + City []string `json:"city"` + SubZones []string `json:"subzones"` + DeviceClass []string `json:"device_class"` + Labels map[string]string `json:"labels"` + Hosts []string `json:"hosts"` + BkCloudIds []int `json:"bk_cloud_ids"` + RsTypes []string `json:"resource_types"` + MountPoint string `json:"mount_point"` + Cpu apply.MeasureRange `json:"cpu"` + Mem apply.MeasureRange `json:"mem"` + Disk apply.MeasureRange `json:"disk"` + DiskType string `json:"disk_type"` + StorageSpecs []apply.DiskSpec `json:"storage_spec"` + // true,false,"" + GseAgentAlive string `json:"gse_agent_alive"` + Limit int `json:"limit"` + Offset int `json:"offset"` +} + +// List TODO +func (c *MachineResourceHandler) List(r *rf.Context) { + var input MachineResourceGetterInputParam + if c.Prepare(r, &input) != nil { + return + } + requestId := r.GetString("request_id") + if err := input.paramCheck(); err != nil { + c.SendResponse(r, errno.ErrErrInvalidParam.AddErr(err), nil, requestId) + return + } + db := model.DB.Self.Table(model.TbRpDetailName()) + input.queryBs(db) + var count int64 + if err := db.Count(&count).Error; err != nil { + c.SendResponse(r, err, requestId, err.Error()) + return + } + if input.Limit > 0 { + db = db.Offset(input.Offset).Limit(input.Limit) + } + var data []model.TbRpDetail + if err := db.Find(&data).Error; err != nil { + c.SendResponse(r, errno.ErrDBQuery.AddErr(err), requestId, err.Error()) + return + } + c.SendResponse(r, nil, map[string]interface{}{"details": data, "count": count}, requestId) +} + +func (c *MachineResourceGetterInputParam) paramCheck() (err error) { + if !c.Cpu.Iegal() { + return fmt.Errorf("非法参数 cpu min:%d,max:%d", c.Cpu.Min, c.Cpu.Max) + } + if !c.Mem.Iegal() { + return fmt.Errorf("非法参数 mem min:%d,max:%d", c.Mem.Min, c.Mem.Max) + } + return nil +} + +// matchStorageSpecs TODO +func (c *MachineResourceGetterInputParam) matchStorageSpecs(db *gorm.DB) { + if len(c.StorageSpecs) > 0 { + for _, d := range c.StorageSpecs { + if cmutil.IsNotEmpty(d.MountPoint) { + mp := path.Clean(d.MountPoint) + if cmutil.IsNotEmpty(d.DiskType) { + db.Where(model.JSONQuery("storage_device").Equals(d.DiskType, mp, "disk_type")) + } + logger.Info("storage spec is %v", d) + switch { + case d.MaxSize > 0: + db.Where(model.JSONQuery("storage_device").NumRange(d.MinSize, d.MaxSize, mp, "size")) + case d.MaxSize <= 0 && d.MinSize > 0: + db.Where(model.JSONQuery("storage_device").Gte(d.MinSize, mp, "size")) + } + } + } + } else { + c.Disk.MatchTotalStorageSize(db) + if cmutil.IsNotEmpty(c.MountPoint) { + mp := path.Clean(c.MountPoint) + if cmutil.IsNotEmpty(c.DiskType) { + db.Where(model.JSONQuery("storage_device").Equals(c.DiskType, mp, "disk_type")) + } else { + db.Where(model.JSONQuery("storage_device").KeysContains([]string{mp})) + } + } else { + if cmutil.IsNotEmpty(c.DiskType) { + db.Where(model.JSONQuery("storage_device").SubValContains(c.DiskType, "disk_type")) + } + } + } +} + +func (c *MachineResourceGetterInputParam) queryBs(db *gorm.DB) { + if len(c.Hosts) > 0 { + db.Where("ip in (?)", c.Hosts) + return + } + switch strings.TrimSpace(strings.ToLower(c.GseAgentAlive)) { + case "true": + db.Where("gse_agent_status_code = ? ", bk.GSE_AGENT_OK) + case "false": + db.Where("gse_agent_status_code != ? ", bk.GSE_AGENT_OK) + } + if len(c.BkCloudIds) > 0 { + db.Where("bk_cloud_id in (?) ", c.BkCloudIds) + } + if len(c.RsTypes) > 0 { + // 如果参数["all"],表示选择没有任何资源类型标签的资源 + if c.RsTypes[0] == "all" && len(cmutil.RemoveDuplicate(c.ForBizs)) == 1 { + db.Where("JSON_LENGTH(rs_types) <= 0") + } else { + db.Where(model.JSONQuery("rs_types").Contains(c.RsTypes)) + } + } + c.Cpu.MatchCpu(db) + c.Mem.MatchMem(db) + c.matchStorageSpecs(db) + db.Where("status = ? ", model.Unused) + if len(c.City) > 0 { + db.Where(" city in (?) ", c.City) + } + if len(c.SubZones) > 0 { + db.Where(" sub_zone in (?) ", c.SubZones) + } + if len(c.DeviceClass) > 0 { + db.Where("device_class in (?) ", c.DeviceClass) + } + if len(c.ForBizs) > 0 { + // 如果参数[0],表示选择没有任何业务标签的资源 + if c.ForBizs[0] == 0 && len(cmutil.RemoveDuplicate(c.ForBizs)) == 1 { + db.Where("JSON_LENGTH(dedicated_bizs) <= 0") + } else { + db.Where(model.JSONQuery("dedicated_bizs").Contains(cmutil.IntSliceToStrSlice(c.ForBizs))) + } + } + + db.Order("create_time desc") +} + +// ListAll TODO +func (c *MachineResourceHandler) ListAll(r *rf.Context) { + requestId := r.GetString("request_id") + var data []model.TbRpDetail + db := model.DB.Self.Table(model.TbRpDetailName()).Where("status in (?)", []string{model.Unused, model.Prepoccupied, + model.Preselected}) + err := db.Scan(&data).Error + if err != nil { + c.SendResponse(r, err, requestId, err.Error()) + return + } + var count int64 + if err := db.Count(&count).Error; err != nil { + c.SendResponse(r, err, requestId, err.Error()) + return + } + c.SendResponse(r, nil, map[string]interface{}{"details": data, "count": count}, requestId) +} diff --git a/dbm-services/common/db-resource/internal/controller/manage/rs.go b/dbm-services/common/db-resource/internal/controller/manage/rs.go index 4e3a2ccffd..dc7d547c00 100644 --- a/dbm-services/common/db-resource/internal/controller/manage/rs.go +++ b/dbm-services/common/db-resource/internal/controller/manage/rs.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package manage import ( @@ -5,14 +15,13 @@ import ( "fmt" "dbm-services/common/db-resource/internal/controller" + "dbm-services/common/db-resource/internal/middleware" "dbm-services/common/db-resource/internal/model" - "dbm-services/common/db-resource/internal/svr/apply" "dbm-services/common/db-resource/internal/svr/bk" "dbm-services/common/go-pubpkg/cmutil" "dbm-services/common/go-pubpkg/logger" rf "github.com/gin-gonic/gin" - "gorm.io/gorm" ) // MachineResourceHandler TODO @@ -20,12 +29,20 @@ type MachineResourceHandler struct { controller.BaseHandler } +func init() { + middleware.RequestLoggerFilter.Add("/resource/import") + middleware.RequestLoggerFilter.Add("/resource/update") + middleware.RequestLoggerFilter.Add("/resource/delete") +} + // RegisterRouter TODO func (c *MachineResourceHandler) RegisterRouter(engine *rf.Engine) { r := engine.Group("resource") { r.POST("/list", c.List) + r.POST("/list/all", c.ListAll) r.POST("/update", c.Update) + r.POST("/batch/update", c.BatchUpdate) r.POST("/delete", c.Delete) r.POST("/import", c.Import) r.POST("/mountpoints", c.GetMountPoints) @@ -33,132 +50,93 @@ func (c *MachineResourceHandler) RegisterRouter(engine *rf.Engine) { r.POST("/subzones", c.GetSubZones) r.POST("/deviceclass", c.GetDeviceClass) r.POST("/operation/list", c.OperationInfoList) + r.POST("/spec/sum", c.SpecSum) } } -// MachineResourceGetterInputParam TODO -type MachineResourceGetterInputParam struct { - // 专用业务Ids - ForBizs []int `json:"for_bizs"` - City []string `json:"city"` - SubZones []string `json:"subzones"` - DeviceClass []string `json:"device_class"` - Labels map[string]string `json:"labels"` - Hosts []string `json:"hosts"` - BkCloudIds []int `json:"bk_cloud_ids"` - RsTypes []string `json:"resource_types"` - MountPoint string `json:"mount_point"` - Cpu apply.MeasureRange `json:"cpu"` - Mem apply.MeasureRange `json:"mem"` - Disk apply.MeasureRange `json:"disk"` - DiskType string `json:"disk_type"` - Limit int `json:"limit"` - Offset int `json:"offset"` -} - -// List TODO -func (c *MachineResourceHandler) List(r *rf.Context) { - var input MachineResourceGetterInputParam - if c.Prepare(r, &input) != nil { +// Delete TODO +func (c *MachineResourceHandler) Delete(r *rf.Context) { + var input MachineDeleteInputParam + if err := c.Prepare(r, &input); err != nil { + logger.Error("Preare Error %s", err.Error()) return } requestId := r.GetString("request_id") - db := model.DB.Self.Table(model.TbRpDetailName()) - input.queryBs(db) - var data []model.TbRpDetail - if err := db.Scan(&data).Error; err != nil { - c.SendResponse(r, err, requestId, err.Error()) + affect_row, err := model.DeleteTbRpDetail(input.BkHostIds) + if err != nil { + logger.Error("failed to delete data:%s", err.Error()) + c.SendResponse(r, err, nil, requestId) return } - for index := range data { - if cmutil.IsNotEmpty(data[index].Label) { - k := make(map[string]string) - if err := json.Unmarshal([]byte(data[index].Label), &k); err != nil { - logger.Error("Unmarshal Label Failed %s", err.Error()) - continue - } - data[index].LabelMap = cmutil.CleanStrMap(k) - } - } - var count int64 - if err := db.Count(&count).Error; err != nil { - c.SendResponse(r, err, requestId, err.Error()) + if affect_row == 0 { + c.SendResponse(r, fmt.Errorf("no data was deleted"), nil, requestId) return } - c.SendResponse(r, nil, map[string]interface{}{"details": data, "count": count}, requestId) + c.SendResponse(r, nil, requestId, "Delete Success") +} + +// BatchUpdateMachineInput TODO +type BatchUpdateMachineInput struct { + BkHostIds []int `json:"bk_host_ids" binding:"required,dive,gt=0" ` + ForBizs []int `json:"for_bizs"` + RsTypes []string `json:"resource_types"` + SetBizEmpty bool `json:"set_empty_biz"` + SetRsTypeEmpty bool `json:"set_empty_resource_type"` + StorageDevice map[string]bk.DiskDetail `json:"storage_device"` } -func (c *MachineResourceGetterInputParam) queryBs(db *gorm.DB) { - if len(c.Hosts) > 0 { - db.Where("ip in (?)", c.Hosts) +// BatchUpdate TODO +func (c *MachineResourceHandler) BatchUpdate(r *rf.Context) { + var input BatchUpdateMachineInput + requestId := r.GetString("request_id") + if err := c.Prepare(r, &input); err != nil { + logger.Error("Preare Error %s", err.Error()) return } - if len(c.BkCloudIds) > 0 { - db.Where("bk_cloud_id in (?) ", c.BkCloudIds) - } - if len(c.RsTypes) > 0 { - db.Where(model.JSONQuery("rs_types").Contains(c.RsTypes)) - } - if c.Cpu.Iegal() && c.Cpu.IsNotEmpty() { - db.Where("cpu_num >= ? and cpu_num <= ?", c.Cpu.Min, c.Cpu.Max) - } - if c.Mem.Iegal() && c.Mem.IsNotEmpty() { - db.Where("dram_cap >= ? and dram_cap <= ?", c.Mem.Min, c.Mem.Max) - } - if c.Disk.Iegal() && c.Disk.IsNotEmpty() { - db.Where("total_storage_cap >= ? and total_storage_cap <= ? ", c.Disk.Min, c.Disk.Max) - } - if cmutil.IsNotEmpty(c.MountPoint) { - if cmutil.IsNotEmpty(c.DiskType) { - db.Where(model.JSONQuery("storage_device").Equals(c.DiskType, c.MountPoint, "disk_type")) - } else { - db.Where(model.JSONQuery("storage_device").KeysContains([]string{c.MountPoint})) - } - } else { - if cmutil.IsNotEmpty(c.DiskType) { - db.Where(model.JSONQuery("storage_device").SubValContains(c.DiskType, "disk_type")) + updateMap := make(map[string]interface{}) + if len(input.ForBizs) > 0 { + bizJson, err := json.Marshal(cmutil.IntSliceToStrSlice(input.ForBizs)) + if err != nil { + logger.Error(fmt.Sprintf("conver biz json Failed,Error:%s", err.Error())) + c.SendResponse(r, err, requestId, err.Error()) + return } + updateMap["dedicated_bizs"] = bizJson } - db.Where("status = ? ", model.Unused) - if len(c.City) > 0 { - db.Where(" city in (?) ", c.City) - } - if len(c.SubZones) > 0 { - db.Where(" sub_zone in (?) ", c.SubZones) - } - if len(c.DeviceClass) > 0 { - db.Where("device_class in ? ", c.DeviceClass) + if input.SetBizEmpty { + updateMap["dedicated_bizs"] = "[]" } - if len(c.ForBizs) > 0 { - db.Where(model.JSONQuery("dedicated_bizs").Contains(cmutil.IntSliceToStrSlice(c.ForBizs))) - } - if len(c.Labels) > 0 { - for key, v := range c.Labels { - db.Where("json_contains(label,json_object(?,?))", key, v) + + if len(input.RsTypes) > 0 { + rstypes, err := json.Marshal(input.RsTypes) + if err != nil { + logger.Error(fmt.Sprintf("conver resource types Failed,Error:%s", err.Error())) + c.SendResponse(r, err, requestId, err.Error()) + return } + updateMap["rs_types"] = rstypes } - db.Offset(c.Offset).Limit(c.Limit) -} -// Delete TODO -func (c *MachineResourceHandler) Delete(r *rf.Context) { - var input MachineDeleteInputParam - if err := c.Prepare(r, &input); err != nil { - logger.Error("Preare Error %s", err.Error()) - return + if input.SetRsTypeEmpty { + updateMap["rs_types"] = "[]" } - requestId := r.GetString("request_id") - affect_row, err := model.DeleteTbRpDetail(input.BkHostIds) - if err != nil { - logger.Error("failed to delete data:%s", err.Error()) - c.SendResponse(r, err, nil, requestId) - return + + if len(input.StorageDevice) > 0 { + storageJson, err := json.Marshal(input.StorageDevice) + if err != nil { + logger.Error(fmt.Sprintf("conver resource types Failed,Error:%s", err.Error())) + c.SendResponse(r, err, requestId, err.Error()) + return + } + updateMap["storage_device"] = storageJson } - if affect_row == 0 { - c.SendResponse(r, fmt.Errorf("no data was deleted"), nil, requestId) + err := model.DB.Self.Table(model.TbRpDetailName()).Select("dedicated_bizs", "rs_types", "storage_device"). + Where("bk_host_id in (?)", input.BkHostIds).Updates(updateMap).Error + if err != nil { + c.SendResponse(r, err, requestId, err.Error()) return } - c.SendResponse(r, nil, requestId, "Delete Success") + c.SendResponse(r, nil, "ok", requestId) } // Update TODO diff --git a/dbm-services/common/db-resource/internal/controller/manage/rs_import.go b/dbm-services/common/db-resource/internal/controller/manage/rs_import.go deleted file mode 100644 index d32c86b695..0000000000 --- a/dbm-services/common/db-resource/internal/controller/manage/rs_import.go +++ /dev/null @@ -1,249 +0,0 @@ -package manage - -import ( - "encoding/json" - "fmt" - "sync" - "time" - - "dbm-services/common/db-resource/internal/model" - "dbm-services/common/db-resource/internal/svr/apply" - "dbm-services/common/db-resource/internal/svr/bk" - "dbm-services/common/db-resource/internal/svr/cloud" - "dbm-services/common/db-resource/internal/svr/task" - "dbm-services/common/go-pubpkg/cc.v3" - "dbm-services/common/go-pubpkg/cmutil" - "dbm-services/common/go-pubpkg/logger" - - rf "github.com/gin-gonic/gin" -) - -// ImportMachParam TODO -type ImportMachParam struct { - BkCloudId int `json:"bk_cloud_id"` - // ForBizs 业务标签,表示这个资源将来给ForBizs这个业务使用 - ForBizs []int `json:"for_bizs"` - RsTypes []string `json:"resource_types"` - BkBizId int `json:"bk_biz_id" binding:"number"` - Hosts []HostBase `json:"hosts" binding:"gt=0,dive,required"` - Labels map[string]string `json:"labels"` - apply.ActionInfo -} - -func (p ImportMachParam) getOperationInfo(requestId string) model.TbRpOperationInfo { - return model.TbRpOperationInfo{ - RequestID: requestId, - OperationType: model.Imported, - TotalCount: len(p.getIps()), - TaskId: p.TaskId, - BillId: p.BillId, - Operator: p.Operator, - CreateTime: time.Now(), - UpdateTime: time.Now(), - } -} - -func (p ImportMachParam) getIps() (ips []string) { - for _, v := range p.Hosts { - if !cmutil.IsEmpty(v.Ip) { - ips = append(ips, v.Ip) - } - } - return -} - -// HostBase TODO -type HostBase struct { - Ip string `json:"ip" ` - HostId int `json:"host_id" binding:"required"` -} - -func (p *ImportMachParam) existCheck() (err error) { - var alreadyExistRs []model.TbRpDetail - err = model.DB.Self.Table(model.TbRpDetailName()).Where("bk_cloud_id = ? and ip in (?)", p.BkCloudId, p.getIps()). - Scan(&alreadyExistRs).Error - if err != nil { - return err - } - if len(alreadyExistRs) > 0 { - errMsg := "already exist:\n " - for _, r := range alreadyExistRs { - errMsg += fmt.Sprintf(" bk_cloud_id:%d,ip:%s \n", r.BkCloudID, r.IP) - } - return fmt.Errorf(errMsg) - } - return nil -} - -// Import TODO -func (c *MachineResourceHandler) Import(r *rf.Context) { - var input ImportMachParam - if err := c.Prepare(r, &input); err != nil { - logger.Error(fmt.Sprintf("Preare Error %s", err.Error())) - return - } - requestId := r.GetString("request_id") - if err := input.existCheck(); err != nil { - c.SendResponse(r, err, requestId, err.Error()) - return - } - resp, err := ImportByListHostBiz(input) - if err != nil { - logger.Error(fmt.Sprintf("ImportByIps failed %s", err.Error())) - c.SendResponse(r, err, requestId, err.Error()) - return - } - if len(resp.NotFoundInCCHosts) == len(input.Hosts) { - c.SendResponse(r, fmt.Errorf("all machines failed to query cmdb information"), resp, requestId) - return - } - task.RecordRsOperatorInfoChan <- input.getOperationInfo(requestId) - c.SendResponse(r, err, resp, requestId) -} - -// ImportHostResp TODO -type ImportHostResp struct { - SearchDiskErrInfo map[string]string `json:"search_disk_err_info"` - NotFoundInCCHosts []string `json:"not_found_in_cc_hosts"` -} - -// ImportByListHostBiz TODO -func ImportByListHostBiz(param ImportMachParam) (resp *ImportHostResp, err error) { - var ccHostsInfo []*cc.Host - var berr, derr error - var failedHostInfo map[string]string - var notFoundHosts []string - var elems []model.TbRpDetail - resp = &ImportHostResp{} - wg := sync.WaitGroup{} - diskMap := make(map[string]*bk.ShellResCollection) - - lableJson, err := cmutil.ConverMapToJsonStr(cmutil.CleanStrMap(param.Labels)) - if err != nil { - logger.Error(fmt.Sprintf("ConverLableToJsonStr Failed,Error:%s", err.Error())) - return nil, err - } - bizJson := []byte("[]") - if len(param.ForBizs) > 0 { - bizJson, err = json.Marshal(cmutil.IntSliceToStrSlice(param.ForBizs)) - if err != nil { - logger.Error(fmt.Sprintf("conver biz json Failed,Error:%s", err.Error())) - return nil, err - } - } - rstypes := []byte("[]") - if len(param.RsTypes) > 0 { - rstypes, err = json.Marshal(param.RsTypes) - if err != nil { - logger.Error(fmt.Sprintf("conver resource types Failed,Error:%s", err.Error())) - return nil, err - } - } - targetHosts := cmutil.RemoveDuplicate(param.getIps()) - wg.Add(2) - go func() { - defer wg.Done() - ccHostsInfo, notFoundHosts, berr = bk.BatchQueryHostsInfo(param.BkBizId, targetHosts) - }() - // get disk information in batch - go func() { - defer wg.Done() - diskMap, failedHostInfo, derr = bk.GetDiskInfo(targetHosts, param.BkCloudId, param.BkBizId) - }() - wg.Wait() - resp.SearchDiskErrInfo = failedHostInfo - resp.NotFoundInCCHosts = notFoundHosts - if berr != nil { - logger.Error("query host cc info failed %s", berr.Error()) - return resp, berr - } - if len(notFoundHosts) >= len(param.Hosts) { - return resp, fmt.Errorf("all hosts query empty in cc") - } - - if derr != nil { - logger.Error("search disk info by job failed %s", derr.Error()) - // return - } - hostsMap := make(map[string]struct{}) - for _, host := range targetHosts { - hostsMap[host] = struct{}{} - } - for _, emptyhost := range notFoundHosts { - delete(hostsMap, emptyhost) - } - // further probe disk specific information - probeFromCloud(diskMap) - logger.Info("more info %v", ccHostsInfo) - for _, h := range ccHostsInfo { - delete(hostsMap, h.InnerIP) - el := model.TbRpDetail{ - RsTypes: rstypes, - DedicatedBizs: bizJson, - BkCloudID: param.BkCloudId, - BkBizId: param.BkBizId, - AssetID: h.AssetID, - BkHostID: h.BKHostId, - IP: h.InnerIP, - Label: lableJson, - DeviceClass: h.DeviceClass, - DramCap: h.BkMem, - CPUNum: h.BkCpu, - City: h.IdcCityName, - CityID: h.IdcCityId, - SubZone: h.SZone, - SubZoneID: h.SZoneID, - RackID: h.Equipment, - SvrTypeName: h.SvrTypeName, - Status: model.Unused, - NetDeviceID: h.LinkNetdeviceId, - StorageDevice: []byte("{}"), - TotalStorageCap: h.BkDisk, - UpdateTime: time.Now(), - CreateTime: time.Now(), - } - el.SetMore(h.InnerIP, diskMap) - elems = append(elems, el) - } - - if err := model.DB.Self.Table(model.TbRpDetailName()).Create(elems).Error; err != nil { - logger.Error("failed to save resource: %s", err.Error()) - return resp, err - } - return resp, err -} - -// probeFromCloud Detect The Disk Type Again Through The Cloud Interface -func probeFromCloud(diskMap map[string]*bk.ShellResCollection) { - var clouder cloud.Disker - var err error - if clouder, err = cloud.NewDisker(); err != nil { - return - } - ctr := make(chan struct{}, 5) - wg := sync.WaitGroup{} - for ip := range diskMap { - // if the disk id and region obtained by job are all empty skip the request for cloud api - ctr <- struct{}{} - wg.Add(1) - go func(ip string) { - defer func() { wg.Done(); <-ctr }() - dkinfo := diskMap[ip] - diskIds := bk.GetAllDiskIds(dkinfo.Disk) - if cmutil.IsEmpty(dkinfo.TxRegion) || len(diskIds) <= 0 { - return - } - cloudInfo, err := clouder.DescribeDisk(diskIds, dkinfo.TxRegion) - if err != nil { - logger.Error("call clouder describe disk info failed %s", err.Error()) - return - } - for _, dk := range dkinfo.Disk { - if v, ok := cloudInfo[dk.DiskId]; ok { - dk.DiskType = v - } - } - }(ip) - } - wg.Wait() -} diff --git a/dbm-services/common/db-resource/internal/controller/manage/rs_operation_info.go b/dbm-services/common/db-resource/internal/controller/manage/rs_operation_info.go index b7b2cffee9..93bf3be92a 100644 --- a/dbm-services/common/db-resource/internal/controller/manage/rs_operation_info.go +++ b/dbm-services/common/db-resource/internal/controller/manage/rs_operation_info.go @@ -1,10 +1,22 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package manage import ( "fmt" + "strings" "dbm-services/common/db-resource/internal/model" "dbm-services/common/go-pubpkg/cmutil" + "dbm-services/common/go-pubpkg/errno" "dbm-services/common/go-pubpkg/logger" "github.com/gin-gonic/gin" @@ -15,10 +27,13 @@ import ( type GetOperationInfoParam struct { OperationType string `json:"operation_type"` BillIds []string `json:"bill_ids"` + BillTypes []string `json:"bill_types"` TaskIds []string `json:"task_ids"` + IpList []string `json:"ip_list"` Operator string `json:"operator"` BeginTime string `json:"begin_time" binding:"omitempty,datetime=2006-01-02 15:04:05" ` EndTime string `json:"end_time" binding:"omitempty,datetime=2006-01-02 15:04:05"` + Orderby string `json:"orderby"` Limit int `json:"limit"` Offset int `json:"offset"` } @@ -33,23 +48,36 @@ func (o MachineResourceHandler) OperationInfoList(r *gin.Context) { } db := model.DB.Self.Table(model.TbRpOperationInfoTableName()) input.query(db) - var data []model.TbRpOperationInfo - if err := db.Scan(&data).Error; err != nil { - o.SendResponse(r, err, err.Error(), requestId) - return - } var count int64 if err := db.Count(&count).Error; err != nil { - o.SendResponse(r, err, requestId, err.Error()) + o.SendResponse(r, errno.ErrDBQuery.AddErr(err), requestId, err.Error()) + return + } + var data []model.TbRpOperationInfo + if input.Limit > 0 { + db = db.Offset(input.Offset).Limit(input.Limit) + } + if err := db.Scan(&data).Error; err != nil { + o.SendResponse(r, errno.ErrDBQuery.AddErr(err), err.Error(), requestId) return } + o.SendResponse(r, nil, map[string]interface{}{"details": data, "count": count}, requestId) } func (p GetOperationInfoParam) query(db *gorm.DB) { + if len(p.IpList) > 0 { + for _, ip := range p.IpList { + db.Or(model.JSONQuery("ip_list").Contains([]string{ip})) + } + return + } if len(p.BillIds) > 0 { db.Where("bill_id in (?)", p.BillIds) } + if len(p.BillTypes) > 0 { + db.Where("bill_type in (?)", p.BillTypes) + } if len(p.TaskIds) > 0 { db.Where("task_id in (?)", p.TaskIds) } @@ -65,7 +93,10 @@ func (p GetOperationInfoParam) query(db *gorm.DB) { if cmutil.IsNotEmpty(p.BeginTime) { db.Where("create_time >= ? ", p.BeginTime) } - if p.Limit > 0 { - db.Offset(p.Offset).Limit(p.Limit) + switch strings.ToLower(strings.TrimSpace(p.Orderby)) { + case "asc": + db.Order("create_time asc") + default: + db.Order("create_time desc") } } diff --git a/dbm-services/common/db-resource/internal/controller/manage/spec.go b/dbm-services/common/db-resource/internal/controller/manage/spec.go new file mode 100644 index 0000000000..d787c4c21d --- /dev/null +++ b/dbm-services/common/db-resource/internal/controller/manage/spec.go @@ -0,0 +1,91 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package manage + +import ( + "strconv" + + "github.com/gin-gonic/gin" + + "dbm-services/common/db-resource/internal/model" + "dbm-services/common/db-resource/internal/svr/apply" + "dbm-services/common/db-resource/internal/svr/bk" + "dbm-services/common/go-pubpkg/cmutil" + "dbm-services/common/go-pubpkg/errno" + "dbm-services/common/go-pubpkg/logger" +) + +// SpecCheckInput TODO +type SpecCheckInput struct { + ResourceType string `json:"resource_type"` + BkCloudId int `json:"bk_cloud_id"` + ForbizId int `json:"for_biz_id"` + Details []SpecInfo `json:"details" binding:"required,gt=0,dive"` +} + +// SpecInfo TODO +type SpecInfo struct { + GroupMark string `json:"group_mark" binding:"required" ` + DeviceClass []string `json:"device_class"` + Spec apply.Spec `json:"spec"` + StorageSpecs []apply.DiskSpec `json:"storage_spec"` +} + +// SpecSum TODO +func (m MachineResourceHandler) SpecSum(r *gin.Context) { + var input SpecCheckInput + requestId := r.GetString("request_id") + if err := m.Prepare(r, &input); err != nil { + m.SendResponse(r, err, err.Error(), requestId) + return + } + rpdata := make(map[string]int64) + for _, item := range input.Details { + var count int64 + s := &apply.SearchContext{ + BkCloudId: input.BkCloudId, + IntetionBkBizId: input.ForbizId, + RsType: input.ResourceType, + ApplyObjectDetail: &apply.ApplyObjectDetail{ + GroupMark: item.GroupMark, + DeviceClass: item.DeviceClass, + Spec: item.Spec, + StorageSpecs: item.StorageSpecs, + }, + } + for _, fn := range s.Matcher() { + db := model.DB.Self.Table(model.TbRpDetailName()).Select("count(*)") + db.Where("gse_agent_status_code = ? ", bk.GSE_AGENT_OK) + db.Where(" bk_cloud_id = ? and status = ? ", input.BkCloudId, model.Unused) + // 如果没有指定资源类型,表示只能选择无资源类型标签的资源 + // 没有资源类型标签的资源可以被所有其他类型使用 + if input.ForbizId > 0 { + db.Where(model.JSONQuery("dedicated_bizs").Contains([]string{strconv.Itoa(input.ForbizId)})) + } + if cmutil.IsEmpty(input.ResourceType) { + db.Where("JSON_LENGTH(rs_types) <= 0") + } else { + db.Where("? or JSON_LENGTH(rs_types) <= 0 ", model.JSONQuery("rs_types").Contains([]string{input.ResourceType})) + } + s.MatchStorage(db) + fn(db) + var cnt int64 + if err := db.Scan(&cnt).Error; err != nil { + logger.Error("query pre check count failed %s", err.Error()) + m.SendResponse(r, errno.ErrDBQuery.AddErr(err), err.Error(), requestId) + return + } + count += cnt + } + rpdata[item.GroupMark] = count + } + m.SendResponse(r, nil, rpdata, requestId) +} diff --git a/dbm-services/common/db-resource/internal/lock/lock.go b/dbm-services/common/db-resource/internal/lock/lock.go index 02804bc982..4ec5ae9573 100644 --- a/dbm-services/common/db-resource/internal/lock/lock.go +++ b/dbm-services/common/db-resource/internal/lock/lock.go @@ -1,2 +1,68 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + // Package lock TODO package lock + +import ( + "context" + "fmt" + "time" + + "dbm-services/common/db-resource/internal/config" + "dbm-services/common/go-pubpkg/logger" + + "github.com/go-redis/redis/v8" +) + +var rdb *redis.Client + +// init TODO +func init() { + logger.Info("redis addr %s", config.AppConfig.Redis.Addr) + rdb = redis.NewClient(&redis.Options{ + Addr: config.AppConfig.Redis.Addr, + Password: config.AppConfig.Redis.Password, + DB: 0, + }) +} + +// RedisLock TODO +type RedisLock struct { + Name string + RandKey string + Expiry time.Duration +} + +// TryLock TODO +func (r *RedisLock) TryLock() (err error) { + ok, err := rdb.SetNX(context.TODO(), r.Name, r.RandKey, r.Expiry).Result() + if err != nil { + return err + } + if !ok { + return fmt.Errorf("setnx %s lock failed", r.Name) + } + return nil +} + +// Unlock TODO +func (r *RedisLock) Unlock() (err error) { + luaStript := `if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end` + v, err := rdb.Eval(context.TODO(), luaStript, []string{r.Name}, []interface{}{r.RandKey}).Int() + if err != nil { + logger.Error("del lock failed %s", err.Error()) + return err + } + if v != 1 { + return fmt.Errorf("unlock failed,key is %s,val %s", r.Name, r.RandKey) + } + return nil +} diff --git a/dbm-services/common/db-resource/internal/lock/lock_test.go b/dbm-services/common/db-resource/internal/lock/lock_test.go new file mode 100644 index 0000000000..b6e5ce72b4 --- /dev/null +++ b/dbm-services/common/db-resource/internal/lock/lock_test.go @@ -0,0 +1,42 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package lock_test + +import ( + "testing" + "time" + + "dbm-services/common/db-resource/internal/lock" + "dbm-services/common/go-pubpkg/cmutil" +) + +func TestRedisLock(t *testing.T) { + t.Log("start testing...") + // lock.InitRedisDb() + l := lock.NewSpinLock(&lock.RedisLock{ + Name: "Tendb", + RandKey: cmutil.RandStr(16), + Expiry: 10 * time.Second, + }, 30, 1*time.Second) + for i := 0; i < 20; i++ { + go func(j int) { + if err := l.Lock(); err != nil { + t.Log(j, "lock failed") + return + } + t.Log(j, "lock success") + time.Sleep(100 * time.Millisecond) + l.Unlock() + }(i) + } + + time.Sleep(20 * time.Second) +} diff --git a/dbm-services/common/db-resource/internal/lock/redis_lock.go b/dbm-services/common/db-resource/internal/lock/redis_lock.go deleted file mode 100644 index e0e0b6b217..0000000000 --- a/dbm-services/common/db-resource/internal/lock/redis_lock.go +++ /dev/null @@ -1,56 +0,0 @@ -package lock - -import ( - "context" - "fmt" - "time" - - "dbm-services/common/db-resource/internal/config" - "dbm-services/common/go-pubpkg/logger" - - "github.com/go-redis/redis/v8" -) - -var rdb *redis.Client - -// init TODO -func init() { - rdb = redis.NewClient(&redis.Options{ - Addr: config.AppConfig.RedisDb.Addr, - Password: config.AppConfig.RedisDb.Pwd, - DB: 0, - }) -} - -// RedisLock TODO -type RedisLock struct { - Name string - RandKey string - Expiry time.Duration -} - -// TryLock TODO -func (r *RedisLock) TryLock() (err error) { - ok, err := rdb.SetNX(context.TODO(), r.Name, r.RandKey, r.Expiry).Result() - if err != nil { - return err - } - if !ok { - return fmt.Errorf("setnx %s lock failed", r.Name) - } - return nil -} - -// Unlock TODO -func (r *RedisLock) Unlock() (err error) { - luaStript := `if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end` - v, err := rdb.Eval(context.TODO(), luaStript, []string{r.Name}, []interface{}{r.RandKey}).Int() - if err != nil { - logger.Error("del lock failed %s", err.Error()) - return err - } - if v != 1 { - return fmt.Errorf("unlock failed,key is %s,val %s", r.Name, r.RandKey) - } - return nil -} diff --git a/dbm-services/common/db-resource/internal/lock/redis_lock_test.go b/dbm-services/common/db-resource/internal/lock/redis_lock_test.go deleted file mode 100644 index a7b0b1a2f9..0000000000 --- a/dbm-services/common/db-resource/internal/lock/redis_lock_test.go +++ /dev/null @@ -1,32 +0,0 @@ -package lock_test - -import ( - "testing" - "time" - - "dbm-services/common/db-resource/internal/lock" - "dbm-services/common/go-pubpkg/cmutil" -) - -func TestRedisLock(t *testing.T) { - t.Log("start testing...") - // lock.InitRedisDb() - l := lock.NewSpinLock(&lock.RedisLock{ - Name: "Tendb", - RandKey: cmutil.RandStr(16), - Expiry: 10 * time.Second, - }, 30, 1*time.Second) - for i := 0; i < 20; i++ { - go func(j int) { - if err := l.Lock(); err != nil { - t.Log(j, "lock failed") - return - } - t.Log(j, "lock success") - time.Sleep(100 * time.Millisecond) - l.Unlock() - }(i) - } - - time.Sleep(20 * time.Second) -} diff --git a/dbm-services/common/db-resource/internal/lock/spinlock.go b/dbm-services/common/db-resource/internal/lock/spinlock.go index d0dfe63851..9962dff02f 100644 --- a/dbm-services/common/db-resource/internal/lock/spinlock.go +++ b/dbm-services/common/db-resource/internal/lock/spinlock.go @@ -4,6 +4,8 @@ import ( "fmt" "math/rand" "time" + + "dbm-services/common/go-pubpkg/logger" ) // TryLocker TODO @@ -29,16 +31,16 @@ type SpinLock struct { } // Lock TODO -func (l *SpinLock) Lock() error { +func (l *SpinLock) Lock() (err error) { for i := 0; i < l.spinTries; i++ { - var err error if err = l.lock.TryLock(); err == nil { return nil } + logger.Warn("%d: lock %s failed %s", i, err.Error()) time.Sleep(l.spinInterval) time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond) } - return fmt.Errorf("spin lock failed after %f seconds", float64(l.spinTries)*l.spinInterval.Seconds()) + return fmt.Errorf("spin lock failed:%s ,after %f seconds", err.Error(), float64(l.spinTries)*l.spinInterval.Seconds()) } // Unlock TODO diff --git a/dbm-services/common/db-resource/internal/middleware/middleware.go b/dbm-services/common/db-resource/internal/middleware/middleware.go index c596467de4..aa06432e4d 100644 --- a/dbm-services/common/db-resource/internal/middleware/middleware.go +++ b/dbm-services/common/db-resource/internal/middleware/middleware.go @@ -1,21 +1,53 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + // Package middleware TODO package middleware import ( "bytes" "encoding/json" - "io/ioutil" + "io" "net/http" "time" "dbm-services/common/db-resource/internal/controller" "dbm-services/common/db-resource/internal/model" + "dbm-services/common/go-pubpkg/cmutil" "dbm-services/common/go-pubpkg/logger" "github.com/gin-contrib/requestid" "github.com/gin-gonic/gin" ) +// RequestLoggerFilter TODO +var RequestLoggerFilter *ApiLoggerFilter + +func init() { + RequestLoggerFilter = &ApiLoggerFilter{} +} + +// ApiLoggerFilter TODO +type ApiLoggerFilter struct { + WhitelistUri []string +} + +func (a *ApiLoggerFilter) filter(uri string) bool { + return cmutil.HasElem(uri, a.WhitelistUri) +} + +// Add TODO +func (a *ApiLoggerFilter) Add(uri string) { + a.WhitelistUri = append(a.WhitelistUri, uri) +} + type bodyLogWriter struct { gin.ResponseWriter body *bytes.Buffer @@ -56,9 +88,12 @@ func ApiLogger(c *gin.Context) { rid := requestid.Get(c) c.Set("request_id", rid) if c.Request.Method == http.MethodPost { + if !RequestLoggerFilter.filter(c.Request.RequestURI) { + return + } var bodyBytes []byte // read from the original request body - bodyBytes, err := ioutil.ReadAll(c.Request.Body) + bodyBytes, err := io.ReadAll(c.Request.Body) if err != nil { return } @@ -66,10 +101,11 @@ func ApiLogger(c *gin.Context) { bodyBytes = []byte("{}") } // create a new buffer and replace the original request body - c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes)) + c.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) if err := model.CreateTbRequestLog(model.TbRequestLog{ RequestID: rid, RequestUser: "", + RequestUrl: c.Request.RequestURI, RequestBody: string(bodyBytes), SourceIP: c.Request.RemoteAddr, CreateTime: time.Now(), diff --git a/dbm-services/common/db-resource/internal/model/TbDeviceSpec.go b/dbm-services/common/db-resource/internal/model/TbDeviceSpec.go deleted file mode 100644 index c13f9f89a5..0000000000 --- a/dbm-services/common/db-resource/internal/model/TbDeviceSpec.go +++ /dev/null @@ -1,35 +0,0 @@ -package model - -import ( - "fmt" - - "dbm-services/common/go-pubpkg/logger" -) - -// TbDeviceSpec TODO -type TbDeviceSpec struct { - ID int `gorm:"primaryKey;column:id;type:int(11);not null" json:"-"` - ResourceType string `gorm:"column:resource_type;type:int(11);not null" json:"resource_type"` - DeviceClass string `gorm:"unique;column:device_class;type:varchar(64);not null" json:"device_class"` - CPUNum int `gorm:"column:cpu_num;type:int(11);not null" json:"cpu_num"` - DramCap int `gorm:"column:dram_cap;type:int(11);not null" json:"dram_cap"` - SsdCap int `gorm:"column:ssd_cap;type:int(11);not null" json:"ssd_cap"` - SsdNum int `gorm:"column:ssd_num;type:int(11);not null" json:"ssd_num"` - HddCap int `gorm:"column:hdd_cap;type:int(11);not null" json:"hdd_cap"` - IsLocalStorge int `gorm:"column:is_local_storge;type:int(11);not null" json:"is_local_storge"` -} - -// TbDeviceSpecName TODO -func TbDeviceSpecName() string { - return "tb_device_spec" -} - -// GetDeviceSpecFromClass TODO -func GetDeviceSpecFromClass(deviceClass string) (m TbDeviceSpec, err error) { - err = DB.Self.Table(TbDeviceSpecName()).Where("device_class = ? ", deviceClass).First(&m).Error - if err != nil { - logger.Error(fmt.Sprintf("Query DeviceSpec By DeviceClass Failed %s", err.Error())) - return - } - return -} diff --git a/dbm-services/common/db-resource/internal/model/TbRpDetail.go b/dbm-services/common/db-resource/internal/model/TbRpDetail.go index 0e7a95447e..e94afdeb81 100644 --- a/dbm-services/common/db-resource/internal/model/TbRpDetail.go +++ b/dbm-services/common/db-resource/internal/model/TbRpDetail.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package model import ( @@ -32,7 +42,7 @@ type TbRpDetail struct { DedicatedBizs json.RawMessage `gorm:"column:dedicated_bizs;type:json;comment:'专属业务,可属于多个'" json:"for_bizs"` RsTypes json.RawMessage `gorm:"column:rs_types;type:json;comment:'资源类型标签'" json:"resource_types"` Bizs map[string]string `gorm:"-" json:"-"` - BkHostID int `gorm:"column:bk_host_id;type:int(11);not null;comment:'bk主机ID'" json:"bk_host_id"` + BkHostID int `gorm:"index:idx_host_id;column:bk_host_id;type:int(11);not null;comment:'bk主机ID'" json:"bk_host_id"` IP string `gorm:"uniqueIndex:ip;column:ip;type:varchar(20);not null" json:"ip"` // svr ip AssetID string `gorm:"column:asset_id;type:varchar(64);not null;comment:'固定资产编号'" json:"asset_id"` DeviceClass string `gorm:"column:device_class;type:varchar(64);not null" json:"device_class"` // 对应机型 A30,D3 @@ -49,14 +59,21 @@ type TbRpDetail struct { SubZoneID string `gorm:"column:sub_zone_id;type:varchar(64);not null" json:"sub_zone_id"` // 园区ID cc_device_szone_id RackID string `gorm:"column:rack_id;type:varchar(64);not null" json:"rack_id"` // 存放机架ID,判断是否是同机架 NetDeviceID string `gorm:"column:net_device_id;type:varchar(128)" json:"net_device_id"` // 网络设备ID, 判断是同交换机 - Label string `gorm:"column:label;type:json" json:"label"` // 标签 + Label json.RawMessage `gorm:"column:label;type:json" json:"label"` // 标签 LabelMap map[string]string `gorm:"-" json:"-"` - IsInit int `gorm:"column:is_init;type:int(11);comment:'是否初始化过'" json:"-"` // 是否初始化过 - IsIdle int `gorm:"column:is_idle;type:int(11);comment:'是否空闲检查过'" json:"-"` // 是否空闲检查过 - Status string `gorm:"column:status;type:varchar(20);not null" json:"status"` // Unused: 未使用 Used: 已经售卖被使用: Preselected:预占用 - ConsumeTime time.Time `gorm:"column:consume_time;type:timestamp;default:1970-01-01 08:00:01" json:"consume_time"` // 消费时间 - UpdateTime time.Time `gorm:"column:update_time;type:timestamp;default:CURRENT_TIMESTAMP()" json:"update_time"` // 最后修改时间 - CreateTime time.Time `gorm:"column:create_time;type:timestamp;default:CURRENT_TIMESTAMP()" json:"create_time"` // 创建时间 + IsInit int `gorm:"column:is_init;type:int(11);comment:'是否初始化过'" json:"-"` // 是否初始化过 + IsIdle int `gorm:"column:is_idle;type:int(11);comment:'是否空闲检查过'" json:"-"` // 是否空闲检查过 + Status string `gorm:"column:status;type:varchar(20);not null" json:"status"` // Unused: 未使用 Used: 已经售卖被使用: Preselected:预占用 + BkAgentId string `gorm:"index:idx_bk_agent_id;column:bk_agent_id;type:varchar(64);not null" json:"bk_agent_id"` + // gse Agent当前运行状态码, -1:未知 0:初始安装 1:启动中 2:运行中 3:有损状态 4:繁忙状态 5:升级中 6:停止中 7:解除安装 + AgentStatusCode int `gorm:"column:gse_agent_status_code;type:int(11);not null" json:"gse_agent_status_code"` + // agent status 最后一次更新时间 + AgentStatusUpdateTime time.Time `gorm:"column:agent_status_update_time;type:timestamp;default:1970-01-01 08:00:01" json:"agent_status_update_time"` + ConsumeTime time.Time `gorm:"column:consume_time;type:timestamp;default:1970-01-01 08:00:01" json:"consume_time"` // 消费时间 + UpdateTime time.Time `gorm:"column:update_time;type:timestamp;default:CURRENT_TIMESTAMP()" json:"update_time"` // 最后修改时间 + CreateTime time.Time `gorm:"column:create_time;type:timestamp;default:CURRENT_TIMESTAMP()" json:"create_time"` // 创建时间 + // foreiginKey:关联表的结构字段 references:当前表的结构字段 + // SubStorages []TbRpStorageItem `gorm:"foreignKey:BkHostID;references:BkHostID"` } // TableName TODO @@ -116,6 +133,23 @@ func (t *TbRpDetail) SetMore(ip string, diskMap map[string]*bk.ShellResCollectio } } +// func (t *TbRpDetail) getSubStorages(disks []bk.DiskInfo) (storages []TbRpStorageItem) { +// for _, d := range disks { +// storages = append(storages, TbRpStorageItem{ +// BkCloudID: t.BkCloudID, +// BkHostID: t.BkHostID, +// Size: d.Size, +// CloudDiskId: d.DiskId, +// MountPoint: d.MountPoint, +// FileType: d.FileType, +// DiskType: d.DiskType, +// CreateTime: time.Now(), +// UpdateTime: time.Now(), +// }) +// } +// return +// } + // TbRpDetailGetter TODO func TbRpDetailGetter() ([]TbRpDetail, error) { return nil, nil diff --git a/dbm-services/common/db-resource/internal/model/TbRpDetailArchive.go b/dbm-services/common/db-resource/internal/model/TbRpDetailArchive.go index 7b969ab729..a522b1296d 100644 --- a/dbm-services/common/db-resource/internal/model/TbRpDetailArchive.go +++ b/dbm-services/common/db-resource/internal/model/TbRpDetailArchive.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package model import ( @@ -36,12 +46,17 @@ type TbRpDetailArchive struct { NetDeviceID string `gorm:"column:net_device_id;type:varchar(128)" json:"net_device_id"` // 网络设备ID, 判断是同交换机 Label string `gorm:"column:label;type:json" json:"label"` // 标签 LabelMap map[string]string `gorm:"-"` - IsInit int `gorm:"column:is_init;type:int(11);comment:'是否初始化过'" json:"-"` // 是否初始化过 - IsIdle int `gorm:"column:is_idle;type:int(11);comment:'是否空闲检查过'" json:"-"` // 是否空闲检查过 - Status string `gorm:"column:status;type:varchar(20);not null" json:"status"` // Unused: 未使用 Used: 已经售卖被使用: Preselected:预占用 - ConsumeTime time.Time `gorm:"column:consume_time;type:timestamp;default:1970-01-01 08:00:01" json:"consume_time"` // 消费时间 - UpdateTime time.Time `gorm:"column:update_time;type:timestamp;default:CURRENT_TIMESTAMP()" json:"update_time"` // 最后修改时间 - CreateTime time.Time `gorm:"column:create_time;type:timestamp;default:CURRENT_TIMESTAMP()" json:"create_time"` // 创建时间 + IsInit int `gorm:"column:is_init;type:int(11);comment:'是否初始化过'" json:"-"` // 是否初始化过 + IsIdle int `gorm:"column:is_idle;type:int(11);comment:'是否空闲检查过'" json:"-"` // 是否空闲检查过 + Status string `gorm:"column:status;type:varchar(20);not null" json:"status"` // Unused: 未使用 Used: 已经售卖被使用: Preselected:预占用 + BkAgentId string `gorm:"index:idx_bk_agent_id;column:bk_agent_id;type:varchar(64);not null" json:"bk_agent_id"` + // gse Agent当前运行状态码, -1:未知 0:初始安装 1:启动中 2:运行中 3:有损状态 4:繁忙状态 5:升级中 6:停止中 7:解除安装 + AgentStatusCode int `gorm:"column:gse_agent_status_code;type:int(11);not null" json:"gse_agent_status_code"` + // agent status 最后一次更新时间 + AgentStatusUpdateTime time.Time `gorm:"column:agent_status_update_time;type:timestamp;default:1970-01-01 08:00:01" json:"agent_status_update_time"` + ConsumeTime time.Time `gorm:"column:consume_time;type:timestamp;default:1970-01-01 08:00:01" json:"consume_time"` // 消费时间 + UpdateTime time.Time `gorm:"column:update_time;type:timestamp;default:CURRENT_TIMESTAMP()" json:"update_time"` // 最后修改时间 + CreateTime time.Time `gorm:"column:create_time;type:timestamp;default:CURRENT_TIMESTAMP()" json:"create_time"` // 创建时间 } // initarchive TODO diff --git a/dbm-services/common/db-resource/internal/model/TbRpOperatorInfo.go b/dbm-services/common/db-resource/internal/model/TbRpOperatorInfo.go index 1da16c17da..87f40c5e17 100644 --- a/dbm-services/common/db-resource/internal/model/TbRpOperatorInfo.go +++ b/dbm-services/common/db-resource/internal/model/TbRpOperatorInfo.go @@ -1,6 +1,19 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package model -import "time" +import ( + "encoding/json" + "time" +) const ( // Consumed TODO @@ -11,16 +24,20 @@ const ( // TbRpOperationInfo TODO type TbRpOperationInfo struct { - ID int `gorm:"primaryKey;auto_increment;not null" json:"-"` - RequestID string `gorm:"index:idx_request_id;column:request_id;type:varchar(64);not null" json:"request_id"` - TotalCount int `gorm:"column:total_count;type:int(11);comment:'task Id'" json:"total_count"` - OperationType string `gorm:"column:operation_type;type:varchar(64);not null;comment:'operation type'" json:"operation_type"` - Operator string `gorm:"column:operator;type:varchar(64);not null;comment:'operator user'" json:"operator"` - Status string `gorm:"column:status;type:varchar(64);not null;comment:'operator user'" json:"-"` - TaskId string `gorm:"column:task_id;type:varchar(128);not null;comment:'task Id'" json:"task_id"` - BillId string `gorm:"column:bill_id;type:varchar(128);not null;comment:'bill Id'" json:"bill_id"` - UpdateTime time.Time `gorm:"column:update_time;type:timestamp" json:"update_time"` // 最后修改时间 - CreateTime time.Time `gorm:"column:create_time;type:datetime" json:"create_time"` // 创建时间 + ID int `gorm:"primaryKey;auto_increment;not null" json:"-"` + RequestID string `gorm:"index:idx_request_id;column:request_id;type:varchar(64);not null" json:"request_id"` + TotalCount int `gorm:"column:total_count;type:int(11);comment:task Id" json:"total_count"` + BkHostIds json.RawMessage `gorm:"column:bk_host_ids;type:json;comment:主机Id" json:"bk_host_ids"` + IpList json.RawMessage `gorm:"column:ip_list;type:json;comment:主机ip" json:"ip_list"` + OperationType string `gorm:"column:operation_type;type:varchar(64);not null;comment:'operation type'" json:"operation_type"` + Operator string `gorm:"column:operator;type:varchar(64);not null;comment:'operator user'" json:"operator"` + Status string `gorm:"column:status;type:varchar(64);not null;comment: status" json:"-"` + TaskId string `gorm:"column:task_id;type:varchar(128);not null;comment:'task Id'" json:"task_id"` + BillId string `gorm:"column:bill_id;type:varchar(128);not null;comment:'bill Id'" json:"bill_id"` + BillType string `gorm:"column:bill_type;type:varchar(128);not null;comment:'bill type'" json:"bill_type"` + Desc string `gorm:"column:desc;type:varchar(256);not null;comment:'desc'" json:"desc"` + UpdateTime time.Time `gorm:"column:update_time;type:timestamp" json:"update_time"` // 最后修改时间 + CreateTime time.Time `gorm:"column:create_time;type:datetime" json:"create_time"` // 创建时间 } // TableName TODO diff --git a/dbm-services/common/db-resource/internal/model/TbRpStorageItem.go b/dbm-services/common/db-resource/internal/model/TbRpStorageItem.go new file mode 100644 index 0000000000..3f55dcffa5 --- /dev/null +++ b/dbm-services/common/db-resource/internal/model/TbRpStorageItem.go @@ -0,0 +1,30 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package model + +// // TbRpStorageItem TODO +// type TbRpStorageItem struct { +// DiskId int `gorm:"primaryKey;column:disk_id;type:int(11);not null" json:"disk_id"` +// BkCloudID int `gorm:"column:bk_cloud_id;type:int(11);not null;comment:'云区域 ID'" json:"bk_cloud_id"` +// BkHostID int `gorm:"index:idx_host_id;column:bk_host_id;type:int(11);not null" json:"bk_host_id"` +// MountPoint string `gorm:"column:mount_point;type:varchar(20);not null" json:"mount_point"` // 磁盘挂载点 +// CloudDiskId string `gorm:"column:cloud_disk_id;type:varchar(32);not null" json:"cloud_disk_id"` // disk id +// DiskType string `gorm:"column:disk_type;type:varchar(32);not null" json:"disk_type"` // disk_type +// FileType string `gorm:"column:file_type;type:varchar(32);not null" json:"file_type"` // 文件系统类型 +// Size int `gorm:"column:size;type:int(11);not null;" json:"size"` // 磁盘大小 +// UpdateTime time.Time `gorm:"column:update_time;type:timestamp" json:"update_time"` // 最后修改时间 +// CreateTime time.Time `gorm:"column:create_time;type:datetime" json:"create_time"` // 创建时间 +// } + +// // TTbRpStorageItemName TODO +// func TTbRpStorageItemName() string { +// return "tb_rp_storage_item" +// } diff --git a/dbm-services/common/db-resource/internal/model/model.go b/dbm-services/common/db-resource/internal/model/model.go index f0cf5ff84b..d751501edc 100644 --- a/dbm-services/common/db-resource/internal/model/model.go +++ b/dbm-services/common/db-resource/internal/model/model.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + // Package model TODO package model @@ -32,7 +42,6 @@ var DB *Database var CMDBDB *Database func init() { - log.Println("init db model..") createSysDb() orm_db := initSelfDB() sqlDB, err := orm_db.DB() @@ -63,7 +72,8 @@ func createSysDb() { pwd := config.AppConfig.Db.PassWord addr := config.AppConfig.Db.Addr testConn := openDB(user, pwd, addr, "") - err := testConn.Exec(fmt.Sprintf("create database IF NOT EXISTS `%s`;", config.AppConfig.Db.Name)).Error + dbname := config.AppConfig.Db.Name + err := testConn.Exec(fmt.Sprintf("create database IF NOT EXISTS `%s`;", dbname)).Error if err != nil { log.Fatalf("init create db failed:%s", err.Error()) } @@ -71,6 +81,20 @@ func createSysDb() { if err != nil { log.Fatalf("init create db failed:%s", err.Error()) } + var autoIncrement sql.NullInt64 + err = testConn.Raw(fmt.Sprintf("select max(id) from `%s`.`%s`", dbname, TbRpDetailArchiveName())).Scan(&autoIncrement). + Error + if err != nil { + log.Printf("get max autoIncrement from tb_rp_detail_archive failed :%s", err.Error()) + } + + if autoIncrement.Valid { + testConn.Exec(fmt.Sprintf("alter table `%s`.`%s` AUTO_INCREMENT = %d ", dbname, TbRpDetailName(), + autoIncrement.Int64+1)) + if err != nil { + log.Fatalf("get max autoIncrement from tb_rp_detail_archive failed :%s", err.Error()) + } + } sqldb.Close() } @@ -128,67 +152,6 @@ func migration() { DB.Self.AutoMigrate(&TbRpDetail{}, &TbRequestLog{}, &TbRpDetailArchive{}, &TbRpApplyDetailLog{}, &TbRpOperationInfo{}) } -// QueryCountSelfCommon TODO -func (db Database) QueryCountSelfCommon(sqltext string) (int, error) { - var count int - c_db := db.Self.Raw(sqltext) - if c_db.Error != nil { - return 0, c_db.Error - } - if err := c_db.Row().Scan(&count); err != nil { - return 0, err - } - return count, nil -} - -// QuerySelfCommon TODO -func (db *Database) QuerySelfCommon(sqltext string) ([]map[string]interface{}, error) { - cursor, err := db.SelfSqlDB.Query(sqltext) - if err != nil || cursor.Err() != nil { - return nil, fmt.Errorf("db query failed, err: %+v", err) - } - defer cursor.Close() - columns, err := cursor.Columns() - if err != nil { - return nil, fmt.Errorf("get columns failed, err: %+v", err) - } - columnTypes, err := cursor.ColumnTypes() - if err != nil { - return nil, fmt.Errorf("get column types failed, err: %+v", err) - } - - count := len(columns) - values := make([]interface{}, count) - scanArgs := make([]interface{}, count) - for i := range values { - scanArgs[i] = &values[i] - } - - dataRows := make([]map[string]interface{}, 0) - for cursor.Next() { - row := make(map[string]interface{}) - err := cursor.Scan(scanArgs...) - if err != nil { - return nil, fmt.Errorf("scan data failed, err: %+v", err) - } - for i, col := range columns { - columnType := columnTypes[i] - columnType.ScanType() - var v interface{} - val := values[i] - b, ok := val.([]byte) - if ok { - v = string(b) - } else { - v = val - } - row[col] = v - } - dataRows = append(dataRows, row) - } - return dataRows, err -} - // JSONQueryExpression json query expression, implements clause.Expression interface to use as querier type JSONQueryExpression struct { column string @@ -269,6 +232,7 @@ func (jsonQuery *JSONQueryExpression) NumRange(min int, max int, keys ...string) func (jsonQuery *JSONQueryExpression) Gte(val int, keys ...string) *JSONQueryExpression { jsonQuery.keys = keys jsonQuery.Gtv = val + jsonQuery.gte = true return jsonQuery } @@ -276,6 +240,7 @@ func (jsonQuery *JSONQueryExpression) Gte(val int, keys ...string) *JSONQueryExp func (jsonQuery *JSONQueryExpression) Lte(val int, keys ...string) *JSONQueryExpression { jsonQuery.keys = keys jsonQuery.Ltv = val + jsonQuery.lte = true return jsonQuery } diff --git a/dbm-services/common/db-resource/internal/routers/router.go b/dbm-services/common/db-resource/internal/routers/router.go deleted file mode 100644 index f71fe1887a..0000000000 --- a/dbm-services/common/db-resource/internal/routers/router.go +++ /dev/null @@ -1,18 +0,0 @@ -package routers - -import ( - "dbm-services/common/db-resource/internal/controller/apply" - "dbm-services/common/db-resource/internal/controller/manage" - - "github.com/gin-gonic/gin" -) - -// RegisterRoutes TODO -func RegisterRoutes(engine *gin.Engine) { - // 注册路由 - apply := apply.ApplyHandler{} - apply.RegisterRouter(engine) - // 机器资源管理 - manage := manage.MachineResourceHandler{} - manage.RegisterRouter(engine) -} diff --git a/dbm-services/common/db-resource/internal/routers/routers.go b/dbm-services/common/db-resource/internal/routers/routers.go index c0dc2a6696..f433fcfc3a 100644 --- a/dbm-services/common/db-resource/internal/routers/routers.go +++ b/dbm-services/common/db-resource/internal/routers/routers.go @@ -1,2 +1,29 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + // Package routers TODO package routers + +import ( + "dbm-services/common/db-resource/internal/controller/apply" + "dbm-services/common/db-resource/internal/controller/manage" + + "github.com/gin-gonic/gin" +) + +// RegisterRoutes TODO +func RegisterRoutes(engine *gin.Engine) { + // 注册路由 + apply := apply.ApplyHandler{} + apply.RegisterRouter(engine) + // 机器资源管理 + manage := manage.MachineResourceHandler{} + manage.RegisterRouter(engine) +} diff --git a/dbm-services/common/db-resource/internal/svr/apply/api.go b/dbm-services/common/db-resource/internal/svr/apply/api.go index 8814298e79..3d1e3799bf 100644 --- a/dbm-services/common/db-resource/internal/svr/apply/api.go +++ b/dbm-services/common/db-resource/internal/svr/apply/api.go @@ -1,20 +1,45 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package apply import ( + "encoding/json" "fmt" "path" "time" + "gorm.io/gorm" + "dbm-services/common/db-resource/internal/model" "dbm-services/common/go-pubpkg/cmutil" + "dbm-services/common/go-pubpkg/logger" ) // ParamCheck TODO func (param *ApplyRequestInputParam) ParamCheck() (err error) { for _, a := range param.Details { + for _, d := range a.StorageSpecs { + if d.MaxSize > 0 && d.MinSize > d.MaxSize { + return fmt.Errorf("min %d great thane min %d", d.MinSize, d.MaxSize) + } + } + if !a.Spec.Cpu.Iegal() { + return fmt.Errorf("cpu参数不合法: min:%d,max:%d", a.Spec.Cpu.Min, a.Spec.Cpu.Max) + } + if !a.Spec.Mem.Iegal() { + return fmt.Errorf("mem参数不合法: min:%d,max:%d", a.Spec.Mem.Min, a.Spec.Mem.Max) + } // 如果只是申请一个机器,则没有亲和性的必要 if a.Count <= 1 { - return nil + continue } switch a.Affinity { case SAME_SUBZONE, SAME_SUBZONE_CROSS_SWTICH: @@ -31,19 +56,15 @@ func (param *ApplyRequestInputParam) ParamCheck() (err error) { case NONE: return nil } - for _, d := range a.StorageSpecs { - if d.MaxSize > 0 && d.MinSize > d.MaxSize { - return fmt.Errorf("min %d great thane min %d", d.MinSize, d.MaxSize) - } - } } - return + return nil } // ActionInfo TODO type ActionInfo struct { TaskId string `json:"task_id"` BillId string `json:"bill_id"` + BillType string `json:"bill_type"` Operator string `json:"operator"` } @@ -51,27 +72,52 @@ type ActionInfo struct { type ApplyRequestInputParam struct { ResourceType string `json:"resource_type"` // 申请的资源用作的用途 Redis|MySQL|Proxy DryRun bool `json:"dry_run"` - BkCloudId int `json:"bk_cloud_id" binding:"number"` + BkCloudId int `json:"bk_cloud_id"` ForbizId int `json:"for_biz_id"` Details []ApplyObjectDetail `json:"details" binding:"required,gt=0,dive"` ActionInfo } // GetOperationInfo TODO -func (c ApplyRequestInputParam) GetOperationInfo(requestId string) model.TbRpOperationInfo { +func (c ApplyRequestInputParam) GetOperationInfo(requestId, mode string, + data []model.BatchGetTbDetailResult) model.TbRpOperationInfo { var count int + var bkHostIds []int + var ipList []string for _, v := range c.Details { count += v.Count } + for _, group := range data { + for _, host := range group.Data { + bkHostIds = append(bkHostIds, host.BkHostID) + ipList = append(ipList, host.IP) + } + } + var desc string + bkHostIdsBytes, err := json.Marshal(bkHostIds) + if err != nil { + desc += "failed to serialize bkhost ids" + logger.Error("json marshal failed %s", err.Error()) + } + ipListBytes, err := json.Marshal(ipList) + if err != nil { + desc += "failed to serialize ipList" + logger.Error("json marshal failed %s", err.Error()) + } return model.TbRpOperationInfo{ RequestID: requestId, TotalCount: count, OperationType: model.Consumed, + BkHostIds: bkHostIdsBytes, + IpList: ipListBytes, BillId: c.BillId, + BillType: c.BillType, TaskId: c.TaskId, Operator: c.Operator, + Status: mode, CreateTime: time.Now(), UpdateTime: time.Now(), + Desc: desc, } } @@ -96,6 +142,7 @@ const ( // ApplyObjectDetail TODO type ApplyObjectDetail struct { + BkCloudId int `json:"bk_cloud_id"` GroupMark string `json:"group_mark" binding:"required" ` // 资源组标记 Labels map[string]string `json:"labels"` // 标签 // 通过机型规格 或者 资源规格描述来匹配资源 @@ -113,6 +160,69 @@ type ApplyObjectDetail struct { Count int `json:"count" binding:"required,min=1"` // 申请数量 } +// GetDiskMatchInfo TODO +func (a *ApplyObjectDetail) GetDiskMatchInfo() (message string) { + if len(a.StorageSpecs) > 0 { + for _, d := range a.StorageSpecs { + if cmutil.IsNotEmpty(d.MountPoint) { + message += fmt.Sprintf("disk: mount point: %s", d.MountPoint) + } + if !cmutil.IsNotEmpty(d.DiskType) { + message += " disk type: " + d.DiskType + } + switch { + case d.MaxSize > 0 && d.MinSize > 0: + message += fmt.Sprintf(" size: %d ~ %d G ", d.MinSize, d.MaxSize) + case d.MaxSize > 0 && d.MaxSize <= 0: + message += fmt.Sprintf(" size <= %d G ", d.MaxSize) + case d.MaxSize <= 0 && d.MinSize > 0: + message += fmt.Sprintf(" size >= %d G ", d.MinSize) + } + } + message += "\n\r" + } + return +} + +// GetMessage TODO +func (a *ApplyObjectDetail) GetMessage() (message string) { + message += fmt.Sprintf("group: %s\n\r", a.GroupMark) + if len(a.DeviceClass) > 0 { + message += fmt.Sprintf("device_class: %v\n\r", a.DeviceClass) + } + if a.Spec.NotEmpty() { + if a.Spec.Cpu.IsNotEmpty() { + message += fmt.Sprintf("cpu: %d ~ %d 核\n\r", a.Spec.Cpu.Min, a.Spec.Cpu.Max) + } + if a.Spec.Mem.IsNotEmpty() { + message += fmt.Sprintf("mem: %d ~ %d M\n\r", a.Spec.Mem.Min, a.Spec.Mem.Max) + } + } + message += a.GetDiskMatchInfo() + if !a.LocationSpec.IsEmpty() { + message += fmt.Sprintf("city: %s \n\r", a.LocationSpec.City) + if len(a.LocationSpec.SubZoneIds) > 0 { + if a.LocationSpec.IncludeOrExclude { + message += fmt.Sprintf("subzoneId must exist in the %v", a.LocationSpec.SubZoneIds) + } else { + message += fmt.Sprintf("subzoneId must not exist in the %v", a.LocationSpec.SubZoneIds) + } + } + } + switch a.Affinity { + case NONE: + message += "资源亲和性: NONE\n\r" + case CROS_SUBZONE: + message += "资源亲和性: 同城跨园区\n\r" + case SAME_SUBZONE: + message += "资源亲和性: 同城同园区\n\r" + case SAME_SUBZONE_CROSS_SWTICH: + message += "资源亲和性: 同城同园区 跨交换机跨机架\n\r" + } + message += fmt.Sprintf("申请总数: %d \n\r", a.Count) + return message +} + // GetEmptyDiskSpec TODO func GetEmptyDiskSpec(ds []DiskSpec) (dms []DiskSpec) { for _, v := range ds { @@ -126,6 +236,7 @@ func GetEmptyDiskSpec(ds []DiskSpec) (dms []DiskSpec) { // GetDiskSpecMountPoints TODO func GetDiskSpecMountPoints(ds []DiskSpec) (mountPoints []string) { for _, v := range ds { + logger.Info("disk info %v", v) if v.MountPointIsEmpty() { continue } @@ -137,7 +248,7 @@ func GetDiskSpecMountPoints(ds []DiskSpec) (mountPoints []string) { // Spec TODO type Spec struct { Cpu MeasureRange `json:"cpu"` // cpu range - Mem MeasureRange `json:"mem"` + Mem MeasureRange `json:"ram"` } // IsEmpty TODO @@ -147,7 +258,7 @@ func (s Spec) IsEmpty() bool { // NotEmpty TODO func (s Spec) NotEmpty() bool { - return s.Cpu.IsNotEmpty() && s.Mem.IsNotEmpty() + return s.Cpu.IsNotEmpty() || s.Mem.IsNotEmpty() } // MeasureRange TODO @@ -158,7 +269,37 @@ type MeasureRange struct { // Iegal TODO func (m MeasureRange) Iegal() bool { - return m.Max >= m.Min + if m.IsNotEmpty() { + return m.Max >= m.Min + } + return true +} + +// MatchCpu TODO +func (cpu *MeasureRange) MatchCpu(db *gorm.DB) { + cpu.MatchRange(db, "cpu_num") +} + +// MatchTotalStorageSize TODO +func (disk *MeasureRange) MatchTotalStorageSize(db *gorm.DB) { + disk.MatchRange(db, "total_storage_cap") +} + +// MatchMem TODO +func (mem *MeasureRange) MatchMem(db *gorm.DB) { + mem.MatchRange(db, "dram_cap") +} + +// MatchRange TODO +func (m *MeasureRange) MatchRange(db *gorm.DB, col string) { + switch { + case m.Min > 0 && m.Max > 0: + db.Where(col+" >= ? and "+col+" <= ?", m.Min, m.Max) + case m.Max > 0 && m.Min <= 0: + db.Where(col+" <= ?", m.Max) + case m.Max <= 0 && m.Min > 0: + db.Where(col+" >= ?", m.Min) + } } // IsNotEmpty TODO diff --git a/dbm-services/common/db-resource/internal/svr/apply/apply.go b/dbm-services/common/db-resource/internal/svr/apply/apply.go index 3bca53598a..1d11032a48 100644 --- a/dbm-services/common/db-resource/internal/svr/apply/apply.go +++ b/dbm-services/common/db-resource/internal/svr/apply/apply.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + // Package apply TODO package apply @@ -9,6 +19,7 @@ import ( "dbm-services/common/db-resource/internal/model" "dbm-services/common/db-resource/internal/svr/bk" "dbm-services/common/go-pubpkg/cmutil" + "dbm-services/common/go-pubpkg/errno" "dbm-services/common/go-pubpkg/logger" "gorm.io/gorm" @@ -63,7 +74,7 @@ func CycleApply(param ApplyRequestInputParam) (pickers []*PickerObject, err erro // 挑选符合需求的资源 picker, err = s.PickInstance() if err != nil { - return pickers, fmt.Errorf("Picker for %s Failed,Error is %v", v.GroupMark, err) + return pickers, err } // Debug Print Log 挑选实例分区的情况 picker.DebugDistrubuteLog() @@ -81,7 +92,7 @@ func CycleApply(param ApplyRequestInputParam) (pickers []*PickerObject, err erro // RollBackAllInstanceUnused 将 Instance Status Selling ==> Not Selled : 2 --> 0 func RollBackAllInstanceUnused(ms []*PickerObject) { for _, m := range ms { - if err := m.RollbackSatisfiedInstanceStatusUnused(); err != nil { + if err := m.RollbackUnusedInstance(); err != nil { logger.Error(fmt.Sprintf("Rollback Satisfied Instance Status NotSelled Failed,Error %s", err.Error())) } } @@ -94,103 +105,94 @@ func (o *SearchContext) Matcher() (fns []func(db *gorm.DB)) { case len(o.DeviceClass) == 0 && o.Spec.NotEmpty(): fns = append(fns, o.MatchSpec) // 机型参数存在、资源规格参数不存在,匹配机型 - case len(o.DeviceClass) > 0 && o.Spec.NotEmpty(): + case len(o.DeviceClass) > 0 && o.Spec.IsEmpty(): fns = append(fns, o.MatchDeviceClass) // 机型参数存在、资源规格参数存在,先匹配机型,在匹配资源规格 case len(o.DeviceClass) > 0 && o.Spec.NotEmpty(): fns = append(fns, o.MatchSpec) fns = append(fns, o.MatchDeviceClass) + default: + fns = append(fns, func(db *gorm.DB) {}) } - // 没有条件的时候也需要遍历一遍 - fns = append(fns, func(db *gorm.DB) {}) + return } -func (o *SearchContext) pickBase(db *gorm.DB) (err error) { - db.Where(" bk_cloud_id = ? and status = ? ", o.BkCloudId, model.Unused) +func (o *SearchContext) pickBase(db *gorm.DB) { + db.Where("gse_agent_status_code = ? ", bk.GSE_AGENT_OK) + if o.BkCloudId <= 0 { + db.Where(" bk_cloud_id = ? and status = ? ", o.ApplyObjectDetail.BkCloudId, model.Unused) + } else { + db.Where(" bk_cloud_id = ? and status = ? ", o.BkCloudId, model.Unused) + } // 如果没有指定资源类型,表示只能选择无资源类型标签的资源 // 没有资源类型标签的资源可以被所有其他类型使用 if cmutil.IsEmpty(o.RsType) { db.Where("JSON_LENGTH(rs_types) <= 0") } else { - db.Where(model.JSONQuery("rs_types").Contains([]string{o.RsType})) + db.Where("? or JSON_LENGTH(rs_types) <= 0 ", model.JSONQuery("rs_types").Contains([]string{o.RsType})) } // 如果没有指定专属业务,就表示只能选用公共的资源 // 不能匹配打了业务标签的资源 if o.IntetionBkBizId <= 0 { db.Where("JSON_LENGTH(dedicated_bizs) <= 0") } else { - db.Where(model.JSONQuery("dedicated_bizs").Contains([]string{ + db.Where("? or JSON_LENGTH(dedicated_bizs) <= 0", model.JSONQuery("dedicated_bizs").Contains([]string{ strconv.Itoa(o.IntetionBkBizId)})) } o.MatchLables(db) - if err = o.MatchLocationSpec(db); err != nil { - return err - } + o.MatchLocationSpec(db) o.MatchStorage(db) // 如果需要存在跨园区检查则需要判断是否存在网卡id,机架id等 if o.Affinity == SAME_SUBZONE_CROSS_SWTICH { o.UseNetDeviceIsNotEmpty(db) } - return } // PickCheck TODO func (o *SearchContext) PickCheck() (err error) { var count int64 - db := model.DB.Self.Table(model.TbRpDetailName()).Select("count(*)") - if err := o.pickBase(db); err != nil { - return err - } - for _, fn := range o.Matcher() { + for idx, fn := range o.Matcher() { + logger.Info("前置检查: 第%d轮资源匹配", idx) + db := model.DB.Self.Table(model.TbRpDetailName()).Select("count(*)") + o.pickBase(db) fn(db) var cnt int64 if err := db.Scan(&cnt).Error; err != nil { logger.Error("query pre check count failed %s", err.Error()) - return err + return errno.ErrDBQuery.AddErr(err) } count += cnt } - logger.Info("count is %d", count) if int(count) < o.Count { - return fmt.Errorf("[pre inspection]: total number of resources initially eligible:%d,number of interface requests:%d", - count, o.Count) + return errno.ErrResourceinsufficient.AddErr(fmt.Errorf("申请需求:%s\n\r资源池符合条件的资源总数:%d 小于申请的数量", o.GetMessage(), + count)) } return nil } -// MatchLables TODO -func (o *SearchContext) MatchLables(db *gorm.DB) { - if len(o.Labels) > 0 { - for key, v := range o.Labels { - db.Where(" ( json_contains(label,json_object(?,?) )", key, v) - } - return - } - db.Where(" JSON_TYPE(label) = 'NULL' OR JSON_LENGTH(label) <= 1 ") -} - // PickInstance TODO func (o *SearchContext) PickInstance() (picker *PickerObject, err error) { picker = NewPicker(o.Count, o.GroupMark) - for _, fn := range o.Matcher() { + matchfuncs := o.Matcher() + for _, fn := range matchfuncs { var items []model.TbRpDetail db := model.DB.Self.Table(model.TbRpDetailName()) - if err = o.pickBase(db); err != nil { - return - } + o.pickBase(db) fn(db) if err = db.Scan(&items).Error; err != nil { logger.Error("query failed %s", err.Error()) - return + return nil, errno.ErrDBQuery.AddErr(err) } // 过滤没有挂载点的磁盘匹配需求 - esspec := GetEmptyDiskSpec(o.StorageSpecs) - if len(esspec) > 0 { + logger.Info("storage spec %v", o.StorageSpecs) + diskSpecs := GetEmptyDiskSpec(o.StorageSpecs) + if len(diskSpecs) > 0 { ts := []model.TbRpDetail{} for _, ins := range items { if err := ins.UnmarshalDiskInfo(); err != nil { - logger.Error("umarshal disk failed %s", err.Error()) + logger.Error("%s umarshal disk failed %s", ins.IP, err.Error()) + return picker, err } logger.Info("%v", ins.Storages) noUseStorages := make(map[string]bk.DiskDetail) @@ -201,22 +203,37 @@ func (o *SearchContext) PickInstance() (picker *PickerObject, err error) { } } logger.Info("nouse: %v", noUseStorages) - if matchNoMountPointStorage(esspec, noUseStorages) { + if matchNoMountPointStorage(diskSpecs, noUseStorages) { ts = append(ts, ins) } } if len(ts) <= 0 { - return picker, fmt.Errorf("did not match the appropriate resources") + if len(matchfuncs) < 2 { + return picker, errno.ErrResourceinsufficient.Add(fmt.Sprintf("匹配磁盘%s,的资源为 0", o.GetDiskMatchInfo())) + } + logger.Info("匹配%s的资源为空", o.GetDiskMatchInfo()) + continue } items = ts } o.PickInstanceBase(picker, items) - logger.Info("picker now is %v", picker) if picker.PickerDone() { return picker, nil } } - return nil, fmt.Errorf("all Instances Cannot Satisfy The Requested Parameters") + return nil, errno.ErrResourceinsufficient.Add(fmt.Sprintf("Picker for %s, 所有资源无法满足 %s的参数需求", o.GroupMark, + o.GetMessage())) +} + +// MatchLables TODO +func (o *SearchContext) MatchLables(db *gorm.DB) { + if len(o.Labels) > 0 { + for key, v := range o.Labels { + db.Where(" ( json_contains(label,json_object(?,?) )", key, v) + } + return + } + db.Where(" JSON_TYPE(label) = 'NULL' OR JSON_LENGTH(label) <= 1 ") } func matchNoMountPointStorage(spec []DiskSpec, sinc map[string]bk.DiskDetail) bool { @@ -256,22 +273,22 @@ func (o *ApplyObjectDetail) PickInstanceBase(picker *PickerObject, items []model logger.Info("the anti-affinity is %s", o.Affinity) switch o.Affinity { case NONE: - data := AnalysisResource(items, true) - picker.PickeElements = data - picker.PickerSameSubZone(false) + picker.PickeElements = AnalysisResource(items, true) + picker.PickerRandom() case CROS_SUBZONE: - data := AnalysisResource(items, false) - picker.PickeElements = data + picker.PickeElements = AnalysisResource(items, false) picker.Picker(true) - case SAME_SUBZONE, SAME_SUBZONE_CROSS_SWTICH: - data := AnalysisResource(items, false) - picker.PickeElements = data + case SAME_SUBZONE: + picker.PickeElements = AnalysisResource(items, false) picker.PickerSameSubZone(false) + case SAME_SUBZONE_CROSS_SWTICH: + picker.PickeElements = AnalysisResource(items, false) + picker.PickerSameSubZone(true) } } // MatchLocationSpec TODO -func (o *SearchContext) MatchLocationSpec(db *gorm.DB) (err error) { +func (o *SearchContext) MatchLocationSpec(db *gorm.DB) { if o.LocationSpec.IsEmpty() { return } @@ -303,6 +320,7 @@ func (o *SearchContext) MatchStorage(db *gorm.DB) { if cmutil.IsNotEmpty(d.DiskType) { db.Where(model.JSONQuery("storage_device").Equals(d.DiskType, mp, "disk_type")) } + logger.Info("storage spec is %v", d) switch { case d.MaxSize > 0: db.Where(model.JSONQuery("storage_device").NumRange(d.MinSize, d.MaxSize, mp, "size")) @@ -313,12 +331,10 @@ func (o *SearchContext) MatchStorage(db *gorm.DB) { } } -// MatchSpec TODO // MatchSpec TODO func (o *SearchContext) MatchSpec(db *gorm.DB) { - db.Where(" ( cpu_num >= ? and cpu_num <= ? ) and ( dram_cap >= ? and dram_cap <= ? ) ", o.Spec.Cpu.Min, - o.Spec.Cpu.Max, - o.Spec.Mem.Min, o.Spec.Mem.Max) + o.Spec.Cpu.MatchCpu(db) + o.Spec.Mem.MatchMem(db) } // MatchDeviceClass TODO diff --git a/dbm-services/common/db-resource/internal/svr/apply/core.go b/dbm-services/common/db-resource/internal/svr/apply/core.go index ed61e65436..dbf6a7f94f 100644 --- a/dbm-services/common/db-resource/internal/svr/apply/core.go +++ b/dbm-services/common/db-resource/internal/svr/apply/core.go @@ -16,23 +16,27 @@ import ( const ( // MINDISTRUTE TODO MINDISTRUTE = 20 + // RANDOM TODO + RANDOM = "RANDOM" ) type subzone = string // PickerObject TODO type PickerObject struct { - Item string - Count int - PickDistrbute map[string]int - ExistSubZone []subzone // 已存在的园区 - // SatisfiedAssetIds []string // 已选择 满足的实例 - SatisfiedHostIds []int - PickeElements map[subzone][]InstanceObject // 待选择实例 + Item string + Count int + PickDistrbute map[string]int + ExistSubZone []subzone // 已存在的园区 + SatisfiedHostIds []int + SelectedResources []*model.TbRpDetail + + PickeElements map[subzone][]InstanceObject // 待选择实例 // 资源请求在同园区的时候才生效 ExistEquipmentIds []string // 已存在的设备Id ExistLinkNetdeviceIds []string // 已存在的网卡Id + ProcessLogs []string } // LockReturnPickers TODO @@ -51,7 +55,7 @@ func LockReturnPickers(elements []*PickerObject, mode string) ([]model.BatchGetT } data, err := model.BatchGetSatisfiedByAssetIds(getter, mode) if err != nil { - logger.Error(fmt.Sprintf("选择到合适的实例,获取实例详情失败%s", err.Error())) + logger.Error(fmt.Sprintf("占用机器,更改机器状态失败%s", err.Error())) } if mode == model.Used { sendArchiverTask(data) @@ -95,9 +99,10 @@ func AnalysisResource(ins []model.TbRpDetail, israndom bool) map[string][]Instan Equipment: v.RackID, LinkNetdeviceId: linkids, Nice: createNice(int(v.CPUNum), v.DramCap, 0, 0), + InsDetail: &v, } if israndom { - result["RANDOM"] = append(result["RANDOM"], t) + result[RANDOM] = append(result[RANDOM], t) } else { result[v.SubZone] = append(result[v.SubZone], t) } @@ -137,6 +142,8 @@ func (c *PickerObject) PickerSameSubZone(cross_switch bool) { for _, subzone := range sortSubZones { logger.Info("PickerSameSubZone:PickeElements: %v", c.PickeElements[subzone]) if len(c.PickeElements[subzone]) < c.Count || len(c.PickeElements[subzone]) <= 0 { + c.ProcessLogs = append(c.ProcessLogs, fmt.Sprintf("%s 符合条件的资源有%d,实际需要申请%d,不满足!!!", + subzone, len(c.PickeElements[subzone]), c.Count)) continue } logger.Info("dbeug %v", subzone) @@ -157,6 +164,19 @@ func (c *PickerObject) PickerSameSubZone(cross_switch bool) { } } +// PickerRandom TODO +func (c *PickerObject) PickerRandom() { + for idx := range c.PickeElements[RANDOM] { + logger.Info("loop %d", idx) + c.pickerOne(RANDOM, false) + // 匹配资源完成 + logger.Info("%d,%d", c.Count, len(c.SatisfiedHostIds)) + if c.PickerDone() { + return + } + } +} + // Picker 筛选,匹配资源 // // @receiver c @@ -214,6 +234,7 @@ func (c *PickerObject) pickerOne(key string, cross_switch bool) bool { } c.ExistEquipmentIds = append(c.ExistEquipmentIds, v.Equipment) c.SatisfiedHostIds = append(c.SatisfiedHostIds, v.BkHostId) + c.SelectedResources = append(c.SelectedResources, v.InsDetail) c.ExistLinkNetdeviceIds = append(c.ExistLinkNetdeviceIds, v.LinkNetdeviceId...) c.PickDistrbute[key]++ c.deleteElement(key, v.BkHostId) @@ -268,11 +289,16 @@ func (c *PickerObject) PreselectedSatisfiedInstance() error { return nil } -// RollbackSatisfiedInstanceStatusUnused TODO -func (c *PickerObject) RollbackSatisfiedInstanceStatusUnused() error { +// RollbackUnusedInstance TODO +func (c *PickerObject) RollbackUnusedInstance() error { return model.UpdateTbRpDetailStatusAtSelling(c.SatisfiedHostIds, model.Unused) } +// GetReason TODO +func (c *PickerObject) GetReason() (message string) { + return +} + // CampusNice TODO type CampusNice struct { Campus string `json:"campus"` diff --git a/dbm-services/common/db-resource/internal/svr/apply/instance.go b/dbm-services/common/db-resource/internal/svr/apply/instance.go index a1af259855..59f5aa577b 100644 --- a/dbm-services/common/db-resource/internal/svr/apply/instance.go +++ b/dbm-services/common/db-resource/internal/svr/apply/instance.go @@ -1,11 +1,24 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package apply +import "dbm-services/common/db-resource/internal/model" + // InstanceObject TODO type InstanceObject struct { BkHostId int Equipment string LinkNetdeviceId []string Nice int64 + InsDetail *model.TbRpDetail } // GetLinkNetDeviceIdsInterface TODO diff --git a/dbm-services/common/db-resource/internal/svr/bk/bk.go b/dbm-services/common/db-resource/internal/svr/bk/bk.go index bacbdcba09..17258684dc 100644 --- a/dbm-services/common/db-resource/internal/svr/bk/bk.go +++ b/dbm-services/common/db-resource/internal/svr/bk/bk.go @@ -1,2 +1,14 @@ // Package bk TODO package bk + +const ( + // GSE_AGENT_OK 运行中 + // Agent当前运行状态码, -1:未知 0:初始安装 1:启动中 2:运行中 3:有损状态 4:繁忙状态 5:升级中 6:停止中 7:解除安装 + GSE_AGENT_OK = 2 + // GSE_AGENT_INSTALLING TODO + GSE_AGENT_INSTALLING = 0 + // GSE_AGENT_STARTING TODO + GSE_AGENT_STARTING = 1 + // GSE_AGENT_STATUS_UNKNOWN TODO + GSE_AGENT_STATUS_UNKNOWN = -1 +) diff --git a/dbm-services/common/db-resource/internal/svr/bk/cc.go b/dbm-services/common/db-resource/internal/svr/bk/cc.go index 514b54616c..f5425e7b5d 100644 --- a/dbm-services/common/db-resource/internal/svr/bk/cc.go +++ b/dbm-services/common/db-resource/internal/svr/bk/cc.go @@ -1,6 +1,7 @@ package bk import ( + "net/url" "time" "dbm-services/common/db-resource/internal/config" @@ -12,6 +13,9 @@ import ( // EsbClient TODO var EsbClient *cc.Client +// GseClient TODO +var GseClient *cc.Client + // CCModuleFields TODO var CCModuleFields []string @@ -23,6 +27,11 @@ func init() { logger.Fatal("init cmdb client failed %s", err.Error()) return } + GseClient, err = NewGseClient() + if err != nil { + logger.Fatal("init gse client failed %s", err.Error()) + return + } CCModuleFields = []string{ "bk_host_id", "bk_cloud_id", @@ -42,6 +51,24 @@ func init() { } } +// NewGseClient TODO +func NewGseClient() (*cc.Client, error) { + var apiserver string + var err error + apiserver = config.AppConfig.BkSecretConfig.GseBaseUrl + if cmutil.IsEmpty(apiserver) { + apiserver, err = url.JoinPath(config.AppConfig.BkSecretConfig.BkBaseUrl, "/api/bk-gse/prod") + if err != nil { + return nil, err + } + } + return cc.NewClient(apiserver, cc.Secret{ + BKAppCode: config.AppConfig.BkSecretConfig.BkAppCode, + BKAppSecret: config.AppConfig.BkSecretConfig.BKAppSecret, + BKUsername: config.AppConfig.BkSecretConfig.BkUserName, + }) +} + // NewClient TODO func NewClient() (*cc.Client, error) { return cc.NewClient(config.AppConfig.BkSecretConfig.BkBaseUrl, cc.Secret{ diff --git a/dbm-services/common/db-resource/internal/svr/bk/cc_test.go b/dbm-services/common/db-resource/internal/svr/bk/cc_test.go index 6f66453bc2..be740211d8 100644 --- a/dbm-services/common/db-resource/internal/svr/bk/cc_test.go +++ b/dbm-services/common/db-resource/internal/svr/bk/cc_test.go @@ -66,7 +66,7 @@ func TestReserverCC(t *testing.T) { RsTypes: []string{"MySQL", "Redis"}, Hosts: hosts, } - importResp, err := manage.ImportByListHostBiz(param) + importResp, err := manage.Doimport(param) if err != nil { t.Fatal(err) } diff --git a/dbm-services/common/db-resource/internal/svr/bk/disk.go b/dbm-services/common/db-resource/internal/svr/bk/disk.go index 34ce5eb5e4..46d81ec651 100644 --- a/dbm-services/common/db-resource/internal/svr/bk/disk.go +++ b/dbm-services/common/db-resource/internal/svr/bk/disk.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package bk import ( @@ -6,7 +16,6 @@ import ( "fmt" "time" - "dbm-services/common/db-resource/internal/config" "dbm-services/common/go-pubpkg/cmutil" "dbm-services/common/go-pubpkg/logger" ) @@ -90,26 +99,35 @@ func SetDiskType(elems []DiskInfo, t string) (ds []DiskInfo) { return ds } -// GetDiskInfo TODO -func GetDiskInfo(hosts []string, bk_cloud_id, bk_biz_id int) (ipLogContentMap map[string]*ShellResCollection, - failedipLogInfo map[string]string, err error) { - iplist := []IPList{} +// GetDiskResp TODO +type GetDiskResp struct { + IpLogContentMap map[string]*ShellResCollection + IpFailedLogMap map[string]string +} + +func getIpList(hosts []string, bk_cloud_id int) []IPList { + var ipList []IPList for _, ip := range hosts { - iplist = append(iplist, IPList{ + ipList = append(ipList, IPList{ IP: ip, BkCloudID: bk_cloud_id, }) } + return ipList +} + +// GetDiskInfo TODO +func GetDiskInfo(hosts []string, bk_cloud_id, bk_biz_id int) (resp GetDiskResp, err error) { + iplist := getIpList(hosts, bk_cloud_id) jober := JobV3{ Client: EsbClient, } - logger.Info("api %s", config.AppConfig.BkSecretConfig.BkBaseUrl) job, err := jober.ExecuteJob(&FastExecuteScriptParam{ BkBizID: bk_biz_id, ScriptContent: base64.StdEncoding.EncodeToString(GetDiskInfoShellContent), ScriptTimeout: 300, ScriptLanguage: 1, - AccountAlias: "mysql", + AccountAlias: "root", TargetServer: TargetServer{ IPList: iplist, }, @@ -117,7 +135,7 @@ func GetDiskInfo(hosts []string, bk_cloud_id, bk_biz_id int) (ipLogContentMap ma ) if err != nil { logger.Error("call execute job failed %s", err.Error()) - return nil, nil, err + return GetDiskResp{}, err } // 查询任务 var errCnt int @@ -135,38 +153,21 @@ func GetDiskInfo(hosts []string, bk_cloud_id, bk_biz_id int) (ipLogContentMap ma break } if errCnt > 10 { - return nil, nil, fmt.Errorf("more than 10 errors when query job %d,some err: %s", job.JobInstanceID, err.Error()) + return GetDiskResp{}, fmt.Errorf("more than 10 errors when query job %d,some err: %s", job.JobInstanceID, + err.Error()) } time.Sleep(1 * time.Second) } - // 在查询一遍转态 + // 再查询一遍状态 jobStatus, err = jober.GetJobStatus(&GetJobInstanceStatusParam{ BKBizId: bk_biz_id, JobInstanceID: job.JobInstanceID, }) if err != nil { logger.Error("query job %d status failed %s", job.JobInstanceID, err.Error()) - return nil, nil, err - } - failedipLogInfo = make(map[string]string) - for _, stepInstance := range jobStatus.StepInstanceList { - for _, step_ip_result := range stepInstance.StepIpResultList { - switch step_ip_result.Status { - case 1: - failedipLogInfo[step_ip_result.IP] += "Agent异常\n" - case 12: - failedipLogInfo[step_ip_result.IP] += "任务下发失败\n" - case 403: - failedipLogInfo[step_ip_result.IP] += "任务强制终止成功\n" - case 404: - failedipLogInfo[step_ip_result.IP] += "任务强制终止失败\n" - case 11: - failedipLogInfo[step_ip_result.IP] += "执行失败;\n" - default: - continue - } - } + return GetDiskResp{}, err } + resp.IpFailedLogMap = analyzeJobIpFailedLog(jobStatus) // 查询执行输出 var ipLogs BatchGetJobInstanceIpLogRpData ipLogs, err = jober.BatchGetJobInstanceIpLog(&BatchGetJobInstanceIpLogParam{ @@ -175,14 +176,37 @@ func GetDiskInfo(hosts []string, bk_cloud_id, bk_biz_id int) (ipLogContentMap ma StepInstanceID: job.StepInstanceID, IPList: iplist, }) - ipLogContentMap = make(map[string]*ShellResCollection) + resp.IpLogContentMap = make(map[string]*ShellResCollection) for _, d := range ipLogs.ScriptTaskLogs { var dl ShellResCollection if err = json.Unmarshal([]byte(d.LogContent), &dl); err != nil { logger.Error("unmarshal log content failed %s", err.Error()) continue } - ipLogContentMap[d.Ip] = &dl + resp.IpLogContentMap[d.Ip] = &dl + } + return resp, err +} + +func analyzeJobIpFailedLog(jobStatus GetJobInstanceStatusRpData) map[string]string { + ipFailedLogMap := make(map[string]string) + for _, stepInstance := range jobStatus.StepInstanceList { + for _, step_ip_result := range stepInstance.StepIpResultList { + switch step_ip_result.Status { + case 1: + ipFailedLogMap[step_ip_result.IP] += "Agent异常\n" + case 12: + ipFailedLogMap[step_ip_result.IP] += "任务下发失败\n" + case 403: + ipFailedLogMap[step_ip_result.IP] += "任务强制终止成功\n" + case 404: + ipFailedLogMap[step_ip_result.IP] += "任务强制终止失败\n" + case 11: + ipFailedLogMap[step_ip_result.IP] += "执行失败;\n" + default: + continue + } + } } - return ipLogContentMap, failedipLogInfo, err + return ipFailedLogMap } diff --git a/dbm-services/common/db-resource/internal/svr/bk/get_block_info.sh b/dbm-services/common/db-resource/internal/svr/bk/get_block_info.sh index 6b329a4f95..f680209b92 100644 --- a/dbm-services/common/db-resource/internal/svr/bk/get_block_info.sh +++ b/dbm-services/common/db-resource/internal/svr/bk/get_block_info.sh @@ -1,8 +1,8 @@ #!/bin/bash -SYSBLOCK_DIR="/sys/block" -META_DOMAIN='127.0.0.1' -#META_DOMAIN='metadata.tencentyun.com' +SYSBLOCK_DIR=${SYSBLOCK_DIR:-"/sys/block"} +# 从环境变量中获取META_DOMAIN的值,如果不存在则使用默认值 +META_DOMAIN=${META_DOMAIN:-'127.0.0.1'} getDiskType(){ dname=$1 @@ -125,12 +125,12 @@ memsize=`free -m | awk '/Mem/ {print $2}'` curl http://${META_DOMAIN}/latest/meta-data/placement/region -s -o /dev/null if [ $? -eq 0 ] then - region=`curl http://${META_DOMAIN}/latest/meta-data/placement/region -s` + region=`curl http://${META_DOMAIN}/latest/meta-data/placement/region -s -o /dev/null` fi -curl http://$META_DOMAIN/metadata.tencentyun.com/latest/meta-data/placement/zone -s -o /dev/null +curl http://$META_DOMAIN/latest/meta-data/placement/zone -s -o /dev/null if [ $? -eq 0 ] then - zone=`curl http://${META_DOMAIN}/latest/meta-data/placement/zone -s` + zone=`curl http://${META_DOMAIN}/latest/meta-data/placement/zone -s -o /dev/null` fi echo -n "{\"cpu\":${cpunum},\"mem\":${memsize},\"region\":\"${region}\",\"zone\":\"${zone}\"," length=${#tmp_arr[@]} diff --git a/dbm-services/common/db-resource/internal/svr/bk/job_v3.go b/dbm-services/common/db-resource/internal/svr/bk/job_v3.go index 424dab9115..4603e0ed88 100644 --- a/dbm-services/common/db-resource/internal/svr/bk/job_v3.go +++ b/dbm-services/common/db-resource/internal/svr/bk/job_v3.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package bk import ( @@ -38,7 +48,6 @@ const ( // FastExecuteScriptParam TODO type FastExecuteScriptParam struct { - cc.BaseSecret BkBizID int `json:"bk_biz_id"` ScriptID int `json:"script_id,omitempty"` ScriptContent string `json:"script_content"` @@ -78,7 +87,6 @@ type TopoNodeList struct { // BatchGetJobInstanceIpLogParam TODO type BatchGetJobInstanceIpLogParam struct { - cc.BaseSecret `json:",inline"` BKBizId int `json:"bk_biz_id"` JobInstanceID int64 `json:"job_instance_id"` StepInstanceID int64 `json:"step_instance_id"` @@ -101,7 +109,6 @@ type ScriptTaskLog struct { // GetJobInstanceStatusParam TODO type GetJobInstanceStatusParam struct { - cc.BaseSecret `json:",inline"` BKBizId int `json:"bk_biz_id"` JobInstanceID int64 `json:"job_instance_id"` // 是否返回每个ip上的任务详情,对应返回结果中的step_ip_result_list。默认值为false。 diff --git a/dbm-services/common/db-resource/internal/svr/task/task.go b/dbm-services/common/db-resource/internal/svr/task/task.go index d9923eae91..b87de13d26 100644 --- a/dbm-services/common/db-resource/internal/svr/task/task.go +++ b/dbm-services/common/db-resource/internal/svr/task/task.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + // Package task TODO package task @@ -7,6 +17,9 @@ import ( "time" "dbm-services/common/db-resource/internal/model" + "dbm-services/common/db-resource/internal/svr/bk" + "dbm-services/common/go-pubpkg/cc.v3" + "dbm-services/common/go-pubpkg/cmutil" "dbm-services/common/go-pubpkg/logger" ) @@ -25,8 +38,10 @@ var ArchiverResourceChan chan int // RecordRsOperatorInfoChan TODO var RecordRsOperatorInfoChan chan model.TbRpOperationInfo +// SyncRsGseAgentStatusChan TODO +var SyncRsGseAgentStatusChan chan []string + // RuningTask TODO -// RuningApplyTask var RuningTask chan struct{} func init() { @@ -34,6 +49,7 @@ func init() { ArchiverResourceChan = make(chan int, 200) RecordRsOperatorInfoChan = make(chan model.TbRpOperationInfo, 20) RuningTask = make(chan struct{}, 100) + SyncRsGseAgentStatusChan = make(chan []string, 10) } // init TODO @@ -77,8 +93,11 @@ func init() { if err := recordRsOperationInfo(info); err != nil { logger.Error("failed to record resource operation log %s", err.Error()) } + case agentIds := <-SyncRsGseAgentStatusChan: + if err := UpdateResourceGseAgentStatus(agentIds...); err != nil { + logger.Warn("[sync task]: sync gse agent status failed:%s", err.Error()) + } } - } }() } @@ -113,3 +132,45 @@ func recordTask(data ApplyResponeLogItem) error { func recordRsOperationInfo(data model.TbRpOperationInfo) (err error) { return model.DB.Self.Table(model.TbRpOperationInfoTableName()).Create(&data).Error } + +// UpdateResourceGseAgentStatus TODO +func UpdateResourceGseAgentStatus(agentIds ...string) (err error) { + var agentIdList []string + db := model.DB.Self.Table(model.TbRpDetailName()).Select("bk_agent_id").Where( + "status = ? and agent_status_update_time < date_sub(now(),INTERVAL 5 MINUTE)", model.Unused) + if len(agentIds) > 0 { + db.Where("bk_agent_id in (?)", agentIds) + } + if err = db.Scan(&agentIdList).Error; err != nil { + logger.Error("query resoure list failed %s", err.Error()) + return err + } + for _, gseAgentIdlist := range cmutil.SplitGroup(agentIdList, 1000) { + agentStateList, resp, err := cc.NewListAgentState(bk.GseClient).QueryListAgentInfo(&cc.ListAgentInfoParam{ + AgentIdList: gseAgentIdlist, + }) + if err != nil { + var BkRequestId, BkMessage string + if resp != nil { + BkRequestId = resp.RequestId + BkMessage = resp.Message + } + logger.Error("query gse agent state failed %s;blueking trace id:%s,msg:%s", err.Error(), BkRequestId, + BkMessage) + return err + } + for _, agentState := range agentStateList { + agentId := agentState.BkAgentId + if cmutil.IsEmpty(agentId) { + agentId = fmt.Sprintf("%d:%s", agentState.BkCloudID, agentState.BkHostIp) + } + err = model.DB.Self.Table(model.TbRpDetailName()).Where("bk_agent_id = ? ", agentId).Updates(map[string]interface{}{ + "gse_agent_status_code": agentState.StatusCode, "agent_status_update_time": time.Now()}).Error + if err != nil { + logger.Error("update gse agent status failed %s", err.Error()) + continue + } + } + } + return nil +} diff --git a/dbm-services/common/db-resource/main.go b/dbm-services/common/db-resource/main.go index 4e7c3d88bb..ca0245db0f 100644 --- a/dbm-services/common/db-resource/main.go +++ b/dbm-services/common/db-resource/main.go @@ -7,11 +7,13 @@ import ( "dbm-services/common/db-resource/internal/config" "dbm-services/common/db-resource/internal/middleware" "dbm-services/common/db-resource/internal/routers" + "dbm-services/common/db-resource/internal/svr/task" "dbm-services/common/go-pubpkg/logger" "github.com/gin-contrib/pprof" "github.com/gin-contrib/requestid" "github.com/gin-gonic/gin" + "github.com/robfig/cron/v3" ) var buildstamp = "" @@ -30,6 +32,10 @@ func main() { ctx.SecureJSON(http.StatusOK, map[string]interface{}{"buildstamp": buildstamp, "githash": githash, "version": version}) }) + lcron := cron.New() + initCron(lcron) + lcron.Start() + defer lcron.Stop() engine.Run(config.AppConfig.ListenAddress) } @@ -41,12 +47,20 @@ func init() { } } +func initCron(localcron *cron.Cron) { + localcron.AddFunc("1 */5 * * *", func() { + if err := task.UpdateResourceGseAgentStatus(); err != nil { + logger.Error("update gse status %s", err.Error()) + } + }) +} + // initLogger initialization log func initLogger() (err error) { var writer *os.File formatJson := true level := logger.InfoLevel - writer = os.Stdin + writer = os.Stdout l := logger.New(writer, formatJson, level, map[string]string{}) logger.ResetDefault(l) defer logger.Sync() diff --git a/dbm-services/common/db-resource/pkg/errno/code.go b/dbm-services/common/db-resource/pkg/errno/code.go deleted file mode 100644 index fbd1a6f5c0..0000000000 --- a/dbm-services/common/db-resource/pkg/errno/code.go +++ /dev/null @@ -1,76 +0,0 @@ -package errno - -var ( - // OK TODO - // Common errors - // OK = Errno{Code: 0, Message: ""} - OK = Errno{Code: 0, Message: "", CNMessage: ""} - - // InternalServerError TODO - InternalServerError = Errno{Code: 10001, Message: "Internal server error", CNMessage: "服务器内部错误。"} - // ErrBind TODO - ErrBind = Errno{Code: 10002, Message: "Error occurred while binding the request body to the struct.", - CNMessage: "请求参数发生错误。"} - // ErrString2Int TODO - ErrString2Int = Errno{Code: 10010, Message: "Error occurred while convert string to int."} - // ErrorJsonToMap TODO - ErrorJsonToMap = Errno{Code: 10030, Message: "Error occured while converting json to Map.", - CNMessage: "Json 转为 Map 出现错误!"} - // ErrorUIDBeZero TODO - ErrorUIDBeZero = Errno{Code: 10035, Message: "uid can not be 0!", CNMessage: "uid 不能为 0.!"} - - // ErrTypeAssertion TODO - ErrTypeAssertion = Errno{Code: 10040, Message: "Error occurred while doing type assertion."} - // ErrParameterRequired TODO - ErrParameterRequired = Errno{Code: 10050, Message: "Input paramter required"} - // StartBiggerThanEndTime TODO - StartBiggerThanEndTime = Errno{Code: 10060, Message: "Start time is bigger than end time."} - - // ErrInputParameter TODO - ErrInputParameter = Errno{Code: 10201, Message: "input pramater error.", CNMessage: "输入参数错误"} - - // ErrInvokeAPI TODO - // call other service error - ErrInvokeAPI = Errno{Code: 15000, Message: "Error occurred while invoking API", CNMessage: "调用 API 发生错误!"} - - // InvalidHttpStatusCode TODO - InvalidHttpStatusCode = Errno{Code: 15015, Message: "Invalid http status code", CNMessage: "无效的 http 状态码!"} - - // ErrDoNotHavePrivs TODO - // user errors - ErrDoNotHavePrivs = Errno{Code: 20106, Message: "User don't have Privs."} - // ErrUserIsEmpty TODO - ErrUserIsEmpty = Errno{Code: 20110, Message: "User can't be empty.", CNMessage: "user 不能为空!"} - - // dbrms - - // ErrDBQuery TODO - // model operation errors - ErrDBQuery = Errno{Code: 50200, Message: "DB Query error.", CNMessage: "查询DB错误!"} - // ErrModelFunction TODO - ErrModelFunction = Err{Errno: Errno{Code: 50201, Message: "Error occured while invoking model function.", - CNMessage: "调用 DB model 方法发生错误!"}, Err: nil} - - // ErrGetJSONArray TODO - // data handle error - ErrGetJSONArray = Errno{Code: 50300, Message: "Get simplejson Array error.", CNMessage: ""} - // ErrConvert2Map TODO - ErrConvert2Map = Errno{Code: 50301, Message: "Error occurred while converting the data to Map.", - CNMessage: "Error occurred while converting the data to Map."} - // ErrJSONMarshal TODO - ErrJSONMarshal = Errno{Code: 50302, Message: "Error occurred while marshaling the data to JSON.", - CNMessage: "Error occurred while marshaling the data to JSON."} - // ErrReadEntity TODO - ErrReadEntity = Errno{Code: 50303, Message: "Error occurred while parsing the request parameter.", - CNMessage: "Error occurred while parsing the request parameter."} - // ErrJSONUnmarshal TODO - ErrJSONUnmarshal = Errno{Code: 50304, Message: "Error occurred while Unmarshaling the JSON to data model.", - CNMessage: "Error occurred while Unmarshaling the JSON to data model."} - // ErrBytesToMap TODO - ErrBytesToMap = Errno{Code: 50307, Message: "Error occurred while converting bytes to map.", - CNMessage: "Error occurred while converting bytes to map."} - // ErrorResourceinsufficient TODO - // dbrms - // ErrorResourceinsufficient TODO - ErrorResourceinsufficient = Errno{Code: 60001, Message: "resource insufficient", CNMessage: "资源不足"} -) diff --git a/dbm-services/common/db-resource/pkg/errno/errno.go b/dbm-services/common/db-resource/pkg/errno/errno.go deleted file mode 100644 index 1486264ce9..0000000000 --- a/dbm-services/common/db-resource/pkg/errno/errno.go +++ /dev/null @@ -1,115 +0,0 @@ -// Package errno TODO -package errno - -import ( - "fmt" -) - -// Errno TODO -type Errno struct { - Code int - Message string - CNMessage string -} - -const lang = "zh_CN" - -// Error 用于错误处理 -func (err Errno) Error() string { - switch lang { - case "zh_CN": - return err.CNMessage - case "en_US": - return err.Message - default: - return err.CNMessage - } -} - -// Addf TODO -func (err Errno) Addf(format string, args ...interface{}) error { - return err.Add(fmt.Sprintf(format, args...)) -} - -// Add TODO -func (err Errno) Add(message string) error { - switch lang { - case "zh_CN": - err.CNMessage += message - case "en_US": - err.Message += message - default: - err.CNMessage += message - } - return err -} - -// Err represents an error -type Err struct { - Errno - Err error -} - -// New TODO -func New(errno Errno, err error) *Err { - return &Err{Errno: errno, Err: err} -} - -// Add TODO -func (err Err) Add(message string) error { - switch lang { - case "zh_CN": - err.CNMessage += message - return err - case "en_US": - err.Message += message - return err - default: - err.CNMessage += message - return err - } -} - -// SetMsg TODO -func (err Err) SetMsg(message string) error { - err.Message = message - return err -} - -// SetCNMsg TODO -func (err Err) SetCNMsg(cnMessage string) error { - err.CNMessage = cnMessage - return err -} - -// Addf TODO -func (err Err) Addf(format string, args ...interface{}) error { - return err.Add(fmt.Sprintf(format, args...)) -} - -// DecodeErr TODO -func DecodeErr(err error) (int, string) { - - var CN bool = true - - if err == nil { - return OK.Code, OK.Message - } - - switch typed := err.(type) { - case Err: - if CN { - return typed.Code, typed.CNMessage - } else { - return typed.Code, typed.Message - } - case Errno: - if CN { - return typed.Code, typed.CNMessage - } else { - return typed.Code, typed.Message - } - default: - } - return InternalServerError.Code, err.Error() -} diff --git a/dbm-services/common/db-resource/pkg/util/util.go b/dbm-services/common/db-resource/pkg/util/util.go deleted file mode 100644 index 80d62b1ad1..0000000000 --- a/dbm-services/common/db-resource/pkg/util/util.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package util TODO -package util diff --git a/dbm-services/common/dbha/ha-module/Dockerfile b/dbm-services/common/dbha/ha-module/Dockerfile index 15659576cb..4875f80b05 100644 --- a/dbm-services/common/dbha/ha-module/Dockerfile +++ b/dbm-services/common/dbha/ha-module/Dockerfile @@ -5,9 +5,9 @@ ARG BASEDIR=/home/mysql/dbha COPY dbha /usr/bin/ -RUN groupadd -r mysql && useradd -r -g mysql mysql \ - && /usr/bin/install -m 0775 -o mysql -g root -d ${BASEDIR}\ - && chown -R mysql ${BAKDIR} ${BASEDIR} \ +RUN groupadd -r dbmysql && useradd -r -g dbmysql dbmysql \ + && /usr/bin/install -m 0775 -o dbmysql -g root -d ${BASEDIR}\ + && chown -R dbmysql ${BAKDIR} ${BASEDIR} \ && chmod +x /usr/bin/dbha USER mysql diff --git a/dbm-services/common/dbha/ha-module/agent/connection.go b/dbm-services/common/dbha/ha-module/agent/connection.go index f0ebaea520..99978e5202 100644 --- a/dbm-services/common/dbha/ha-module/agent/connection.go +++ b/dbm-services/common/dbha/ha-module/agent/connection.go @@ -37,12 +37,12 @@ func (gm *GMConnection) Init() error { return nil } -// ReportInstance report instance detect info -func (gm *GMConnection) ReportInstance(dbType string, jsonInfo []byte) error { +// ReportInstance agent report instance detect info to gm +func (gm *GMConnection) ReportInstance(detectType string, jsonInfo []byte) error { var writeBuf string writeBuf += HEADER writeBuf += "\r\n" - writeBuf += dbType + writeBuf += detectType writeBuf += "\r\n" writeBuf += strconv.Itoa(len(jsonInfo)) writeBuf += "\r\n" diff --git a/dbm-services/common/dbha/ha-module/agent/monitor_agent.go b/dbm-services/common/dbha/ha-module/agent/monitor_agent.go index f307de47a3..0d9b3046ff 100644 --- a/dbm-services/common/dbha/ha-module/agent/monitor_agent.go +++ b/dbm-services/common/dbha/ha-module/agent/monitor_agent.go @@ -23,7 +23,8 @@ import ( type MonitorAgent struct { City string Campus string - Type string + //detect dbType + DetectType string // agent ip MonIp string LastFetchInsTime time.Time @@ -40,30 +41,22 @@ type MonitorAgent struct { } // NewMonitorAgent new a new agent do detect -func NewMonitorAgent(conf *config.Config, dbType string) (*MonitorAgent, error) { +func NewMonitorAgent(conf *config.Config, detectType string) (*MonitorAgent, error) { var err error agent := &MonitorAgent{ City: conf.AgentConf.City, Campus: conf.AgentConf.Campus, - Type: dbType, + DetectType: detectType, LastFetchInsTime: time.Now(), LastFetchGMTime: time.Now(), GMInstance: map[string]*GMConnection{}, heartbeat: time.Now(), Conf: conf, - } - agent.CmDBClient, err = client.NewCmDBClient(&conf.DBConf.CMDB, conf.GetCloudId()) - if err != nil { - return nil, err - } - - agent.HaDBClient, err = client.NewHaDBClient(&conf.DBConf.HADB, conf.GetCloudId()) - if err != nil { - return nil, err + CmDBClient: client.NewCmDBClient(&conf.DBConf.CMDB, conf.GetCloudId()), + HaDBClient: client.NewHaDBClient(&conf.DBConf.HADB, conf.GetCloudId()), + MonIp: util.LocalIp, } - agent.MonIp = util.LocalIp - // register agent into err = agent.registerAgentInfoToHaDB() if err != nil { @@ -116,7 +109,7 @@ func (a *MonitorAgent) RefreshInstanceCache() { err := a.FetchDBInstance() if err != nil { log.Logger.Errorf("fetch %s instance failed. err:%s", - a.Type, err.Error()) + a.DetectType, err.Error()) } a.flushInsFetchTime() } @@ -134,13 +127,7 @@ func (a *MonitorAgent) DoDetectSingle(ins dbutil.DataBaseDetect) { a.reportMonitor(ins, err) if ins.NeedReporter() { - err = a.ReporterGM(ins) - if err != nil { - log.Logger.Errorf("reporter gm failed. err:%s", err.Error()) - } - - // reporter HADB - ip, port := ins.GetAddress() + // reporter detect result to hadb err = a.HaDBClient.ReportDBStatus(a.MonIp, ip, port, string(ins.GetType()), string(ins.GetStatus())) if err != nil { @@ -148,6 +135,10 @@ func (a *MonitorAgent) DoDetectSingle(ins dbutil.DataBaseDetect) { "reporter hadb instance status failed. err:%s, ip:%s, port:%d, db_type:%s, status:%s", err.Error(), ip, port, ins.GetType(), ins.GetStatus()) } + err = a.ReporterGM(ins) + if err != nil { + log.Logger.Errorf("reporter gm failed. err:%s", err.Error()) + } ins.UpdateReporterTime() } } @@ -198,23 +189,25 @@ func (a *MonitorAgent) FetchDBInstance() error { } log.Logger.Debugf("fetch db instance info len:%d", len(rawInfo)) - cb, ok := dbmodule.DBCallbackMap[types.DBType(a.Type)] + // get callback function by db type + cb, ok := dbmodule.DBCallbackMap[a.DetectType] if !ok { - err = fmt.Errorf("can't find fetch %s instance callback", a.Type) + err = fmt.Errorf("can't find fetch %s instance callback", a.DetectType) log.Logger.Error(err.Error()) return err } + // unmarshal instance from cmdb struct(api response) to detect struct AllDbInstance, err := cb.FetchDBCallback(rawInfo, a.Conf) if err != nil { log.Logger.Errorf("fetch db instance failed. err:%s", err.Error()) return err } - err = a.FetchInstancePass(types.DBType(a.Type), AllDbInstance) - if err != nil { - log.Logger.Errorf("fetch db instance pass failed,err:%s", err.Error()) - return err - } + //err = a.FetchInstancePass(types.DBType(a.DBType), AllDbInstance) + //if err != nil { + // log.Logger.Errorf("fetch db instance pass failed,err:%s", err.Error()) + // return err + //} a.DBInstance, err = a.moduloHashSharding(AllDbInstance) if err != nil { @@ -255,6 +248,7 @@ func (a *MonitorAgent) FetchGMInstance() error { } } } + log.Logger.Infof("agent get aliveGmInfo:%d, GmInstance:%d", len(gmInfo), len(a.GMInstance)) return nil @@ -300,7 +294,7 @@ func (a *MonitorAgent) ReporterGM(reporterInstance dbutil.DataBaseDetect) error log.Logger.Errorf("instance Serialization failed. err:%s", err.Error()) return err } - err = gmIns.ReportInstance(string(reporterInstance.GetType()), jsonInfo) + err = gmIns.ReportInstance(reporterInstance.GetDetectType(), jsonInfo) if err != nil { log.Logger.Warnf("reporter gm failed. gm_ip:%s, gm_port:%d, err:%s", ip, port, err.Error()) gmIns.IsConnection = false @@ -310,6 +304,10 @@ func (a *MonitorAgent) ReporterGM(reporterInstance dbutil.DataBaseDetect) error return err } } else { + log.Logger.Debugf("reporter gm success. gm info:%s#%d", ip, port) + if err = a.reporterBindGM(fmt.Sprintf("%s#%d", gmIns.Ip, gmIns.Port)); err != nil { + log.Logger.Warnf("update agent's bind gm info failed:%s", err.Error()) + } isReporter = true gmIns.Mutex.Unlock() break @@ -382,7 +380,7 @@ func (a *MonitorAgent) registerAgentInfoToHaDB() error { "agent", a.City, a.Campus, - a.Type) + a.DetectType) if err != nil { return err } @@ -393,7 +391,7 @@ func (a *MonitorAgent) registerAgentInfoToHaDB() error { // only detect the minimum port instance, other instances ignore. func (a *MonitorAgent) moduloHashSharding(allDbInstance []dbutil.DataBaseDetect) (map[string]dbutil.DataBaseDetect, error) { - mod, modValue, err := a.HaDBClient.AgentGetHashValue(a.MonIp, a.Type, a.Conf.AgentConf.FetchInterval) + mod, modValue, err := a.HaDBClient.AgentGetHashValue(a.MonIp, a.DetectType, a.Conf.AgentConf.FetchInterval) if err != nil { log.Logger.Errorf("get Modulo failed and wait next refresh time. err:%s", err.Error()) return nil, err @@ -419,7 +417,16 @@ func (a *MonitorAgent) moduloHashSharding(allDbInstance []dbutil.DataBaseDetect) // reporterHeartbeat send heartbeat to hadb func (a *MonitorAgent) reporterHeartbeat() error { interval := time.Now().Sub(a.heartbeat).Seconds() - err := a.HaDBClient.ReporterAgentHeartbeat(a.Type, int(interval)) + err := a.HaDBClient.ReporterAgentHeartbeat(a.DetectType, int(interval), "N/A") + a.heartbeat = time.Now() + return err +} + +// reporterBindGM send bind gm info to hadb +// only agent trigger double check(report GM) should call this +func (a *MonitorAgent) reporterBindGM(gmInfo string) error { + interval := time.Now().Sub(a.heartbeat).Seconds() + err := a.HaDBClient.ReporterAgentHeartbeat(a.DetectType, int(interval), gmInfo) a.heartbeat = time.Now() return err } @@ -436,13 +443,13 @@ func (a *MonitorAgent) reportMonitor(ins dbutil.DataBaseDetect, err error) { switch ins.GetStatus() { case constvar.SSHCheckFailed: content := "agent detect failed by ssh check, err:" + errInfo - monitor.MonitorSendDetect(ins, constvar.DBHA_EVENT_DETECT_SSH, content) + monitor.MonitorSendDetect(ins, constvar.DBHAEventDetectSSH, content) case constvar.AUTHCheckFailed: content := "agent detect failed by auth check, err:" + errInfo - monitor.MonitorSendDetect(ins, constvar.DBHA_EVENT_DETECT_AUTH, content) + monitor.MonitorSendDetect(ins, constvar.DBHAEventDetectAuth, content) case constvar.DBCheckFailed: content := "agent detect failed by db check, err" + errInfo - monitor.MonitorSendDetect(ins, constvar.DBHA_EVENT_DETECT_DB, content) + monitor.MonitorSendDetect(ins, constvar.DBHAEventDetectDB, content) default: break } diff --git a/dbm-services/common/dbha/ha-module/client/client.go b/dbm-services/common/dbha/ha-module/client/client.go index 3ef2f33d00..997a5d4038 100644 --- a/dbm-services/common/dbha/ha-module/client/client.go +++ b/dbm-services/common/dbha/ha-module/client/client.go @@ -318,7 +318,7 @@ func (c *Client) SpliceUrl(name string, param string) string { } // NewAPIClient create new api http client -func NewAPIClient(c *config.APIConfig, apiType string, cloudId int) (Client, error) { +func NewAPIClient(c *config.APIConfig, apiType string, cloudId int) Client { cli := Client{ Type: apiType, CloudId: cloudId, @@ -335,5 +335,5 @@ func NewAPIClient(c *config.APIConfig, apiType string, cloudId int) (Client, err fmt.Sprintf("http://%s:%d", c.Host, c.Port), }) - return cli, nil + return cli } diff --git a/dbm-services/common/dbha/ha-module/client/cmdb.go b/dbm-services/common/dbha/ha-module/client/cmdb.go index 120c4c02cb..0427a36961 100644 --- a/dbm-services/common/dbha/ha-module/client/cmdb.go +++ b/dbm-services/common/dbha/ha-module/client/cmdb.go @@ -17,19 +17,88 @@ type CmDBClient struct { Client } +// DBInstanceInfoRequest fetch instances list from cmdb by ip +type DBInstanceInfoRequest struct { + DBCloudToken string `json:"db_cloud_token"` + BKCloudID int `json:"bk_cloud_id"` + Addresses []string `json:"addresses"` +} + +// DBInstanceInfoByCityRequest fetch instances list from cmdb by city and status +type DBInstanceInfoByCityRequest struct { + DBCloudToken string `json:"db_cloud_token"` + BKCloudID int `json:"bk_cloud_id"` + LogicalCityIDs []int `json:"logical_city_ids"` + Statuses []string `json:"statuses"` +} + +// DBInstanceInfo instance info +type DBInstanceInfo struct { + IP string `json:"ip"` + Port int `json:"port"` +} + +// SwapMySQLRolePayload mysql instance need to swap role +type SwapMySQLRolePayload struct { + Instance1 DBInstanceInfo `json:"instance1"` + Instance2 DBInstanceInfo `json:"instance2"` +} + +// SwapMySQLRoleRequest swap mysql instance's role in cmdb +type SwapMySQLRoleRequest struct { + DBCloudToken string `json:"db_cloud_token"` + BKCloudID int `json:"bk_cloud_id"` + Payloads []SwapMySQLRolePayload `json:"payloads"` +} + +// SwapRedisRolePayload redis instance need to swap role +type SwapRedisRolePayload struct { + Master DBInstanceInfo `json:"master"` + Slave DBInstanceInfo `json:"slave"` + Domain string `json:"domain"` +} + +// SwapRedisRoleRequest swap redis instance's role in cmdb +type SwapRedisRoleRequest struct { + DBCloudToken string `json:"db_cloud_token"` + BKCloudID int `json:"bk_cloud_id"` + Payloads []SwapRedisRolePayload `json:"payloads"` +} + +// UpdateInstanceStatusPayload update instance status +type UpdateInstanceStatusPayload struct { + IP string `json:"ip"` + Port int `json:"port"` + Status string `json:"status"` +} + +// UpdateInstanceStatusRequest update instance status request +type UpdateInstanceStatusRequest struct { + DBCloudToken string `json:"db_cloud_token"` + BKCloudID int `json:"bk_cloud_id"` + Payloads []UpdateInstanceStatusPayload `json:"payloads"` +} + +// GetClusterDetailByDomainRequest get cluster entry info by domain-name +type GetClusterDetailByDomainRequest struct { + DBCloudToken string `json:"db_cloud_token"` + BKCloudID int `json:"bk_cloud_id"` + Domains []string `json:"domains"` +} + // NewCmDBClient init an new cmdb client to request -func NewCmDBClient(conf *config.APIConfig, cloudId int) (*CmDBClient, error) { - c, err := NewAPIClient(conf, constvar.CmDBName, cloudId) - return &CmDBClient{c}, err +func NewCmDBClient(conf *config.APIConfig, cloudId int) *CmDBClient { + c := NewAPIClient(conf, constvar.CmDBName, cloudId) + return &CmDBClient{c} } // GetDBInstanceInfoByIp fetch instance info from cmdb by ip func (c *CmDBClient) GetDBInstanceInfoByIp(ip string) ([]interface{}, error) { var res []interface{} - req := map[string]interface{}{ - "db_cloud_token": c.Conf.BKConf.BkToken, - "bk_cloud_id": c.CloudId, - "addresses": []string{ip}, + req := DBInstanceInfoRequest{ + DBCloudToken: c.Conf.BKConf.BkToken, + BKCloudID: c.CloudId, + Addresses: []string{ip}, } response, err := c.DoNew( @@ -55,11 +124,11 @@ func (c *CmDBClient) GetDBInstanceInfoByCity(area string) ([]interface{}, error) return nil, err } - req := map[string]interface{}{ - "db_cloud_token": c.Conf.BKConf.BkToken, - "bk_cloud_id": c.CloudId, - "logical_city_ids": []int{areaId}, - "statuses": []string{constvar.RUNNING, constvar.AVAILABLE}, + req := DBInstanceInfoByCityRequest{ + DBCloudToken: c.Conf.BKConf.BkToken, + BKCloudID: c.CloudId, + LogicalCityIDs: []int{areaId}, + Statuses: []string{constvar.RUNNING, constvar.AVAILABLE}, } response, err := c.DoNew( @@ -80,26 +149,49 @@ func (c *CmDBClient) GetDBInstanceInfoByCity(area string) ([]interface{}, error) return res, nil } +// GetDBInstanceInfoByCluster fetch instance info from cmdb by ip +func (c *CmDBClient) GetDBInstanceInfoByCluster(clusterName string) ([]interface{}, error) { + var res []interface{} + req := DBInstanceInfoRequest{ + DBCloudToken: c.Conf.BKConf.BkToken, + BKCloudID: c.CloudId, + Addresses: []string{clusterName}, + } + + response, err := c.DoNew( + http.MethodPost, c.SpliceUrlByPrefix(c.Conf.UrlPre, constvar.CmDBInstanceUrl, ""), req, nil) + if err != nil { + return nil, err + } + if response.Code != 0 { + return nil, fmt.Errorf("%s failed, return code:%d, msg:%s", util.AtWhere(), response.Code, response.Msg) + } + err = json.Unmarshal(response.Data, &res) + if err != nil { + return nil, err + } + return res, nil +} + // SwapMySQLRole swap mysql master and slave's cmdb info func (c *CmDBClient) SwapMySQLRole(masterIp string, masterPort int, slaveIp string, slavePort int) error { - payloads := []map[string]interface{}{ - { - "instance1": map[string]interface{}{ - "ip": masterIp, - "port": masterPort, - }, - "instance2": map[string]interface{}{ - "ip": slaveIp, - "port": slavePort, - }, + payload := SwapMySQLRolePayload{ + Instance1: DBInstanceInfo{ + IP: masterIp, + Port: masterPort, + }, + Instance2: DBInstanceInfo{ + IP: slaveIp, + Port: slavePort, }, } - req := map[string]interface{}{ - "db_cloud_token": c.Conf.BKConf.BkToken, - "bk_cloud_id": c.CloudId, - "payloads": payloads, + req := SwapMySQLRoleRequest{ + DBCloudToken: c.Conf.BKConf.BkToken, + BKCloudID: c.CloudId, + Payloads: []SwapMySQLRolePayload{payload}, } + log.Logger.Debugf("SwapMySQLRole param:%v", req) response, err := c.DoNew( @@ -116,22 +208,22 @@ func (c *CmDBClient) SwapMySQLRole(masterIp string, masterPort int, slaveIp stri // SwapRedisRole swap redis master and slave's role info func (c *CmDBClient) SwapRedisRole(domain string, masterIp string, masterPort int, slaveIp string, slavePort int) error { - payload := map[string]interface{}{ - "master": map[string]interface{}{ - "ip": masterIp, - "port": masterPort, + payload := SwapRedisRolePayload{ + Master: DBInstanceInfo{ + IP: masterIp, + Port: masterPort, }, - "slave": map[string]interface{}{ - "ip": slaveIp, - "port": slavePort, + Slave: DBInstanceInfo{ + IP: slaveIp, + Port: slavePort, }, - "domain": domain, + Domain: domain, } - req := map[string]interface{}{ - "db_cloud_token": c.Conf.BKConf.BkToken, - "bk_cloud_id": c.CloudId, - "payload": payload, + req := SwapRedisRoleRequest{ + DBCloudToken: c.Conf.BKConf.BkToken, + BKCloudID: c.CloudId, + Payloads: []SwapRedisRolePayload{payload}, } log.Logger.Debugf("SwapRedisRole param:%v", req) @@ -149,14 +241,14 @@ func (c *CmDBClient) SwapRedisRole(domain string, masterIp string, // UpdateDBStatus update instance's status func (c *CmDBClient) UpdateDBStatus(ip string, port int, status string) error { - req := map[string]interface{}{ - "db_cloud_token": c.Conf.BKConf.BkToken, - "bk_cloud_id": c.CloudId, - "payloads": []map[string]interface{}{ + req := UpdateInstanceStatusRequest{ + DBCloudToken: c.Conf.BKConf.BkToken, + BKCloudID: c.CloudId, + Payloads: []UpdateInstanceStatusPayload{ { - "ip": ip, - "port": port, - "status": status, + IP: ip, + Port: port, + Status: status, }, }, } @@ -175,14 +267,12 @@ func (c *CmDBClient) UpdateDBStatus(ip string, port int, status string) error { } // GetEntryDetail get cluster's entry(domain) info -func (c *CmDBClient) GetEntryDetail( - cluster string, -) (map[string]interface{}, error) { +func (c *CmDBClient) GetEntryDetail(cluster string) (map[string]interface{}, error) { res := make(map[string]interface{}) - req := map[string]interface{}{ - "db_cloud_token": c.Conf.BKConf.BkToken, - "bk_cloud_id": c.CloudId, - "domains": []string{cluster}, + req := GetClusterDetailByDomainRequest{ + DBCloudToken: c.Conf.BKConf.BkToken, + BKCloudID: c.CloudId, + Domains: []string{cluster}, } log.Logger.Debugf("GetEntryDetail param:%v", req) diff --git a/dbm-services/common/dbha/ha-module/client/hadb.go b/dbm-services/common/dbha/ha-module/client/hadb.go index ab466d302f..21a05bae45 100644 --- a/dbm-services/common/dbha/ha-module/client/hadb.go +++ b/dbm-services/common/dbha/ha-module/client/hadb.go @@ -18,13 +18,6 @@ type HaDBClient struct { Client } -// CommonApiResponse common api response struct -type CommonApiResponse struct { - Code int `json:"code"` - Msg string `json:"msg"` - Data json.RawMessage `json:"data"` -} - // GMInfo gm base info, use to report type GMInfo struct { Ip string `json:"ip"` @@ -50,7 +43,7 @@ type HaStatus struct { ReportInterval int `json:"report_interval,omitempty"` } -// HaStatusRequest TODO +// HaStatusRequest request ha status table type HaStatusRequest struct { DBCloudToken string `json:"db_cloud_token"` BKCloudID int `json:"bk_cloud_id"` @@ -59,7 +52,7 @@ type HaStatusRequest struct { SetArgs *HaStatus `json:"set_args,omitempty"` } -// HaStatusResponse TODO +// HaStatusResponse ha status response type HaStatusResponse struct { RowsAffected int `json:"rowsAffected"` } @@ -76,7 +69,7 @@ type DbStatus struct { LastTime *time.Time `json:"last_time,omitempty"` } -// DbStatusRequest TODO +// DbStatusRequest request db status type DbStatusRequest struct { DBCloudToken string `json:"db_cloud_token"` BKCloudID int `json:"bk_cloud_id"` @@ -85,7 +78,7 @@ type DbStatusRequest struct { SetArgs *DbStatus `json:"set_args,omitempty"` } -// DbStatusResponse TODO +// DbStatusResponse db status response type DbStatusResponse struct { RowsAffected int `json:"rowsAffected"` Uid int `json:"uid"` @@ -113,7 +106,7 @@ type SwitchQueue struct { Cluster string `json:"cluster,omitempty"` } -// SwitchQueueRequest TODO +// SwitchQueueRequest request switch queue type SwitchQueueRequest struct { DBCloudToken string `json:"db_cloud_token"` BKCloudID int `json:"bk_cloud_id"` @@ -122,7 +115,7 @@ type SwitchQueueRequest struct { SetArgs *SwitchQueue `json:"set_args,omitempty"` } -// SwitchQueueResponse TODO +// SwitchQueueResponse switch queue response type SwitchQueueResponse struct { RowsAffected int `json:"rowsAffected"` Uid uint `json:"uid"` @@ -140,7 +133,7 @@ type HaLogs struct { Comment string `json:"comment,omitempty"` } -// HaLogsRequest TODO +// HaLogsRequest request ha_logs table type HaLogsRequest struct { DBCloudToken string `json:"db_cloud_token"` BKCloudID int `json:"bk_cloud_id"` @@ -149,7 +142,7 @@ type HaLogsRequest struct { SetArgs *HaLogs `json:"set_args,omitempty"` } -// HaLogsResponse TODO +// HaLogsResponse response for ha_logs type HaLogsResponse struct { RowsAffected int `json:"rowsAffected"` } @@ -165,7 +158,7 @@ type SwitchLogs struct { Port int `json:"port,omitempty"` } -// SwitchLogRequest TODO +// SwitchLogRequest request switch log type SwitchLogRequest struct { DBCloudToken string `json:"db_cloud_token"` BKCloudID int `json:"bk_cloud_id"` @@ -174,7 +167,7 @@ type SwitchLogRequest struct { SetArgs *SwitchLogs `json:"set_args,omitempty"` } -// SwitchLogResponse TODO +// SwitchLogResponse switch log response type SwitchLogResponse struct { RowsAffected int `json:"rowsAffected"` } @@ -185,9 +178,9 @@ type AgentIp struct { } // NewHaDBClient init hadb client object -func NewHaDBClient(conf *config.APIConfig, cloudId int) (*HaDBClient, error) { - c, err := NewAPIClient(conf, constvar.HaDBName, cloudId) - return &HaDBClient{c}, err +func NewHaDBClient(conf *config.APIConfig, cloudId int) *HaDBClient { + c := NewAPIClient(conf, constvar.HaDBName, cloudId) + return &HaDBClient{c} } // AgentGetGMInfo get gm info from hadb @@ -197,12 +190,12 @@ func (c *HaDBClient) AgentGetGMInfo() ([]GMInfo, error) { BKCloudID: c.CloudId, Name: constvar.AgentGetGMInfo, QueryArgs: &HaStatus{ - Module: "gm", + Module: constvar.GM, Cloud: strconv.Itoa(c.CloudId), }, } - log.Logger.Debugf("AgentGetGMInfo param:%v", req) + log.Logger.Debugf("AgentGetGMInfo param:%#v", req) response, err := c.DoNew(http.MethodPost, c.SpliceUrlByPrefix(c.Conf.UrlPre, constvar.HaStatusUrl, ""), req, nil) @@ -247,7 +240,7 @@ func (c *HaDBClient) ReportDBStatus( }, } - log.Logger.Debugf("update instance status param:%v", updateReq) + log.Logger.Debugf("update instance detect status param:%#v", updateReq) response, err := c.DoNew(http.MethodPost, c.SpliceUrlByPrefix(c.Conf.UrlPre, constvar.DbStatusUrl, ""), updateReq, nil) @@ -367,7 +360,7 @@ func (c *HaDBClient) RegisterDBHAInfo( }, } - log.Logger.Debugf("RegisterDBHAInfo param:%v", req) + log.Logger.Debugf("RegisterDBHAInfo param:%#v", req) response, err := c.DoNew(http.MethodPost, c.SpliceUrlByPrefix(c.Conf.UrlPre, constvar.HaStatusUrl, ""), req, nil) @@ -403,7 +396,7 @@ func (c *HaDBClient) GetAliveAgentInfo(ip string, dbType string, interval int) ( }, } - log.Logger.Debugf("GetAliveAgentInfo param:%v", req) + log.Logger.Debugf("GetAliveAgentInfo param:%#v", req) response, err := c.DoNew(http.MethodPost, c.SpliceUrlByPrefix(c.Conf.UrlPre, constvar.HaStatusUrl, ""), req, nil) @@ -434,7 +427,7 @@ func (c *HaDBClient) GetAliveGMInfo(interval int) ([]GMInfo, error) { }, } - log.Logger.Debugf("GetAliveGMInfo param:%v", req) + log.Logger.Debugf("GetAliveGMInfo param:%#v", req) response, err := c.DoNew(http.MethodPost, c.SpliceUrlByPrefix(c.Conf.UrlPre, constvar.HaStatusUrl, ""), req, nil) @@ -459,7 +452,7 @@ func (c *HaDBClient) GetAliveGMInfo(interval int) ([]GMInfo, error) { } // ReporterAgentHeartbeat report agent heartbeat to ha_status table -func (c *HaDBClient) ReporterAgentHeartbeat(dbType string, interval int) error { +func (c *HaDBClient) ReporterAgentHeartbeat(detectType string, interval int, gmInfo string) error { var result HaStatusResponse currentTime := time.Now() @@ -469,15 +462,16 @@ func (c *HaDBClient) ReporterAgentHeartbeat(dbType string, interval int) error { Name: constvar.ReporterAgentHeartbeat, QueryArgs: &HaStatus{ IP: util.LocalIp, - DbType: dbType, + DbType: detectType, }, SetArgs: &HaStatus{ ReportInterval: interval, LastTime: ¤tTime, + TakeOverGm: gmInfo, }, } - log.Logger.Debugf("ReporterAgentHeartbeat param:%v", req) + log.Logger.Debugf("ReporterAgentHeartbeat param:%#v", req) response, err := c.DoNew(http.MethodPost, c.SpliceUrlByPrefix(c.Conf.UrlPre, constvar.HaStatusUrl, ""), req, nil) @@ -514,7 +508,7 @@ func (c *HaDBClient) ReporterGMHeartbeat(module string, interval int) error { }, } - log.Logger.Debugf("ReporterGMHeartbeat param:%v", req) + log.Logger.Debugf("ReporterGMHeartbeat param:%#v", req) response, err := c.DoNew(http.MethodPost, c.SpliceUrlByPrefix(c.Conf.UrlPre, constvar.HaStatusUrl, ""), req, nil) @@ -548,7 +542,7 @@ func (c *HaDBClient) QuerySingleTotal(ip string, port int, interval int) (int, e }, } - log.Logger.Debugf("QuerySingleTotal param:%v", req) + log.Logger.Debugf("QuerySingleTotal param:%#v", req) response, err := c.DoNew(http.MethodPost, c.SpliceUrlByPrefix(c.Conf.UrlPre, constvar.SwitchQueueUrl, ""), req, nil) @@ -581,7 +575,7 @@ func (c *HaDBClient) QueryIntervalTotal(interval int) (int, error) { }, } - log.Logger.Debugf("QueryIntervalTotal param:%v", req) + log.Logger.Debugf("QueryIntervalTotal param:%#v", req) response, err := c.DoNew(http.MethodPost, c.SpliceUrlByPrefix(c.Conf.UrlPre, constvar.SwitchQueueUrl, ""), req, nil) @@ -616,7 +610,7 @@ func (c *HaDBClient) QuerySingleIDC(ip string, idc string) (int, error) { }, } - log.Logger.Debugf("QuerySingleIDC param:%v", req) + log.Logger.Debugf("QuerySingleIDC param:%#v", req) response, err := c.DoNew(http.MethodPost, c.SpliceUrlByPrefix(c.Conf.UrlPre, constvar.SwitchQueueUrl, ""), req, nil) @@ -650,7 +644,7 @@ func (c *HaDBClient) UpdateTimeDelay(ip string, port int, app string) error { }, } - log.Logger.Debugf("UpadteTimeDelay param:%v", req) + log.Logger.Debugf("UpadteTimeDelay param:%#v", req) response, err := c.DoNew(http.MethodPost, c.SpliceUrl(constvar.UpdateTimeDelay, ""), req, nil) if err != nil { @@ -670,32 +664,13 @@ func (c *HaDBClient) UpdateTimeDelay(ip string, port int, app string) error { } // InsertSwitchQueue insert pre-switch instance to switch queue -func (c *HaDBClient) InsertSwitchQueue(ip string, port int, idc string, confirmCheckTime time.Time, - app string, dbType string, cluster string) (uint, error) { +func (c *HaDBClient) InsertSwitchQueue(reqInfo *SwitchQueueRequest) (uint, error) { var result SwitchQueueResponse - currentTime := time.Now() - req := SwitchQueueRequest{ - DBCloudToken: c.Conf.BKConf.BkToken, - BKCloudID: c.CloudId, - Name: constvar.InsertSwitchQueue, - SetArgs: &SwitchQueue{ - IP: ip, - Port: port, - Idc: idc, - App: app, - ConfirmCheckTime: &confirmCheckTime, - DbType: dbType, - Cloud: strconv.Itoa(c.CloudId), - Cluster: cluster, - SwitchStartTime: ¤tTime, - }, - } - - log.Logger.Debugf("InsertSwitchQueue param:%v", req) + log.Logger.Debugf("InsertSwitchQueue param:%#v", reqInfo) response, err := c.DoNew(http.MethodPost, - c.SpliceUrlByPrefix(c.Conf.UrlPre, constvar.SwitchQueueUrl, ""), req, nil) + c.SpliceUrlByPrefix(c.Conf.UrlPre, constvar.SwitchQueueUrl, ""), reqInfo, nil) if err != nil { return 0, err } @@ -723,7 +698,7 @@ func (c *HaDBClient) QuerySlaveCheckConfig(ip string, port int, app string) (int "app": app, }) - log.Logger.Debugf("QuerySlaveCheckConfig param:%v", req) + log.Logger.Debugf("QuerySlaveCheckConfig param:%#v", req) response, err := c.DoNew(http.MethodGet, c.SpliceUrl(constvar.QuerySlaveCheckConfig, req), nil, nil) if err != nil { @@ -740,35 +715,13 @@ func (c *HaDBClient) QuerySlaveCheckConfig(ip string, port int, app string) (int } // UpdateSwitchQueue TODO -func (c *HaDBClient) UpdateSwitchQueue(uid uint, ip string, port int, status string, - slaveIp string, slavePort int, confirmResult string, switchResult string, dbRole string) error { +func (c *HaDBClient) UpdateSwitchQueue(reqInfo *SwitchQueueRequest) error { var result SwitchQueueResponse - currentTime := time.Now() - req := SwitchQueueRequest{ - DBCloudToken: c.Conf.BKConf.BkToken, - BKCloudID: c.CloudId, - Name: constvar.UpdateSwitchQueue, - QueryArgs: &SwitchQueue{ - Uid: uid, - }, - SetArgs: &SwitchQueue{ - IP: ip, - Port: port, - Status: status, - ConfirmResult: confirmResult, - SwitchResult: switchResult, - DbRole: dbRole, - SlaveIP: slaveIp, - SlavePort: slavePort, - SwitchFinishedTime: ¤tTime, - }, - } - - log.Logger.Debugf("UpdateSwitchQueue param:%v", req) + log.Logger.Debugf("UpdateSwitchQueue param:%#v", reqInfo) response, err := c.DoNew(http.MethodPost, - c.SpliceUrlByPrefix(c.Conf.UrlPre, constvar.SwitchQueueUrl, ""), req, nil) + c.SpliceUrlByPrefix(c.Conf.UrlPre, constvar.SwitchQueueUrl, ""), reqInfo, nil) if err != nil { return err } @@ -801,7 +754,7 @@ func (c *HaDBClient) InsertSwitchLog(swId uint, ip string, port int, result stri }, } - log.Logger.Debugf("InsertSwitchLog param:%v", req) + log.Logger.Debugf("InsertSwitchLog param:%#v", req) response, err := c.DoNew(http.MethodPost, c.SpliceUrlByPrefix(c.Conf.UrlPre, constvar.SwitchLogUrl, ""), req, nil) diff --git a/dbm-services/common/dbha/ha-module/client/name_service.go b/dbm-services/common/dbha/ha-module/client/name_service.go index 9577359333..46fa7ffa7e 100644 --- a/dbm-services/common/dbha/ha-module/client/name_service.go +++ b/dbm-services/common/dbha/ha-module/client/name_service.go @@ -40,9 +40,9 @@ type DomainInfo struct { } // NewNameServiceClient create new PolarisClbGWClient instance -func NewNameServiceClient(conf *config.APIConfig, cloudId int) (*NameServiceClient, error) { - c, err := NewAPIClient(conf, constvar.DnsName, cloudId) - return &NameServiceClient{c}, err +func NewNameServiceClient(conf *config.APIConfig, cloudId int) *NameServiceClient { + c := NewAPIClient(conf, constvar.DnsName, cloudId) + return &NameServiceClient{c} } // GetDomainInfoByIp get domain info from dns by ip @@ -100,7 +100,7 @@ func (c *NameServiceClient) DeleteDomain(domainName string, app string, ip strin "db_cloud_token": c.Conf.BKConf.BkToken, "bk_cloud_id": c.CloudId, "app": app, - "domains": [](map[string]interface{}){ + "domains": []map[string]interface{}{ map[string]interface{}{ "domain_name": domainName, "instances": []string{ @@ -133,34 +133,13 @@ func (c *NameServiceClient) DeleteDomain(domainName string, app string, ip strin // PolarisClbGWResp the response format for polaris and clb type PolarisClbGWResp struct { - Message string `json:"message"` - Status int `json:"status"` - Ips []string `json:"ips,omitempty"` -} - -// PolarisClbBodyParseCB the http body process callback for polaris and clb api -func PolarisClbBodyParseCB(b []byte) (interface{}, error) { - result := &PolarisClbGWResp{} - err := json.Unmarshal(b, result) - if err != nil { - log.Logger.Errorf("unmarshall %s to %+v get an error:%s", string(b), *result, err.Error()) - return nil, fmt.Errorf("json unmarshal failed, err: %+v", err) - } - - // check response and data is nil - if result.Status != statusSuccess { - log.Logger.Errorf("result.Code is %d not equal to %d,message:%s", - result.Status, statusSuccess, result.Message) - return nil, fmt.Errorf("%v - %v", result.Status, result.Message) - } - return result, nil + Ips []string `json:"ips,omitempty"` } // ClbDeRegister un-register address to clb -func (c *NameServiceClient) ClbDeRegister(region string, lbid string, listenid string, addr string) error { +func (c *NameServiceClient) ClbDeRegister( + region string, lbid string, listenid string, addr string) error { req := map[string]interface{}{ - "db_cloud_token": c.Conf.BKConf.BkToken, - "bk_cloud_id": c.CloudId, "region": region, "loadbalancerid": lbid, "listenerid": listenid, @@ -168,18 +147,15 @@ func (c *NameServiceClient) ClbDeRegister(region string, lbid string, listenid s } log.Logger.Debugf("ClbDeRegister param:%v", req) - response, err := c.DoNewForCB(http.MethodPost, + response, err := c.DoNew(http.MethodPost, c.SpliceUrlByPrefix(c.Conf.UrlPre, constvar.CLBDeRegisterUrl, ""), - req, nil, PolarisClbBodyParseCB) + req, nil) if err != nil { + log.Logger.Errorf("ClbDeRegister failed,%s", err.Error()) return err } - gwRsp := response.(*PolarisClbGWResp) - if gwRsp.Status != 0 { - return fmt.Errorf("%s failed, return code:%d, msg:%s", - util.AtWhere(), gwRsp.Status, gwRsp.Message) - } + log.Logger.Debugf("ClbDeRegister:%v", response) return nil } @@ -188,29 +164,28 @@ func (c *NameServiceClient) ClbGetTargets( region string, lbid string, listenid string, ) ([]string, error) { req := map[string]interface{}{ - "db_cloud_token": c.Conf.BKConf.BkToken, - "bk_cloud_id": c.CloudId, "region": region, "loadbalancerid": lbid, "listenerid": listenid, } log.Logger.Debugf("ClbDeRegister param:%v", req) - response, err := c.DoNewForCB(http.MethodPost, + response, err := c.DoNew(http.MethodPost, c.SpliceUrlByPrefix(c.Conf.UrlPre, constvar.CLBGetTargetsUrl, ""), - req, nil, PolarisClbBodyParseCB) + req, nil) if err != nil { + log.Logger.Errorf("ClbGetTargets failed,%s", err.Error()) return nil, err } - gwRsp := response.(*PolarisClbGWResp) - if gwRsp.Status != 0 { - gwErr := fmt.Errorf("%s failed, return code:%d, msg:%s", - util.AtWhere(), gwRsp.Status, gwRsp.Message) - return nil, gwErr + log.Logger.Debugf("ClbGet Response:%v", response) + var gwResp PolarisClbGWResp + err = json.Unmarshal(response.Data, &gwResp) + if err != nil { + log.Logger.Errorf("ClbGetTargets failed,%s", err.Error()) + return make([]string, 0), err } - - return gwRsp.Ips, nil + return gwResp.Ips, nil } // GetPolarisTargets get target address from polaris @@ -222,25 +197,26 @@ func (c *NameServiceClient) GetPolarisTargets(servicename string) ([]string, err } log.Logger.Debugf("GetPolarisTargets param:%v", req) - response, err := c.DoNewForCB(http.MethodPost, + response, err := c.DoNew(http.MethodPost, c.SpliceUrlByPrefix(c.Conf.UrlPre, constvar.PolarisTargetsUrl, ""), - req, nil, PolarisClbBodyParseCB) + req, nil) if err != nil { return nil, err } - gwRsp := response.(*PolarisClbGWResp) - if gwRsp.Status != 0 { - gwErr := fmt.Errorf("%s failed, return code:%d, msg:%s", - util.AtWhere(), gwRsp.Status, gwRsp.Message) - return nil, gwErr + var gwResp PolarisClbGWResp + err = json.Unmarshal(response.Data, &gwResp) + if err != nil { + log.Logger.Errorf("ClbGetTargets failed,%s", err.Error()) + return make([]string, 0), err } - return gwRsp.Ips, nil + return gwResp.Ips, nil } // PolarisUnBindTarget unbind address from polaris -func (c *NameServiceClient) PolarisUnBindTarget(servicename string, servertoken string, addr string) error { +func (c *NameServiceClient) PolarisUnBindTarget( + servicename string, servertoken string, addr string) error { req := map[string]interface{}{ "db_cloud_token": c.Conf.BKConf.BkToken, "bk_cloud_id": c.CloudId, @@ -250,18 +226,13 @@ func (c *NameServiceClient) PolarisUnBindTarget(servicename string, servertoken } log.Logger.Debugf("PolarisUnBindTarget param:%v", req) - response, err := c.DoNewForCB(http.MethodPost, + response, err := c.DoNew(http.MethodPost, c.SpliceUrlByPrefix(c.Conf.UrlPre, constvar.PolarisUnBindUrl, ""), - req, nil, PolarisClbBodyParseCB) + req, nil) if err != nil { + log.Logger.Errorf("PolarisUnBindTarget failed,%s", err.Error()) return err } - - gwRsp := response.(*PolarisClbGWResp) - if gwRsp.Status != 0 { - return fmt.Errorf("%s failed, return code:%d, msg:%s", - util.AtWhere(), gwRsp.Status, gwRsp.Message) - } - + log.Logger.Debugf("PolarisUnBindTarget response:%v", response) return nil } diff --git a/dbm-services/common/dbha/ha-module/client/remote_config.go b/dbm-services/common/dbha/ha-module/client/remote_config.go index 92c3caffb3..be8878fe83 100644 --- a/dbm-services/common/dbha/ha-module/client/remote_config.go +++ b/dbm-services/common/dbha/ha-module/client/remote_config.go @@ -17,9 +17,9 @@ type RemoteConfigClient struct { } // NewRemoteConfigClient create new RemoteConfigClient instance -func NewRemoteConfigClient(conf *config.APIConfig, cloudId int) (*RemoteConfigClient, error) { - c, err := NewAPIClient(conf, constvar.DBConfigName, cloudId) - return &RemoteConfigClient{c}, err +func NewRemoteConfigClient(conf *config.APIConfig, cloudId int) *RemoteConfigClient { + c := NewAPIClient(conf, constvar.DBConfigName, cloudId) + return &RemoteConfigClient{c} } // BatchGetConfigItem the batch api for get configure item diff --git a/dbm-services/common/dbha/ha-module/config/config.go b/dbm-services/common/dbha/ha-module/config/config.go index 3eada1ac23..5152748483 100644 --- a/dbm-services/common/dbha/ha-module/config/config.go +++ b/dbm-services/common/dbha/ha-module/config/config.go @@ -53,7 +53,7 @@ type AgentConfig struct { // instance campus for detect Campus string `yaml:"campus"` // cloud id for agent - Cloud string `yaml:"cloud"` + Cloud string `yaml:"cloud" validate:"required"` // fetch cmdb instance's interval(second) FetchInterval int `yaml:"fetch_interval"` ReportInterval int `yaml:"reporter_interval"` @@ -110,6 +110,8 @@ type DBConfig struct { MySQL MySQLConfig `yaml:"mysql"` // Redis instance detect info Redis RedisConfig `yaml:"redis"` + // Riak instance detect info + Riak RiakConfig `yaml:"riak"` } // MySQLConfig mysql instance connect info @@ -126,6 +128,11 @@ type RedisConfig struct { Timeout int `yaml:"timeout"` } +// RiakConfig riak detect configure +type RiakConfig struct { + Timeout int `yaml:"timeout"` +} + // SSHConfig ssh detect configure type SSHConfig struct { Port int `yaml:"port"` diff --git a/dbm-services/common/dbha/ha-module/constvar/constant.go b/dbm-services/common/dbha/ha-module/constvar/constant.go index de1f8411e9..99baf97cc5 100644 --- a/dbm-services/common/dbha/ha-module/constvar/constant.go +++ b/dbm-services/common/dbha/ha-module/constvar/constant.go @@ -1,68 +1,103 @@ package constvar const ( - // Agent TODO + // Agent component name Agent = "agent" - // GM TODO + // GM component name GM = "gm" - // GCM TODO + // GCM component name GCM = "gcm" - // GMM TODO + // GMM component name GMM = "gmm" - // GQA TODO + // GQA component name GQA = "gqa" - // GDM TODO + // GDM component name GDM = "gdm" ) +// cluster type in cmdb const ( - // MySQLClusterType TODO - MySQLClusterType = "tendbha" - // MySQLMetaType TODO - MySQLMetaType = "backend" - // MySQLProxyMetaType TODO - MySQLProxyMetaType = "proxy" - // MySQL TODO - MySQL = "tendbha:backend" - // MySQLProxy TODO - MySQLProxy = "tendbha:proxy" - // MySQLMaster TODO - MySQLMaster = "backend_master" - // MySQLSlave TODO - MySQLSlave = "backend_slave" - // MySQLRepeater TODO - MySQLRepeater = "backend_repeater" - // RedisClusterType TODO - RedisClusterType = "TwemproxyRedisInstance" - // TendisplusClusterType TODO - TendisplusClusterType = "PredixyTendisplusCluster" - // RedisMetaType TODO + // TenDBHA cluster with proxy, mysql component + TenDBHA = "tendbha" + // TenDBCluster cluster with spider, mysql component + TenDBCluster = "tendbcluster" + // RedisCluster cluster with twemproxy component + RedisCluster = "TwemproxyRedisInstance" + // TendisplusCluster cluster with predixy component + TendisplusCluster = "PredixyTendisplusCluster" +) + +// instance type name in cmdb +const ( + //TenDBStorageType storage type name in tendbha + TenDBStorageType = "backend" + //TenDBProxyType proxy type name in tendbha + TenDBProxyType = "proxy" + + //TenDBClusterStorageType storage type name in tendbcluster + TenDBClusterStorageType = "remote" + //TenDBClusterProxyType proxy type name in tendbcluster + TenDBClusterProxyType = "spider" + + // RedisMetaType storage layer type name in rediscluster RedisMetaType = "tendiscache" - // PredixyMetaType TODO - PredixyMetaType = "predixy" - // TwemproxyMetaType TODO + // TwemproxyMetaType proxy layer type name RedisCluster TwemproxyMetaType = "twemproxy" - // TendisplusMetaType TODO + + // PredixyMetaType proxy layer type name in TendisplusCluster + PredixyMetaType = "predixy" + // TendisplusMetaType storage layer type name in TendisplusCluster TendisplusMetaType = "tendisplus" - // TendisCache TODO - TendisCache = "Rediscache" - // Twemproxy TODO - Twemproxy = "Twemproxy" - // Predixy TODO +) + +// instance role in cmdb +const ( + // TenDBStorageMaster tendbha backend master role + TenDBStorageMaster = "backend_master" + // TenDBStorageSlave tendbha backend slave role + TenDBStorageSlave = "backend_slave" + // TenDBStorageRepeater tendbha backend repeater role + TenDBStorageRepeater = "backend_repeater" + + // TenDBClusterStorageMaster tendbcluster remote master role + TenDBClusterStorageMaster = "remote_master" + // TenDBClusterStorageSlave tendbcluster remoteslave role + TenDBClusterStorageSlave = "remote_slave" + // TenDBClusterProxyMaster tendbcluster remote master role + TenDBClusterProxyMaster = "spider_master" + // TenDBClusterProxySlave tendbcluster remoteslave role + TenDBClusterProxySlave = "spider_slave" +) + +// detect type in config.yaml +const ( + // DetectTenDBHA detect TenDBHA + DetectTenDBHA = "tendbha" + // DetectTenDBCluster detect TenDBCluster + DetectTenDBCluster = "tendbcluster" + + // DetectRedis detect tendiscache and twemproxy + DetectRedis = "TwemproxyRedisInstance" + // DetectTendisplus detect tendisplus and predixy + DetectTendisplus = "PredixyTendisplusCluster" + + //Predixy if specified, agent detect would detect TendisplusCluster's proxy layer Predixy = "Predixy" - // Tendisplus TODO + //Tendisplus if specified, agent detect would detect TendisplusCluster's storage layer Tendisplus = "Tendisplus" + // Riak TODO + Riak = "riak" ) +// wrapper name in TenDBCluster +// Anytime compare should ignore case const ( - // AutoSwitch TODO - AutoSwitch = "AutoSwitch" - // HandSwitch TODO - HandSwitch = "HandSwitch" - // NoSwitch TODO - NoSwitch = "NoSwitch" + WrapperSpiderMaster = "SPIDER" + WrapperSpiderSlave = "SPIDER_SLAVE" + WrapperTdbctl = "TDBCTL" + WrapperMySQLMaster = "mysql" + WrapperMySQLSlave = "mysql_slave" ) - const ( // DBCheckSuccess TODO DBCheckSuccess = "DB_check_success" @@ -151,19 +186,26 @@ const ( // CmDBEntryDetailUrl TODO CmDBEntryDetailUrl = "dbmeta/dbha/entry_detail/" // CLBDeRegisterUrl TODO - CLBDeRegisterUrl = "clb_deregister_part_target/" + CLBDeRegisterUrl = "deregister_part_target/" // CLBGetTargetsUrl TODO - CLBGetTargetsUrl = "clb_get_target_private_ips/" + CLBGetTargetsUrl = "get_target_private_ips/" // PolarisTargetsUrl TODO - PolarisTargetsUrl = "polaris_describe_targets/" + PolarisTargetsUrl = "describe_targets/" // PolarisUnBindUrl TODO - PolarisUnBindUrl = "polaris_unbind_part_targets/" + PolarisUnBindUrl = "unbind_part_targets/" // BKConfigBatchUrl TODO BKConfigBatchUrl = "bkconfig/v1/confitem/batchget/" // BKConfigQueryUrl TODO BKConfigQueryUrl = "bkconfig/v1/confitem/query/" ) +// name service's type +const ( + EntryDns = "dns" + EntryPolaris = "polaris" + EntryClb = "clb" +) + const ( // CmDBName TODO CmDBName = "cmdb" @@ -205,80 +247,84 @@ const ( ) const ( - // LOG_DEBUG TODO - LOG_DEBUG = "LOG_DEBUG" - // LOG_INFO TODO - LOG_INFO = "LOG_INFO" - // LOG_WARN TODO - LOG_WARN = "LOG_WARN" - // LOG_ERROR TODO - LOG_ERROR = "LOG_ERROR" - // LOG_PANIC TODO - LOG_PANIC = "LOG_PANIC" - // LOG_FATAL TODO - LOG_FATAL = "LOG_FATAL" - - // LOG_DEF_PATH TODO - LOG_DEF_PATH = "./dbha.log" - // LOG_DEF_BACKUPS TODO - LOG_DEF_BACKUPS = 5 - // LOG_DEF_AGE TODO - LOG_DEF_AGE = 30 - // LOG_DEF_SIZE TODO - LOG_DEF_SIZE = 1024 - // LOG_MIN_SIZE TODO - LOG_MIN_SIZE = 1 + // LogDebug TODO + LogDebug = "LOG_DEBUG" + // LogInfo TODO + LogInfo = "LOG_INFO" + // LogWarn TODO + LogWarn = "LOG_WARN" + // LogError TODO + LogError = "LOG_ERROR" + // LogPanic TODO + LogPanic = "LOG_PANIC" + // LogFatal TODO + LogFatal = "LOG_FATAL" + + // LogDefPath TODO + LogDefPath = "./dbha.log" + // LogDefBackups TODO + LogDefBackups = 5 + // LogDefAge TODO + LogDefAge = 30 + // LogDefSize TODO + LogDefSize = 1024 + // LogMinSize TODO + LogMinSize = 1 ) const ( - // REDIS_MAX_DIE_TIME TODO - REDIS_MAX_DIE_TIME = 600 - // REDIS_DEF_AUTH TODO - REDIS_DEF_AUTH = "tendis+test" + // RedisMaxDieTime TODO + RedisMaxDieTime = 600 + // RedisDefAuth TODO + RedisDefAuth = "tendis+test" ) const ( - // REDIS_PASSWORD_LACK TODO - REDIS_PASSWORD_LACK = "NOAUTH Authentication required" - // REDIS_PASSWORD_INVALID TODO - REDIS_PASSWORD_INVALID = "invalid password" - // PREDIXY_PASSWORD_LACK TODO - PREDIXY_PASSWORD_LACK = "auth permission deny" - - // SSH_PASSWORD_LACK_OR_INVALID TODO - SSH_PASSWORD_LACK_OR_INVALID = "unable to authenticate" + // RedisPasswordLack TODO + RedisPasswordLack = "NOAUTH Authentication required" + // RedisPasswordInvalid TODO + RedisPasswordInvalid = "invalid password" + // PredixyPasswordLack TODO + PredixyPasswordLack = "auth permission deny" + + // SSHPasswordLackORInvalid TODO + SSHPasswordLackORInvalid = "unable to authenticate" ) const ( - // DBHA_EVENT_NAME TODO - DBHA_EVENT_NAME = "dbha_event" - // DBHA_EVENT_REDIS_SWITCH_SUCC TODO - DBHA_EVENT_REDIS_SWITCH_SUCC = "dbha_redis_switch_succ" - // DBHA_EVENT_REDIS_SWITCH_ERR TODO - DBHA_EVENT_REDIS_SWITCH_ERR = "dbha_redis_switch_err" - // DBHA_EVENT_MYSQL_SWITCH_SUCC TODO - DBHA_EVENT_MYSQL_SWITCH_SUCC = "dbha_mysql_switch_ok" - // DBHA_EVENT_MYSQL_SWITCH_ERR TODO - DBHA_EVENT_MYSQL_SWITCH_ERR = "dbha_mysql_switch_err" - // DBHA_EVENT_DETECT_AUTH TODO - DBHA_EVENT_DETECT_AUTH = "dbha_detect_auth_fail" - // DBHA_EVENT_DETECT_SSH TODO - DBHA_EVENT_DETECT_SSH = "dbha_detect_ssh_fail" - // DBHA_EVENT_DETECT_DB TODO - DBHA_EVENT_DETECT_DB = "dbha_detect_db_fail" - // DBHA_EVENT_DOUBLE_CHECK_SSH TODO - DBHA_EVENT_DOUBLE_CHECK_SSH = "dbha_doublecheck_ssh_fail" - // DBHA_EVENT_DOUBLE_CHECK_AUTH TODO - DBHA_EVENT_DOUBLE_CHECK_AUTH = "dbha_doublecheck_auth_fail" - // DBHA_EVENT_SYSTEM TODO - DBHA_EVENT_SYSTEM = "dbha_system" - - // MONITOR_INFO_SWITCH TODO - MONITOR_INFO_SWITCH = 0 - // MONITOR_INFO_DETECT TODO - MONITOR_INFO_DETECT = 1 - // MONITOR_INFO_SYSTEM TODO - MONITOR_INFO_SYSTEM = 2 + // DBHAEventName TODO + DBHAEventName = "dbha_event" + // DBHAEventRedisSwitchSucc TODO + DBHAEventRedisSwitchSucc = "dbha_redis_switch_succ" + // DBHAEventRedisSwitchErr TODO + DBHAEventRedisSwitchErr = "dbha_redis_switch_err" + // DBHAEventMysqlSwitchSucc TODO + DBHAEventMysqlSwitchSucc = "dbha_mysql_switch_ok" + // DBHAEventMysqlSwitchErr TODO + DBHAEventMysqlSwitchErr = "dbha_mysql_switch_err" + // DBHAEventMysqlSwitchSucc TODO + DBHAEventRiakSwitchSucc = "dbha_riak_switch_ok" + // DBHAEventMysqlSwitchErr TODO + DBHAEventRiakSwitchErr = "dbha_riak_switch_err" + // DBHAEventDetectAuth TODO + DBHAEventDetectAuth = "dbha_detect_auth_fail" + // DBHAEventDetectSSH TODO + DBHAEventDetectSSH = "dbha_detect_ssh_fail" + // DBHAEventDetectDB TODO + DBHAEventDetectDB = "dbha_detect_db_fail" + // DBHAEventDoubleCheckSSH TODO + DBHAEventDoubleCheckSSH = "dbha_doublecheck_ssh_fail" + // DBHAEventDoubleCheckAuth TODO + DBHAEventDoubleCheckAuth = "dbha_doublecheck_auth_fail" + // DBHAEventSystem TODO + DBHAEventSystem = "dbha_system" + + // MonitorInfoSwitch TODO + MonitorInfoSwitch = 0 + // MonitorInfoDetect TODO + MonitorInfoDetect = 1 + // MonitorInfoSystem TODO + MonitorInfoSystem = 2 // MonitorReportType TODO MonitorReportType = "agent" @@ -287,25 +333,38 @@ const ( ) // status in switch_logs(result field) -// NB: Any adjustments need to be notified to the front-end development +// NB: Any adjustments need to be notified to the front-end developer +const ( + InfoResult = "info" + FailResult = "failed" + SuccessResult = "success" +) + +// status in tb_mon_switch_queue(status field) const ( - CHECK_SWITCH_INFO = "info" - CHECK_SWITCH_FAIL = "failed" - SWITCH_INFO = "info" - SWITCH_SUCC = "success" - SWITCH_FAIL = "failed" - UPDATEMETA_INFO = "info" - UPDATEMETA_FAIL = "failed" - - SWITCH_INFO_DOUBLECHECK = "info" - SWITCH_INFO_SLAVE_IP = "slave_ip" - SWITCH_INFO_SLAVE_PORT = "slave_port" + SwitchStart = "doing" + SwitchFailed = "failed" + SwitchSuccess = "success" +) + +// gcm use blow switch key to set/get switch instance info +// more detail refer to DataBaseSwitch.SetInfo/DataBaseSwitch.GetInfo +const ( + // DoubleCheckInfoKey gqa use to set double check info(gmm generated) + DoubleCheckInfoKey = "dc_info" + // DoubleCheckTimeKey gqa use to set double check time(gmm generated) + DoubleCheckTimeKey = "dc_time" + // SlaveIpKey use to set slave ip + SlaveIpKey = "slave_ip" + // SlavePortKey use to set slave port + SlavePortKey = "slave_port" ) // checksum sql const ( // CheckSumSql checksum number - CheckSumSql = "select count(distinct `db`, tbl) from infodba_schema.checksum where ts > date_sub(now(), interval 7 day)" + CheckSumSql = "select count(distinct `db`, tbl) from infodba_schema.checksum where ts > date_sub(now(), " + + "interval 7 day)" // CheckSumFailSql inconsistent checksum number CheckSumFailSql = "select count(distinct `db`, tbl,chunk) from infodba_schema.checksum where " + "(this_crc <> master_crc or this_cnt <> master_cnt) and ts > date_sub(now(), interval 7 day)" @@ -316,6 +375,15 @@ const ( // timezone const ( - TZ_UTC = "UTC" - TZ_CST = "CST" + TZUTC = "UTC" + TZCST = "CST" +) + +const ( + // DefaultDatabase default database info mysql instance + DefaultDatabase = "infodba_schema" +) + +const ( + RiakHttpPort = 8098 ) diff --git a/dbm-services/common/dbha/ha-module/constvar/constvar.go b/dbm-services/common/dbha/ha-module/constvar/constvar.go deleted file mode 100644 index 47405fe6eb..0000000000 --- a/dbm-services/common/dbha/ha-module/constvar/constvar.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package constvar TODO -package constvar diff --git a/dbm-services/common/dbha/ha-module/dbha.go b/dbm-services/common/dbha/ha-module/dbha.go index 63b5689a66..623a06dfdf 100644 --- a/dbm-services/common/dbha/ha-module/dbha.go +++ b/dbm-services/common/dbha/ha-module/dbha.go @@ -65,28 +65,23 @@ func main() { switch dbhaType { case constvar.Agent: // new agent for each db type - for _, dbType := range conf.AgentConf.ActiveDBType { - go func(dbType string) { - Agent, err := agent.NewMonitorAgent(conf, dbType) + for _, clusterType := range conf.AgentConf.ActiveDBType { + go func(clusterType string) { + Agent, err := agent.NewMonitorAgent(conf, clusterType) if err != nil { - log.Logger.Fatalf("agent init failed. dbtype:%s err:%s", dbType, err.Error()) + log.Logger.Fatalf("agent init failed. clustertype:%s err:%s", clusterType, err.Error()) } err = Agent.Run() if err != nil { - log.Logger.Fatalf("agent run failed. dbtype:%s err:%s", dbType, err.Error()) + log.Logger.Fatalf("agent run failed. clustertype:%s err:%s", clusterType, err.Error()) } - }(dbType) + }(clusterType) } var c chan struct{} <-c case constvar.GM: - GM, err := gm.NewGM(conf) - if err != nil { - log.Logger.Fatalf("GM init failed. err:%s", err.Error()) - os.Exit(1) - } - + GM := gm.NewGM(conf) if err = GM.Run(); err != nil { log.Logger.Fatalf("GM run failed. err:%s", err.Error()) os.Exit(1) diff --git a/dbm-services/common/dbha/ha-module/dbmodule/dbmysql/MySQLBackend_switch.go b/dbm-services/common/dbha/ha-module/dbmodule/dbmysql/MySQLBackend_switch.go new file mode 100644 index 0000000000..082d44e96e --- /dev/null +++ b/dbm-services/common/dbha/ha-module/dbmodule/dbmysql/MySQLBackend_switch.go @@ -0,0 +1,156 @@ +package dbmysql + +import ( + "dbm-services/common/dbha/ha-module/client" + "dbm-services/common/dbha/ha-module/constvar" + "dbm-services/common/dbha/ha-module/dbutil" + "dbm-services/common/dbha/ha-module/log" + "fmt" + "strconv" +) + +// MySQLSwitch defined mysql switch struct +type MySQLSwitch struct { + MySQLCommonSwitch + //proxy layer instance used(spider, proxy) + AdminPort int + //storage layer instance used + Proxy []dbutil.ProxyInfo +} + +// ShowSwitchInstanceInfo show mysql instance's switch info +func (ins *MySQLSwitch) ShowSwitchInstanceInfo() string { + str := fmt.Sprintf("<%s#%d IDC:%s Role:%s Status:%s Bzid:%s ClusterType:%s MachineType:%s>", + ins.Ip, ins.Port, ins.IDC, ins.Role, ins.Status, ins.App, ins.ClusterType, + ins.MetaType) + //TODO right way to check empty? + if ins.StandBySlave != (dbutil.SlaveInfo{}) { + str = fmt.Sprintf("%s Switch from MASTER:<%s#%d> to SLAVE:<%s#%d>", + str, ins.Ip, ins.Port, ins.StandBySlave.Ip, ins.StandBySlave.Port) + } + return str +} + +// CheckSwitch check slave before switch +func (ins *MySQLSwitch) CheckSwitch() (bool, error) { + var err error + if ins.Role == constvar.TenDBStorageSlave { + ins.ReportLogs(constvar.InfoResult, "instance is slave, needn't check") + return false, nil + } else if ins.Role == constvar.TenDBStorageRepeater { + ins.ReportLogs(constvar.FailResult, "instance is repeater, dbha not support") + return false, err + } else if ins.Role == constvar.TenDBStorageMaster { + log.Logger.Infof("info:{%s} is master", ins.ShowSwitchInstanceInfo()) + + log.Logger.Infof("check slave status. info{%s}", ins.ShowSwitchInstanceInfo()) + if ins.StandBySlave == (dbutil.SlaveInfo{}) { + ins.ReportLogs(constvar.FailResult, "no standby slave info found") + return false, err + } + if ins.StandBySlave.Status == constvar.UNAVAILABLE { + ins.ReportLogs(constvar.FailResult, "standby slave's status is unavailable") + return false, err + } + + ins.SetInfo(constvar.SlaveIpKey, ins.StandBySlave.Ip) + ins.SetInfo(constvar.SlavePortKey, ins.StandBySlave.Port) + err = ins.CheckSlaveStatus() + if err != nil { + ins.ReportLogs(constvar.FailResult, err.Error()) + return false, err + } + + log.Logger.Infof("start to switch. info{%s}", ins.ShowSwitchInstanceInfo()) + + if len(ins.Proxy) == 0 { + // available instance usual without proxy + log.Logger.Infof("without proxy! info:{%s}", ins.ShowSwitchInstanceInfo()) + ins.ReportLogs(constvar.InfoResult, "without proxy!") + return false, nil + } + } else { + err = fmt.Errorf("info:{%s} unknown role", ins.ShowSwitchInstanceInfo()) + log.Logger.Error(err) + ins.ReportLogs(constvar.FailResult, "instance unknown role") + return false, err + } + + ins.ReportLogs(constvar.InfoResult, "db-mysql check switch ok") + return true, nil +} + +// DoSwitch do switch from master to slave +// 1. refresh all proxy's backend to 1.1.1.1 +// 2. reset slave +// 3. get slave's consistent binlog pos +// 4. refresh backend to alive(slave) mysql +func (ins *MySQLSwitch) DoSwitch() error { + successFlag := true + proxyUser := ins.Config.DBConf.MySQL.ProxyUser + proxyPass := ins.Config.DBConf.MySQL.ProxyPass + ins.ReportLogs(constvar.InfoResult, "one phase:update all proxy's backend to 1.1.1.1 first") + for _, proxyIns := range ins.Proxy { + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("try to flush proxy:[%s:%d]'s backends to 1.1.1.1", + proxyIns.Ip, proxyIns.Port)) + err := SwitchProxyBackendAddress(proxyIns.Ip, proxyIns.AdminPort, proxyUser, + proxyPass, "1.1.1.1", 3306) + if err != nil { + ins.ReportLogs(constvar.FailResult, fmt.Sprintf("flush proxy's backend failed: %s", err.Error())) + return fmt.Errorf("flush proxy's backend to 1.1.1.1 failed") + } + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("flush proxy:[%s:%d]'s backends to 1.1.1.1 success", + proxyIns.Ip, proxyIns.Port)) + } + ins.ReportLogs(constvar.InfoResult, "all proxy flush backends to 1.1.1.1 success") + + ins.ReportLogs(constvar.InfoResult, "try to reset slave") + binlogFile, binlogPosition, err := ins.ResetSlave() + if err != nil { + ins.ReportLogs(constvar.FailResult, fmt.Sprintf("reset slave failed:%s", err.Error())) + return fmt.Errorf("reset slave failed") + } + ins.StandBySlave.BinlogFile = binlogFile + ins.StandBySlave.BinlogPosition = strconv.Itoa(int(binlogPosition)) + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("reset slave success, consistent binlog info:%s,%d", + ins.StandBySlave.BinlogFile, ins.StandBySlave.BinlogPosition)) + + ins.ReportLogs(constvar.InfoResult, "two phase: update all proxy's backend to new master") + for _, proxyIns := range ins.Proxy { + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("try to flush proxy[%s:%d]'s backend to [%s:%d]", + proxyIns.Ip, proxyIns.Port, ins.StandBySlave.Ip, ins.StandBySlave.Port)) + err = SwitchProxyBackendAddress(proxyIns.Ip, proxyIns.AdminPort, proxyUser, + proxyPass, ins.StandBySlave.Ip, ins.StandBySlave.Port) + if err != nil { + ins.ReportLogs(constvar.FailResult, fmt.Sprintf("flush proxy[%s:%d]'s backend to new master failed:%s", + proxyIns.Ip, proxyIns.Port, err.Error())) + successFlag = false + } + ins.ReportLogs(constvar.InfoResult, "flush proxy's backend to new master success") + } + + if !successFlag { + return fmt.Errorf("not all proxy's backend switch to new master") + } + + ins.ReportLogs(constvar.InfoResult, "all proxy flush backends to new master success") + return nil +} + +// RollBack do switch rollback +func (ins *MySQLSwitch) RollBack() error { + return nil +} + +// UpdateMetaInfo swap master, slave 's meta info in cmdb +func (ins *MySQLSwitch) UpdateMetaInfo() error { + cmdbClient := client.NewCmDBClient(&ins.Config.DBConf.CMDB, ins.Config.GetCloudId()) + if err := cmdbClient.SwapMySQLRole(ins.Ip, ins.Port, + ins.StandBySlave.Ip, ins.StandBySlave.Port); err != nil { + updateErrLog := fmt.Sprintf("swap db-mysql role failed. err:%s", err.Error()) + ins.ReportLogs(constvar.FailResult, updateErrLog) + return err + } + ins.ReportLogs(constvar.InfoResult, "update meta info success") + return nil +} diff --git a/dbm-services/common/dbha/ha-module/dbmodule/dbmysql/MySQLProxy_switch.go b/dbm-services/common/dbha/ha-module/dbmodule/dbmysql/MySQLProxy_switch.go new file mode 100644 index 0000000000..fbc458ef05 --- /dev/null +++ b/dbm-services/common/dbha/ha-module/dbmodule/dbmysql/MySQLProxy_switch.go @@ -0,0 +1,39 @@ +package dbmysql + +import ( + "fmt" + + "dbm-services/common/dbha/ha-module/constvar" + "dbm-services/common/dbha/ha-module/dbutil" +) + +// MySQLProxySwitch define proxy switch detail info +type MySQLProxySwitch struct { + MySQLCommonSwitch + AdminPort int + Entry dbutil.BindEntry +} + +// CheckSwitch check whether proxy allowed swtich, always true at present +func (ins *MySQLProxySwitch) CheckSwitch() (bool, error) { + return true, nil +} + +// DoSwitch mysql-proxy do switch +// delete ip under the entry +func (ins *MySQLProxySwitch) DoSwitch() error { + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("try to release ip[%s] from all cluster entery", ins.Ip)) + return ins.DeleteNameService(ins.Entry) +} + +// ShowSwitchInstanceInfo display switch proxy info +func (ins *MySQLProxySwitch) ShowSwitchInstanceInfo() string { + str := fmt.Sprintf("<%s#%d IDC:%s Status:%s Bzid:%s ClusterType:%s MachineType:%s> switch", + ins.Ip, ins.Port, ins.IDC, ins.Status, ins.App, ins.ClusterType, ins.MetaType) + return str +} + +// RollBack proxy do rollback +func (ins *MySQLProxySwitch) RollBack() error { + return nil +} diff --git a/dbm-services/common/dbha/ha-module/dbmodule/dbmysql/MySQL_callback.go b/dbm-services/common/dbha/ha-module/dbmodule/dbmysql/MySQL_callback.go new file mode 100644 index 0000000000..b882a06b09 --- /dev/null +++ b/dbm-services/common/dbha/ha-module/dbmodule/dbmysql/MySQL_callback.go @@ -0,0 +1,194 @@ +package dbmysql + +import ( + "encoding/json" + "fmt" + "strconv" + + "dbm-services/common/dbha/ha-module/client" + "dbm-services/common/dbha/ha-module/config" + "dbm-services/common/dbha/ha-module/constvar" + "dbm-services/common/dbha/ha-module/dbutil" + "dbm-services/common/dbha/ha-module/log" +) + +// UnMarshalMySQLInstanceByCmdb convert cmdb instance info to MySQLDetectInstanceInfoFromCmDB +func UnMarshalMySQLInstanceByCmdb(instances []interface{}, + clusterType string) ([]*MySQLDetectInstanceInfoFromCmDB, error) { + var ( + ret []*MySQLDetectInstanceInfoFromCmDB + ) + cache := map[string]*MySQLDetectInstanceInfoFromCmDB{} + + for _, v := range instances { + ins := dbutil.DBInstanceInfoDetail{} + rawData, err := json.Marshal(v) + if err != nil { + return nil, fmt.Errorf("marshal instance info failed:%s", err.Error()) + } + if err = json.Unmarshal(rawData, &ins); err != nil { + return nil, fmt.Errorf("unmarshal instance info failed:%s", err.Error()) + } + if ins.ClusterType != clusterType || (ins.Status != constvar.RUNNING && ins.Status != constvar.AVAILABLE) { + continue + } + cacheIns, ok := cache[ins.IP] + //only need detect the minimum port instance + if !ok || ok && ins.Port < cacheIns.Port { + cache[ins.IP] = &MySQLDetectInstanceInfoFromCmDB{ + Ip: ins.IP, + Port: ins.Port, + App: strconv.Itoa(ins.BKBizID), + ClusterType: ins.ClusterType, + MetaType: ins.MachineType, + Cluster: ins.Cluster, + } + } + } + + for _, cacheIns := range cache { + ret = append(ret, cacheIns) + } + + return ret, nil +} + +// NewMySQLClusterByCmDB unmarshal cmdb instances to detect instance struct +// filter only TenDB cluster +func NewMySQLClusterByCmDB(instances []interface{}, conf *config.Config) ([]dbutil.DataBaseDetect, error) { + var ( + err error + unmarshalIns []*MySQLDetectInstanceInfoFromCmDB + ret []dbutil.DataBaseDetect + ) + + unmarshalIns, err = UnMarshalMySQLInstanceByCmdb(instances, constvar.DetectTenDBHA) + + if err != nil { + return nil, err + } + + for _, uIns := range unmarshalIns { + ret = append(ret, AgentNewMySQLDetectInstance(uIns, conf)) + } + + return ret, err +} + +// NewSpiderClusterByCmDB unmarshal cmdb instances to detect instance struct +// filter only TenDBCluster +func NewSpiderClusterByCmDB(instances []interface{}, conf *config.Config) ([]dbutil.DataBaseDetect, error) { + var ( + err error + unmarshalIns []*MySQLDetectInstanceInfoFromCmDB + ret []dbutil.DataBaseDetect + ) + + unmarshalIns, err = UnMarshalMySQLInstanceByCmdb(instances, constvar.DetectTenDBCluster) + + if err != nil { + return nil, err + } + + for _, uIns := range unmarshalIns { + ret = append(ret, AgentNewMySQLDetectInstance(uIns, conf)) + } + + return ret, err +} + +// NewMySQLSwitchInstance unmarshal cmdb instances to switch instance +// GQA call this and send to gcm switch +func NewMySQLSwitchInstance(instances []interface{}, conf *config.Config) ([]dbutil.DataBaseSwitch, error) { + var ret []dbutil.DataBaseSwitch + initFunc := func(ins dbutil.DBInstanceInfoDetail) (dbutil.DataBaseSwitch, error) { + mysqlCommon := MySQLCommonSwitch{ + BaseSwitch: dbutil.BaseSwitch{ + Ip: ins.IP, + Port: ins.Port, + IDC: strconv.Itoa(ins.BKIdcCityID), + Status: ins.Status, + App: strconv.Itoa(ins.BKBizID), + ClusterType: ins.ClusterType, + MetaType: ins.MachineType, + Cluster: ins.Cluster, + Config: conf, + CmDBClient: client.NewCmDBClient(&conf.DBConf.CMDB, conf.GetCloudId()), + HaDBClient: client.NewHaDBClient(&conf.DBConf.HADB, conf.GetCloudId()), + }, + } + + switch ins.MachineType { + case constvar.TenDBStorageType: + swIns := &MySQLSwitch{ + MySQLCommonSwitch: mysqlCommon, + } + swIns.SetStandbySlave(ins.Receiver) + swIns.SetInstanceRole(ins.InstanceRole) + swIns.Proxy = ins.ProxyInstanceSet + return swIns, nil + case constvar.TenDBProxyType: + swIns := &MySQLProxySwitch{ + MySQLCommonSwitch: mysqlCommon, + } + swIns.AdminPort = ins.AdminPort + swIns.Entry = ins.BindEntry + return swIns, nil + case constvar.TenDBClusterStorageType: + swIns := &SpiderStorageSwitch{ + SpiderCommonSwitch: SpiderCommonSwitch{ + MySQLCommonSwitch: mysqlCommon, + ClusterName: ins.Cluster, + }, + } + swIns.SetStandbySlave(ins.Receiver) + swIns.SetInstanceRole(ins.InstanceRole) + swIns.Proxy = ins.ProxyInstanceSet + return swIns, nil + case constvar.TenDBClusterProxyType: + swIns := &SpiderProxyLayerSwitch{ + SpiderCommonSwitch: SpiderCommonSwitch{ + MySQLCommonSwitch: mysqlCommon, + ClusterName: ins.Cluster, + }, + } + swIns.SetInstanceRole(ins.SpiderRole) + swIns.AdminPort = ins.AdminPort + swIns.Entry = ins.BindEntry + return swIns, nil + default: + return nil, fmt.Errorf("unsupport MySQL meta type:%s", ins.MachineType) + } + } + + for _, v := range instances { + ins := dbutil.DBInstanceInfoDetail{} + rawData, err := json.Marshal(v) + if err != nil { + return nil, fmt.Errorf("marshal instance info failed:%s", err.Error()) + } + if err = json.Unmarshal(rawData, &ins); err != nil { + return nil, fmt.Errorf("unmarshal instance info failed:%s", err.Error()) + } + swIns, err := initFunc(ins) + if err != nil { + return nil, err + } + ret = append(ret, swIns) + } + + return ret, nil +} + +// DeserializeMySQL gdm convert agent report info into DataBaseDetect +func DeserializeMySQL(jsonInfo []byte, conf *config.Config) (dbutil.DataBaseDetect, error) { + response := MySQLDetectResponse{} + err := json.Unmarshal(jsonInfo, &response) + if err != nil { + log.Logger.Errorf("json unmarshal failed. jsoninfo:\n%s\n, err:%s", string(jsonInfo), err.Error()) + return nil, err + } + + ret := GMNewMySQLDetectInstance(&response, conf) + return ret, nil +} diff --git a/dbm-services/common/dbha/ha-module/dbmodule/dbmysql/MySQL_common_switch.go b/dbm-services/common/dbha/ha-module/dbmodule/dbmysql/MySQL_common_switch.go new file mode 100644 index 0000000000..e284c6f8ef --- /dev/null +++ b/dbm-services/common/dbha/ha-module/dbmodule/dbmysql/MySQL_common_switch.go @@ -0,0 +1,773 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +// Package dbmysql mysql-related cluster's switch defined in this package, such as TenDBHA,TenDBCluster +package dbmysql + +import ( + "database/sql" + "encoding/json" + "fmt" + "strconv" + "strings" + "time" + + "dbm-services/common/dbha/ha-module/client" + "dbm-services/common/dbha/ha-module/constvar" + "dbm-services/common/dbha/ha-module/dbutil" + "dbm-services/common/dbha/ha-module/log" + + "gorm.io/driver/mysql" + "gorm.io/gorm" +) + +const ( + // GetPrimarySQL sql to get primary tdbctl + GetPrimarySQL = "TDBCTL GET PRIMARY" + // GetRouteSQL TendbCluster used to get all routes + GetRouteSQL = "SELECT Server_name, Host, Username, Password, Port, Wrapper FROM mysql.servers" + //FlushRouteSQL TendbCluster used to flush routes + FlushRouteSQL = "TDBCTL FLUSH ROUTING" + // ForcePrimarySQL enable primary tdbctl force + ForcePrimarySQL = "TDBCTL ENABLE PRIMARY FORCE" + // AlterNodeFormat TDBCTL alter node sql format + AlterNodeFormat = "TDBCTL ALTER NODE %s OPTIONS(HOST '%s',USER '%s',PASSWORD '%s', Port %d)" + // DropNodeFormat TDBCTL drop node sql format, valid string is server_name + DropNodeFormat = "TDBCTL DROP NODE %s" +) + +// MySQLCommonSwitch defined mysql-related switch struct +// TenDBHA, TenDBClusterHA usual include this +type MySQLCommonSwitch struct { + dbutil.BaseSwitch + //instance role type + Role string + //standby slave which master switch to + StandBySlave dbutil.SlaveInfo +} + +// MySQLCommonSwitchUtil common switch util for mysql-related instance used +type MySQLCommonSwitchUtil interface { + // CheckSlaveStatus and blow func TenDB's backend, TenDBCluster's remote used + CheckSlaveStatus() error + GetSlaveCheckSum() (int, int, error) + GetSlaveDelay() (int, int, error) + FindUsefulDatabase() (bool, error) + CheckSlaveSlow() error + ResetSlave() (string, uint64, error) + SetStandbySlave([]dbutil.SlaveInfo) + + // SetInstanceRole and blow func all meta-type instance used + SetInstanceRole(string) + UpdateMetaInfo() error +} + +// SpiderCommonSwitch defined spider-related switch struct +// TenDBCluster special specify, spider/remote usual include this +type SpiderCommonSwitch struct { + MySQLCommonSwitch + //primary tdbctl info + PrimaryTdbctl TdbctlInfo + ClusterName string + //all node's route info, must fill by any-tdbctl + RouteTable []RouteInfo +} + +// DelayInfo defined slave delay info +type DelayInfo struct { + // check whether SQL_Thread hang + SlaveDelay float64 `gorm:"column:slave_delay"` + // check whether IO_Thread hang + TimeDelay float64 `gorm:"column:time_delay"` +} + +// MySQLVariableInfo show variable's result struct +// not appropriate for string value +type MySQLVariableInfo struct { + VariableName string `gorm:"column:Variable_name"` + VariableValue uint64 `gorm:"column:Value"` +} + +// BinlogStatus binlog status info struct +type BinlogStatus struct { + MasterLogFileIndex int + RelayMasterLogFileIndex int + ReadMasterLogPos uint64 + ExecMasterLogPos uint64 + // RetrievedGtidSet string + // ExecutedGtidSet string + // MasterUuid string +} + +// SlaveStatus show slave status info struct +type SlaveStatus struct { + SlaveIoState string `gorm:"column:Slave_IO_State"` + MasterHost string `gorm:"column:Master_Host"` + MasterUser string `gorm:"column:Master_User"` + MasterPort int `gorm:"column:Master_Port"` + ConnectRetry int `gorm:"column:Connect_Retry"` + MasterLogFile string `gorm:"column:Master_Log_File"` + ReadMasterLogPos uint64 `gorm:"column:Read_Master_Log_Pos"` + RelayLogFile string `gorm:"column:Relay_Log_File"` + RelayLogPos uint64 `gorm:"column:Relay_Log_Pos"` + RelayMasterLogFile string `gorm:"column:Relay_Master_Log_File"` + SlaveIoRunning string `gorm:"column:Slave_IO_Running"` + SlaveSqlRunning string `gorm:"column:Slave_SQL_Running"` + ReplicateDoDb string `gorm:"column:Replicate_Do_DB"` + ReplicateIgnoreDb string `gorm:"column:Replicate_Ignore_DB"` + ReplicateDoTable string `gorm:"column:Replicate_Do_Table"` + ReplicateIgnoreTable string `gorm:"column:Replicate_Ignore_Table"` + ReplicateWildDoTable string `gorm:"column:Replicate_Wild_Do_Table"` + ReplicateWildIgnoreTable string `gorm:"column:Replicate_Wild_Ignore_Table"` + LastErrno int `gorm:"column:Last_Errno"` + LastError string `gorm:"column:Last_Error"` + SkipCounter int `gorm:"column:Skip_Counter"` + ExecMasterLogPos uint64 `gorm:"column:Exec_Master_Log_Pos"` + RelayLogSpace uint64 `gorm:"column:Relay_Log_Space"` + UntilCondition string `gorm:"column:Until_Condition"` + UntilLogFile string `gorm:"column:Until_Log_File"` + UntilLogPos uint64 `gorm:"column:Until_Log_Pos"` + MasterSslAllowed string `gorm:"column:Master_SSL_Allowed"` + MasterSslCaFile string `gorm:"column:Master_SSL_CA_File"` + MasterSslCaPath string `gorm:"column:Master_SSL_CA_Path"` + MasterSslCert string `gorm:"column:Master_SSL_Cert"` + MasterSslCipher string `gorm:"column:Master_SSL_Cipher"` + MasterSslKey string `gorm:"column:Master_SSL_Key"` + SecondsBehindMaster int `gorm:"column:Seconds_Behind_Master"` + MasterSslVerifyServerCert string `gorm:"column:Master_SSL_Verify_Server_Cert"` + LastIoErrno int `gorm:"column:Last_IO_Errno"` + LastIoError string `gorm:"column:Last_IO_Error"` + LastSqlErrno int `gorm:"column:Last_SQL_Errno"` + LastSqlError string `gorm:"column:Last_SQL_Error"` + ReplicateIgnoreServerIds string `gorm:"column:Replicate_Ignore_Server_Ids"` + MasterServerId uint64 `gorm:"column:Master_Server_Id"` + MasterUuid string `gorm:"column:Master_UUID"` + MasterInfoFile string `gorm:"column:Master_Info_File"` + SqlDelay uint64 `gorm:"column:SQL_Delay"` + SqlRemainingDelay string `gorm:"column:SQL_Remaining_Delay"` + SlaveSqlRunningState string `gorm:"column:Slave_SQL_Running_State"` + MasterRetryCount int `gorm:"column:Master_Retry_Count"` + MasterBind string `gorm:"column:Master_Bind"` + LastIoErrorTimestamp string `gorm:"column:Last_IO_Error_Timestamp"` + LastSqlErrorTimestamp string `gorm:"column:Last_SQL_Error_Timestamp"` + MasterSslCrl string `gorm:"column:Master_SSL_Crl"` + MasterSslCrlpath string `gorm:"column:Master_SSL_Crlpath"` + RetrievedGtidSet string `gorm:"column:Retrieved_Gtid_Set"` + ExecutedGtidSet string `gorm:"column:Executed_Gtid_Set"` + AutoPosition string `gorm:"column:Auto_Position"` + ReplicateWildParallelTable string `gorm:"column:Replicate_Wild_Parallel_Table"` +} + +// RouteInfo route in mysql.servers +type RouteInfo struct { + ServerName string `gorm:"column:Server_name"` + Host string `gorm:"column:HOST"` + UserName string `gorm:"column:Username"` + Password string `gorm:"column:Password"` + Port int `gorm:"column:PORT"` + Wrapper string `gorm:"column:Wrapper"` +} + +// TdbctlInfo for tdbctl node +type TdbctlInfo struct { + ServerName string `gorm:"column:SERVER_NAME"` + Host string `gorm:"column:HOST"` + Port int `gorm:"column:PORT"` + //if 1, indicate this server is primary + CurrentServer int `gorm:"column:IS_THIS_SERVER"` +} + +// SetStandbySlave only master instance could call this. +// Always use standbySlave.If no standby attribute slave found, use +// the first index slave +func (ins *MySQLCommonSwitch) SetStandbySlave(slaves []dbutil.SlaveInfo) { + if len(slaves) > 0 { + //try to found standby slave + for _, slave := range slaves { + if slave.IsStandBy { + ins.StandBySlave = slave + break + } + } + ins.StandBySlave = slaves[0] + log.Logger.Debugf("set standy slave success:%#v", ins.StandBySlave) + } else { + ins.StandBySlave = dbutil.SlaveInfo{} + } +} + +// SetInstanceRole set instance role type +func (ins *MySQLCommonSwitch) SetInstanceRole(role string) { + ins.Role = role +} + +// GetRole get mysql role type +func (ins *MySQLCommonSwitch) GetRole() string { + return ins.Role +} + +// CheckSlaveStatus check whether slave satisfy to switch +func (ins *MySQLCommonSwitch) CheckSlaveStatus() error { + var ( + checksumCnt, checksumFailCnt, slaveDelay, timeDelay int + ) + gmConf := ins.Config.GMConf + // check_slave_status + ins.ReportLogs(constvar.InfoResult, "try to check slave status info.") + if err := ins.CheckSlaveSlow(); err != nil { + return fmt.Errorf("check slave delay failed,err:%s", err.Error()) + } + + needCheck, err := ins.FindUsefulDatabase() + if err != nil { + log.Logger.Errorf("found user-created database failed. err:%s", err.Error()) + } + + ins.ReportLogs(constvar.InfoResult, "try to check slave checksum info.") + checksumCnt, checksumFailCnt, err = ins.GetSlaveCheckSum() + if err != nil { + log.Logger.Errorf("check slave checksum info failed. err:%s", err.Error()) + return err + } + slaveDelay, timeDelay, err = ins.GetSlaveDelay() + if err != nil { + log.Logger.Errorf("check slave checksum info failed. err:%s", err.Error()) + return err + } + + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("checksumCnt:%d, checksumFail:%d, slaveDelay:%d, timeDelay:%d", + checksumCnt, checksumFailCnt, slaveDelay, timeDelay)) + + if needCheck { + if ins.Status == constvar.AVAILABLE { + checksumCnt = 1 + checksumFailCnt = 0 + slaveDelay = 0 + timeDelay = 0 + ins.ReportLogs(constvar.InfoResult, "instance is available, skip check delay and checksum") + } + + if checksumCnt == 0 { + return fmt.Errorf("none checksum done on this db") + } + + log.Logger.Debugf("checksum have done on slave.") + + if checksumFailCnt > gmConf.GCM.AllowedChecksumMaxOffset { + return fmt.Errorf("too many fail on tables checksum(%d > %d)", checksumFailCnt, + gmConf.GCM.AllowedChecksumMaxOffset) + } + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("checksum failedCnt[%d] in allowed range[%d]", + checksumFailCnt, gmConf.GCM.AllowedChecksumMaxOffset)) + + } else { + ins.ReportLogs(constvar.InfoResult, "none user-created database, skip check checksum") + return nil + } + + if slaveDelay >= gmConf.GCM.AllowedSlaveDelayMax { + return fmt.Errorf("SQL_Thread delay on slave too large than allowed range(%d >= %d)", slaveDelay, + gmConf.GCM.AllowedSlaveDelayMax) + } + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("SQL_THread delay [%d] in allowed range[%d]", + slaveDelay, gmConf.GCM.AllowedSlaveDelayMax)) + + if timeDelay >= gmConf.GCM.AllowedTimeDelayMax { + return fmt.Errorf("IO_Thread delay on slave too large than master(%d >= %d)", timeDelay, + gmConf.GCM.AllowedTimeDelayMax) + } + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("IO_Thread delay [%d] in allowed range[%d]", + timeDelay, gmConf.GCM.AllowedTimeDelayMax)) + + return nil +} + +// GetSlaveCheckSum return value:checksumCnt, checksumFailCnt +func (ins *MySQLCommonSwitch) GetSlaveCheckSum() (int, int, error) { + var ( + checksumCnt, checksumFailCnt int + ) + ip := ins.StandBySlave.Ip + port := ins.StandBySlave.Port + connParam := fmt.Sprintf("%s:%s@(%s:%d)/%s", ins.Config.DBConf.MySQL.User, + ins.Config.DBConf.MySQL.Pass, ip, port, constvar.DefaultDatabase) + db, err := gorm.Open(mysql.Open(connParam), &gorm.Config{ + Logger: log.GormLogger, + }) + if err != nil { + log.Logger.Errorf("open mysql failed. ip:%s, port:%d, err:%s", ip, port, err.Error()) + return 0, 0, err + } + defer func() { + con, _ := db.DB() + if con == nil { + return + } + if err = con.Close(); err != nil { + log.Logger.Warnf("close connect[%s#%d] failed:%s", ip, port, err.Error()) + } + }() + + err = db.Raw(constvar.CheckSumSql).Scan(&checksumCnt).Error + if err != nil { + log.Logger.Errorf("mysql get checksumCnt failed. ip:%s, port:%d, err:%s", ip, port, err.Error()) + return 0, 0, err + } + + err = db.Raw(constvar.CheckSumFailSql).Scan(&checksumFailCnt).Error + if err != nil { + log.Logger.Errorf("mysql get checksumFailCnt failed. ip:%s, port:%d, err:%s", ip, port, err.Error()) + return 0, 0, err + } + + return checksumCnt, checksumFailCnt, nil +} + +// GetSlaveDelay return value: slaveDelay, timeDelay +func (ins *MySQLCommonSwitch) GetSlaveDelay() (int, int, error) { + var ( + delayInfo DelayInfo + ) + ip := ins.StandBySlave.Ip + port := ins.StandBySlave.Port + connParam := fmt.Sprintf("%s:%s@(%s:%d)/%s", ins.Config.DBConf.MySQL.User, + ins.Config.DBConf.MySQL.Pass, ip, port, constvar.DefaultDatabase) + db, err := gorm.Open(mysql.Open(connParam), &gorm.Config{ + Logger: log.GormLogger, + }) + if err != nil { + log.Logger.Errorf("open mysql failed. ip:%s, port:%d, err:%s", ip, port, err.Error()) + return 0, 0, err + } + defer func() { + con, _ := db.DB() + if con == nil { + return + } + if err = con.Close(); err != nil { + log.Logger.Warnf("close connect[%s#%d] failed:%s", ip, port, err.Error()) + } + }() + + slaveStatus := SlaveStatus{} + err = db.Raw("show slave status").Scan(&slaveStatus).Error + if err != nil { + log.Logger.Errorf("show slave status failed. err:%s", err.Error()) + return 0, 0, err + } + log.Logger.Debugf("slave status info:%v", slaveStatus) + + err = db.Raw(constvar.CheckDelaySql, slaveStatus.MasterServerId).Scan(&delayInfo).Error + if err != nil { + log.Logger.Errorf("mysql get delay info failed. ip:%s, port:%d, err:%s", ip, port, err.Error()) + return 0, 0, err + } + + return int(delayInfo.SlaveDelay), int(delayInfo.TimeDelay), nil +} + +// FindUsefulDatabase found user created databases exclude system database +// return val: +// +// true: found +// false: not found +func (ins *MySQLCommonSwitch) FindUsefulDatabase() (bool, error) { + var systemDbs = map[string]bool{ + "mysql": true, + "information_schema": true, + "performance_schema": true, + "test": true, + constvar.DefaultDatabase: true, + "sys": true, + } + ip := ins.StandBySlave.Ip + port := ins.StandBySlave.Port + connParam := fmt.Sprintf("%s:%s@(%s:%d)/%s", ins.Config.DBConf.MySQL.User, + ins.Config.DBConf.MySQL.Pass, ip, port, constvar.DefaultDatabase) + db, err := gorm.Open(mysql.Open(connParam), &gorm.Config{ + Logger: log.GormLogger, + }) + if err != nil { + log.Logger.Errorf("open mysql failed. ip:%s, port:%d, err:%s", ip, port, err.Error()) + return false, err + } + var databases []string + + showDatabaseSql := "show databases" + err = db.Raw(showDatabaseSql).Scan(&databases).Error + if err != nil { + log.Logger.Errorf("show databases faled. ip:%s, port:%d, err:%s", ip, port, err.Error()) + return false, err + } + + for _, database := range databases { + if _, ok := systemDbs[database]; !ok { + return true, nil + } + } + log.Logger.Infof("no user-created database found") + + return false, nil +} + +// CheckSlaveSlow check whether slave replication delay +func (ins *MySQLCommonSwitch) CheckSlaveSlow() error { + ip := ins.StandBySlave.Ip + port := ins.StandBySlave.Port + user := ins.Config.DBConf.MySQL.User + pass := ins.Config.DBConf.MySQL.Pass + allowSlowBytes := ins.Config.GMConf.GCM.ExecSlowKBytes + connParam := fmt.Sprintf("%s:%s@(%s:%d)/%s", user, pass, ip, port, "infodba_schema") + db, err := gorm.Open(mysql.Open(connParam), &gorm.Config{ + Logger: log.GormLogger, + }) + if err != nil { + log.Logger.Errorf("open mysql failed. ip:%s, port:%d, err:%s", ip, port, err.Error()) + return err + } + defer func() { + con, _ := db.DB() + if err = con.Close(); err != nil { + log.Logger.Warnf("close connect[%s#%d] failed:%s", ip, port, err.Error()) + } + }() + + var maxBinlogSize MySQLVariableInfo + err = db.Raw("show variables like 'max_binlog_size'").Scan(&maxBinlogSize).Error + if err != nil { + log.Logger.Errorf("get mas_binlog_size failed. ip:%s, port:%d, err:%s", ip, port, err.Error()) + return err + } + + binlogSizeMByte := maxBinlogSize.VariableValue / (1024 * 1024) + log.Logger.Infof("the slave max_binlog_size value is %d M!", binlogSizeMByte) + + status, err := GetSlaveStatus(db) + if err != nil { + log.Logger.Errorf("get slave status failed. err:%s", err.Error()) + return err + } + log.Logger.Infof("Relay_Master_Log_File_Index:%d, Exec_Master_Log_Pos:%d", + status.RelayMasterLogFileIndex, status.ReadMasterLogPos) + + execSlowKBytes := binlogSizeMByte*1024*uint64(status.MasterLogFileIndex-status.RelayMasterLogFileIndex) - + status.ExecMasterLogPos/1024 + status.ReadMasterLogPos/1024 + + loop := 10 + if execSlowKBytes > uint64(allowSlowBytes) { + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("slave delay kbytes[%d] large than allowed[%d],"+ + "try to loop wait", execSlowKBytes, allowSlowBytes)) + var i int + for i = 0; i < loop; i++ { + time.Sleep(3 * time.Second) + tmpStatus, err := GetSlaveStatus(db) + if err != nil { + log.Logger.Errorf("get slave status failed. err:%s", err.Error()) + return err + } + execSlowKBytes = binlogSizeMByte*1024* + uint64(tmpStatus.MasterLogFileIndex-tmpStatus.RelayMasterLogFileIndex) - + tmpStatus.ExecMasterLogPos/1024 + tmpStatus.ReadMasterLogPos/1024 + if execSlowKBytes <= uint64(allowSlowBytes) { + // todo: for GTID + break + } + log.Logger.Warnf("loop[%d],slave slower too much: Execute %dK,default value is:%d", + i, execSlowKBytes, allowSlowBytes) + } + if i == loop { + return fmt.Errorf("after loop wait, slave still slower too much: Execute %dK, default value is:%d", + execSlowKBytes, allowSlowBytes) + } + } + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("check slave[%s:%d] status success", ip, port)) + return nil +} + +// GetSlaveStatus get slave status info +func GetSlaveStatus(db *gorm.DB) (BinlogStatus, error) { + slaveStatus := SlaveStatus{} + ret := BinlogStatus{} + err := db.Raw("show slave status").Scan(&slaveStatus).Error + if err != nil { + log.Logger.Errorf("show slave status failed. err:%s", err.Error()) + return BinlogStatus{}, err + } + + if slaveStatus.SlaveIoRunning != "Yes" || slaveStatus.SlaveSqlRunning != "Yes" { + return BinlogStatus{}, fmt.Errorf("slave's SQL_thread[%s], IO_Thread[%s] is abnormal", + slaveStatus.SlaveSqlRunning, slaveStatus.SlaveIoRunning) + } + + if !strings.Contains(slaveStatus.MasterLogFile, ".") { + log.Logger.Errorf("can't find master log file. master_log_file:%s", + slaveStatus.MasterLogFile) + return BinlogStatus{}, fmt.Errorf("can't find master log file") + } + + ret.MasterLogFileIndex, err = strconv.Atoi(strings.Split(slaveStatus.MasterLogFile, ".")[1]) + if err != nil { + log.Logger.Errorf("split master log file failed. err:%s, master_log_file:%s", err.Error(), + slaveStatus.MasterLogFile) + } + + if !strings.Contains(slaveStatus.RelayMasterLogFile, ".") { + log.Logger.Errorf("can't find master log file. relay_master_log_file:%s", + slaveStatus.RelayMasterLogFile) + return BinlogStatus{}, fmt.Errorf("can't find master log file") + } + + ret.RelayMasterLogFileIndex, err = strconv.Atoi(strings.Split(slaveStatus.RelayMasterLogFile, ".")[1]) + if err != nil { + log.Logger.Errorf("split master log file failed. relay_master_log_file:%s", + slaveStatus.RelayMasterLogFile) + return BinlogStatus{}, err + } + + ret.ReadMasterLogPos = slaveStatus.ReadMasterLogPos + ret.ExecMasterLogPos = slaveStatus.ExecMasterLogPos + // ret.RetrievedGtidSet = slaveStatus.RetrievedGtidSet + // ret.ExecutedGtidSet = slaveStatus.ExecutedGtidSet + // ret.MasterUuid = slaveStatus.MasterUuid + return ret, nil +} + +// MasterStatus master status struct +type MasterStatus struct { + File string + Position uint64 + BinlogDoDB string + BinlogIgnoreDB string + ExecutedGtidSet string +} + +// ResetSlave do reset slave +func (ins *MySQLCommonSwitch) ResetSlave() (string, uint64, error) { + slaveIp := ins.StandBySlave.Ip + slavePort := ins.StandBySlave.Port + user := ins.Config.DBConf.MySQL.User + pass := ins.Config.DBConf.MySQL.Pass + log.Logger.Infof("gonna RESET SLAVE on %s:%d", slaveIp, slavePort) + + connParam := fmt.Sprintf("%s:%s@(%s:%d)/%s", user, pass, slaveIp, slavePort, "infodba_schema") + db, err := gorm.Open(mysql.Open(connParam), &gorm.Config{ + Logger: log.GormLogger, + }) + if err != nil { + log.Logger.Errorf("open mysql failed. ip:%s, port:%d, err:%s", slaveIp, slavePort, err.Error()) + return "", 0, err + } + + stopSql := "stop slave" + masterSql := "show master status" + resetSql := "reset slave /*!50516 all */" + + err = db.Exec(stopSql).Error + if err != nil { + return "", 0, fmt.Errorf("stop slave failed. err:%s", err.Error()) + } + log.Logger.Infof("execute %s success", stopSql) + + var masterStatus MasterStatus + err = db.Raw(masterSql).Scan(&masterStatus).Error + if err != nil { + return "", 0, fmt.Errorf("show master status failed, err:%s", err.Error()) + } + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("get new master binlog info succeed. binlog_file:%s, "+ + "binlog_pos:%d", masterStatus.File, masterStatus.Position)) + + err = db.Exec(resetSql).Error + if err != nil { + return "", 0, fmt.Errorf("reset slave failed. err:%s", err.Error()) + } + log.Logger.Infof("executed %s on %s:%d successd", resetSql, slaveIp, slavePort) + + return masterStatus.File, masterStatus.Position, nil +} + +// UpdateMetaInfo swap master, slave 's meta info in cmdb +func (ins *MySQLCommonSwitch) UpdateMetaInfo() error { + return nil +} + +// SetRoutes set switch instance's route info +// 1.set primary tdbctl(here only set the raw primary, even if it already broken-down) +// 2.set all nodes' route info +// 1). use cluster name fetch all spider-master instance, include itself(broken-down) +// 2). connect any-other tdbctl, execute 'TDBCTL GET PRIMARY' to get primary node +// TDBCTL GET PRIMARY sql return result example: +// +-------------+------------+------+----------------+ +// | SERVER_NAME | HOST | PORT | IS_THIS_SERVER | +// +-------------+------------+------+----------------+ +// | TDBCTL0 | 127.0.0.1 | 3306 | 1 | +// +-------------+------------+------+----------------+ +// NB: gorm's func may abnormal for some tdbctl command(its bug?), so all db operator should +// use sql.DB at present +func (ins *SpiderCommonSwitch) SetRoutes() error { + foundPrimary := false + dbConf := ins.Config.DBConf.MySQL + cmdbClient := client.NewCmDBClient(&ins.Config.DBConf.CMDB, ins.Config.GetCloudId()) + rawData, err := cmdbClient.GetDBInstanceInfoByCluster(ins.ClusterName) + if err != nil { + return fmt.Errorf("get all cluster instance info failed:%s", err.Error()) + } + + for _, v := range rawData { + cmdbIns := dbutil.DBInstanceInfoDetail{} + rawIns, jsonErr := json.Marshal(v) + if jsonErr != nil { + return fmt.Errorf("get tdbctl primary failed:%s", jsonErr.Error()) + } + if jsonErr = json.Unmarshal(rawIns, &cmdbIns); jsonErr != nil { + return fmt.Errorf("get tdbctl primary failed:%s", jsonErr.Error()) + } + //only spider-master had tdbctl node, should connect use admin port + if !foundPrimary && cmdbIns.SpiderRole == constvar.TenDBClusterProxyMaster && + cmdbIns.Status != constvar.UNAVAILABLE { + primaryTdbctl := TdbctlInfo{} + //skip connect itself(already broken-down) + if cmdbIns.IP == ins.Ip && cmdbIns.Port == ins.Port { + continue + } + + //try to connect a tdbctl node, and get primary tdbctl + log.Logger.Debugf("try to connect tdbctl and get primary:%s#%d", cmdbIns.IP, cmdbIns.AdminPort) + connParam := fmt.Sprintf("%s:%s@(%s:%d)/%s", + dbConf.User, dbConf.Pass, cmdbIns.IP, cmdbIns.AdminPort, constvar.DefaultDatabase) + if currentConn, connErr := dbutil.ConnMySQL(connParam); connErr != nil { + log.Logger.Warnf("connect tdbctl[%s#%d] failed:%s, retry others", + cmdbIns.IP, cmdbIns.AdminPort, connErr.Error()) + //connect failed, try another + continue + } else { + //get primary tdbctl from connected tdbctl + //TODO: gorm bug? must use sql.DB instead here + if err = currentConn.QueryRow(GetPrimarySQL).Scan(&primaryTdbctl.ServerName, + &primaryTdbctl.Host, &primaryTdbctl.Port, &primaryTdbctl.CurrentServer); err != nil { + log.Logger.Warnf("execute [%s] failed:%s", GetPrimarySQL, err.Error()) + _ = currentConn.Close() + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("get primary tdbctl failed:%s,"+ + "try others", err.Error())) + continue + } + + //if primary tdbctl break-down: try to get all route from any-other(current connected) tdbctl; + //if non-primary tdbctl broken-down: + // 1)current connected tdbctl is primary, get all route directly + // 2)otherwise, get all route from real primary tdbctl. + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("get primary tdbctl success, info:%s#%d", + primaryTdbctl.Host, primaryTdbctl.Port)) + + foundPrimary = true + primaryConn := currentConn + ins.PrimaryTdbctl = primaryTdbctl + //check whether breakdown node is primary tdbctl + if primaryTdbctl.Host == ins.Ip && primaryTdbctl.Port == cmdbIns.AdminPort { + ins.ReportLogs(constvar.InfoResult, "primary tdbctl broken-down") + //current break-down node is primary tdbctl + ins.PrimaryTdbctl.CurrentServer = 1 + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("get route from secondary tdbctl[%s#%d]", + cmdbIns.IP, cmdbIns.AdminPort)) + } else { + ins.ReportLogs(constvar.InfoResult, "non-primary tdbctl broken-down") + //current break-down node is not primary + ins.PrimaryTdbctl.CurrentServer = 0 + if primaryTdbctl.CurrentServer != 1 { + //current connected tdbctl not primary tdbctl, close and connect to real primary + _ = currentConn.Close() + ins.ReportLogs(constvar.InfoResult, "try to connect real primary tdbctl and get route") + connParam = fmt.Sprintf("%s:%s@(%s:%d)/%s", + dbConf.User, dbConf.Pass, ins.PrimaryTdbctl.Host, + ins.PrimaryTdbctl.Port, constvar.DefaultDatabase) + if primaryConn, err = dbutil.ConnMySQL(connParam); err != nil { + return fmt.Errorf("connect primary tdbctl[%s#%d] failed:%s", + ins.PrimaryTdbctl.Host, ins.PrimaryTdbctl.Port, err.Error()) + } + } + } + + if ins.RouteTable, err = ins.QueryRouteInfo(primaryConn); err != nil { + _ = primaryConn.Close() + return fmt.Errorf("get all route info failed:%s", err.Error()) + } + _ = primaryConn.Close() + break + } + } + } + + if !foundPrimary { + return fmt.Errorf("no appropriate primary tdbctl found") + } + + ins.ReportLogs(constvar.InfoResult, "found route, primary tdbctl success") + + return nil +} + +// GetRouteInfo get route info from route table by ip,port +func (ins *SpiderCommonSwitch) GetRouteInfo(host string, port int) *RouteInfo { + for _, node := range ins.RouteTable { + if node.Host == host && node.Port == port { + return &node + } + } + return nil +} + +// QueryRouteInfo query route info from mysql.servers +func (ins *SpiderCommonSwitch) QueryRouteInfo(db *sql.DB) ([]RouteInfo, error) { + routeTable := make([]RouteInfo, 0) + rows, err := db.Query(GetRouteSQL) + if err != nil { + return nil, err + } + for rows.Next() { + route := RouteInfo{} + if err := rows.Scan(&route.ServerName, &route.Host, &route.UserName, + &route.Password, &route.Port, &route.Wrapper); err != nil { + return nil, fmt.Errorf("query route info failed:%s", err.Error()) + } + routeTable = append(routeTable, route) + } + if len(routeTable) == 0 { + return nil, fmt.Errorf("no route info found") + } + ins.ReportLogs(constvar.InfoResult, "query route table success") + + return routeTable, nil +} + +func (ins *SpiderCommonSwitch) ConnectPrimaryTdbctl() (*sql.DB, error) { + mysqlConf := ins.Config.DBConf.MySQL + connParam := fmt.Sprintf("%s:%s@(%s:%d)/%s", + mysqlConf.User, mysqlConf.Pass, ins.PrimaryTdbctl.Host, ins.PrimaryTdbctl.Port, constvar.DefaultDatabase) + primaryConn, err := dbutil.ConnMySQL(connParam) + if err != nil { + return nil, fmt.Errorf("connect primary tdbctl failed:%s", err.Error()) + } + return primaryConn, nil +} + +// RemoveNodeFromRoute connect primary node and remove input node's route +func (ins *SpiderCommonSwitch) RemoveNodeFromRoute(primaryConn *sql.DB, host string, port int) error { + routeInfo := ins.GetRouteInfo(host, port) + dropSQL := fmt.Sprintf(DropNodeFormat, routeInfo.ServerName) + if result, err := primaryConn.Exec(dropSQL); err != nil { + return fmt.Errorf("execute[%s] failed:%s", dropSQL, err.Error()) + } else { + if rowCnt, _ := result.RowsAffected(); rowCnt != 1 { + //TODO: current tdbctl server's rowsAffected incorrect. Next version, should return error instead + log.Logger.Warnf("execute[%s] failed, rowsAffected num :%d", dropSQL, rowCnt) + } + } + + return nil +} diff --git a/dbm-services/common/dbha/ha-module/dbmodule/mysql/MySQL_detect.go b/dbm-services/common/dbha/ha-module/dbmodule/dbmysql/MySQL_detect.go similarity index 75% rename from dbm-services/common/dbha/ha-module/dbmodule/mysql/MySQL_detect.go rename to dbm-services/common/dbha/ha-module/dbmodule/dbmysql/MySQL_detect.go index ae6eade88e..85efac116d 100644 --- a/dbm-services/common/dbha/ha-module/dbmodule/mysql/MySQL_detect.go +++ b/dbm-services/common/dbha/ha-module/dbmodule/dbmysql/MySQL_detect.go @@ -1,4 +1,4 @@ -package mysql +package dbmysql import ( "encoding/json" @@ -13,14 +13,21 @@ import ( "dbm-services/common/dbha/ha-module/types" "dbm-services/common/dbha/ha-module/util" + mysqlDriver "github.com/go-sql-driver/mysql" "gorm.io/driver/mysql" "gorm.io/gorm" ) const ( - replaceSql = "replace into infodba_schema.check_heartbeat(uid) values(1)" + replaceSql = "REPLACE INTO infodba_schema.check_heartbeat(uid) value(@@server_id)" ) +// IgnoreErrorNumber MySQL detect ignore error number +// 1040:ER_CON_COUNT_ERROR +// 1044:ER_DBACCESS_DENIED_ERROR +// 1045:ER_ACCESS_DENIED_ERROR +var IgnoreErrorNumber = []uint16{1040, 1044, 1045} + // MySQLDetectInstance mysql instance detect struct type MySQLDetectInstance struct { dbutil.BaseDetectDB @@ -45,8 +52,8 @@ type MySQLDetectInstanceInfoFromCmDB struct { Cluster string } -// NewMySQLDetectInstance1 convert cmdb info to detect info -func NewMySQLDetectInstance1(ins *MySQLDetectInstanceInfoFromCmDB, conf *config.Config) *MySQLDetectInstance { +// AgentNewMySQLDetectInstance convert CmDBInstanceUrl response info to MySQLDetectInstance +func AgentNewMySQLDetectInstance(ins *MySQLDetectInstanceInfoFromCmDB, conf *config.Config) *MySQLDetectInstance { return &MySQLDetectInstance{ BaseDetectDB: dbutil.BaseDetectDB{ Ip: ins.Ip, @@ -57,6 +64,7 @@ func NewMySQLDetectInstance1(ins *MySQLDetectInstanceInfoFromCmDB, conf *config. ReportInterval: conf.AgentConf.ReportInterval + rand.Intn(20), Status: constvar.DBCheckSuccess, Cluster: ins.Cluster, + ClusterType: ins.ClusterType, SshInfo: dbutil.Ssh{ Port: conf.SSH.Port, User: conf.SSH.User, @@ -71,18 +79,19 @@ func NewMySQLDetectInstance1(ins *MySQLDetectInstanceInfoFromCmDB, conf *config. } } -// NewMySQLDetectInstance2 convert api response info into detect info -func NewMySQLDetectInstance2(ins *MySQLDetectResponse, dbType string, conf *config.Config) *MySQLDetectInstance { +// GMNewMySQLDetectInstance GDM convert agent report info into MySQLDetectInstance +func GMNewMySQLDetectInstance(ins *MySQLDetectResponse, conf *config.Config) *MySQLDetectInstance { return &MySQLDetectInstance{ BaseDetectDB: dbutil.BaseDetectDB{ Ip: ins.DBIp, Port: ins.DBPort, App: ins.App, - DBType: types.DBType(dbType), + DBType: types.DBType(ins.DBType), ReporterTime: time.Unix(0, 0), ReportInterval: conf.AgentConf.ReportInterval + rand.Intn(20), Status: types.CheckStatus(ins.Status), Cluster: ins.Cluster, + ClusterType: ins.ClusterType, SshInfo: dbutil.Ssh{ Port: conf.SSH.Port, User: conf.SSH.User, @@ -97,7 +106,17 @@ func NewMySQLDetectInstance2(ins *MySQLDetectResponse, dbType string, conf *conf } } -// Detection TODO +// GetType return dbType +func (m *MySQLDetectInstance) GetType() types.DBType { + return m.DBType +} + +// GetDetectType return dbType +func (m *MySQLDetectInstance) GetDetectType() string { + return m.ClusterType +} + +// Detection agent, gmm call this do lived detect // return error: // // not nil: check db failed or do ssh failed @@ -117,6 +136,16 @@ func (m *MySQLDetectInstance) Detection() error { select { case mysqlErr = <-errChan: if mysqlErr != nil { + if err, ok := mysqlErr.(*mysqlDriver.MySQLError); ok { + for _, num := range IgnoreErrorNumber { + if num == err.Number { + log.Logger.Warnf("ignore error:%d, check mysql ok. ip:%s, port:%d, app:%s", + err.Number, m.Ip, m.Port, m.App) + m.Status = constvar.DBCheckSuccess + return nil + } + } + } log.Logger.Warnf("check mysql failed. ip:%s, port:%d, app:%s", m.Ip, m.Port, m.App) m.Status = constvar.DBCheckFailed needRecheck = false @@ -158,7 +187,7 @@ func (m *MySQLDetectInstance) CheckMySQL(errChan chan error) { Logger: log.GormLogger, }) if err != nil { - log.Logger.Warnf("open mysql failed. ip:%s, port:%d, err:%s", m.Ip, m.Port, err.Error()) + log.Logger.Warnf("open mysql failed. ip:%s, port:1G%d, err:%s", m.Ip, m.Port, err.Error()) errChan <- err return } @@ -168,13 +197,15 @@ func (m *MySQLDetectInstance) CheckMySQL(errChan chan error) { } defer func() { - db, _ := m.realDB.DB() - if err := db.Close(); err != nil { - log.Logger.Warnf("close connect[%s#%d] failed:%s", m.Ip, m.Port, err.Error()) + if m.realDB != nil { + db, _ := m.realDB.DB() + if err := db.Close(); err != nil { + log.Logger.Warnf("close connect[%s#%d] failed:%s", m.Ip, m.Port, err.Error()) + } + // need set to nil, otherwise agent would cache connection + // and may cause connection leak + m.realDB = nil } - // need set to nil, otherwise agent would cache connection - // and may cause connection leak - m.realDB = nil }() err := m.realDB.Exec(replaceSql).Error diff --git a/dbm-services/common/dbha/ha-module/dbmodule/mysql/MySQL_proxy_handle.go b/dbm-services/common/dbha/ha-module/dbmodule/dbmysql/MySQL_proxy_handle.go similarity index 99% rename from dbm-services/common/dbha/ha-module/dbmodule/mysql/MySQL_proxy_handle.go rename to dbm-services/common/dbha/ha-module/dbmodule/dbmysql/MySQL_proxy_handle.go index 4afeadb659..b1fa78c09a 100644 --- a/dbm-services/common/dbha/ha-module/dbmodule/mysql/MySQL_proxy_handle.go +++ b/dbm-services/common/dbha/ha-module/dbmodule/dbmysql/MySQL_proxy_handle.go @@ -1,4 +1,4 @@ -package mysql +package dbmysql import ( "database/sql" diff --git a/dbm-services/common/dbha/ha-module/dbmodule/dbmysql/SpiderProxyLayer_switch.go b/dbm-services/common/dbha/ha-module/dbmodule/dbmysql/SpiderProxyLayer_switch.go new file mode 100644 index 0000000000..7bb156f542 --- /dev/null +++ b/dbm-services/common/dbha/ha-module/dbmodule/dbmysql/SpiderProxyLayer_switch.go @@ -0,0 +1,191 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ +// Package dbmysql +// SpiderProxyLayer file defined spider node's fail-over main logic. + +package dbmysql + +import ( + "dbm-services/common/dbha/ha-module/client" + "dbm-services/common/dbha/ha-module/constvar" + "dbm-services/common/dbha/ha-module/dbutil" + "dbm-services/common/dbha/ha-module/log" + "fmt" + "strings" +) + +// SpiderProxyLayerSwitch spider node switch +type SpiderProxyLayerSwitch struct { + SpiderCommonSwitch + //proxy layer instance used(spider, proxy) + AdminPort int + //storage layer instance used + Entry dbutil.BindEntry + Proxy []dbutil.ProxyInfo +} + +// ElectPrimary elect a new primary, only spider-master broken-down may do this +func (ins *SpiderProxyLayerSwitch) ElectPrimary() (TdbctlInfo, error) { + dbConf := ins.Config.DBConf + log.Logger.Debugf("route info:%#v", ins.RouteTable) + for _, node := range ins.RouteTable { + //only spider-master had tdbctl node, should connect use admin port + if strings.EqualFold(node.Wrapper, constvar.WrapperTdbctl) { + //exclude broken-down tdbctl node + if node.Host == ins.Ip && node.Port == ins.AdminPort { + continue + } + + tdbctlIns := MySQLSwitch{ + MySQLCommonSwitch: MySQLCommonSwitch{ + BaseSwitch: dbutil.BaseSwitch{ + Ip: ins.Ip, + Port: ins.AdminPort, + Config: ins.Config, + CmDBClient: client.NewCmDBClient(&dbConf.CMDB, ins.Config.GetCloudId()), + HaDBClient: client.NewHaDBClient(&dbConf.HADB, ins.Config.GetCloudId()), + SwitchUid: ins.GetSwitchUid(), + }, + StandBySlave: dbutil.SlaveInfo{ + Ip: node.Host, + Port: node.Port, + }, + }, + } + if err := tdbctlIns.CheckSlaveStatus(); err != nil { + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("tdbctl[%s#%d] not satisfied elect:%s,"+ + "try another", node.Host, node.Port, err.Error())) + continue + } + + //try to connect a tdbctl node, and get primary tdbctl + connParam := fmt.Sprintf("%s:%s@(%s:%d)/%s", + dbConf.MySQL.User, dbConf.MySQL.Pass, node.Host, node.Port, "infodba_schema") + if tdbctlConn, err := dbutil.ConnMySQL(connParam); err != nil { + log.Logger.Warnf("connect tdbctl[%s#%d] failed:%s, retry others", + node.Host, node.Port, err.Error()) + //connect failed, try another + continue + } else { + if _, err = tdbctlConn.Exec(ForcePrimarySQL); err != nil { + _ = tdbctlConn.Close() + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("tdbctl[%s#%d] do "+ + "[%s] failed:%s, try another", node.Host, node.Port, ForcePrimarySQL, err.Error())) + continue + } + _ = tdbctlConn.Close() + + ins.ReportLogs(constvar.InfoResult, "try to reset slave on new primary") + if binlogFile, binlogPosition, err := tdbctlIns.ResetSlave(); err != nil { + ins.ReportLogs(constvar.FailResult, fmt.Sprintf("reset slave failed:%s", err.Error())) + } else { + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("reset slave success,"+ + "consistent binlog info:%s,%d", binlogFile, binlogPosition)) + } + return TdbctlInfo{ + ServerName: node.ServerName, + Host: node.Host, + Port: node.Port, + }, nil + } + } + } + return TdbctlInfo{}, fmt.Errorf("elect new tdbctl primary node failed, no satified node found") +} + +// CheckSwitch check whether satisfy switch before do real switch +func (ins *SpiderProxyLayerSwitch) CheckSwitch() (bool, error) { + return true, nil +} + +// DoSwitch do spider(include tdbctl) switch +// 1. release broken-down node's name service if exist +// 2. found primary tdbctl, if primary broken-down, do elect first +// 3. remove broken-down node from primary-tdbctl route table +// 4. primary-tdbctl do flush routing +func (ins *SpiderProxyLayerSwitch) DoSwitch() error { + //set switch instance's route info(set primary tdbctl also) + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("get all route table before switch")) + if err := ins.SetRoutes(); err != nil { + return err + } + + //1. update name service + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("try to release ip[%s#%d] from all entery", + ins.Ip, ins.Port)) + if err := ins.DeleteNameService(ins.Entry); err != nil { + return err + } + + //2. connect primary tdbctl + //if primary tdbctl broken-down, elect new reliable primary first + oldPrimary := ins.PrimaryTdbctl + if oldPrimary.CurrentServer == 1 { + ins.ReportLogs(constvar.InfoResult, "primary tdbctl broken-down, try to elect a new one") + if newPrimary, err := ins.ElectPrimary(); err != nil { + return err + } else { + ins.PrimaryTdbctl = newPrimary + } + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("elect new primary tdbctl success:%s#%d", + ins.PrimaryTdbctl.Host, ins.PrimaryTdbctl.Port)) + + } + + log.Logger.Debugf("connect to primary tdbctl and update route") + primaryConn, err := ins.ConnectPrimaryTdbctl() + if err != nil { + return err + } + defer func() { + _ = primaryConn.Close() + }() + + //3. remove broken-down spider node from route table + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("remove spider node[%s#%d] from route table", + ins.Ip, ins.Port)) + if err := ins.RemoveNodeFromRoute(primaryConn, ins.Ip, ins.Port); err != nil { + ins.ReportLogs(constvar.FailResult, fmt.Sprintf("remove spider node failed:%s", err.Error())) + return err + } + ins.ReportLogs(constvar.InfoResult, "remove spider node success") + + //4. remove broken-down tdbctl node from route table + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("remove tdbctl node[%s#%d] from route table", + ins.Ip, ins.AdminPort)) + if err := ins.RemoveNodeFromRoute(primaryConn, ins.Ip, ins.AdminPort); err != nil { + ins.ReportLogs(constvar.FailResult, fmt.Sprintf("remove tdbctl node[%s#%d] from route-table failed:%s", + ins.Ip, ins.AdminPort, err.Error())) + return err + } + ins.ReportLogs(constvar.InfoResult, "remove tdbctl node success") + + //5. flush routing + ins.ReportLogs(constvar.InfoResult, "flush route table") + if _, err = primaryConn.Exec(FlushRouteSQL); err != nil { + ins.ReportLogs(constvar.FailResult, fmt.Sprintf("flush route failed:%s", err.Error())) + return fmt.Errorf("execute[%s] failed:%s", FlushRouteSQL, err.Error()) + } + ins.ReportLogs(constvar.SuccessResult, "flush ok, switch success") + + return nil +} + +// RollBack proxy do rollback +func (ins *SpiderProxyLayerSwitch) RollBack() error { + return nil +} + +// ShowSwitchInstanceInfo show db-mysql instance's switch info +func (ins *SpiderProxyLayerSwitch) ShowSwitchInstanceInfo() string { + return fmt.Sprintf("<%s#%d IDC:%s Role:%s Status:%s Bzid:%s ClusterType:%s MachineType:%s>", + ins.Ip, ins.Port, ins.IDC, ins.Role, ins.Status, ins.App, ins.ClusterType, + ins.MetaType) +} diff --git a/dbm-services/common/dbha/ha-module/dbmodule/dbmysql/SpiderStorageLayer_switch.go b/dbm-services/common/dbha/ha-module/dbmodule/dbmysql/SpiderStorageLayer_switch.go new file mode 100644 index 0000000000..d133038cdd --- /dev/null +++ b/dbm-services/common/dbha/ha-module/dbmodule/dbmysql/SpiderStorageLayer_switch.go @@ -0,0 +1,159 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +// Package dbmysql +// SpiderStorageLayer_switch defined remote node's fail-over main logic. + +package dbmysql + +import ( + "fmt" + + "dbm-services/common/dbha/ha-module/client" + "dbm-services/common/dbha/ha-module/constvar" + "dbm-services/common/dbha/ha-module/dbutil" + "dbm-services/common/dbha/ha-module/log" +) + +const ( +// sql format to alter node +) + +// SpiderStorageSwitch spider remote node switch +type SpiderStorageSwitch struct { + SpiderCommonSwitch + //proxy layer instance used(spider, proxy) + Proxy []dbutil.ProxyInfo +} + +// CheckSwitch check slave before switch +func (ins *SpiderStorageSwitch) CheckSwitch() (bool, error) { + var err error + if ins.Role == constvar.TenDBClusterStorageSlave { + ins.ReportLogs(constvar.InfoResult, "instance is slave, skip switch check") + return false, nil + } else if ins.Role == constvar.TenDBClusterStorageMaster { + log.Logger.Infof("info:{%s} is master", ins.ShowSwitchInstanceInfo()) + + log.Logger.Infof("check slave status. info{%s}", ins.ShowSwitchInstanceInfo()) + if ins.StandBySlave == (dbutil.SlaveInfo{}) { + ins.ReportLogs(constvar.FailResult, "no slave info found") + return false, err + } + if ins.StandBySlave.Status == constvar.UNAVAILABLE { + ins.ReportLogs(constvar.FailResult, "standby slave's status is unavailable") + return false, err + } + ins.SetInfo(constvar.SlaveIpKey, ins.StandBySlave.Ip) + ins.SetInfo(constvar.SlavePortKey, ins.StandBySlave.Port) + err = ins.CheckSlaveStatus() + if err != nil { + ins.ReportLogs(constvar.FailResult, err.Error()) + return false, err + } + + log.Logger.Infof("start to switch. info{%s}", ins.ShowSwitchInstanceInfo()) + + if len(ins.Proxy) == 0 { + // available instance usual without proxy + log.Logger.Infof("without spider! info:{%s}", ins.ShowSwitchInstanceInfo()) + ins.ReportLogs(constvar.InfoResult, "without proxy!") + return false, nil + } + + } else { + return false, fmt.Errorf("unknown instance role:%s", ins.Role) + } + + ins.ReportLogs(constvar.InfoResult, "remote node check slave info finished.") + return true, nil +} + +// DoSwitch do remote switch +// 1. connect primary tdbctl and update route +// 2. flush routing +func (ins *SpiderStorageSwitch) DoSwitch() error { + //set primary, set all route info + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("get all route table before switch")) + if err := ins.SetRoutes(); err != nil { + return err + } + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("old master[%s#%d], old slave[%s#%d]", + ins.Ip, ins.Port, ins.StandBySlave.Ip, ins.StandBySlave.Port)) + oldMaster := ins.GetRouteInfo(ins.Ip, ins.Port) + newMaster := ins.GetRouteInfo(ins.StandBySlave.Ip, ins.StandBySlave.Port) + if oldMaster == nil || newMaster == nil { + ins.ReportLogs(constvar.FailResult, "get master/slave's route failed") + return fmt.Errorf("no master/slave's record found in route table") + } + + //connect to primary tdbctl + log.Logger.Debugf("connect to primary tdbctl") + primaryConn, err := ins.ConnectPrimaryTdbctl() + if err != nil { + return fmt.Errorf("connect to primary tdbctl[%s#%d] failed:%s", + ins.PrimaryTdbctl.Host, ins.PrimaryTdbctl.Port, err.Error()) + } + defer func() { + _ = primaryConn.Close() + }() + + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("try to update route from old master to new master")) + alterSQL := fmt.Sprintf(AlterNodeFormat, oldMaster.ServerName, newMaster.Host, + newMaster.UserName, newMaster.Password, newMaster.Port) + if result, err := primaryConn.Exec(alterSQL); err != nil { + return fmt.Errorf("execute TDBCTL ALTER NODE failed:%s", err.Error()) + } else { + if rowCnt, _ := result.RowsAffected(); rowCnt == 0 { + //TODO: current tdbctl server's rowsAffected incorrect. Next version, should return error instead + log.Logger.Warnf("execute TDBCTL ALTER NODE failed, rowsAffected 0") + } + } + ins.ReportLogs(constvar.InfoResult, "update route info success, do flush next") + + if _, err = primaryConn.Exec(FlushRouteSQL); err != nil { + ins.ReportLogs(constvar.FailResult, fmt.Sprintf("flush route failed:%s", err.Error())) + return fmt.Errorf("execute[%s] failed:%s", FlushRouteSQL, err.Error()) + } + ins.ReportLogs(constvar.InfoResult, "flush route ok, switch success") + + return nil +} + +// ShowSwitchInstanceInfo show db-mysql instance's switch info +func (ins *SpiderStorageSwitch) ShowSwitchInstanceInfo() string { + str := fmt.Sprintf("<%s#%d IDC:%s Role:%s Status:%s Bzid:%s ClusterType:%s MachineType:%s>", + ins.Ip, ins.Port, ins.IDC, ins.Role, ins.Status, ins.App, ins.ClusterType, + ins.MetaType) + //TODO right way to check empty? + if ins.StandBySlave != (dbutil.SlaveInfo{}) { + str = fmt.Sprintf("%s Switch from MASTER:<%s#%d> to SLAVE:<%s#%d>", + str, ins.Ip, ins.Port, ins.StandBySlave.Ip, ins.StandBySlave.Port) + } + return str +} + +// RollBack proxy do rollback +func (ins *SpiderStorageSwitch) RollBack() error { + return nil +} + +// UpdateMetaInfo swap master, slave 's meta info in cmdb +func (ins *SpiderStorageSwitch) UpdateMetaInfo() error { + cmdbClient := client.NewCmDBClient(&ins.Config.DBConf.CMDB, ins.Config.GetCloudId()) + if err := cmdbClient.SwapMySQLRole(ins.Ip, ins.Port, + ins.StandBySlave.Ip, ins.StandBySlave.Port); err != nil { + updateErrLog := fmt.Sprintf("swap db-mysql role failed. err:%s", err.Error()) + ins.ReportLogs(constvar.FailResult, updateErrLog) + return err + } + ins.ReportLogs(constvar.InfoResult, "update meta info success") + return nil +} diff --git a/dbm-services/common/dbha/ha-module/dbmodule/dbmysql/mysql.go b/dbm-services/common/dbha/ha-module/dbmodule/dbmysql/mysql.go new file mode 100644 index 0000000000..625a5a5b09 --- /dev/null +++ b/dbm-services/common/dbha/ha-module/dbmodule/dbmysql/mysql.go @@ -0,0 +1,2 @@ +// Package dbmysql TODO +package dbmysql diff --git a/dbm-services/common/dbha/ha-module/dbmodule/mysql/MySQLProxy_callback.go b/dbm-services/common/dbha/ha-module/dbmodule/mysql/MySQLProxy_callback.go deleted file mode 100644 index af90787b8b..0000000000 --- a/dbm-services/common/dbha/ha-module/dbmodule/mysql/MySQLProxy_callback.go +++ /dev/null @@ -1,183 +0,0 @@ -package mysql - -import ( - "encoding/json" - "fmt" - "strconv" - - "dbm-services/common/dbha/ha-module/client" - "dbm-services/common/dbha/ha-module/config" - "dbm-services/common/dbha/ha-module/constvar" - "dbm-services/common/dbha/ha-module/dbutil" - "dbm-services/common/dbha/ha-module/log" -) - -// NewMySQLProxyInstanceByCmDB Agent通过CMDB获取的信息来生成需要探测的实例 -func NewMySQLProxyInstanceByCmDB(instances []interface{}, - conf *config.Config) ([]dbutil.DataBaseDetect, error) { - var ( - err error - unmarshalIns []*MySQLDetectInstanceInfoFromCmDB - ret []dbutil.DataBaseDetect - ) - - unmarshalIns, err = UnMarshalMySQLInstanceByCmdb(instances, constvar.MySQLClusterType, - constvar.MySQLProxyMetaType) - - if err != nil { - return nil, err - } - - for _, uIns := range unmarshalIns { - pIns := &MySQLProxyDetectInstanceInfoFromCmDB{ - MySQLDetectInstanceInfoFromCmDB: *uIns, - } - ret = append(ret, NewMySQLProxyDetectInstance1(pIns, conf)) - } - - return ret, err -} - -// DeserializeMySQLProxy 反序列化从Agent上报上来的故障实例 -func DeserializeMySQLProxy(jsonInfo []byte, conf *config.Config) (dbutil.DataBaseDetect, error) { - response := MySQLProxyDetectResponse{} - err := json.Unmarshal(jsonInfo, &response) - if err != nil { - log.Logger.Errorf("json unmarshal failed. jsoninfo:\n%s\n, err:%s", string(jsonInfo), err.Error()) - return nil, err - } - var ret dbutil.DataBaseDetect - ret = NewMySQLProxyDetectInstance2(&response, constvar.MySQLProxy, conf) - return ret, nil -} - -// NewMySQLProxySwitchInstance get instance switch info -func NewMySQLProxySwitchInstance(instances []interface{}, conf *config.Config) ([]dbutil.DataBaseSwitch, error) { - var err error - var ret []dbutil.DataBaseSwitch - for _, v := range instances { - ins := v.(map[string]interface{}) - inf, ok := ins["ip"] - if !ok { - err = fmt.Errorf("umarshal failed. ip not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - ip := inf.(string) - - inf, ok = ins["port"] - if !ok { - err = fmt.Errorf("umarshal failed. port not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - port := int(inf.(float64)) - - inf, ok = ins["bk_idc_city_id"] - if !ok { - err = fmt.Errorf("umarshal failed. role not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - idc := strconv.Itoa(int(inf.(float64))) - - inf, ok = ins["status"] - if !ok { - err = fmt.Errorf("umarshal failed. ip not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - status := inf.(string) - - inf, ok = ins["cluster"] - if !ok { - err = fmt.Errorf("umarshal failed. cluster not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - cluster := inf.(string) - - inf, ok = ins["bk_biz_id"] - if !ok { - err = fmt.Errorf("umarshal failed. app not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - app := strconv.Itoa(int(inf.(float64))) - - inf, ok = ins["cluster_type"] - if !ok { - err = fmt.Errorf("umarshal failed. cluster_type not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - clusterType := inf.(string) - - inf, ok = ins["machine_type"] - if !ok { - err = fmt.Errorf("umarshal failed. machine_type not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - metaType := inf.(string) - - inf, ok = ins["admin_port"] - if !ok { - err = fmt.Errorf("umarshal failed. admin_port not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - adminPort := int(inf.(float64)) - - inf, ok = ins["bind_entry"] - if !ok { - err = fmt.Errorf("umarshal failed. proxyinstance_set not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - bindEntry := inf.(map[string]interface{}) - - cmdbClient, err := client.NewCmDBClient(&conf.DBConf.CMDB, conf.GetCloudId()) - if err != nil { - return nil, err - } - - hadbClient, err := client.NewHaDBClient(&conf.DBConf.HADB, conf.GetCloudId()) - if err != nil { - return nil, err - } - - dnsClient, err := client.NewNameServiceClient(&conf.DNS.BindConf, conf.GetCloudId()) - if err != nil { - return nil, err - } - - swIns := MySQLProxySwitch{ - BaseSwitch: dbutil.BaseSwitch{ - Ip: ip, - Port: port, - IDC: idc, - Status: status, - App: app, - ClusterType: clusterType, - MetaType: metaType, - Cluster: cluster, - CmDBClient: cmdbClient, - HaDBClient: hadbClient, - }, - AdminPort: adminPort, - DnsClient: dnsClient, - } - - inf, ok = bindEntry["dns"] - if !ok { - err = fmt.Errorf("umarshal failed. dns not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - swIns.Entry.Dns = inf.([]interface{}) - - ret = append(ret, &swIns) - } - return ret, nil -} diff --git a/dbm-services/common/dbha/ha-module/dbmodule/mysql/MySQLProxy_detect.go b/dbm-services/common/dbha/ha-module/dbmodule/mysql/MySQLProxy_detect.go deleted file mode 100644 index a271f1ff08..0000000000 --- a/dbm-services/common/dbha/ha-module/dbmodule/mysql/MySQLProxy_detect.go +++ /dev/null @@ -1,37 +0,0 @@ -package mysql - -import ( - "dbm-services/common/dbha/ha-module/config" -) - -// MySQLProxyDetectInstance defined proxy detect info -type MySQLProxyDetectInstance struct { - MySQLDetectInstance -} - -// MySQLProxyDetectResponse defined proxy response struct -type MySQLProxyDetectResponse struct { - MySQLDetectResponse -} - -// MySQLProxyDetectInstanceInfoFromCmDB defined proxy detect info in cmdb -type MySQLProxyDetectInstanceInfoFromCmDB struct { - MySQLDetectInstanceInfoFromCmDB -} - -// NewMySQLProxyDetectInstance1 return detect info in cmdb -func NewMySQLProxyDetectInstance1(ins *MySQLProxyDetectInstanceInfoFromCmDB, - conf *config.Config) *MySQLProxyDetectInstance { - return &MySQLProxyDetectInstance{ - MySQLDetectInstance: *NewMySQLDetectInstance1(&ins.MySQLDetectInstanceInfoFromCmDB, - conf), - } -} - -// NewMySQLProxyDetectInstance2 return detect info by agent report -func NewMySQLProxyDetectInstance2(ins *MySQLProxyDetectResponse, dbType string, - conf *config.Config) *MySQLProxyDetectInstance { - return &MySQLProxyDetectInstance{ - MySQLDetectInstance: *NewMySQLDetectInstance2(&ins.MySQLDetectResponse, dbType, conf), - } -} diff --git a/dbm-services/common/dbha/ha-module/dbmodule/mysql/MySQLProxy_switch.go b/dbm-services/common/dbha/ha-module/dbmodule/mysql/MySQLProxy_switch.go deleted file mode 100644 index a0615cc58a..0000000000 --- a/dbm-services/common/dbha/ha-module/dbmodule/mysql/MySQLProxy_switch.go +++ /dev/null @@ -1,90 +0,0 @@ -package mysql - -import ( - "fmt" - - "dbm-services/common/dbha/ha-module/client" - "dbm-services/common/dbha/ha-module/constvar" - "dbm-services/common/dbha/ha-module/dbutil" - "dbm-services/common/dbha/ha-module/log" -) - -// MySQLProxySwitch define proxy switch detail info -type MySQLProxySwitch struct { - dbutil.BaseSwitch - AdminPort int - Entry dbutil.BindEntry - DnsClient *client.NameServiceClient -} - -// CheckSwitch check whether proxy allowed swtich, always true at present -func (ins *MySQLProxySwitch) CheckSwitch() (bool, error) { - return true, nil -} - -// DoSwitch proxy do switch -// 1. get domain info -// 2. delete ip under the domain -func (ins *MySQLProxySwitch) DoSwitch() error { - ins.ReportLogs(constvar.SWITCH_FAIL, fmt.Sprintf("get domain info by ip:%s", ins.Ip)) - dnsInfos, err := ins.DnsClient.GetDomainInfoByIp(ins.Ip) - log.Logger.Debugf("dnsInfos:%v", dnsInfos) - if err != nil { - switchErrLog := fmt.Sprintf("get domain info by ip failed: %s", err.Error()) - ins.ReportLogs(constvar.SWITCH_FAIL, switchErrLog) - return err - } - if len(dnsInfos) == 0 { - switchErrLog := "mysql proxy without domain info." - ins.ReportLogs(constvar.SWITCH_FAIL, switchErrLog) - return fmt.Errorf("no domain info found for mysql-proxy") - } - - ins.ReportLogs(constvar.SWITCH_INFO, fmt.Sprintf("start release ip[%s] from domain", ins.Ip)) - for _, dnsInfo := range dnsInfos { - ipInfos, err := ins.DnsClient.GetDomainInfoByDomain(dnsInfo.DomainName) - if err != nil { - switchErrLog := fmt.Sprintf("get domain info by domain name failed. err:%s", err.Error()) - ins.ReportLogs(constvar.SWITCH_FAIL, switchErrLog) - return err - } - if len(ipInfos) == 0 { - switchErrLog := fmt.Sprintf("domain name: %s without ip.", dnsInfo.DomainName) - ins.ReportLogs(constvar.SWITCH_FAIL, switchErrLog) - return fmt.Errorf("domain name: %s without ip", dnsInfo.DomainName) - } - if len(ipInfos) == 1 { - switchOkLog := fmt.Sprintf("domain name: %s only one ip. so we skip it.", - dnsInfo.DomainName) - ins.ReportLogs(constvar.SWITCH_INFO, switchOkLog) - } else { - err = ins.DnsClient.DeleteDomain(dnsInfo.DomainName, dnsInfo.App, ins.Ip, ins.Port) - if err != nil { - switchErrLog := fmt.Sprintf("delete domain %s failed:%s", dnsInfo.DomainName, err.Error()) - ins.ReportLogs(constvar.SWITCH_FAIL, switchErrLog) - return err - } - switchOkLog := fmt.Sprintf("delete domain %s success.", dnsInfo.DomainName) - log.Logger.Infof("%s, info:{%s}", switchOkLog, ins.ShowSwitchInstanceInfo()) - ins.ReportLogs(constvar.SWITCH_INFO, switchOkLog) - } - } - return nil -} - -// ShowSwitchInstanceInfo display switch proxy info -func (ins *MySQLProxySwitch) ShowSwitchInstanceInfo() string { - str := fmt.Sprintf("<%s#%d IDC:%s Status:%s Bzid:%s ClusterType:%s MachineType:%s> switch", - ins.Ip, ins.Port, ins.IDC, ins.Status, ins.App, ins.ClusterType, ins.MetaType) - return str -} - -// RollBack proxy do rollback -func (ins *MySQLProxySwitch) RollBack() error { - return nil -} - -// UpdateMetaInfo update cmdb meta info, do nothing at present -func (ins *MySQLProxySwitch) UpdateMetaInfo() error { - return nil -} diff --git a/dbm-services/common/dbha/ha-module/dbmodule/mysql/MySQL_callback.go b/dbm-services/common/dbha/ha-module/dbmodule/mysql/MySQL_callback.go deleted file mode 100644 index ef0ddcfb47..0000000000 --- a/dbm-services/common/dbha/ha-module/dbmodule/mysql/MySQL_callback.go +++ /dev/null @@ -1,343 +0,0 @@ -package mysql - -import ( - "encoding/json" - "fmt" - "strconv" - - "dbm-services/common/dbha/ha-module/client" - "dbm-services/common/dbha/ha-module/config" - "dbm-services/common/dbha/ha-module/constvar" - "dbm-services/common/dbha/ha-module/dbutil" - "dbm-services/common/dbha/ha-module/log" -) - -// UnMarshalMySQLInstanceByCmdb convert cmdb instance info to MySQLDetectInstanceInfoFromCmDB -func UnMarshalMySQLInstanceByCmdb(instances []interface{}, - uClusterType string, uMetaType string) ([]*MySQLDetectInstanceInfoFromCmDB, error) { - var ( - err error - ret []*MySQLDetectInstanceInfoFromCmDB - ) - cache := map[string]*MySQLDetectInstanceInfoFromCmDB{} - - for _, v := range instances { - ins := v.(map[string]interface{}) - inf, ok := ins["cluster_type"] - if !ok { - err = fmt.Errorf("umarshal failed. cluster_type not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - clusterType := inf.(string) - if clusterType != uClusterType { - continue - } - inf, ok = ins["machine_type"] - if !ok { - err = fmt.Errorf("umarshal failed. machine_type not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - metaType := inf.(string) - if metaType != uMetaType { - continue - } - inf, ok = ins["status"] - if !ok { - err = fmt.Errorf("umarshal failed. status not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - status := inf.(string) - if status != constvar.RUNNING && status != constvar.AVAILABLE { - continue - } - inf, ok = ins["cluster"] - if !ok { - err = fmt.Errorf("umarshal failed. cluster not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - cluster := inf.(string) - - inf, ok = ins["ip"] - if !ok { - err = fmt.Errorf("umarshal failed. ip not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - ip := inf.(string) - inf, ok = ins["port"] - if !ok { - err = fmt.Errorf("umarshal failed. port not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - port := int(inf.(float64)) - inf, ok = ins["bk_biz_id"] - if !ok { - err = fmt.Errorf("umarshal failed. app not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - app := strconv.Itoa(int(inf.(float64))) - cacheIns, ok := cache[ip] - if ok { - if port < cacheIns.Port { - cache[ip] = &MySQLDetectInstanceInfoFromCmDB{ - Ip: ip, - Port: port, - App: app, - ClusterType: clusterType, - MetaType: metaType, - Cluster: cluster, - } - } - } else { - cache[ip] = &MySQLDetectInstanceInfoFromCmDB{ - Ip: ip, - Port: port, - App: app, - ClusterType: clusterType, - MetaType: metaType, - Cluster: cluster, - } - } - } - - for _, cacheIns := range cache { - ret = append(ret, cacheIns) - } - - return ret, nil -} - -// NewMySQLInstanceByCmDB unmarshal cmdb instances to detect instance struct -func NewMySQLInstanceByCmDB(instances []interface{}, Conf *config.Config) ([]dbutil.DataBaseDetect, error) { - var ( - err error - unmarshalIns []*MySQLDetectInstanceInfoFromCmDB - ret []dbutil.DataBaseDetect - ) - - unmarshalIns, err = UnMarshalMySQLInstanceByCmdb(instances, constvar.MySQLClusterType, - constvar.MySQLMetaType) - - if err != nil { - return nil, err - } - - for _, uIns := range unmarshalIns { - ret = append(ret, NewMySQLDetectInstance1(uIns, Conf)) - } - - return ret, err -} - -// NewMySQLSwitchInstance unmarshal cmdb instances to switch instance struct -func NewMySQLSwitchInstance(instances []interface{}, conf *config.Config) ([]dbutil.DataBaseSwitch, error) { - var err error - var ret []dbutil.DataBaseSwitch - for _, v := range instances { - ins := v.(map[string]interface{}) - inf, ok := ins["ip"] - if !ok { - err = fmt.Errorf("umarshal failed. ip not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - ip := inf.(string) - - inf, ok = ins["port"] - if !ok { - err = fmt.Errorf("umarshal failed. port not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - port := int(inf.(float64)) - - inf, ok = ins["bk_idc_city_id"] - if !ok { - err = fmt.Errorf("umarshal failed. role not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - idc := strconv.Itoa(int(inf.(float64))) - - inf, ok = ins["instance_role"] - if !ok { - err = fmt.Errorf("umarshal failed. role not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - role := inf.(string) - - inf, ok = ins["status"] - if !ok { - err = fmt.Errorf("umarshal failed. ip not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - status := inf.(string) - - inf, ok = ins["cluster"] - if !ok { - err = fmt.Errorf("umarshal failed. cluster not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - cluster := inf.(string) - - inf, ok = ins["bk_biz_id"] - if !ok { - err = fmt.Errorf("umarshal failed. app not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - app := strconv.Itoa(int(inf.(float64))) - - inf, ok = ins["cluster_type"] - if !ok { - err = fmt.Errorf("umarshal failed. cluster_type not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - clusterType := inf.(string) - - inf, ok = ins["machine_type"] - if !ok { - err = fmt.Errorf("umarshal failed. machine_type not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - metaType := inf.(string) - - inf, ok = ins["receiver"] - if !ok { - err = fmt.Errorf("umarshal failed. receiver not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - slave := inf.([]interface{}) - - inf, ok = ins["proxyinstance_set"] - if !ok { - err = fmt.Errorf("umarshal failed. proxyinstance_set not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - proxy := inf.([]interface{}) - - cmdbClient, err := client.NewCmDBClient(&conf.DBConf.CMDB, conf.GetCloudId()) - if err != nil { - return nil, err - } - - hadbClient, err := client.NewHaDBClient(&conf.DBConf.HADB, conf.GetCloudId()) - if err != nil { - return nil, err - } - - swIns := MySQLSwitch{ - BaseSwitch: dbutil.BaseSwitch{ - Ip: ip, - Port: port, - IDC: idc, - Status: status, - App: app, - ClusterType: clusterType, - MetaType: metaType, - Cluster: cluster, - CmDBClient: cmdbClient, - HaDBClient: hadbClient, - }, - Role: role, - AllowedChecksumMaxOffset: conf.GMConf.GCM.AllowedChecksumMaxOffset, - AllowedSlaveDelayMax: conf.GMConf.GCM.AllowedSlaveDelayMax, - AllowedTimeDelayMax: conf.GMConf.GCM.AllowedTimeDelayMax, - ExecSlowKBytes: conf.GMConf.GCM.ExecSlowKBytes, - MySQLUser: conf.DBConf.MySQL.User, - MySQLPass: conf.DBConf.MySQL.Pass, - ProxyUser: conf.DBConf.MySQL.ProxyUser, - ProxyPass: conf.DBConf.MySQL.ProxyPass, - Timeout: conf.DBConf.MySQL.Timeout, - } - - for _, rawInfo := range slave { - mapInfo := rawInfo.(map[string]interface{}) - inf, ok = mapInfo["ip"] - if !ok { - err = fmt.Errorf("umarshal failed. slave ip not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - slaveIp := inf.(string) - inf, ok = mapInfo["port"] - if !ok { - err = fmt.Errorf("umarshal failed. slave port not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - slavePort := inf.(float64) - swIns.Slave = append(swIns.Slave, MySQLSlaveInfo{ - Ip: slaveIp, - Port: int(slavePort), - }) - } - - for _, rawInfo := range proxy { - mapInfo := rawInfo.(map[string]interface{}) - inf, ok = mapInfo["ip"] - if !ok { - err = fmt.Errorf("umarshal failed. proxy ip not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - proxyIp := inf.(string) - inf, ok = mapInfo["port"] - if !ok { - err = fmt.Errorf("umarshal failed. proxy port not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - proxyPort := inf.(float64) - inf, ok = mapInfo["admin_port"] - if !ok { - err = fmt.Errorf("umarshal failed. proxy port not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - proxyAdminPort := inf.(float64) - var status string - inf, ok = mapInfo["status"] - if !ok { - status = "" - } else { - status = inf.(string) - } - swIns.Proxy = append(swIns.Proxy, dbutil.ProxyInfo{ - Ip: proxyIp, - Port: int(proxyPort), - AdminPort: int(proxyAdminPort), - Status: status, - }) - } - - ret = append(ret, &swIns) - } - return ret, nil -} - -// DeserializeMySQL convert response info to detect info -func DeserializeMySQL(jsonInfo []byte, Conf *config.Config) (dbutil.DataBaseDetect, error) { - response := MySQLDetectResponse{} - err := json.Unmarshal(jsonInfo, &response) - if err != nil { - log.Logger.Errorf("json unmarshal failed. jsoninfo:\n%s\n, err:%s", string(jsonInfo), err.Error()) - return nil, err - } - - ret := NewMySQLDetectInstance2(&response, constvar.MySQL, Conf) - return ret, nil -} diff --git a/dbm-services/common/dbha/ha-module/dbmodule/mysql/MySQL_switch.go b/dbm-services/common/dbha/ha-module/dbmodule/mysql/MySQL_switch.go deleted file mode 100644 index 8f611e0de9..0000000000 --- a/dbm-services/common/dbha/ha-module/dbmodule/mysql/MySQL_switch.go +++ /dev/null @@ -1,598 +0,0 @@ -package mysql - -import ( - "fmt" - "strconv" - "strings" - "time" - - "dbm-services/common/dbha/ha-module/constvar" - "dbm-services/common/dbha/ha-module/dbutil" - "dbm-services/common/dbha/ha-module/log" - - "gorm.io/driver/mysql" - "gorm.io/gorm" -) - -// MySQLSwitch defined mysql switch struct -type MySQLSwitch struct { - dbutil.BaseSwitch - Role string - Slave []MySQLSlaveInfo - Proxy []dbutil.ProxyInfo - Entry dbutil.BindEntry - AllowedChecksumMaxOffset int - AllowedSlaveDelayMax int - AllowedTimeDelayMax int - ExecSlowKBytes int - MySQLUser string - MySQLPass string - ProxyUser string - ProxyPass string - Timeout int -} - -// MySQLSlaveInfo defined slave switch info -type MySQLSlaveInfo struct { - Ip string - Port int - BinlogFile string - BinlogPosition string -} - -// DelayInfo defined slave delay info -type DelayInfo struct { - // check whether SQL_Thread hang - SlaveDelay float64 `gorm:"column:slave_delay"` - // check whether IO_Thread hang - TimeDelay float64 `gorm:"column:time_delay"` -} - -// MySQLVariableInfo show variable's result struct -// not appropriate for string value -type MySQLVariableInfo struct { - VariableName string `gorm:"column:Variable_name"` - VariableValue uint64 `gorm:"column:Value"` -} - -// BinlogStatus binlog status info struct -type BinlogStatus struct { - MasterLogFileIndex int - RelayMasterLogFileIndex int - ReadMasterLogPos uint64 - ExecMasterLogPos uint64 - // RetrievedGtidSet string - // ExecutedGtidSet string - // MasterUuid string -} - -// SlaveStatus show slave status info struct -type SlaveStatus struct { - SlaveIoState string `gorm:"column:Slave_IO_State"` - MasterHost string `gorm:"column:Master_Host"` - MasterUser string `gorm:"column:Master_User"` - MasterPort int `gorm:"column:Master_Port"` - ConnectRetry int `gorm:"column:Connect_Retry"` - MasterLogFile string `gorm:"column:Master_Log_File"` - ReadMasterLogPos uint64 `gorm:"column:Read_Master_Log_Pos"` - RelayLogFile string `gorm:"column:Relay_Log_File"` - RelayLogPos uint64 `gorm:"column:Relay_Log_Pos"` - RelayMasterLogFile string `gorm:"column:Relay_Master_Log_File"` - SlaveIoRunning string `gorm:"column:Slave_IO_Running"` - SlaveSqlRunning string `gorm:"column:Slave_SQL_Running"` - ReplicateDoDb string `gorm:"column:Replicate_Do_DB"` - ReplicateIgnoreDb string `gorm:"column:Replicate_Ignore_DB"` - ReplicateDoTable string `gorm:"column:Replicate_Do_Table"` - ReplicateIgnoreTable string `gorm:"column:Replicate_Ignore_Table"` - ReplicateWildDoTable string `gorm:"column:Replicate_Wild_Do_Table"` - ReplicateWildIgnoreTable string `gorm:"column:Replicate_Wild_Ignore_Table"` - LastErrno int `gorm:"column:Last_Errno"` - LastError string `gorm:"column:Last_Error"` - SkipCounter int `gorm:"column:Skip_Counter"` - ExecMasterLogPos uint64 `gorm:"column:Exec_Master_Log_Pos"` - RelayLogSpace uint64 `gorm:"column:Relay_Log_Space"` - UntilCondition string `gorm:"column:Until_Condition"` - UntilLogFile string `gorm:"column:Until_Log_File"` - UntilLogPos uint64 `gorm:"column:Until_Log_Pos"` - MasterSslAllowed string `gorm:"column:Master_SSL_Allowed"` - MasterSslCaFile string `gorm:"column:Master_SSL_CA_File"` - MasterSslCaPath string `gorm:"column:Master_SSL_CA_Path"` - MasterSslCert string `gorm:"column:Master_SSL_Cert"` - MasterSslCipher string `gorm:"column:Master_SSL_Cipher"` - MasterSslKey string `gorm:"column:Master_SSL_Key"` - SecondsBehindMaster int `gorm:"column:Seconds_Behind_Master"` - MasterSslVerifyServerCert string `gorm:"column:Master_SSL_Verify_Server_Cert"` - LastIoErrno int `gorm:"column:Last_IO_Errno"` - LastIoError string `gorm:"column:Last_IO_Error"` - LastSqlErrno int `gorm:"column:Last_SQL_Errno"` - LastSqlError string `gorm:"column:Last_SQL_Error"` - ReplicateIgnoreServerIds string `gorm:"column:Replicate_Ignore_Server_Ids"` - MasterServerId uint64 `gorm:"column:Master_Server_Id"` - MasterUuid string `gorm:"column:Master_UUID"` - MasterInfoFile string `gorm:"column:Master_Info_File"` - SqlDelay uint64 `gorm:"column:SQL_Delay"` - SqlRemainingDelay string `gorm:"column:SQL_Remaining_Delay"` - SlaveSqlRunningState string `gorm:"column:Slave_SQL_Running_State"` - MasterRetryCount int `gorm:"column:Master_Retry_Count"` - MasterBind string `gorm:"column:Master_Bind"` - LastIoErrorTimestamp string `gorm:"column:Last_IO_Error_Timestamp"` - LastSqlErrorTimestamp string `gorm:"column:Last_SQL_Error_Timestamp"` - MasterSslCrl string `gorm:"column:Master_SSL_Crl"` - MasterSslCrlpath string `gorm:"column:Master_SSL_Crlpath"` - RetrievedGtidSet string `gorm:"column:Retrieved_Gtid_Set"` - ExecutedGtidSet string `gorm:"column:Executed_Gtid_Set"` - AutoPosition string `gorm:"column:Auto_Position"` - ReplicateWildParallelTable string `gorm:"column:Replicate_Wild_Parallel_Table"` -} - -// GetRole get mysql role type -func (ins *MySQLSwitch) GetRole() string { - return ins.Role -} - -// ShowSwitchInstanceInfo show mysql instance's switch info -func (ins *MySQLSwitch) ShowSwitchInstanceInfo() string { - str := fmt.Sprintf("<%s#%d IDC:%s Role:%s Status:%s Bzid:%s ClusterType:%s MachineType:%s>", - ins.Ip, ins.Port, ins.IDC, ins.Role, ins.Status, ins.App, ins.ClusterType, - ins.MetaType) - if len(ins.Slave) > 0 { - str = fmt.Sprintf("%s Switch from MASTER:<%s#%d> to SLAVE:<%s#%d>", - str, ins.Ip, ins.Port, ins.Slave[0].Ip, ins.Slave[0].Port) - } - return str -} - -// CheckSwitch check slave before switch -func (ins *MySQLSwitch) CheckSwitch() (bool, error) { - var err error - if ins.Role == constvar.MySQLSlave { - ins.ReportLogs(constvar.CHECK_SWITCH_INFO, "instance is slave, needn't check") - return false, nil - } else if ins.Role == constvar.MySQLRepeater { - ins.ReportLogs(constvar.CHECK_SWITCH_FAIL, "instance is repeater, dbha not support") - return false, err - } else if ins.Role == constvar.MySQLMaster { - log.Logger.Infof("info:{%s} is master", ins.ShowSwitchInstanceInfo()) - - log.Logger.Infof("check slave status. info{%s}", ins.ShowSwitchInstanceInfo()) - if len(ins.Slave) == 0 { - ins.ReportLogs(constvar.CHECK_SWITCH_FAIL, "no slave info found") - return false, err - } - ins.SetInfo(constvar.SWITCH_INFO_SLAVE_IP, ins.Slave[0].Ip) - ins.SetInfo(constvar.SWITCH_INFO_SLAVE_PORT, ins.Slave[0].Port) - err = ins.CheckSlaveStatus() - if err != nil { - ins.ReportLogs(constvar.CHECK_SWITCH_FAIL, err.Error()) - return false, err - } - - log.Logger.Infof("start to switch. info{%s}", ins.ShowSwitchInstanceInfo()) - - if len(ins.Proxy) == 0 { - // available instance usual without proxy - log.Logger.Infof("without proxy! info:{%s}", ins.ShowSwitchInstanceInfo()) - ins.ReportLogs(constvar.CHECK_SWITCH_INFO, "without proxy!") - return false, nil - } - } else { - err = fmt.Errorf("info:{%s} unknown role", ins.ShowSwitchInstanceInfo()) - log.Logger.Error(err) - ins.ReportLogs(constvar.CHECK_SWITCH_FAIL, "instance unknown role") - return false, err - } - - ins.ReportLogs(constvar.CHECK_SWITCH_INFO, "mysql check switch ok") - return true, nil -} - -// DoSwitch do switch from master to slave -// 1. refresh all proxys's backend to 1.1.1.1 -// 2. reset slave -// 3. get slave's consistent binlog pos -// 4. refresh backend to alive(slave) mysql -func (ins *MySQLSwitch) DoSwitch() error { - successFlag := true - ins.ReportLogs(constvar.SWITCH_INFO, "one phase:update all proxy's backend to 1.1.1.1 first") - for _, proxyIns := range ins.Proxy { - ins.ReportLogs(constvar.SWITCH_INFO, fmt.Sprintf("try to flush proxy:[%s:%d]'s backends to 1.1.1.1", - proxyIns.Ip, proxyIns.Port)) - err := SwitchProxyBackendAddress(proxyIns.Ip, proxyIns.AdminPort, ins.ProxyUser, - ins.ProxyPass, "1.1.1.1", 3306) - if err != nil { - ins.ReportLogs(constvar.SWITCH_FAIL, fmt.Sprintf("flush proxy's backend failed: %s", err.Error())) - return fmt.Errorf("flush proxy's backend to 1.1.1.1 failed") - } - ins.ReportLogs(constvar.SWITCH_INFO, fmt.Sprintf("flush proxy:[%s:%d]'s backends to 1.1.1.1 success", - proxyIns.Ip, proxyIns.Port)) - } - ins.ReportLogs(constvar.SWITCH_INFO, "all proxy flush backends to 1.1.1.1 success") - - ins.ReportLogs(constvar.SWITCH_INFO, "try to reset slave") - binlogFile, binlogPosition, err := ins.ResetSlave() - if err != nil { - ins.ReportLogs(constvar.SWITCH_FAIL, fmt.Sprintf("reset slave failed:%s", err.Error())) - return fmt.Errorf("reset slave failed") - } - ins.ReportLogs(constvar.SWITCH_INFO, "reset slave success") - ins.Slave[0].BinlogFile = binlogFile - ins.Slave[0].BinlogPosition = strconv.Itoa(int(binlogPosition)) - - ins.ReportLogs(constvar.SWITCH_INFO, "two phase: update all proxy's backend to new master") - for _, proxyIns := range ins.Proxy { - ins.ReportLogs(constvar.SWITCH_INFO, fmt.Sprintf("try to flush proxy[%s:%d]'s backend to [%s:%d]", - proxyIns.Ip, proxyIns.Port, ins.Slave[0].Ip, ins.Slave[0].Port)) - err = SwitchProxyBackendAddress(proxyIns.Ip, proxyIns.AdminPort, ins.ProxyUser, - ins.ProxyPass, ins.Slave[0].Ip, ins.Slave[0].Port) - if err != nil { - ins.ReportLogs(constvar.SWITCH_FAIL, fmt.Sprintf("flush proxy[%s:%d]'s backend to new master failed:%s", - proxyIns.Ip, proxyIns.Port, err.Error())) - successFlag = false - } - ins.ReportLogs(constvar.SWITCH_INFO, "flush proxy's backend to new master success") - } - - if !successFlag { - return fmt.Errorf("not all proxy's backend switch to new master") - } - - ins.ReportLogs(constvar.SWITCH_INFO, "all proxy flush backends to new master success") - return nil -} - -// RollBack do switch rollback -func (ins *MySQLSwitch) RollBack() error { - return nil -} - -// UpdateMetaInfo swap master, slave 's meta info in cmdb -func (ins *MySQLSwitch) UpdateMetaInfo() error { - // TODO: default 1 master 1 slave, for support multi slave, slave need to add switch_weight - // for chose a slave to failover. - err := ins.CmDBClient.SwapMySQLRole(ins.Ip, ins.Port, - ins.Slave[0].Ip, ins.Slave[0].Port) - if err != nil { - updateErrLog := fmt.Sprintf("swap mysql role failed. err:%s", err.Error()) - log.Logger.Errorf("%s, info:{%s}", updateErrLog, ins.ShowSwitchInstanceInfo()) - ins.ReportLogs(constvar.UPDATEMETA_FAIL, updateErrLog) - return err - } - ins.ReportLogs(constvar.UPDATEMETA_INFO, "update meta info success") - return nil -} - -// CheckSlaveStatus check whether slave satisfy to switch -func (ins *MySQLSwitch) CheckSlaveStatus() error { - // check_slave_status - ins.ReportLogs(constvar.CHECK_SWITCH_INFO, "try to check slave status info.") - if err := ins.CheckSlaveSlow(); err != nil { - return fmt.Errorf("slave delay too much. err:%s", err.Error()) - } - - needCheck, err := ins.FindUsefulDatabase() - if err != nil { - log.Logger.Errorf("found user-created database failed. err:%s, info:{%s}", err.Error(), - ins.ShowSwitchInstanceInfo()) - } - - ins.ReportLogs(constvar.CHECK_SWITCH_INFO, "try to check slave checksum info.") - checksumCnt, checksumFail, slaveDelay, timeDelay, err := ins.GetMySQLSlaveCheckSum() - if err != nil { - log.Logger.Errorf("check slave checksum info failed. err:%s, info:{%s}", err.Error(), - ins.ShowSwitchInstanceInfo()) - return err - } - - ins.ReportLogs(constvar.CHECK_SWITCH_INFO, fmt.Sprintf("checksumCnt:%d, checksumFail:%d, slaveDelay:%d, timeDelay:%d", - checksumCnt, checksumFail, slaveDelay, timeDelay)) - - if needCheck { - if ins.Status == constvar.AVAILABLE { - checksumCnt = 1 - checksumFail = 0 - slaveDelay = 0 - timeDelay = 0 - ins.ReportLogs(constvar.SWITCH_INFO, "instance is available, skip check delay and checksum") - } - - if checksumCnt == 0 { - return fmt.Errorf("none checksum done on this db") - } - - log.Logger.Debugf("checksum have done on slave. info:{%s}", ins.ShowSwitchInstanceInfo()) - - if checksumFail > ins.AllowedChecksumMaxOffset { - return fmt.Errorf("too many fail on tables checksum(%d > %d)", checksumFail, - ins.AllowedChecksumMaxOffset) - } - ins.ReportLogs(constvar.CHECK_SWITCH_INFO, fmt.Sprintf("checksum failedCnt[%d] in allowed range[%d]", - checksumFail, ins.AllowedChecksumMaxOffset)) - - } else { - ins.ReportLogs(constvar.CHECK_SWITCH_INFO, "none user-created database, skip check checksum") - return nil - } - - if slaveDelay >= ins.AllowedSlaveDelayMax { - return fmt.Errorf("SQL_Thread delay on slave too large than allowed range(%d >= %d)", slaveDelay, - ins.AllowedSlaveDelayMax) - } - ins.ReportLogs(constvar.CHECK_SWITCH_INFO, fmt.Sprintf("SQL_THread delay [%d] in allowed range[%d]", - slaveDelay, ins.AllowedSlaveDelayMax)) - - if timeDelay >= ins.AllowedTimeDelayMax { - return fmt.Errorf("IO_Thread delay on slave too large than master(%d >= %d)", timeDelay, - ins.AllowedTimeDelayMax) - } - ins.ReportLogs(constvar.CHECK_SWITCH_INFO, fmt.Sprintf("IO_Thread delay [%d] in allowed range[%d]", - timeDelay, ins.AllowedTimeDelayMax)) - - return nil -} - -// GetMySQLSlaveCheckSum return value:checksum, checktime, slave_delay, time_delay -func (ins *MySQLSwitch) GetMySQLSlaveCheckSum() (int, int, int, int, error) { - var ( - checksumCnt, checksumFailCnt int - delayInfo DelayInfo - ) - ip := ins.Slave[0].Ip - port := ins.Slave[0].Port - connParam := fmt.Sprintf("%s:%s@(%s:%d)/%s", ins.MySQLUser, ins.MySQLPass, - ip, port, "infodba_schema") - db, err := gorm.Open(mysql.Open(connParam), &gorm.Config{ - Logger: log.GormLogger, - }) - if err != nil { - log.Logger.Errorf("open mysql failed. ip:%s, port:%d, err:%s", ip, port, err.Error()) - return 0, 0, 0, 0, err - } - defer func() { - con, _ := db.DB() - if err = con.Close(); err != nil { - log.Logger.Warnf("close connect[%s#%d] failed:%s", ip, port, err.Error()) - } - }() - - slaveStatus := SlaveStatus{} - err = db.Raw("show slave status").Scan(&slaveStatus).Error - if err != nil { - log.Logger.Errorf("show slave status failed. err:%s", err.Error()) - return 0, 0, 0, 0, err - } - log.Logger.Debugf("slave status info:%v", slaveStatus) - - err = db.Raw(constvar.CheckSumSql).Scan(&checksumCnt).Error - if err != nil { - log.Logger.Errorf("mysql get checksumCnt failed. ip:%s, port:%d, err:%s", ip, port, err.Error()) - return 0, 0, 0, 0, err - } - - err = db.Raw(constvar.CheckSumFailSql).Scan(&checksumFailCnt).Error - if err != nil { - log.Logger.Errorf("mysql get checksumFailCnt failed. ip:%s, port:%d, err:%s", ip, port, err.Error()) - return 0, 0, 0, 0, err - } - - err = db.Raw(constvar.CheckDelaySql, slaveStatus.MasterServerId).Scan(&delayInfo).Error - if err != nil { - log.Logger.Errorf("mysql get delay info failed. ip:%s, port:%d, err:%s", ip, port, err.Error()) - return 0, 0, 0, 0, err - } - - return checksumCnt, checksumFailCnt, int(delayInfo.SlaveDelay), int(delayInfo.TimeDelay), nil -} - -// FindUsefulDatabase found user created databases exclude system database -// return val: -// -// true: found -// false: not found -func (ins *MySQLSwitch) FindUsefulDatabase() (bool, error) { - var systemDbs = map[string]bool{ - "mysql": true, - "information_schema": true, - "performance_schema": true, - "test": true, - "infodba_schema": true, - "sys": true, - } - ip := ins.Slave[0].Ip - port := ins.Slave[0].Port - connParam := fmt.Sprintf("%s:%s@(%s:%d)/%s", ins.MySQLUser, ins.MySQLPass, - ip, port, "infodba_schema") - db, err := gorm.Open(mysql.Open(connParam), &gorm.Config{ - Logger: log.GormLogger, - }) - if err != nil { - log.Logger.Errorf("open mysql failed. ip:%s, port:%d, err:%s", ip, port, err.Error()) - return false, err - } - var databases []string - - showDatabaseSql := "show databases" - err = db.Raw(showDatabaseSql).Scan(&databases).Error - if err != nil { - log.Logger.Errorf("show databases faled. ip:%s, port:%d, err:%s", ip, port, err.Error()) - return false, err - } - - for _, database := range databases { - if _, ok := systemDbs[database]; !ok { - return true, nil - } - } - log.Logger.Infof("no user-created database found") - - return false, nil -} - -// CheckSlaveSlow check whether slave replication delay -func (ins *MySQLSwitch) CheckSlaveSlow() error { - ip := ins.Slave[0].Ip - port := ins.Slave[0].Port - connParam := fmt.Sprintf("%s:%s@(%s:%d)/%s", ins.MySQLUser, ins.MySQLPass, ip, port, "infodba_schema") - db, err := gorm.Open(mysql.Open(connParam), &gorm.Config{ - Logger: log.GormLogger, - }) - if err != nil { - log.Logger.Errorf("open mysql failed. ip:%s, port:%d, err:%s", ip, port, err.Error()) - return err - } - defer func() { - con, _ := db.DB() - if err = con.Close(); err != nil { - log.Logger.Warnf("close connect[%s#%d] failed:%s", ip, port, err.Error()) - } - }() - - var maxBinlogSize MySQLVariableInfo - err = db.Raw("show variables like 'max_binlog_size'").Scan(&maxBinlogSize).Error - if err != nil { - log.Logger.Errorf("get mas_binlog_size failed. ip:%s, port:%d, err:%s", ip, port, err.Error()) - return err - } - - binlogSizeMByte := maxBinlogSize.VariableValue / (1024 * 1024) - log.Logger.Infof("the slave max_binlog_size value is %d M!", binlogSizeMByte) - - status, err := GetSlaveStatus(db) - if err != nil { - log.Logger.Errorf("get slave status failed. err:%s", err.Error()) - return err - } - ins.ReportLogs(constvar.CHECK_SWITCH_INFO, fmt.Sprintf("Relay_Master_Log_File_Index:%d, Exec_Master_Log_Pos:%d", - status.RelayMasterLogFileIndex, status.ReadMasterLogPos)) - - execSlowKBytes := binlogSizeMByte*1024*uint64(status.MasterLogFileIndex-status.RelayMasterLogFileIndex) - - status.ExecMasterLogPos/1024 + status.ReadMasterLogPos/1024 - - loop := 10 - if execSlowKBytes > uint64(ins.ExecSlowKBytes) { - ins.ReportLogs(constvar.CHECK_SWITCH_INFO, fmt.Sprintf("slave delay kbytes[%d] large than allowed[%d],"+ - "try to loop wait", execSlowKBytes, ins.ExecSlowKBytes)) - var i int - for i = 0; i < loop; i++ { - time.Sleep(3 * time.Second) - tmpStatus, err := GetSlaveStatus(db) - if err != nil { - log.Logger.Errorf("get slave status failed. err:%s", err.Error()) - return err - } - execSlowKBytes = binlogSizeMByte*1024* - uint64(tmpStatus.MasterLogFileIndex-tmpStatus.RelayMasterLogFileIndex) - - tmpStatus.ExecMasterLogPos/1024 + tmpStatus.ReadMasterLogPos/1024 - if execSlowKBytes <= uint64(ins.ExecSlowKBytes) { - // todo: for GTID - break - } - log.Logger.Warnf("loop[%d],slave slower too much: Execute %dK,default value is:%d", - i, execSlowKBytes, ins.ExecSlowKBytes) - } - if i == loop { - return fmt.Errorf("after loop wait, slave still slower too much: Execute %dK, default value is:%d", - execSlowKBytes, ins.ExecSlowKBytes) - } - } - ins.ReportLogs(constvar.CHECK_SWITCH_INFO, fmt.Sprintf("check slave[%s:%d] status success", ip, port)) - return nil -} - -// GetSlaveStatus get slave status info -func GetSlaveStatus(db *gorm.DB) (BinlogStatus, error) { - slaveStatus := SlaveStatus{} - ret := BinlogStatus{} - err := db.Raw("show slave status").Scan(&slaveStatus).Error - if err != nil { - log.Logger.Errorf("show slave status failed. err:%s", err.Error()) - return BinlogStatus{}, err - } - - if slaveStatus.SlaveIoRunning != "Yes" || slaveStatus.SlaveSqlRunning != "Yes" { - return BinlogStatus{}, fmt.Errorf("slave's SQL_thread[%s], IO_Thread[%s] is abnormal", - slaveStatus.SlaveSqlRunning, slaveStatus.SlaveIoRunning) - } - - if !strings.Contains(slaveStatus.MasterLogFile, ".") { - log.Logger.Errorf("can't find master log file. master_log_file:%s", - slaveStatus.MasterLogFile) - return BinlogStatus{}, fmt.Errorf("can't find master log file") - } - - ret.MasterLogFileIndex, err = strconv.Atoi(strings.Split(slaveStatus.MasterLogFile, ".")[1]) - if err != nil { - log.Logger.Errorf("split master log file failed. err:%s, master_log_file:%s", err.Error(), - slaveStatus.MasterLogFile) - } - - if !strings.Contains(slaveStatus.RelayMasterLogFile, ".") { - log.Logger.Errorf("can't find master log file. relay_master_log_file:%s", - slaveStatus.RelayMasterLogFile) - return BinlogStatus{}, fmt.Errorf("can't find master log file") - } - - ret.RelayMasterLogFileIndex, err = strconv.Atoi(strings.Split(slaveStatus.RelayMasterLogFile, ".")[1]) - if err != nil { - log.Logger.Errorf("split master log file failed. relay_master_log_file:%s", - slaveStatus.RelayMasterLogFile) - return BinlogStatus{}, err - } - - ret.ReadMasterLogPos = slaveStatus.ReadMasterLogPos - ret.ExecMasterLogPos = slaveStatus.ExecMasterLogPos - // ret.RetrievedGtidSet = slaveStatus.RetrievedGtidSet - // ret.ExecutedGtidSet = slaveStatus.ExecutedGtidSet - // ret.MasterUuid = slaveStatus.MasterUuid - return ret, nil -} - -// MasterStatus master status struct -type MasterStatus struct { - File string - Position uint64 - BinlogDoDB string - BinlogIgnoreDB string - ExecutedGtidSet string -} - -// ResetSlave do reset slave -func (ins *MySQLSwitch) ResetSlave() (string, uint64, error) { - slaveIp := ins.Slave[0].Ip - slavePort := ins.Slave[0].Port - log.Logger.Infof("gonna RESET SLAVE on %s:%d", slaveIp, slavePort) - - connParam := fmt.Sprintf("%s:%s@(%s:%d)/%s", ins.MySQLUser, ins.MySQLPass, slaveIp, slavePort, "infodba_schema") - db, err := gorm.Open(mysql.Open(connParam), &gorm.Config{ - Logger: log.GormLogger, - }) - if err != nil { - log.Logger.Errorf("open mysql failed. ip:%s, port:%d, err:%s", slaveIp, slavePort, err.Error()) - return "", 0, err - } - - stopSql := "stop slave" - masterSql := "show master status" - resetSql := "reset slave /*!50516 all */" - - err = db.Exec(stopSql).Error - if err != nil { - return "", 0, fmt.Errorf("stop slave failed. err:%s", err.Error()) - } - log.Logger.Infof("execute %s success", stopSql) - - var masterStatus MasterStatus - err = db.Raw(masterSql).Scan(&masterStatus).Error - if err != nil { - return "", 0, fmt.Errorf("show master status failed, err:%s", err.Error()) - } - log.Logger.Infof("get new master binlog info succeed. binlog_file:%s, binlog_pos:%d", masterStatus.File, - masterStatus.Position) - - err = db.Exec(resetSql).Error - if err != nil { - return "", 0, fmt.Errorf("reset slave failed. err:%s", err.Error()) - } - log.Logger.Infof("executed %s on %s:%d successd", resetSql, slaveIp, slavePort) - - return masterStatus.File, masterStatus.Position, nil -} diff --git a/dbm-services/common/dbha/ha-module/dbmodule/mysql/mysql.go b/dbm-services/common/dbha/ha-module/dbmodule/mysql/mysql.go deleted file mode 100644 index b65d989eef..0000000000 --- a/dbm-services/common/dbha/ha-module/dbmodule/mysql/mysql.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package mysql TODO -package mysql diff --git a/dbm-services/common/dbha/ha-module/dbmodule/redis/predixy_callback.go b/dbm-services/common/dbha/ha-module/dbmodule/redis/predixy_callback.go deleted file mode 100644 index 5b897abe18..0000000000 --- a/dbm-services/common/dbha/ha-module/dbmodule/redis/predixy_callback.go +++ /dev/null @@ -1,94 +0,0 @@ -package redis - -import ( - "encoding/json" - - "dbm-services/common/dbha/ha-module/config" - "dbm-services/common/dbha/ha-module/constvar" - "dbm-services/common/dbha/ha-module/dbutil" - "dbm-services/common/dbha/ha-module/log" -) - -// NewPredixyInstanceByCmdb Agent通过CMDB获取的信息来生成需要探测的实例 -func NewPredixyInstanceByCmdb(instances []interface{}, - conf *config.Config) ([]dbutil.DataBaseDetect, error) { - var ( - err error - unmarshalIns []*RedisDetectInfoFromCmDB - ret []dbutil.DataBaseDetect - ) - - unmarshalIns, err = UnMarshalRedisInstanceByCmdb( - instances, constvar.TendisplusClusterType, - constvar.PredixyMetaType) - - if err != nil { - return nil, err - } - - for _, uIns := range unmarshalIns { - ret = append(ret, NewPredixyDetectInstance(uIns, conf)) - } - - return ret, err -} - -// DeserializePredixy 反序列化从Agent上报上来的故障实例 -func DeserializePredixy(jsonInfo []byte, conf *config.Config) (dbutil.DataBaseDetect, error) { - response := RedisDetectResponse{} - err := json.Unmarshal(jsonInfo, &response) - if err != nil { - log.Logger.Errorf("json unmarshal failed. jsoninfo:\n%s\n, err:%s", - string(jsonInfo), err.Error()) - return nil, err - } - ret := NewPredixyDetectInstanceFromRsp(&response, conf) - return ret, nil -} - -// NewPredixySwitchInstance TODO -func NewPredixySwitchInstance(instances []interface{}, - conf *config.Config) ([]dbutil.DataBaseSwitch, error) { - var err error - var ret []dbutil.DataBaseSwitch - for _, v := range instances { - swIns, err := CreateRedisProxySwitchInfo(v, conf) - if err != nil { - log.Logger.Errorf("parse predixy switch instance failed,err:%s", - err.Error()) - continue - } - - if swIns.MetaType != constvar.PredixyMetaType { - log.Logger.Errorf("Create predixy switch while the metaType[%s] != %s", - swIns.MetaType, constvar.PredixyMetaType) - continue - } - - if swIns.CheckFetchEntryDetail() { - edErr := swIns.GetEntryDetailInfo() - if edErr != nil { - log.Logger.Errorf("GetEntryDetail failed in NewPredixySwitch,err:%s", - edErr.Error()) - } - } - - pw := PredixySwitch{ - RedisProxySwitchInfo: *swIns, - } - - passwd, err := GetInstancePassByCluster( - constvar.Predixy, pw.Cluster, conf, - ) - if err != nil { - log.Logger.Errorf("get predixy switch passwd failed,err:%s,info:%s", - err.Error(), pw.ShowSwitchInstanceInfo()) - } else { - log.Logger.Infof("get predixy switch passwd[%s]", passwd) - pw.Pass = passwd - } - ret = append(ret, &pw) - } - - return ret, err -} diff --git a/dbm-services/common/dbha/ha-module/dbmodule/redis/predixy_detect.go b/dbm-services/common/dbha/ha-module/dbmodule/redis/predixy_detect.go index 359f4aedfa..e4c10c930c 100644 --- a/dbm-services/common/dbha/ha-module/dbmodule/redis/predixy_detect.go +++ b/dbm-services/common/dbha/ha-module/dbmodule/redis/predixy_detect.go @@ -12,12 +12,12 @@ import ( "dbm-services/common/dbha/ha-module/util" ) -// PredixyDetectInstance TODO +// PredixyDetectInstance predixy detect instance type PredixyDetectInstance struct { RedisDetectBase } -// Detection TODO +// Detection detect predixy instance func (ins *PredixyDetectInstance) Detection() error { err := ins.DoPredixyDetection() if err == nil && ins.Status == constvar.DBCheckSuccess { @@ -92,7 +92,7 @@ func (ins *PredixyDetectInstance) DoPredixyDetection() error { } } -// Serialization TODO +// Serialization serialize predixy detect instance func (ins *PredixyDetectInstance) Serialization() ([]byte, error) { response := RedisDetectResponse{ BaseDetectDBResponse: ins.NewDBResponse(), @@ -107,25 +107,29 @@ func (ins *PredixyDetectInstance) Serialization() ([]byte, error) { return resByte, nil } -// ShowDetectionInfo TODO +// ShowDetectionInfo show detect instance information func (ins *PredixyDetectInstance) ShowDetectionInfo() string { str := fmt.Sprintf("ip:%s, port:%d, status:%s, DBType:%s", ins.Ip, ins.Port, ins.Status, ins.DBType) return str } -// NewPredixyDetectInstance TODO +// NewPredixyDetectInstance create predixy detect ins, +// +// used by FetchDBCallback func NewPredixyDetectInstance(ins *RedisDetectInfoFromCmDB, conf *config.Config) *PredixyDetectInstance { return &PredixyDetectInstance{ - RedisDetectBase: *GetDetectBaseByInfo(ins, constvar.Predixy, conf), + RedisDetectBase: *GetDetectBaseByInfo(ins, constvar.PredixyMetaType, conf), } } -// NewPredixyDetectInstanceFromRsp TODO +// NewPredixyDetectInstanceFromRsp create predixy detect ins, +// +// used by gm/DeserializeCallback func NewPredixyDetectInstanceFromRsp(ins *RedisDetectResponse, conf *config.Config) *PredixyDetectInstance { return &PredixyDetectInstance{ - RedisDetectBase: *GetDetectBaseByRsp(ins, constvar.Predixy, conf), + RedisDetectBase: *GetDetectBaseByRsp(ins, constvar.PredixyMetaType, conf), } } diff --git a/dbm-services/common/dbha/ha-module/dbmodule/redis/predixy_switch.go b/dbm-services/common/dbha/ha-module/dbmodule/redis/predixy_switch.go index 7f7d270cd3..1aeccf24c2 100644 --- a/dbm-services/common/dbha/ha-module/dbmodule/redis/predixy_switch.go +++ b/dbm-services/common/dbha/ha-module/dbmodule/redis/predixy_switch.go @@ -7,19 +7,19 @@ import ( "dbm-services/common/dbha/ha-module/log" ) -// PredixySwitch TODO +// PredixySwitch predixy switch instance type PredixySwitch struct { RedisProxySwitchInfo } -// CheckSwitch TODO +// CheckSwitch no nothing to check func (ins *PredixySwitch) CheckSwitch() (bool, error) { return true, nil } -// DoSwitch TODO +// DoSwitch kick predixy from gateway func (ins *PredixySwitch) DoSwitch() error { - ins.ReportLogs(constvar.SWITCH_INFO, + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("handle predixy switch[%s:%d]", ins.Ip, ins.Port)) err := ins.KickOffDns() cErr := ins.KickOffClb() @@ -27,25 +27,25 @@ func (ins *PredixySwitch) DoSwitch() error { if err != nil { predixyErrLog := fmt.Sprintf("Predixy kick dns failed,err:%s", err.Error()) log.Logger.Errorf("%s info:%s", predixyErrLog, ins.ShowSwitchInstanceInfo()) - ins.ReportLogs(constvar.SWITCH_FAIL, predixyErrLog) + ins.ReportLogs(constvar.FailResult, predixyErrLog) return err } if cErr != nil { predixyErrLog := fmt.Sprintf("Predixy kick clb failed,err:%s", cErr.Error()) log.Logger.Errorf("%s info:%s", predixyErrLog, ins.ShowSwitchInstanceInfo()) - ins.ReportLogs(constvar.SWITCH_FAIL, predixyErrLog) + ins.ReportLogs(constvar.FailResult, predixyErrLog) return cErr } if pErr != nil { predixyErrLog := fmt.Sprintf("Predixy kick polaris failed,err:%s", pErr.Error()) log.Logger.Errorf("%s info:%s", predixyErrLog, ins.ShowSwitchInstanceInfo()) - ins.ReportLogs(constvar.SWITCH_FAIL, predixyErrLog) + ins.ReportLogs(constvar.FailResult, predixyErrLog) return pErr } succLog := fmt.Sprintf("Predixy do switch ok,dns[%t] clb[%t] polaris[%t]", ins.ApiGw.DNSFlag, ins.ApiGw.CLBFlag, ins.ApiGw.PolarisFlag) - ins.ReportLogs(constvar.SWITCH_INFO, succLog) + ins.ReportLogs(constvar.InfoResult, succLog) return nil } @@ -54,12 +54,12 @@ func (ins *PredixySwitch) RollBack() error { return nil } -// UpdateMetaInfo TODO +// UpdateMetaInfo nothing to update func (ins *PredixySwitch) UpdateMetaInfo() error { return nil } -// ShowSwitchInstanceInfo TODO +// ShowSwitchInstanceInfo show predixy switch information func (ins *PredixySwitch) ShowSwitchInstanceInfo() string { format := `<%s#%d IDC:%s Status:%s App:%s ClusterType:%s MachineType:%s Cluster:%s> switch` str := fmt.Sprintf( diff --git a/dbm-services/common/dbha/ha-module/dbmodule/redis/redis_base.go b/dbm-services/common/dbha/ha-module/dbmodule/redis/redis_base.go index f16b5ae412..35e78a7ff6 100644 --- a/dbm-services/common/dbha/ha-module/dbmodule/redis/redis_base.go +++ b/dbm-services/common/dbha/ha-module/dbmodule/redis/redis_base.go @@ -1,6 +1,7 @@ package redis import ( + "encoding/json" "fmt" "math/rand" "strconv" @@ -15,70 +16,39 @@ import ( "dbm-services/common/dbha/ha-module/util" ) -// PolarisInfo TODO -type PolarisInfo struct { - Namespace string - Service string - Token string -} - -// CLBInfo TODO -type CLBInfo struct { - Region string - LoadBalanceId string - ListenId string -} - -// DNSInfo TODO -type DNSInfo struct { - Domain string -} - -// GWInfo TODO +// GWInfo the gateway information type GWInfo struct { - PolarisFlag bool - Polaris []PolarisInfo - CLBFlag bool - CLB []CLBInfo - DNSFlag bool - DNS []DNSInfo + PolarisFlag bool + CLBFlag bool + DNSFlag bool + DNSForword bool + ServiceEntry dbutil.BindEntry } -// RedisSwitchInfo TODO +// RedisSwitchInfo redis switch instance information type RedisSwitchInfo struct { dbutil.BaseSwitch - AdminPort int - ApiGw GWInfo - DnsClient *client.NameServiceClient - PolarisGWClient *client.NameServiceClient - MasterConf string - SlaveConf string - Proxy []dbutil.ProxyInfo - Slave []RedisSlaveInfo - Pass string - Timeout int + AdminPort int + Proxy []dbutil.ProxyInfo + Slave []dbutil.SlaveInfo + Pass string + Timeout int } -// RedisSlaveInfo TODO -type RedisSlaveInfo struct { - Ip string `json:"ip"` - Port int `json:"port"` -} - -// RedisDetectBase TODO +// RedisDetectBase redis detect information type RedisDetectBase struct { dbutil.BaseDetectDB Pass string Timeout int } -// RedisDetectResponse TODO +// RedisDetectResponse redis detect response type RedisDetectResponse struct { dbutil.BaseDetectDBResponse Pass string `json:"pass"` } -// RedisDetectInfoFromCmDB TODO +// RedisDetectInfoFromCmDB redis detect information from cmdb type RedisDetectInfoFromCmDB struct { Ip string Port int @@ -89,14 +59,12 @@ type RedisDetectInfoFromCmDB struct { Cluster string } -// RedisProxySwitchInfo TODO +// RedisProxySwitchInfo redis proxy switch information type RedisProxySwitchInfo struct { dbutil.BaseSwitch - AdminPort int - ApiGw GWInfo - DnsClient *client.NameServiceClient - PolarisGWClient *client.NameServiceClient - Pass string + AdminPort int + ApiGw GWInfo + Pass string } // CheckSSH redis do ssh check @@ -113,14 +81,80 @@ func (ins *RedisDetectBase) CheckSSH() error { return nil } -// ShowSwitchInstanceInfo TODO +// GetType return dbType +func (ins *RedisDetectBase) GetType() types.DBType { + return ins.DBType +} + +// GetDetectType return clusterType +func (ins *RedisDetectBase) GetDetectType() string { + return ins.ClusterType +} + +// GetDetectBaseByInfo get detect instance by cmdb +func GetDetectBaseByInfo(ins *RedisDetectInfoFromCmDB, + dbType string, conf *config.Config) *RedisDetectBase { + passwd := GetRedisMachinePasswd(ins.App, conf) + return &RedisDetectBase{ + BaseDetectDB: dbutil.BaseDetectDB{ + Ip: ins.Ip, + Port: ins.Port, + App: ins.App, + DBType: types.DBType(dbType), + ReporterTime: time.Unix(0, 0), + ReportInterval: conf.AgentConf.ReportInterval + rand.Intn(20), + Status: constvar.DBCheckSuccess, + Cluster: ins.Cluster, + ClusterType: ins.ClusterType, + SshInfo: dbutil.Ssh{ + Port: conf.SSH.Port, + User: conf.SSH.User, + Pass: passwd, + Dest: conf.SSH.Dest, + Timeout: conf.SSH.Timeout, + }, + }, + Pass: ins.Pass, + Timeout: conf.DBConf.Redis.Timeout, + } +} + +// GetDetectBaseByRsp get detect instance by agent response +func GetDetectBaseByRsp(ins *RedisDetectResponse, + dbType string, conf *config.Config) *RedisDetectBase { + passwd := GetRedisMachinePasswd(ins.App, conf) + return &RedisDetectBase{ + BaseDetectDB: dbutil.BaseDetectDB{ + Ip: ins.DBIp, + Port: ins.DBPort, + App: ins.App, + DBType: types.DBType(dbType), + ReporterTime: time.Unix(0, 0), + ReportInterval: conf.AgentConf.ReportInterval + rand.Intn(20), + Status: types.CheckStatus(ins.Status), + Cluster: ins.Cluster, + ClusterType: ins.ClusterType, + SshInfo: dbutil.Ssh{ + Port: conf.SSH.Port, + User: conf.SSH.User, + Pass: passwd, + Dest: conf.SSH.Dest, + Timeout: conf.SSH.Timeout, + }, + }, + Pass: ins.Pass, + Timeout: conf.DBConf.Redis.Timeout, + } +} + +// ShowSwitchInstanceInfo show instance information func (ins *RedisProxySwitchInfo) ShowSwitchInstanceInfo() string { str := fmt.Sprintf("ip:%s, port:%d, IDC:%s, status:%s, app:%s, cluster_type:%s, machine_type:%s", ins.Ip, ins.Port, ins.IDC, ins.Status, ins.App, ins.ClusterType, ins.MetaType) return str } -// KickOffDns TODO +// KickOffDns kick instance from dns func (ins *RedisProxySwitchInfo) KickOffDns() error { if !ins.ApiGw.DNSFlag { log.Logger.Infof("no need kickDNS,info:%s", @@ -128,35 +162,9 @@ func (ins *RedisProxySwitchInfo) KickOffDns() error { return nil } - for _, dnsInfo := range ins.ApiGw.DNS { - ipInfos, err := ins.DnsClient.GetDomainInfoByDomain(dnsInfo.Domain) - if err != nil { - log.Logger.Errorf("get domain info by domain name failed. err:%s, info:{%s}", - err.Error(), ins.ShowSwitchInstanceInfo()) - return err - } - - if len(ipInfos) == 0 { - log.Logger.Errorf("domain name: %s without ip. info:{%s}", - dnsInfo.Domain, ins.ShowSwitchInstanceInfo()) - return fmt.Errorf("domain name: %s without ip", dnsInfo.Domain) - } else if len(ipInfos) == 1 { - log.Logger.Warnf("domain name: %s only one ip. so we skip it. info:{%s}", - dnsInfo.Domain, ins.ShowSwitchInstanceInfo()) - } else { - err = ins.DnsClient.DeleteDomain( - dnsInfo.Domain, ins.App, ins.Ip, ins.Port, - ) - if err != nil { - log.Logger.Errorf("delete domain %s failed. err:%s, info:{%s}", - dnsInfo.Domain, err.Error(), ins.ShowSwitchInstanceInfo()) - return err - } - log.Logger.Infof("delete domain %s success. info:{%s}", - dnsInfo.Domain, ins.ShowSwitchInstanceInfo()) - } - } - return nil + return ins.DeleteNameService(dbutil.BindEntry{ + Dns: ins.ApiGw.ServiceEntry.Dns, + }) } // KickOffClb TODO @@ -167,31 +175,10 @@ func (ins *RedisProxySwitchInfo) KickOffClb() error { return nil } - for _, clbInfo := range ins.ApiGw.CLB { - ips, err := ins.PolarisGWClient.ClbGetTargets( - clbInfo.Region, clbInfo.LoadBalanceId, clbInfo.ListenId, - ) - if err != nil { - log.Logger.Errorf("call ClbGetTargets failed,info:%s", - ins.ShowSwitchInstanceInfo()) - return err - } - - addr := fmt.Sprintf("%s:%d", ins.Ip, ins.Port) - if len(ips) > 1 { - err := ins.PolarisGWClient.ClbDeRegister( - clbInfo.Region, clbInfo.LoadBalanceId, clbInfo.ListenId, addr, - ) - if err != nil { - log.Logger.Errorf("Kickoff %s from clb failed,info:%s", - addr, ins.ShowSwitchInstanceInfo()) - return err - } - } else { - log.Logger.Infof("CLB only left one ip, and no need to kickoff,info:%s", - ins.ShowSwitchInstanceInfo()) - } - } + /*return ins.DeleteNameService(dbutil.BindEntry{ + Clb: ins.ApiGw.ServiceEntry.Clb, + })*/ + // cmdb not support fetch clb information return nil } @@ -203,84 +190,24 @@ func (ins *RedisProxySwitchInfo) KickOffPolaris() error { return nil } - for _, pinfo := range ins.ApiGw.Polaris { - ips, err := ins.PolarisGWClient.GetPolarisTargets(pinfo.Service) - if err != nil { - log.Logger.Errorf("call GetPolarisTargets failed,info:%s,err:%s", - ins.ShowSwitchInstanceInfo(), err.Error()) - return err - } - - addr := fmt.Sprintf("%s:%d", ins.Ip, ins.Port) - if len(ips) > 1 { - err := ins.PolarisGWClient.PolarisUnBindTarget( - pinfo.Service, pinfo.Token, addr) - if err != nil { - log.Logger.Errorf("Kickoff %s from polaris failed,info:%s,err=%s", - addr, ins.ShowSwitchInstanceInfo(), err.Error()) - return err - } - } else { - log.Logger.Infof("Polaris only left one ip, and no need to kickoff,info:%s", - ins.ShowSwitchInstanceInfo()) - } - } - return nil -} - -// CheckFetchEntryDetail TODO -func (ins *RedisProxySwitchInfo) CheckFetchEntryDetail() bool { - if ins.ApiGw.PolarisFlag || ins.ApiGw.CLBFlag || ins.ApiGw.DNSFlag { - return true - } else { - return false - } -} - -// GetEntryDetailInfo TODO -func (ins *RedisProxySwitchInfo) GetEntryDetailInfo() error { - entry, err := ins.CmDBClient.GetEntryDetail(ins.Cluster) - if err != nil { - log.Logger.Errorf("GetEntryDetail failed, info:%s,err:%s", - ins.ShowSwitchInstanceInfo(), err.Error()) - return err - } - - clusterEntryInfo, ok := entry[ins.Cluster] - if !ok { - entryErr := fmt.Errorf("GetEntryDetail can not find [%s] in [%v]", - ins.Cluster, entry) - log.Logger.Errorf(entryErr.Error()) - return entryErr - } - - entryInfo, ok := clusterEntryInfo.(map[string]interface{}) - if !ok { - entryErr := fmt.Errorf("GetEntryDetail transfer type fail,[%v]", - clusterEntryInfo) - log.Logger.Errorf(entryErr.Error()) - return entryErr - } - err = ParseAPIGWInfo(entryInfo, &ins.ApiGw) - if err != nil { - log.Logger.Errorf("Parse APIGW failed, info:%s,err:%s", - ins.ShowSwitchInstanceInfo(), err.Error()) - return err - } + /*return ins.DeleteNameService(dbutil.BindEntry{ + Polaris: ins.ApiGw.ServiceEntry.Polaris, + })*/ + // cmdb not support fetch polaris information return nil } -// UnMarshalRedisInstanceByCmdb TODO +// UnMarshalRedisInstanceByCmdb parse the information from cmdb func UnMarshalRedisInstanceByCmdb(instances []interface{}, - uClusterType string, uMetaType string) ([]*RedisDetectInfoFromCmDB, error) { + uClusterType string) ([]*RedisDetectInfoFromCmDB, error) { var ( err error ret []*RedisDetectInfoFromCmDB ) for _, v := range instances { - ins := v.(map[string]interface{}) - inf, ok := ins["cluster_type"] + vMap := v.(map[string]interface{}) + inf, ok := vMap["cluster_type"] if !ok { err = fmt.Errorf("umarshal failed. cluster_type not exist") log.Logger.Errorf(err.Error()) @@ -290,63 +217,23 @@ func UnMarshalRedisInstanceByCmdb(instances []interface{}, if clusterType != uClusterType { continue } - inf, ok = ins["machine_type"] - if !ok { - err = fmt.Errorf("umarshal failed. machine_type not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - metaType := inf.(string) - if metaType != uMetaType { - continue - } - inf, ok = ins["status"] - if !ok { - err = fmt.Errorf("umarshal failed. status not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - status := inf.(string) - if status != constvar.RUNNING && status != constvar.AVAILABLE { - continue - } - inf, ok = ins["ip"] - if !ok { - err = fmt.Errorf("umarshal failed. ip not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - ip := inf.(string) - inf, ok = ins["port"] - if !ok { - err = fmt.Errorf("umarshal failed. port not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - port := int(inf.(float64)) - inf, ok = ins["bk_biz_id"] - if !ok { - err = fmt.Errorf("umarshal failed. app not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - app := strconv.Itoa(int(inf.(float64))) - inf, ok = ins["cluster"] - if !ok { - err = fmt.Errorf("umarshal failed. cluster not exist") - log.Logger.Errorf(err.Error()) - return nil, err + ins := dbutil.DBInstanceInfoDetail{} + rawData, err := json.Marshal(v) + if err != nil { + return nil, fmt.Errorf("marshal instance info failed:%s", err.Error()) + } + if err = json.Unmarshal(rawData, &ins); err != nil { + return nil, fmt.Errorf("unmarshal instance info failed:%s", err.Error()) } - cluster := inf.(string) detechInfo := &RedisDetectInfoFromCmDB{ - Ip: ip, - Port: port, - App: app, - ClusterType: clusterType, - MetaType: metaType, - Cluster: cluster, + Ip: ins.IP, + Port: ins.Port, + App: strconv.Itoa(ins.BKBizID), + ClusterType: ins.ClusterType, + MetaType: ins.MachineType, + Cluster: ins.Cluster, } ret = append(ret, detechInfo) @@ -354,480 +241,126 @@ func UnMarshalRedisInstanceByCmdb(instances []interface{}, return ret, nil } -// CreateRedisProxySwitchInfo TODO -func CreateRedisProxySwitchInfo( - instance interface{}, conf *config.Config, -) (*RedisProxySwitchInfo, error) { - var err error - +// GetClusterAndMetaFromIns get cluster and meta from instance +func GetClusterAndMetaFromIns(instance interface{}) (string, string, error) { ins := instance.(map[string]interface{}) - inf, ok := ins["ip"] - if !ok { - err = fmt.Errorf("umarshal failed. ip not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - ip := inf.(string) - - inf, ok = ins["port"] - if !ok { - err = fmt.Errorf("umarshal failed. port not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - port := int(inf.(float64)) - - inf, ok = ins["bk_idc_city_id"] - if !ok { - err = fmt.Errorf("umarshal failed. role not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - idc := strconv.Itoa(int(inf.(float64))) - - inf, ok = ins["status"] + inf, ok := ins["cluster_type"] if !ok { - err = fmt.Errorf("umarshal failed. ip not exist") + err := fmt.Errorf("umarshal failed. cluster_type not exist") log.Logger.Errorf(err.Error()) - return nil, err - } - status := inf.(string) - - inf, ok = ins["bk_biz_id"] - if !ok { - err = fmt.Errorf("umarshal failed. app not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - app := strconv.Itoa(int(inf.(float64))) - - inf, ok = ins["cluster_type"] - if !ok { - err = fmt.Errorf("umarshal failed. cluster_type not exist") - log.Logger.Errorf(err.Error()) - return nil, err + return "", "", err } clusterType := inf.(string) inf, ok = ins["machine_type"] if !ok { - err = fmt.Errorf("umarshal failed. machine_type not exist") + err := fmt.Errorf("umarshal failed. meta_type not exist") log.Logger.Errorf(err.Error()) - return nil, err + return "", "", err } metaType := inf.(string) + return clusterType, metaType, nil +} - inf, ok = ins["cluster"] - if !ok { - err = fmt.Errorf("umarshal failed. cluster not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - cluster := inf.(string) - - inf, ok = ins["admin_port"] - if !ok { - err = fmt.Errorf("umarshal failed. admin_port not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - adminPort := int(inf.(float64)) - - inf, ok = ins["bind_entry"] - if !ok { - err = fmt.Errorf("umarshal failed. bind_entry not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - bindEntry := inf.(map[string]interface{}) +// CreateRedisProxySwitchInfo +func CreateRedisProxySwitchInfo( + instance interface{}, conf *config.Config, +) (*RedisProxySwitchInfo, error) { + var err error - cmdbClient, err := client.NewCmDBClient(&conf.DBConf.CMDB, conf.GetCloudId()) + ins := dbutil.DBInstanceInfoDetail{} + rawData, err := json.Marshal(instance) if err != nil { - return nil, err + return nil, fmt.Errorf("marshal instance info failed:%s", err.Error()) } - - hadbClient, err := client.NewHaDBClient(&conf.DBConf.HADB, conf.GetCloudId()) - if err != nil { - return nil, err + if err = json.Unmarshal(rawData, &ins); err != nil { + return nil, fmt.Errorf("unmarshal instance info failed:%s", err.Error()) } + cmdbClient := client.NewCmDBClient(&conf.DBConf.CMDB, conf.GetCloudId()) + hadbClient := client.NewHaDBClient(&conf.DBConf.HADB, conf.GetCloudId()) + swIns := RedisProxySwitchInfo{ BaseSwitch: dbutil.BaseSwitch{ - Ip: ip, - Port: port, - IDC: idc, - Status: status, - App: app, - ClusterType: clusterType, - MetaType: metaType, - Cluster: cluster, + Ip: ins.IP, + Port: ins.Port, + IDC: strconv.Itoa(ins.BKIdcCityID), + Status: ins.Status, + App: strconv.Itoa(ins.BKBizID), + ClusterType: ins.ClusterType, + MetaType: ins.MachineType, + Cluster: ins.Cluster, CmDBClient: cmdbClient, HaDBClient: hadbClient, + Config: conf, }, - AdminPort: adminPort, + AdminPort: ins.AdminPort, } - _, ok = bindEntry["dns"] - if !ok { + if ins.BindEntry.Dns == nil { log.Logger.Infof("switch info not contain dns") swIns.ApiGw.DNSFlag = false } else { swIns.ApiGw.DNSFlag = true - swIns.DnsClient, err = client.NewNameServiceClient(&conf.DNS.BindConf, conf.GetCloudId()) - if err != nil { - log.Logger.Errorf("Create dns client failed,conf:%v", - conf.DNS.BindConf) - return nil, err + swIns.ApiGw.ServiceEntry.Dns = ins.BindEntry.Dns + for _, dns := range ins.BindEntry.Dns { + if dns.ForwardEntryId != 0 { + swIns.ApiGw.DNSForword = true + } } } - _, ok = bindEntry["polaris"] - if !ok { + if ins.BindEntry.Polaris != nil && len(ins.BindEntry.Polaris) > 0 { + swIns.ApiGw.PolarisFlag = true + } else { log.Logger.Infof("switch info not contain polaris") swIns.ApiGw.PolarisFlag = false - } else { - swIns.ApiGw.PolarisFlag = true + // clb not support } - _, ok = bindEntry["clb"] - if !ok { + if ins.BindEntry.Clb != nil && len(ins.BindEntry.Clb) > 0 { + swIns.ApiGw.CLBFlag = true + } else { log.Logger.Infof("switch info not contain clb") swIns.ApiGw.CLBFlag = false - } else { - swIns.ApiGw.CLBFlag = true + // polaris not support } - if swIns.ApiGw.CLBFlag || swIns.ApiGw.PolarisFlag { - swIns.PolarisGWClient, err = client.NewNameServiceClient(&conf.DNS.PolarisConf, conf.GetCloudId()) - if err != nil { - log.Logger.Errorf("create polaris client failed,conf:%v", - conf.DNS.PolarisConf) - return nil, err - } - } - return &swIns, nil } -// ParseAPIGWInfo TODO -func ParseAPIGWInfo(entryDetail map[string]interface{}, apiGW *GWInfo) error { - if nil == apiGW { - return fmt.Errorf("input apiGW is nil") - } - - log.Logger.Infof("input entryDetail:%v", entryDetail) - if apiGW.PolarisFlag { - pVal, ok := entryDetail["polaris"] - if !ok { - err := fmt.Errorf("have PolarisFlag ture but entryDetail lack polaris") - log.Logger.Errorf(err.Error()) - return err - } else { - pArr := pVal.([]interface{}) - if nil == pArr { - return fmt.Errorf("type trans failed while parse polaris") - } - for _, polaris := range pArr { - var pIns PolarisInfo - pInfo := polaris.(map[string]interface{}) - pname, pok := pInfo["polaris_name"] - if pok { - pIns.Service = pname.(string) - } - ptoken, pok := pInfo["polaris_token"] - if pok { - pIns.Token = ptoken.(string) - } - apiGW.Polaris = append(apiGW.Polaris, pIns) - } - } - } - - if apiGW.CLBFlag { - cVal, ok := entryDetail["clb"] - if !ok { - err := fmt.Errorf("have CLBFlag ture but entryDetail lack clb") - log.Logger.Errorf(err.Error()) - return err - } else { - cArr := cVal.([]interface{}) - if nil == cArr { - return fmt.Errorf("type trans failed while parse CLB") - } - for _, clb := range cArr { - var cins CLBInfo - cinfo := clb.(map[string]interface{}) - clbid, cok := cinfo["clb_id"] - if cok { - cins.LoadBalanceId = clbid.(string) - } - listenId, cok := cinfo["listener_id"] - if cok { - cins.ListenId = listenId.(string) - } - domain, cok := cinfo["clb_domain"] - if cok { - cins.Region = domain.(string) - } - } - } - } - - if apiGW.DNSFlag { - dVal, ok := entryDetail["dns"] - if !ok { - err := fmt.Errorf("have DNSFlag ture but entryDetail lack dns") - log.Logger.Errorf(err.Error()) - return err - } else { - dArr := dVal.([]interface{}) - if nil == dArr { - return fmt.Errorf("type trans failed while parse CLB") - } - for _, dns := range dArr { - var dnsIns DNSInfo - dinfo := dns.(map[string]interface{}) - domain, dok := dinfo["domain"] - if dok { - dnsIns.Domain = domain.(string) - } - apiGW.DNS = append(apiGW.DNS, dnsIns) - } - } - } - - return nil -} - -// CreateRedisSwitchInfo TODO +// CreateRedisSwitchInfo create redis switch instance func CreateRedisSwitchInfo(instance interface{}, conf *config.Config) (*RedisSwitchInfo, error) { var err error - ins := instance.(map[string]interface{}) - inf, ok := ins["ip"] - if !ok { - err = fmt.Errorf("umarshal failed. ip not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - ip := inf.(string) - - inf, ok = ins["port"] - if !ok { - err = fmt.Errorf("umarshal failed. port not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - port := int(inf.(float64)) - - inf, ok = ins["bk_idc_city_id"] - if !ok { - err = fmt.Errorf("umarshal failed. role not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - idc := strconv.Itoa(int(inf.(float64))) - - inf, ok = ins["status"] - if !ok { - err = fmt.Errorf("umarshal failed. ip not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - status := inf.(string) - - inf, ok = ins["bk_biz_id"] - if !ok { - err = fmt.Errorf("umarshal failed. app not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - app := strconv.Itoa(int(inf.(float64))) - - inf, ok = ins["cluster_type"] - if !ok { - err = fmt.Errorf("umarshal failed. cluster_type not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - clusterType := inf.(string) - - inf, ok = ins["machine_type"] - if !ok { - err = fmt.Errorf("umarshal failed. machine_type not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - metaType := inf.(string) - - inf, ok = ins["cluster"] - if !ok { - err = fmt.Errorf("umarshal failed. cluster not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - cluster := inf.(string) - - _, ok = ins["bind_entry"] - if !ok { - err = fmt.Errorf("umarshal failed. bind_entry not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - - cmdbClient, err := client.NewCmDBClient(&conf.DBConf.CMDB, conf.GetCloudId()) + ins := dbutil.DBInstanceInfoDetail{} + rawData, err := json.Marshal(instance) if err != nil { - return nil, err + return nil, fmt.Errorf("marshal instance info failed:%s", err.Error()) } - - hadbClient, err := client.NewHaDBClient(&conf.DBConf.HADB, conf.GetCloudId()) - if err != nil { - return nil, err + if err = json.Unmarshal(rawData, &ins); err != nil { + return nil, fmt.Errorf("unmarshal instance info failed:%s", err.Error()) } - inf, ok = ins["receiver"] - if !ok { - err = fmt.Errorf("umarshal failed. receiver not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - slave := inf.([]interface{}) - - inf, ok = ins["proxyinstance_set"] - if !ok { - err = fmt.Errorf("umarshal failed. proxyinstance_set not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - proxy := inf.([]interface{}) + cmdbClient := client.NewCmDBClient(&conf.DBConf.CMDB, conf.GetCloudId()) + hadbClient := client.NewHaDBClient(&conf.DBConf.HADB, conf.GetCloudId()) swIns := RedisSwitchInfo{ BaseSwitch: dbutil.BaseSwitch{ - Ip: ip, - Port: port, - IDC: idc, - Status: status, - App: app, - ClusterType: clusterType, - MetaType: metaType, - Cluster: cluster, + Ip: ins.IP, + Port: ins.Port, + IDC: strconv.Itoa(ins.BKIdcCityID), + Status: ins.Status, + App: strconv.Itoa(ins.BKBizID), + ClusterType: ins.ClusterType, + MetaType: ins.MachineType, + Cluster: ins.Cluster, CmDBClient: cmdbClient, HaDBClient: hadbClient, + Config: conf, }, Timeout: conf.DBConf.Redis.Timeout, - } - - for _, rawInfo := range slave { - mapInfo := rawInfo.(map[string]interface{}) - inf, ok = mapInfo["ip"] - if !ok { - err = fmt.Errorf("umarshal failed. slave ip not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - slaveIp := inf.(string) - inf, ok = mapInfo["port"] - if !ok { - err = fmt.Errorf("umarshal failed. slave port not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - slavePort := inf.(float64) - swIns.Slave = append(swIns.Slave, RedisSlaveInfo{ - Ip: slaveIp, - Port: int(slavePort), - }) - } - - for _, rawInfo := range proxy { - mapInfo := rawInfo.(map[string]interface{}) - inf, ok = mapInfo["ip"] - if !ok { - err = fmt.Errorf("umarshal failed. proxy ip not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - proxyIp := inf.(string) - inf, ok = mapInfo["port"] - if !ok { - err = fmt.Errorf("umarshal failed. proxy port not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - proxyPort := inf.(float64) - inf, ok = mapInfo["admin_port"] - if !ok { - err = fmt.Errorf("umarshal failed. proxy port not exist") - log.Logger.Errorf(err.Error()) - return nil, err - } - proxyAdminPort := inf.(float64) - var status string - inf, ok = mapInfo["status"] - if !ok { - status = "" - } else { - status = inf.(string) - } - swIns.Proxy = append(swIns.Proxy, dbutil.ProxyInfo{ - Ip: proxyIp, - Port: int(proxyPort), - AdminPort: int(proxyAdminPort), - Status: status, - }) + Slave: ins.Receiver, + Proxy: ins.ProxyInstanceSet, } return &swIns, nil } - -// GetDetectBaseByInfo TODO -func GetDetectBaseByInfo(ins *RedisDetectInfoFromCmDB, - dbType string, conf *config.Config) *RedisDetectBase { - passwd := GetRedisMachinePasswd(ins.App, conf) - return &RedisDetectBase{ - BaseDetectDB: dbutil.BaseDetectDB{ - Ip: ins.Ip, - Port: ins.Port, - App: ins.App, - DBType: types.DBType(dbType), - ReporterTime: time.Unix(0, 0), - ReportInterval: conf.AgentConf.ReportInterval + rand.Intn(20), - Status: constvar.DBCheckSuccess, - Cluster: ins.Cluster, - SshInfo: dbutil.Ssh{ - Port: conf.SSH.Port, - User: conf.SSH.User, - Pass: passwd, - Dest: conf.SSH.Dest, - Timeout: conf.SSH.Timeout, - }, - }, - Pass: ins.Pass, - Timeout: conf.DBConf.Redis.Timeout, - } -} - -// GetDetectBaseByRsp TODO -func GetDetectBaseByRsp(ins *RedisDetectResponse, - dbType string, conf *config.Config) *RedisDetectBase { - passwd := GetRedisMachinePasswd(ins.App, conf) - return &RedisDetectBase{ - BaseDetectDB: dbutil.BaseDetectDB{ - Ip: ins.DBIp, - Port: ins.DBPort, - App: ins.App, - DBType: types.DBType(dbType), - ReporterTime: time.Unix(0, 0), - ReportInterval: conf.AgentConf.ReportInterval + rand.Intn(20), - Status: types.CheckStatus(ins.Status), - Cluster: ins.Cluster, - SshInfo: dbutil.Ssh{ - Port: conf.SSH.Port, - User: conf.SSH.User, - Pass: passwd, - Dest: conf.SSH.Dest, - Timeout: conf.SSH.Timeout, - }, - }, - Pass: ins.Pass, - Timeout: conf.DBConf.Redis.Timeout, - } -} diff --git a/dbm-services/common/dbha/ha-module/dbmodule/redis/redis_callback.go b/dbm-services/common/dbha/ha-module/dbmodule/redis/redis_callback.go deleted file mode 100644 index 1e0f4303b2..0000000000 --- a/dbm-services/common/dbha/ha-module/dbmodule/redis/redis_callback.go +++ /dev/null @@ -1,86 +0,0 @@ -package redis - -import ( - "encoding/json" - - "dbm-services/common/dbha/ha-module/config" - "dbm-services/common/dbha/ha-module/constvar" - "dbm-services/common/dbha/ha-module/dbutil" - "dbm-services/common/dbha/ha-module/log" -) - -// NewRedisInstanceByCmdb Agent通过CMDB获取的信息来生成需要探测的实例 -func NewRedisInstanceByCmdb(instances []interface{}, - conf *config.Config) ([]dbutil.DataBaseDetect, error) { - var ( - err error - unmarshalIns []*RedisDetectInfoFromCmDB - ret []dbutil.DataBaseDetect - ) - - unmarshalIns, err = UnMarshalRedisInstanceByCmdb(instances, - constvar.RedisClusterType, constvar.RedisMetaType) - - if err != nil { - return nil, err - } - - for _, uIns := range unmarshalIns { - ret = append(ret, NewRedisDetectInstance(uIns, conf)) - } - - return ret, err -} - -// DeserializeRedis 反序列化从Agent上报上来的故障实例 -func DeserializeRedis(jsonInfo []byte, - conf *config.Config) (dbutil.DataBaseDetect, error) { - response := RedisDetectResponse{} - err := json.Unmarshal(jsonInfo, &response) - if err != nil { - log.Logger.Errorf("json unmarshal failed. jsoninfo:\n%s\n, err:%s", - string(jsonInfo), err.Error()) - return nil, err - } - ret := NewRedisDetectInstanceFromRsp(&response, conf) - return ret, nil -} - -// NewRedisSwitchInstance TODO -func NewRedisSwitchInstance(instances []interface{}, - conf *config.Config) ([]dbutil.DataBaseSwitch, error) { - var err error - var ret []dbutil.DataBaseSwitch - for _, v := range instances { - swIns, err := CreateRedisSwitchInfo(v, conf) - if err != nil { - log.Logger.Errorf("parse redis switch instance failed,err:%s", - err.Error()) - continue - } - - if swIns.MetaType != constvar.RedisMetaType { - log.Logger.Errorf("Create redis switch while the metaType[%s] != %s", - swIns.MetaType, constvar.RedisMetaType) - continue - } - - pw := RedisSwitch{ - RedisSwitchInfo: *swIns, - Config: conf, - } - - passwd, err := GetInstancePassByCluster( - constvar.TendisCache, pw.Cluster, conf, - ) - if err != nil { - log.Logger.Errorf("get redis switch passwd failed,err:%s,info:%s", - err.Error(), pw.ShowSwitchInstanceInfo()) - } else { - pw.Pass = passwd - } - ret = append(ret, &pw) - } - - return ret, err -} diff --git a/dbm-services/common/dbha/ha-module/dbmodule/redis/redis_detect.go b/dbm-services/common/dbha/ha-module/dbmodule/redis/redis_detect.go index 159caf440f..febc1cc369 100644 --- a/dbm-services/common/dbha/ha-module/dbmodule/redis/redis_detect.go +++ b/dbm-services/common/dbha/ha-module/dbmodule/redis/redis_detect.go @@ -13,12 +13,12 @@ import ( "dbm-services/common/dbha/ha-module/util" ) -// RedisDetectInstance TODO +// RedisDetectInstance redis detect instance type RedisDetectInstance struct { RedisDetectBase } -// Detection TODO +// Detection detection api func (ins *RedisDetectInstance) Detection() error { err := ins.DoRedisDetection() if err == nil && ins.Status == constvar.DBCheckSuccess { @@ -52,7 +52,7 @@ func (ins *RedisDetectInstance) Detection() error { } } -// DoRedisDetection TODO +// DoRedisDetection do detect action func (ins *RedisDetectInstance) DoRedisDetection() error { r := &client.RedisClient{} addr := fmt.Sprintf("%s:%d", ins.Ip, ins.Port) @@ -105,7 +105,7 @@ func (ins *RedisDetectInstance) DoRedisDetection() error { return nil } -// Serialization TODO +// Serialization do serialize func (ins *RedisDetectInstance) Serialization() ([]byte, error) { response := RedisDetectResponse{ BaseDetectDBResponse: ins.NewDBResponse(), @@ -120,7 +120,7 @@ func (ins *RedisDetectInstance) Serialization() ([]byte, error) { return resByte, nil } -// GetRole TODO +// GetRole get role of instance func (ins *RedisDetectInstance) GetRole(info string) (string, error) { beginPos := strings.Index(info, "role:") if beginPos < 0 { @@ -141,7 +141,7 @@ func (ins *RedisDetectInstance) GetRole(info string) (string, error) { return roleInfo, nil } -// DoSetCheck TODO +// DoSetCheck check redis set response func (ins *RedisDetectInstance) DoSetCheck(r *client.RedisClient) error { keyFormat := "dbha:agent:%s" checkKey := fmt.Sprintf(keyFormat, ins.Ip) @@ -175,25 +175,29 @@ func (ins *RedisDetectInstance) DoSetCheck(r *client.RedisClient) error { } } -// ShowDetectionInfo TODO +// ShowDetectionInfo show detect instance information func (ins *RedisDetectInstance) ShowDetectionInfo() string { str := fmt.Sprintf("ip:%s, port:%d, status:%s, DBType:%s", ins.Ip, ins.Port, ins.Status, ins.DBType) return str } -// NewRedisDetectInstance TODO +// NewRedisDetectInstance create Redis detect ins, +// +// used by FetchDBCallback func NewRedisDetectInstance(ins *RedisDetectInfoFromCmDB, conf *config.Config) *RedisDetectInstance { return &RedisDetectInstance{ - RedisDetectBase: *GetDetectBaseByInfo(ins, constvar.TendisCache, conf), + RedisDetectBase: *GetDetectBaseByInfo(ins, constvar.RedisMetaType, conf), } } -// NewRedisDetectInstanceFromRsp TODO +// NewRedisDetectInstanceFromRsp create Redis detect ins, +// +// used by gm/DeserializeCallback func NewRedisDetectInstanceFromRsp(ins *RedisDetectResponse, conf *config.Config) *RedisDetectInstance { return &RedisDetectInstance{ - RedisDetectBase: *GetDetectBaseByRsp(ins, constvar.TendisCache, conf), + RedisDetectBase: *GetDetectBaseByRsp(ins, constvar.RedisMetaType, conf), } } diff --git a/dbm-services/common/dbha/ha-module/dbmodule/redis/redis_switch.go b/dbm-services/common/dbha/ha-module/dbmodule/redis/redis_switch.go index a174421c2b..4a6c69769d 100644 --- a/dbm-services/common/dbha/ha-module/dbmodule/redis/redis_switch.go +++ b/dbm-services/common/dbha/ha-module/dbmodule/redis/redis_switch.go @@ -14,52 +14,52 @@ import ( "dbm-services/common/dbha/ha-module/util" ) -// RedisSwitch TODO +// RedisSwitch redis switch instance type RedisSwitch struct { RedisSwitchInfo Config *config.Config FLock *util.FileLock } -// CheckSwitch TODO +// CheckSwitch check redis status before switch func (ins *RedisSwitch) CheckSwitch() (bool, error) { ins.ReportLogs( - constvar.CHECK_SWITCH_INFO, fmt.Sprintf("handle instance[%s:%d]", ins.Ip, ins.Port), + constvar.InfoResult, fmt.Sprintf("handle instance[%s:%d]", ins.Ip, ins.Port), ) if len(ins.Slave) != 1 { redisErr := fmt.Errorf("redis have invald slave[%d]", len(ins.Slave)) log.Logger.Errorf("%s info:%s", redisErr.Error(), ins.ShowSwitchInstanceInfo()) - ins.ReportLogs(constvar.CHECK_SWITCH_FAIL, redisErr.Error()) + ins.ReportLogs(constvar.FailResult, redisErr.Error()) return false, redisErr } - ins.SetInfo(constvar.SWITCH_INFO_SLAVE_IP, ins.Slave[0].Ip) - ins.SetInfo(constvar.SWITCH_INFO_SLAVE_PORT, ins.Slave[0].Port) + ins.SetInfo(constvar.SlaveIpKey, ins.Slave[0].Ip) + ins.SetInfo(constvar.SlavePortKey, ins.Slave[0].Port) err := ins.DoLockByFile() if err != nil { redisErrLog := fmt.Sprintf("RedisSwitch lockfile failed,err:%s", err.Error()) log.Logger.Errorf("%s info:%s", redisErrLog, ins.ShowSwitchInstanceInfo()) - ins.ReportLogs(constvar.CHECK_SWITCH_FAIL, redisErrLog) + ins.ReportLogs(constvar.FailResult, redisErrLog) return false, err } - ins.ReportLogs(constvar.CHECK_SWITCH_INFO, fmt.Sprintf("twemproxy infos:%v", ins.Proxy)) + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("twemproxy infos:%v", ins.Proxy)) _, err = ins.CheckTwemproxyPing() if err != nil { ins.DoUnLockByFile() redisErrLog := fmt.Sprintf("RedisSwitch check twemproxy failed,err:%s", err.Error()) log.Logger.Errorf("%s info:%s", redisErrLog, ins.ShowSwitchInstanceInfo()) - ins.ReportLogs(constvar.CHECK_SWITCH_FAIL, redisErrLog) + ins.ReportLogs(constvar.FailResult, redisErrLog) return false, err } ins.ReportLogs( - constvar.CHECK_SWITCH_INFO, "RedisSwitch lock file and check twemproxy ok", + constvar.InfoResult, "RedisSwitch lock file and check twemproxy ok", ) return true, nil } -// DoSwitch TODO +// DoSwitch do switch action func (ins *RedisSwitch) DoSwitch() error { log.Logger.Infof("redis do switch.info:{%s}", ins.ShowSwitchInstanceInfo()) r := &client.RedisClient{} @@ -74,7 +74,7 @@ func (ins *RedisSwitch) DoSwitch() error { ins.DoUnLockByFile() redisErrLog := fmt.Sprintf("Slave[%s] exec slaveOf no one failed,%s", addr, err.Error()) log.Logger.Errorf("%s info:%s", redisErrLog, ins.ShowSwitchInstanceInfo()) - ins.ReportLogs(constvar.SWITCH_FAIL, redisErrLog) + ins.ReportLogs(constvar.FailResult, redisErrLog) return err } @@ -83,7 +83,7 @@ func (ins *RedisSwitch) DoSwitch() error { ins.DoUnLockByFile() redisErr := fmt.Errorf("redis info response type is not string") log.Logger.Errorf("%s info:%s", redisErr.Error(), ins.ShowSwitchInstanceInfo()) - ins.ReportLogs(constvar.SWITCH_FAIL, redisErr.Error()) + ins.ReportLogs(constvar.FailResult, redisErr.Error()) return redisErr } @@ -93,31 +93,31 @@ func (ins *RedisSwitch) DoSwitch() error { redisErr := fmt.Errorf("redis do slaveof failed,slave:%d,rsp:%s", len(ins.Slave), rspInfo) log.Logger.Errorf("%s info:%s", redisErr.Error(), ins.ShowSwitchInstanceInfo()) - ins.ReportLogs(constvar.SWITCH_FAIL, redisErr.Error()) + ins.ReportLogs(constvar.FailResult, redisErr.Error()) return redisErr } slaveOfOk := fmt.Sprintf("do slaveof no one ok,mark slave[%s] as master", addr) log.Logger.Infof("RedisSwitch %s info:%s", slaveOfOk, ins.ShowSwitchInstanceInfo()) - ins.ReportLogs(constvar.SWITCH_INFO, slaveOfOk) + ins.ReportLogs(constvar.InfoResult, slaveOfOk) ok, switchNum := ins.TwemproxySwitchM2S(ins.Ip, ins.Port, slave.Ip, slave.Port) if !ok { switchPart := fmt.Sprintf("redis switch proxy part success,succ:{%d},fail[%d],total:{%d}", switchNum, len(ins.Proxy)-switchNum, len(ins.Proxy)) log.Logger.Infof("%s info:%s", switchPart, ins.ShowSwitchInstanceInfo()) - ins.ReportLogs(constvar.SWITCH_FAIL, switchPart) + ins.ReportLogs(constvar.FailResult, switchPart) return nil } switchAllOk := fmt.Sprintf("redis switch twemproxy ok,succ[%d],fail[%d],total[%d]", switchNum, len(ins.Proxy)-switchNum, len(ins.Proxy)) log.Logger.Infof("%s info:%s", switchAllOk, ins.ShowSwitchInstanceInfo()) - ins.ReportLogs(constvar.SWITCH_INFO, switchAllOk) + ins.ReportLogs(constvar.InfoResult, switchAllOk) return nil } -// ShowSwitchInstanceInfo TODO +// ShowSwitchInstanceInfo show switch instance information func (ins *RedisSwitch) ShowSwitchInstanceInfo() string { format := `<%s#%d IDC:%s Status:%s App:%s ClusterType:%s MachineType:%s Cluster:%s>` str := fmt.Sprintf( @@ -136,14 +136,14 @@ func (ins *RedisSwitch) RollBack() error { return nil } -// UpdateMetaInfo TODO +// UpdateMetaInfo swap redis role information from cmdb func (ins *RedisSwitch) UpdateMetaInfo() error { defer ins.DoUnLockByFile() - ins.ReportLogs(constvar.UPDATEMETA_INFO, "handle swap_role for cmdb") + ins.ReportLogs(constvar.InfoResult, "handle swap_role for cmdb") if len(ins.Slave) != 1 { redisErr := fmt.Errorf("redis have invald slave[%d]", len(ins.Slave)) log.Logger.Errorf("%s info:%s", redisErr.Error(), ins.ShowSwitchInstanceInfo()) - ins.ReportLogs(constvar.UPDATEMETA_FAIL, redisErr.Error()) + ins.ReportLogs(constvar.FailResult, redisErr.Error()) return redisErr } @@ -152,12 +152,12 @@ func (ins *RedisSwitch) UpdateMetaInfo() error { if err != nil { redisErrLog := fmt.Sprintf("swap redis role failed. err:%s", err.Error()) log.Logger.Errorf("%s info:%s", redisErrLog, ins.ShowSwitchInstanceInfo()) - ins.ReportLogs(constvar.UPDATEMETA_FAIL, redisErrLog) + ins.ReportLogs(constvar.FailResult, redisErrLog) return err } swapOk := fmt.Sprintf("cluster[%s] swap_role slave[%s#%d] master[%s#%d] ok", ins.Cluster, ins.Ip, ins.Port, ins.Slave[0].Ip, ins.Slave[0].Port) - ins.ReportLogs(constvar.UPDATEMETA_INFO, swapOk) + ins.ReportLogs(constvar.InfoResult, swapOk) return nil } @@ -171,14 +171,14 @@ func (ins *RedisSwitch) DoLockByFile() error { if err != nil { lockErrLog := fmt.Sprintf("lockfile failed,path:%s,err:%s", path, err.Error()) log.Logger.Errorf("%s info:%s", lockErrLog, ins.ShowSwitchInstanceInfo()) - ins.ReportLogs(constvar.CHECK_SWITCH_FAIL, lockErrLog) + ins.ReportLogs(constvar.FailResult, lockErrLog) return err } else { log.Logger.Infof("RedisSwitch lockfile ok,path:%s,info:%s", path, ins.ShowSwitchInstanceInfo()) ins.FLock = fl ins.ReportLogs( - constvar.CHECK_SWITCH_INFO, fmt.Sprintf("instance lock file %s ok", path), + constvar.InfoResult, fmt.Sprintf("instance lock file %s ok", path), ) return nil } @@ -197,21 +197,21 @@ func (ins *RedisSwitch) DoUnLockByFile() { lockErrLog := fmt.Sprintf("RedisSwitch unlock failed,path:%s,err:%s", ins.FLock.Path, err.Error()) log.Logger.Errorf("%s info:%s", lockErrLog, ins.ShowSwitchInstanceInfo()) - ins.ReportLogs(constvar.CHECK_SWITCH_FAIL, lockErrLog) + ins.ReportLogs(constvar.FailResult, lockErrLog) return } else { log.Logger.Infof("RedisSwitch unlock ok,path:%s,info:%s", ins.FLock.Path, ins.ShowSwitchInstanceInfo()) ins.ReportLogs( - constvar.CHECK_SWITCH_INFO, fmt.Sprintf("instance unlock file %s ok", ins.FLock.Path), + constvar.InfoResult, fmt.Sprintf("instance unlock file %s ok", ins.FLock.Path), ) return } } -// CheckTwemproxyPing TODO +// CheckTwemproxyPing check twemproxy ping cmd func (ins *RedisSwitch) CheckTwemproxyPing() ([]dbutil.ProxyInfo, error) { - ins.ReportLogs(constvar.CHECK_SWITCH_INFO, + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("twemproxy ping:start check_ping, with [%d] twemproxy", len(ins.Proxy)), ) @@ -242,7 +242,7 @@ func (ins *RedisSwitch) CheckTwemproxyPing() ([]dbutil.ProxyInfo, error) { } wg.Wait() - ins.ReportLogs(constvar.CHECK_SWITCH_INFO, + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("twemproxy ping:[%d] check ping,with [%d] ok,[%d] kickoff", len(ins.Proxy), len(proxyServers)-len(kickProxys), len(kickProxys))) ins.KickOffTwemproxy(kickProxys) @@ -252,32 +252,32 @@ func (ins *RedisSwitch) CheckTwemproxyPing() ([]dbutil.ProxyInfo, error) { redisErrLog := fmt.Sprintf("twemproxy conf not equal,err:%s, info:%s", err.Error(), ins.ShowSwitchInstanceInfo()) log.Logger.Errorf(redisErrLog) - ins.ReportLogs(constvar.CHECK_SWITCH_FAIL, redisErrLog) + ins.ReportLogs(constvar.FailResult, redisErrLog) return nil, err } - ins.ReportLogs(constvar.CHECK_SWITCH_INFO, + ins.ReportLogs(constvar.InfoResult, "all twemproxy nosqlproxy servers is equal") return kickProxys, nil } -// KickOffTwemproxy TODO +// KickOffTwemproxy kick twemproxy from gateway func (ins *RedisSwitch) KickOffTwemproxy(kickProxys []dbutil.ProxyInfo) { if len(kickProxys) == 0 { - ins.ReportLogs(constvar.CHECK_SWITCH_INFO, + ins.ReportLogs(constvar.InfoResult, "all twemproxy sames to be ok,ignore kickOff...") return } kickLog := fmt.Sprintf("need to kickoff twemproxy [%d]", len(kickProxys)) - ins.ReportLogs(constvar.CHECK_SWITCH_INFO, kickLog) + ins.ReportLogs(constvar.InfoResult, kickLog) log.Logger.Infof("RedisSwitch %s", kickLog) for _, proxy := range kickProxys { - ins.ReportLogs(constvar.CHECK_SWITCH_INFO, + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("do kickoff bad ping twemproxys,twemproxy:%v", proxy)) ins.DoKickTwemproxy(proxy) } - ins.ReportLogs(constvar.CHECK_SWITCH_INFO, "kickoff bad ping twemproxy done") + ins.ReportLogs(constvar.InfoResult, "kickoff bad ping twemproxy done") return } @@ -367,7 +367,7 @@ func (ins *RedisSwitch) DoTwemproxyPing(proxy dbutil.ProxyInfo) (map[string]stri checkErrLog := fmt.Sprintf("twemproxy ping: communicate failed,proxy[%s:%d],err=%s", proxy.Ip, proxy.Port, err.Error()) log.Logger.Errorf("RedisSwitch %s info:%s", checkErrLog, ins.ShowSwitchInstanceInfo()) - ins.ReportLogs(constvar.CHECK_SWITCH_FAIL, checkErrLog) + ins.ReportLogs(constvar.FailResult, checkErrLog) return nil, nil } @@ -376,7 +376,7 @@ func (ins *RedisSwitch) DoTwemproxyPing(proxy dbutil.ProxyInfo) (map[string]stri checkErrLog := fmt.Sprintf("twemproxy ping: parse rsp[%s] failed,proxy[%s:%d]", rsp, proxy.Ip, proxy.Port) log.Logger.Errorf("RedisSwitch %s info:%s", checkErrLog, ins.ShowSwitchInstanceInfo()) - ins.ReportLogs(constvar.CHECK_SWITCH_FAIL, checkErrLog) + ins.ReportLogs(constvar.FailResult, checkErrLog) return nil, nil } @@ -386,7 +386,7 @@ func (ins *RedisSwitch) DoTwemproxyPing(proxy dbutil.ProxyInfo) (map[string]stri redisErr := fmt.Errorf(format, proxy.Ip, proxy.Port, proxy.AdminPort, masterInfo, proxy.Ip, proxy.Port, rsp) log.Logger.Errorf("RedisSwitch %s info:%s", redisErr.Error(), ins.ShowSwitchInstanceInfo()) - ins.ReportLogs(constvar.CHECK_SWITCH_FAIL, redisErr.Error()) + ins.ReportLogs(constvar.FailResult, redisErr.Error()) return proxyIns, redisErr } log.Logger.Debugf("RedisSwitch do ping twemproxy proxy[%s:%d] ok, twemproxyConf:%v", @@ -403,14 +403,14 @@ func (ins *RedisSwitch) DoKickTwemproxy(proxy dbutil.ProxyInfo) error { redisErr := fmt.Errorf("get twemproxy[%s:%d:%d] from cmdb failed", proxy.Ip, proxy.Port, proxy.AdminPort) log.Logger.Errorf("%s info:%s", redisErr.Error(), ins.ShowSwitchInstanceInfo()) - ins.ReportLogs(constvar.CHECK_SWITCH_FAIL, redisErr.Error()) + ins.ReportLogs(constvar.FailResult, redisErr.Error()) return redisErr } if len(infos) == 0 { redisErr := fmt.Errorf("the number of proxy[%d] is invalid", len(infos)) log.Logger.Errorf("%s info:%s", redisErr.Error(), ins.ShowSwitchInstanceInfo()) - ins.ReportLogs(constvar.CHECK_SWITCH_FAIL, redisErr.Error()) + ins.ReportLogs(constvar.FailResult, redisErr.Error()) return redisErr } @@ -436,26 +436,16 @@ func (ins *RedisSwitch) DoKickTwemproxy(proxy dbutil.ProxyInfo) error { kickSkipLog := fmt.Sprintf("skip while status is [%s],twemproxy[%s:%d]", proxyIns.Status, proxyIns.Ip, proxyIns.Port) log.Logger.Infof("RedisSwitch %s", kickSkipLog) - ins.ReportLogs(constvar.CHECK_SWITCH_INFO, kickSkipLog) + ins.ReportLogs(constvar.InfoResult, kickSkipLog) continue } - if proxyIns.CheckFetchEntryDetail() { - edErr := proxyIns.GetEntryDetailInfo() - if edErr != nil { - kickErrLog := fmt.Sprintf("GetEntryDetail failed while Kick Twemproxy:%s,err:%s", - proxyIns.ShowSwitchInstanceInfo(), edErr.Error()) - log.Logger.Errorf("RedisSwitch %s", kickErrLog) - return edErr - } - } - err = proxyIns.KickOffDns() if err != nil { kickErrLog := fmt.Sprintf("kick twemproxy failed by dns,proxy=%s,err=%s", proxyIns.ShowSwitchInstanceInfo(), err.Error()) log.Logger.Errorf("RedisSwitch %s", kickErrLog) - ins.ReportLogs(constvar.CHECK_SWITCH_FAIL, kickErrLog) + ins.ReportLogs(constvar.FailResult, kickErrLog) return err } err = proxyIns.KickOffPolaris() @@ -463,7 +453,7 @@ func (ins *RedisSwitch) DoKickTwemproxy(proxy dbutil.ProxyInfo) error { kickErrLog := fmt.Sprintf("kick twemproxy failed by polaris,proxy=%s,err=%s", proxyIns.ShowSwitchInstanceInfo(), err.Error()) log.Logger.Errorf("RedisSwitch %s", kickErrLog) - ins.ReportLogs(constvar.CHECK_SWITCH_FAIL, kickErrLog) + ins.ReportLogs(constvar.FailResult, kickErrLog) return err } err = proxyIns.KickOffClb() @@ -471,7 +461,7 @@ func (ins *RedisSwitch) DoKickTwemproxy(proxy dbutil.ProxyInfo) error { kickErrLog := fmt.Sprintf("kick twemproxy failed by clb,proxy=%s,err=%s", proxyIns.ShowSwitchInstanceInfo(), err.Error()) log.Logger.Errorf("RedisSwitch %s", kickErrLog) - ins.ReportLogs(constvar.CHECK_SWITCH_FAIL, kickErrLog) + ins.ReportLogs(constvar.FailResult, kickErrLog) return err } } @@ -479,11 +469,11 @@ func (ins *RedisSwitch) DoKickTwemproxy(proxy dbutil.ProxyInfo) error { kickOkLog := fmt.Sprintf(" kick twemproxy[%s:%d-%d] ok", proxy.Ip, proxy.Port, proxy.AdminPort) log.Logger.Infof("RedisSwitch %s", kickOkLog) - ins.ReportLogs(constvar.CHECK_SWITCH_INFO, kickOkLog) + ins.ReportLogs(constvar.InfoResult, kickOkLog) return nil } -// TwemproxySwitchM2S TODO +// TwemproxySwitchM2S twemproxy switch master and slave role func (ins *RedisSwitch) TwemproxySwitchM2S(masterIp string, masterPort int, slaveIp string, slavePort int) (bool, int) { var successSwitchNum int64 = 0 @@ -541,7 +531,7 @@ func (ins *RedisSwitch) TwemproxySwitchSingle(proxy dbutil.ProxyInfo, redisErr := fmt.Errorf("twemproxy[%s:%d:%d] switch %s to %s failed,cmd:%s,err:%s", proxy.Ip, proxy.Port, proxy.AdminPort, masterAddr, slaveAddr, cmdInfo, err.Error()) log.Logger.Errorf("RedisSwitch %s info:%s", redisErr.Error(), ins.ShowSwitchInstanceInfo()) - ins.ReportLogs(constvar.SWITCH_FAIL, redisErr.Error()) + ins.ReportLogs(constvar.FailResult, redisErr.Error()) return false, redisErr } @@ -551,7 +541,7 @@ func (ins *RedisSwitch) TwemproxySwitchSingle(proxy dbutil.ProxyInfo, redisErr := fmt.Errorf("switch twemproxy[%s:%d:%d] from %s to %s failed", proxy.Ip, proxy.Port, proxy.AdminPort, masterAddr, slaveAddr) log.Logger.Errorf("RedisSwitch %s info:%s", redisErr.Error(), ins.ShowSwitchInstanceInfo()) - ins.ReportLogs(constvar.SWITCH_FAIL, redisErr.Error()) + ins.ReportLogs(constvar.FailResult, redisErr.Error()) return false, redisErr } } diff --git a/dbm-services/common/dbha/ha-module/dbmodule/redis/rediscluster_callback.go b/dbm-services/common/dbha/ha-module/dbmodule/redis/rediscluster_callback.go new file mode 100644 index 0000000000..4946a9709c --- /dev/null +++ b/dbm-services/common/dbha/ha-module/dbmodule/redis/rediscluster_callback.go @@ -0,0 +1,209 @@ +package redis + +import ( + "encoding/json" + "fmt" + + "dbm-services/common/dbha/ha-module/config" + "dbm-services/common/dbha/ha-module/constvar" + "dbm-services/common/dbha/ha-module/dbutil" + "dbm-services/common/dbha/ha-module/log" +) + +// RedisClusterNewIns Agent通过CMDB获取的信息来生成需要探测的实例 +func RedisClusterNewIns(instances []interface{}, + conf *config.Config) ([]dbutil.DataBaseDetect, error) { + + var ( + err error + unmarshalIns []*RedisDetectInfoFromCmDB + ret []dbutil.DataBaseDetect + ) + + unmarshalIns, err = UnMarshalRedisInstanceByCmdb(instances, constvar.RedisCluster) + if err != nil { + log.Logger.Errorf("RedisCluster UnMarshal failed,%s,err:%s", + constvar.RedisCluster, err.Error()) + return nil, err + } + + var redisIns, twemIns []dbutil.DataBaseDetect + for _, uIns := range unmarshalIns { + // check the cluster_type should be TwemproxyRedisInstance + if uIns.ClusterType != constvar.RedisCluster { + continue + } + + // create detect instance by machine_type + if uIns.MetaType == constvar.RedisMetaType { + redisIns = append(redisIns, NewRedisDetectInstance(uIns, conf)) + count, _ := GetInstancePass(constvar.RedisMetaType, redisIns, conf) + if count != len(redisIns) { + log.Logger.Errorf("RedisCluster redis passwd part failed,succ:%d,total:%d", + count, len(redisIns)) + } + } else if uIns.MetaType == constvar.TwemproxyMetaType { + twemIns = append(twemIns, NewTwemproxyDetectInstance(uIns, conf)) + count, _ := GetInstancePass(constvar.TwemproxyMetaType, twemIns, conf) + if count != len(twemIns) { + log.Logger.Errorf("RedisCluster twemproxy passwd part failed,succ:%d,total:%d", + count, len(twemIns)) + } + } else { + log.Logger.Errorf("RedisCluster cluster is %s but meta type is invalid", + constvar.RedisCluster) + } + } + + ret = append(ret, redisIns...) + ret = append(ret, twemIns...) + return ret, err +} + +// RedisClusterDeserialize 反序列化从Agent上报上来的故障实例 +func RedisClusterDeserialize(jsonInfo []byte, + conf *config.Config) (dbutil.DataBaseDetect, error) { + response := RedisDetectResponse{} + err := json.Unmarshal(jsonInfo, &response) + if err != nil { + log.Logger.Errorf("json unmarshal failed. jsoninfo:\n%s\n, err:%s", + string(jsonInfo), err.Error()) + return nil, err + } + + // cluster_type should be equal to TwemproxyRedisInstance + if response.ClusterType != constvar.RedisCluster { + unmatchErr := fmt.Errorf("cluster unmatch %s:%s", + response.ClusterType, constvar.RedisCluster) + log.Logger.Errorf("RedisCluster Deserialize. %s", unmatchErr.Error()) + return nil, unmatchErr + } + + // create detect instance by machine_type + if response.DBType == constvar.RedisMetaType { + ret := NewRedisDetectInstanceFromRsp(&response, conf) + return ret, nil + } else if response.DBType == constvar.TwemproxyMetaType { + ret := NewTwemproxyDetectInstanceFromRsp(&response, conf) + return ret, nil + } else { + unmatchErr := fmt.Errorf("RedisCluster meta_type not exist,%s", + response.DBType) + log.Logger.Errorf("deserialize failed,%s", unmatchErr.Error()) + return nil, err + } +} + +// RedisClusterNewSwitchIns create redis cluster switch ins +func RedisClusterNewSwitchIns(instances []interface{}, + conf *config.Config) ([]dbutil.DataBaseSwitch, error) { + var err error + var ret []dbutil.DataBaseSwitch + for _, v := range instances { + // get cluster_type and meta type from instance information + clusterType, metaType, err := GetClusterAndMetaFromIns(v) + if err != nil { + log.Logger.Errorf("redis ins lack cluster and meta info,%v,err:%s", + v, err.Error()) + continue + } + + // check cluster_type is equal to TwemproxyRedisInstance + if clusterType != constvar.RedisCluster { + continue + } + + // create instance by machine_type + if metaType == constvar.RedisMetaType { + pw, err := NewRedisSwitchIns(v, conf) + if err != nil { + log.Logger.Errorf("new redis switch ins failed:%s", err.Error()) + continue + } + ret = append(ret, pw) + } else if metaType == constvar.TwemproxyMetaType { + pw, err := NewTwemproxySwitchIns(v, conf) + if err != nil { + log.Logger.Errorf("new twemproxy switch ins failed:%s", err.Error()) + continue + } + ret = append(ret, pw) + } else { + log.Logger.Errorf("redis cluster[%s] not fit meta[%s]", + clusterType, metaType) + continue + } + } + return ret, err +} + +// NewRedisSwitchIns create redis switch instance +func NewRedisSwitchIns(instance interface{}, + conf *config.Config) (dbutil.DataBaseSwitch, error) { + swIns, err := CreateRedisSwitchInfo(instance, conf) + if err != nil { + //stuff err + log.Logger.Errorf("parse redis switch instance failed,err:%s", + err.Error()) + return nil, err + } + + // check machine_type is equal to tendiscache + if swIns.MetaType != constvar.RedisMetaType { + log.Logger.Errorf("Create redis switch while the metaType[%s] != %s", + swIns.MetaType, constvar.RedisMetaType) + return nil, err + } + + pw := RedisSwitch{ + RedisSwitchInfo: *swIns, + Config: conf, + } + + // get the password of redis switch instance + passwd, err := GetInstancePassByCluster( + constvar.RedisMetaType, pw.Cluster, conf, + ) + if err != nil { + log.Logger.Errorf("get redis switch passwd failed,err:%s,info:%s", + err.Error(), pw.ShowSwitchInstanceInfo()) + } else { + pw.Pass = passwd + } + + return &pw, nil +} + +// NewTwemproxySwitchIns create twemproxy switch instance +func NewTwemproxySwitchIns(instance interface{}, + conf *config.Config) (dbutil.DataBaseSwitch, error) { + swIns, err := CreateRedisProxySwitchInfo(instance, conf) + if err != nil { + log.Logger.Errorf("parse twemproxy switch instance failed,err:%s", + err.Error()) + return nil, err + } + + // check machine_type is equal to twemproxy + if swIns.MetaType != constvar.TwemproxyMetaType { + log.Logger.Errorf("Create Twemproxy switch while the metaType[%s] != %s", + swIns.MetaType, constvar.TwemproxyMetaType) + return nil, err + } + + pw := TwemproxySwitch{ + RedisProxySwitchInfo: *swIns, + } + + // get the password of twemproxy switch instance + passwd, err := GetInstancePassByCluster( + constvar.TwemproxyMetaType, pw.Cluster, conf, + ) + if err != nil { + log.Logger.Errorf("get twemproxy switch passwd failed,err:%s,info:%s", + err.Error(), pw.ShowSwitchInstanceInfo()) + } else { + pw.Pass = passwd + } + return &pw, nil +} diff --git a/dbm-services/common/dbha/ha-module/dbmodule/redis/svr_password.go b/dbm-services/common/dbha/ha-module/dbmodule/redis/svr_password.go index 61c91969e3..1467c7d01b 100644 --- a/dbm-services/common/dbha/ha-module/dbmodule/redis/svr_password.go +++ b/dbm-services/common/dbha/ha-module/dbmodule/redis/svr_password.go @@ -100,12 +100,7 @@ func GetMysqlMachinePasswd( func GetMachinePasswordRemote(app string, confFile string, confType string, confName string, levelName string, levelValue string, namespace string, remoteconf config.APIConfig, cloudId int) (map[string]interface{}, error) { - remoteConfigClient, err := client.NewRemoteConfigClient(&remoteconf, cloudId) - if err != nil { - log.Logger.Errorf("SSHPWD new dbconfig client failed,err:%s", - err.Error()) - return make(map[string]interface{}), err - } + remoteConfigClient := client.NewRemoteConfigClient(&remoteconf, cloudId) configData, err := remoteConfigClient.GetConfigItem( app, confFile, confType, confName, @@ -141,7 +136,7 @@ func GetMachinePasswordRemote(app string, confFile string, confType string, // GetInstancePass get redis instances cluster password by batch api func GetInstancePass(dbType types.DBType, insArr []dbutil.DataBaseDetect, conf *config.Config) (int, error) { - if dbType == constvar.MySQLProxy || dbType == constvar.MySQL { + if dbType == constvar.DetectTenDBHA || dbType == constvar.DetectTenDBCluster { return 0, nil } @@ -175,12 +170,7 @@ func GetInstancePass(dbType types.DBType, log.Logger.Debugf("PassWDClusters cachePasswd:%v,NeedQuery:%v", clusterPasswd, clusters) - remoteConfigClient, err := client.NewRemoteConfigClient(&conf.DNS.RemoteConf, conf.GetCloudId()) - if err != nil { - log.Logger.Errorf("PassWDClusters new db config client failed,err:%s", - err.Error()) - return 0, err - } + remoteConfigClient := client.NewRemoteConfigClient(&conf.DNS.RemoteConf, conf.GetCloudId()) NewPasswds := QueryPasswords(remoteConfigClient, cFile, cType, cName, lName, clusters, namespace) @@ -210,8 +200,8 @@ func GetInstancePass(dbType types.DBType, } if succCount != len(insArr) { - passErr := fmt.Errorf("PassWDClusters not all instance get passwd,succ:%d,all:%d", - succCount, len(insArr)) + passErr := fmt.Errorf("PassWDClusters not all instance get passwd,dbtype:%s,succ:%d,all:%d", + dbType, succCount, len(insArr)) log.Logger.Errorf(passErr.Error()) return succCount, passErr } @@ -221,7 +211,7 @@ func GetInstancePass(dbType types.DBType, // GetInstancePassByCluster get single redis cluster password func GetInstancePassByCluster(dbType types.DBType, cluster string, conf *config.Config) (string, error) { - if dbType == constvar.MySQLProxy || dbType == constvar.MySQL { + if dbType == constvar.DetectTenDBHA || dbType == constvar.DetectTenDBCluster { return "", nil } @@ -232,12 +222,6 @@ func GetInstancePassByCluster(dbType types.DBType, return "", passErr } - remoteConfigClient, err := client.NewRemoteConfigClient(&conf.DNS.RemoteConf, conf.GetCloudId()) - if err != nil { - log.Logger.Errorf("PassWDCluster new dbconfig client failed,err:%s", err.Error()) - return "", err - } - key := fmt.Sprintf("%s-%s-%s-%s-%s-%s", cFile, cType, cName, lName, namespace, cluster) cachePasswd, ok := passwdCache.Get(key) @@ -245,6 +229,7 @@ func GetInstancePassByCluster(dbType types.DBType, return cachePasswd.(string), nil } + remoteConfigClient := client.NewRemoteConfigClient(&conf.DNS.RemoteConf, conf.GetCloudId()) clusterPasswds := QueryPasswords(remoteConfigClient, cFile, cType, cName, lName, []string{cluster}, namespace) @@ -272,7 +257,6 @@ func QueryPasswords(remoteConfigClient *client.RemoteConfigClient, cFile string, log.Logger.Errorf(err.Error()) return clusterPasswd } - content, cok := configData["content"] if !cok { passErr := fmt.Errorf("PassWDQuery content not exist") @@ -315,7 +299,7 @@ func QueryPasswords(remoteConfigClient *client.RemoteConfigClient, cFile string, // SetPasswordToInstance set password to redis detection instance func SetPasswordToInstance(dbType types.DBType, passwd string, ins dbutil.DataBaseDetect) error { - if dbType == constvar.TendisCache { + if dbType == constvar.RedisMetaType { cacheP, isCache := ins.(*RedisDetectInstance) if isCache { cacheP.Pass = passwd @@ -323,7 +307,7 @@ func SetPasswordToInstance(dbType types.DBType, passErr := fmt.Errorf("the type[%s] of instance transfer type failed", dbType) return passErr } - } else if dbType == constvar.Twemproxy { + } else if dbType == constvar.TwemproxyMetaType { twemP, isTwem := ins.(*TwemproxyDetectInstance) if isTwem { twemP.Pass = passwd @@ -331,7 +315,7 @@ func SetPasswordToInstance(dbType types.DBType, passErr := fmt.Errorf("the type[%s] of instance transfer type failed", dbType) return passErr } - } else if dbType == constvar.Predixy { + } else if dbType == constvar.PredixyMetaType { predixyP, isPredixy := ins.(*PredixyDetectInstance) if isPredixy { predixyP.Pass = passwd @@ -339,7 +323,7 @@ func SetPasswordToInstance(dbType types.DBType, passErr := fmt.Errorf("the type[%s] of instance transfer type failed", dbType) return passErr } - } else if dbType == constvar.Tendisplus { + } else if dbType == constvar.TendisplusMetaType { tendisP, isTendis := ins.(*TendisplusDetectInstance) if isTendis { tendisP.Pass = passwd @@ -360,17 +344,17 @@ func SetPasswordToInstance(dbType types.DBType, // conf_type conf_file conf_name conf_name level_name namespace func GetConfigParamByDbType(dbType types.DBType, ) (string, string, string, string, string, error) { - if dbType == constvar.TendisCache { - clusterMeta := constvar.RedisClusterType + if dbType == constvar.RedisMetaType { + clusterMeta := constvar.RedisCluster return "proxyconf", "Twemproxy-latest", "redis_password", "cluster", clusterMeta, nil - } else if dbType == constvar.Twemproxy { - clusterMeta := constvar.RedisClusterType + } else if dbType == constvar.TwemproxyMetaType { + clusterMeta := constvar.RedisCluster return "proxyconf", "Twemproxy-latest", "password", "cluster", clusterMeta, nil - } else if dbType == constvar.Predixy { - clusterMeta := constvar.TendisplusClusterType + } else if dbType == constvar.PredixyMetaType { + clusterMeta := constvar.TendisplusCluster return "proxyconf", "Predixy-latest", "password", "cluster", clusterMeta, nil - } else if dbType == constvar.Tendisplus { - clusterMeta := constvar.TendisplusClusterType + } else if dbType == constvar.TendisplusMetaType { + clusterMeta := constvar.TendisplusCluster return "proxyconf", "Predixy-latest", "redis_password", "cluster", clusterMeta, nil } else { return "", "", "", "", "", nil diff --git a/dbm-services/common/dbha/ha-module/dbmodule/redis/tendiscluster_callback.go b/dbm-services/common/dbha/ha-module/dbmodule/redis/tendiscluster_callback.go new file mode 100644 index 0000000000..1c49d298d7 --- /dev/null +++ b/dbm-services/common/dbha/ha-module/dbmodule/redis/tendiscluster_callback.go @@ -0,0 +1,206 @@ +package redis + +import ( + "encoding/json" + "fmt" + + "dbm-services/common/dbha/ha-module/config" + "dbm-services/common/dbha/ha-module/constvar" + "dbm-services/common/dbha/ha-module/dbutil" + "dbm-services/common/dbha/ha-module/log" +) + +// TendisClusterNewIns create tendisplus and predixy detect instance +func TendisClusterNewIns(instances []interface{}, + conf *config.Config) ([]dbutil.DataBaseDetect, error) { + var ( + err error + unmarshalIns []*RedisDetectInfoFromCmDB + ret []dbutil.DataBaseDetect + ) + + unmarshalIns, err = UnMarshalRedisInstanceByCmdb(instances, constvar.TendisplusCluster) + if err != nil { + log.Logger.Errorf("TendisCluster UnMarshal failed,%s,err:%s", + constvar.TendisplusCluster, err.Error()) + return nil, err + } + + var tendisIns, predixyIns []dbutil.DataBaseDetect + for _, uIns := range unmarshalIns { + // check the cluster_type should be PredixyTendisplusCluster + if uIns.ClusterType != constvar.TendisplusCluster { + continue + } + + // create detect instance by machine_type + if uIns.MetaType == constvar.TendisplusMetaType { + tendisIns = append(tendisIns, NewTendisplusDetectInstance(uIns, conf)) + count, _ := GetInstancePass(constvar.TendisplusMetaType, tendisIns, conf) + if count != len(tendisIns) { + log.Logger.Errorf("TendisCluster tendisplus passwd part failed,succ:%d,total:%d", + count, len(tendisIns)) + } + } else if uIns.MetaType == constvar.PredixyMetaType { + predixyIns = append(predixyIns, NewPredixyDetectInstance(uIns, conf)) + count, _ := GetInstancePass(constvar.PredixyMetaType, predixyIns, conf) + if count != len(predixyIns) { + log.Logger.Errorf("TendisCluster predixy passwd part failed,succ:%d,total:%d", + count, len(predixyIns)) + } + } else { + log.Logger.Errorf("TendisCluster cluster is %s but meta type is invalid", + constvar.TendisplusCluster) + } + } + + ret = append(ret, tendisIns...) + ret = append(ret, predixyIns...) + return ret, err +} + +// TendisClusterDeserialize deserialize tendisplus and predixy detect instance +func TendisClusterDeserialize(jsonInfo []byte, + conf *config.Config) (dbutil.DataBaseDetect, error) { + response := RedisDetectResponse{} + err := json.Unmarshal(jsonInfo, &response) + if err != nil { + log.Logger.Errorf("json unmarshal failed. jsoninfo:\n%s\n, err:%s", + string(jsonInfo), err.Error()) + return nil, err + } + + // cluster_type should be equal to PredixyTendisplusCluster + if response.ClusterType != constvar.TendisplusCluster { + unmatchErr := fmt.Errorf("cluster unmatch %s:%s", + response.ClusterType, constvar.TendisplusCluster) + log.Logger.Errorf("TendisCluster Deserialize. %s", unmatchErr.Error()) + return nil, err + } + + // create detect instance by machine_type + if response.DBType == constvar.TendisplusMetaType { + ret := NewTendisplusDetectInstanceFromRsp(&response, conf) + return ret, nil + } else if response.DBType == constvar.PredixyMetaType { + ret := NewPredixyDetectInstanceFromRsp(&response, conf) + return ret, nil + } else { + unmatchErr := fmt.Errorf("TendisCluster meta_type not exist,%s", + response.DBType) + log.Logger.Errorf("deserialize failed,%s", unmatchErr.Error()) + return nil, err + } +} + +// TendisClusterNewSwitchIns create tendisplus and predixy switch instance +func TendisClusterNewSwitchIns(instances []interface{}, + conf *config.Config) ([]dbutil.DataBaseSwitch, error) { + var err error + var ret []dbutil.DataBaseSwitch + for _, v := range instances { + // get cluster_type and meta type from instance information + clusterType, metaType, err := GetClusterAndMetaFromIns(v) + if err != nil { + log.Logger.Errorf("tendis ins lack cluster and meta info,%v,err:%s", + v, err.Error()) + continue + } + + // check cluster_type is equal to PredixyTendisplusCluster + if clusterType != constvar.TendisplusCluster { + continue + } + + // create instance by machine_type + if metaType == constvar.TendisplusMetaType { + pw, err := NewTendisplusSwitchIns(v, conf) + if err != nil { + log.Logger.Errorf("new tendis switch ins failed:%s", err.Error()) + continue + } + ret = append(ret, pw) + } else if metaType == constvar.PredixyMetaType { + pw, err := NewPredixySwitchIns(v, conf) + if err != nil { + log.Logger.Errorf("new predixy switch ins failed:%s", err.Error()) + continue + } + ret = append(ret, pw) + } else { + log.Logger.Errorf("tendis cluster[%s] not fit meta[%s]", + clusterType, metaType) + } + } + return ret, err +} + +// NewTendisplusSwitchIns new tendisplus switch instance +func NewTendisplusSwitchIns(instance interface{}, + conf *config.Config) (dbutil.DataBaseSwitch, error) { + swIns, err := CreateRedisSwitchInfo(instance, conf) + if err != nil { + log.Logger.Errorf("parse tendisplus switch instance failed,err:%s", + err.Error()) + return nil, err + } + + // check machine_type is equal to tendisplus + if swIns.MetaType != constvar.TendisplusMetaType { + log.Logger.Errorf("Create tendisplus switch while the metaType[%s] != %s", + swIns.MetaType, constvar.TendisplusMetaType) + return nil, err + } + + pw := TendisplusSwitch{ + RedisSwitchInfo: *swIns, + } + + // get the password of switch instance + passwd, err := GetInstancePassByCluster( + constvar.TendisplusMetaType, pw.Cluster, conf, + ) + if err != nil { + log.Logger.Errorf("get tendisplus switch passwd failed,err:%s,info:%s", + err.Error(), pw.ShowSwitchInstanceInfo()) + } else { + pw.Pass = passwd + } + + return &pw, nil +} + +// NewPredixySwitchInstance new predixy switch instance +func NewPredixySwitchIns(instance interface{}, + conf *config.Config) (dbutil.DataBaseSwitch, error) { + swIns, err := CreateRedisProxySwitchInfo(instance, conf) + if err != nil { + log.Logger.Errorf("parse predixy switch instance failed,err:%s", + err.Error()) + return nil, err + } + + // check machine_type is equal to Predixy + if swIns.MetaType != constvar.PredixyMetaType { + log.Logger.Errorf("Create predixy switch while the metaType[%s] != %s", + swIns.MetaType, constvar.PredixyMetaType) + return nil, err + } + + pw := PredixySwitch{ + RedisProxySwitchInfo: *swIns, + } + + // get the password of switch instance + passwd, err := GetInstancePassByCluster( + constvar.PredixyMetaType, pw.Cluster, conf, + ) + if err != nil { + log.Logger.Errorf("get predixy switch passwd failed,err:%s,info:%s", + err.Error(), pw.ShowSwitchInstanceInfo()) + } else { + log.Logger.Infof("get predixy switch passwd[%s]", passwd) + pw.Pass = passwd + } + return &pw, nil +} diff --git a/dbm-services/common/dbha/ha-module/dbmodule/redis/tendisplus_callback.go b/dbm-services/common/dbha/ha-module/dbmodule/redis/tendisplus_callback.go deleted file mode 100644 index 80e39a907a..0000000000 --- a/dbm-services/common/dbha/ha-module/dbmodule/redis/tendisplus_callback.go +++ /dev/null @@ -1,86 +0,0 @@ -package redis - -import ( - "encoding/json" - - "dbm-services/common/dbha/ha-module/config" - "dbm-services/common/dbha/ha-module/constvar" - "dbm-services/common/dbha/ha-module/dbutil" - "dbm-services/common/dbha/ha-module/log" -) - -// NewTendisplusInstanceByCmdb Agent通过CMDB获取的信息来生成需要探测的实例 -func NewTendisplusInstanceByCmdb(instances []interface{}, - conf *config.Config) ([]dbutil.DataBaseDetect, error) { - var ( - err error - unmarshalIns []*RedisDetectInfoFromCmDB - ret []dbutil.DataBaseDetect - ) - - unmarshalIns, err = UnMarshalRedisInstanceByCmdb(instances, - constvar.TendisplusClusterType, constvar.TendisplusMetaType) - - if err != nil { - return nil, err - } - - for _, uIns := range unmarshalIns { - ret = append(ret, NewTendisplusDetectInstance(uIns, conf)) - } - - return ret, err -} - -// DeserializeTendisplus 反序列化从Agent上报上来的故障实例 -func DeserializeTendisplus(jsonInfo []byte, - conf *config.Config) (dbutil.DataBaseDetect, error) { - response := RedisDetectResponse{} - err := json.Unmarshal(jsonInfo, &response) - if err != nil { - log.Logger.Errorf("json unmarshal failed. jsoninfo:\n%s\n, err:%s", - string(jsonInfo), err.Error()) - return nil, err - } - ret := NewTendisplusDetectInstanceFromRsp(&response, conf) - return ret, nil -} - -// NewTendisplusSwitchInstance TODO -func NewTendisplusSwitchInstance(instances []interface{}, - conf *config.Config) ([]dbutil.DataBaseSwitch, error) { - var err error - var ret []dbutil.DataBaseSwitch - for _, v := range instances { - swIns, err := CreateRedisSwitchInfo(v, conf) - if err != nil { - log.Logger.Errorf("parse tendisplus switch instance failed,err:%s", - err.Error()) - continue - } - - if swIns.MetaType != constvar.TendisplusMetaType { - log.Logger.Errorf("Create tendisplus switch while the metaType[%s] != %s", - swIns.MetaType, constvar.TendisplusMetaType) - continue - } - - pw := TendisplusSwitch{ - RedisSwitchInfo: *swIns, - } - - passwd, err := GetInstancePassByCluster( - constvar.Tendisplus, pw.Cluster, conf, - ) - if err != nil { - log.Logger.Errorf("get tendisplus switch passwd failed,err:%s,info:%s", - err.Error(), pw.ShowSwitchInstanceInfo()) - } else { - pw.Pass = passwd - } - - ret = append(ret, &pw) - } - - return ret, err -} diff --git a/dbm-services/common/dbha/ha-module/dbmodule/redis/tendisplus_detect.go b/dbm-services/common/dbha/ha-module/dbmodule/redis/tendisplus_detect.go index b2cc7fd3b0..f346df37af 100644 --- a/dbm-services/common/dbha/ha-module/dbmodule/redis/tendisplus_detect.go +++ b/dbm-services/common/dbha/ha-module/dbmodule/redis/tendisplus_detect.go @@ -13,12 +13,12 @@ import ( "dbm-services/common/dbha/ha-module/util" ) -// TendisplusDetectInstance TODO +// TendisplusDetectInstance tendisplus detect instance type TendisplusDetectInstance struct { RedisDetectBase } -// Detection TODO +// Detection detect tendisplus instance func (ins *TendisplusDetectInstance) Detection() error { err := ins.DoTendisDetection() if err == nil && ins.Status == constvar.DBCheckSuccess { @@ -105,7 +105,7 @@ func (ins *TendisplusDetectInstance) DoTendisDetection() error { return nil } -// Serialization TODO +// Serialization serialize tendisplus instance func (ins *TendisplusDetectInstance) Serialization() ([]byte, error) { response := RedisDetectResponse{ BaseDetectDBResponse: ins.NewDBResponse(), @@ -120,7 +120,7 @@ func (ins *TendisplusDetectInstance) Serialization() ([]byte, error) { return resByte, nil } -// GetRole TODO +// GetRole get role information func (ins *TendisplusDetectInstance) GetRole(info string) (string, error) { beginPos := strings.Index(info, "role:") if beginPos < 0 { @@ -141,7 +141,7 @@ func (ins *TendisplusDetectInstance) GetRole(info string) (string, error) { return roleInfo, nil } -// DoSetCheck TODO +// DoSetCheck check set cmd is ok or not func (ins *TendisplusDetectInstance) DoSetCheck() error { r := &client.RedisClient{} addr := fmt.Sprintf("%s:%d", ins.Ip, ins.Port) @@ -180,25 +180,29 @@ func (ins *TendisplusDetectInstance) DoSetCheck() error { } } -// ShowDetectionInfo TODO +// ShowDetectionInfo show detect instance information func (ins *TendisplusDetectInstance) ShowDetectionInfo() string { str := fmt.Sprintf("ip:%s, port:%d, status:%s, DBType:%s", ins.Ip, ins.Port, ins.Status, ins.DBType) return str } -// NewTendisplusDetectInstance TODO +// NewTendisplusDetectInstance create tendisplus detect ins, +// +// used by FetchDBCallback func NewTendisplusDetectInstance(ins *RedisDetectInfoFromCmDB, conf *config.Config) *TendisplusDetectInstance { return &TendisplusDetectInstance{ - RedisDetectBase: *GetDetectBaseByInfo(ins, constvar.Tendisplus, conf), + RedisDetectBase: *GetDetectBaseByInfo(ins, constvar.TendisplusMetaType, conf), } } -// NewTendisplusDetectInstanceFromRsp TODO +// NewTendisplusDetectInstanceFromRsp create tendisplus detect ins, +// +// used by gm/DeserializeCallback func NewTendisplusDetectInstanceFromRsp(ins *RedisDetectResponse, conf *config.Config) *TendisplusDetectInstance { return &TendisplusDetectInstance{ - RedisDetectBase: *GetDetectBaseByRsp(ins, constvar.Tendisplus, conf), + RedisDetectBase: *GetDetectBaseByRsp(ins, constvar.TendisplusMetaType, conf), } } diff --git a/dbm-services/common/dbha/ha-module/dbmodule/redis/tendisplus_switch.go b/dbm-services/common/dbha/ha-module/dbmodule/redis/tendisplus_switch.go index a3b77d86ee..999922237c 100644 --- a/dbm-services/common/dbha/ha-module/dbmodule/redis/tendisplus_switch.go +++ b/dbm-services/common/dbha/ha-module/dbmodule/redis/tendisplus_switch.go @@ -6,21 +6,23 @@ import ( "dbm-services/common/dbha/ha-module/client" "dbm-services/common/dbha/ha-module/constvar" + "dbm-services/common/dbha/ha-module/dbutil" "dbm-services/common/dbha/ha-module/log" ) -// TendisplusSwitch TODO +// TendisplusSwitch tendisplus switch instance type TendisplusSwitch struct { RedisSwitchInfo - slave2M *RedisSlaveInfo + // the slave already switched to master + slave2M *dbutil.SlaveInfo } -// CheckSwitch TODO +// CheckSwitch nothing to check func (ins *TendisplusSwitch) CheckSwitch() (bool, error) { return true, nil } -// DoSwitch TODO +// DoSwitch only check the status of tendisplus instance func (ins *TendisplusSwitch) DoSwitch() error { log.Logger.Infof("redis do switch. info:{%s}", ins.ShowSwitchInstanceInfo()) slave2Master := false @@ -47,13 +49,13 @@ func (ins *TendisplusSwitch) DoSwitch() error { } else { switchInfoLog := fmt.Sprintf("no slave change to master, info:%s", ins.ShowSwitchInstanceInfo()) - ins.ReportLogs(constvar.SWITCH_INFO, switchInfoLog) + ins.ReportLogs(constvar.InfoResult, switchInfoLog) log.Logger.Infof(switchInfoLog) return nil } } -// ShowSwitchInstanceInfo TODO +// ShowSwitchInstanceInfo show switch instance func (ins *TendisplusSwitch) ShowSwitchInstanceInfo() string { format := `<%s#%d IDC:%s Status:%s App:%s ClusterType:%s MachineType:%s Cluster:%s> switch` str := fmt.Sprintf( @@ -79,7 +81,7 @@ func (ins *TendisplusSwitch) CheckConfig() bool { } // CheckSlaveMaster check if the slave of this instance is change to master role -func (ins *TendisplusSwitch) CheckSlaveMaster(slave *RedisSlaveInfo) (bool, error) { +func (ins *TendisplusSwitch) CheckSlaveMaster(slave *dbutil.SlaveInfo) (bool, error) { r := &client.RedisClient{} addr := fmt.Sprintf("%s:%d", slave.Ip, slave.Port) r.Init(addr, ins.Pass, ins.Timeout, 0) diff --git a/dbm-services/common/dbha/ha-module/dbmodule/redis/twemproxy_callback.go b/dbm-services/common/dbha/ha-module/dbmodule/redis/twemproxy_callback.go deleted file mode 100644 index e882583e21..0000000000 --- a/dbm-services/common/dbha/ha-module/dbmodule/redis/twemproxy_callback.go +++ /dev/null @@ -1,92 +0,0 @@ -package redis - -import ( - "encoding/json" - - "dbm-services/common/dbha/ha-module/config" - "dbm-services/common/dbha/ha-module/constvar" - "dbm-services/common/dbha/ha-module/dbutil" - "dbm-services/common/dbha/ha-module/log" -) - -// NewTwemproxyInstanceByCmdb Agent通过CMDB获取的信息来生成需要探测的实例 -func NewTwemproxyInstanceByCmdb(instances []interface{}, - conf *config.Config) ([]dbutil.DataBaseDetect, error) { - var ( - err error - unmarshalIns []*RedisDetectInfoFromCmDB - ret []dbutil.DataBaseDetect - ) - - unmarshalIns, err = UnMarshalRedisInstanceByCmdb(instances, - constvar.RedisClusterType, constvar.TwemproxyMetaType) - - if err != nil { - return nil, err - } - - for _, uIns := range unmarshalIns { - ret = append(ret, NewTwemproxyDetectInstance(uIns, conf)) - } - - return ret, err -} - -// DeserializeTwemproxy 反序列化从Agent上报上来的故障实例 -func DeserializeTwemproxy(jsonInfo []byte, - conf *config.Config) (dbutil.DataBaseDetect, error) { - response := RedisDetectResponse{} - err := json.Unmarshal(jsonInfo, &response) - if err != nil { - log.Logger.Errorf("json unmarshal failed. jsoninfo:\n%s\n, err:%s", - string(jsonInfo), err.Error()) - return nil, err - } - ret := NewTwemproxyDetectInstanceFromRsp(&response, conf) - return ret, nil -} - -// NewTwemproxySwitchInstance TODO -func NewTwemproxySwitchInstance(instances []interface{}, - conf *config.Config) ([]dbutil.DataBaseSwitch, error) { - var err error - var ret []dbutil.DataBaseSwitch - for _, v := range instances { - swIns, err := CreateRedisProxySwitchInfo(v, conf) - if err != nil { - log.Logger.Errorf("parse twemproxy switch instance failed,err:%s", - err.Error()) - continue - } - - if swIns.MetaType != constvar.TwemproxyMetaType { - log.Logger.Errorf("Create Twemproxy switch while the metaType[%s] != %s", - swIns.MetaType, constvar.TwemproxyMetaType) - continue - } - if swIns.CheckFetchEntryDetail() { - edErr := swIns.GetEntryDetailInfo() - if edErr != nil { - log.Logger.Errorf("GetEntryDetail failed in NewTwemproxySwitch,err:%s", - edErr.Error()) - } - } - - pw := TwemproxySwitch{ - RedisProxySwitchInfo: *swIns, - } - - passwd, err := GetInstancePassByCluster( - constvar.Twemproxy, pw.Cluster, conf, - ) - if err != nil { - log.Logger.Errorf("get twemproxy switch passwd failed,err:%s,info:%s", - err.Error(), pw.ShowSwitchInstanceInfo()) - } else { - pw.Pass = passwd - } - ret = append(ret, &pw) - } - - return ret, err -} diff --git a/dbm-services/common/dbha/ha-module/dbmodule/redis/twemproxy_detect.go b/dbm-services/common/dbha/ha-module/dbmodule/redis/twemproxy_detect.go index 2e38e46cec..8466cbf58d 100644 --- a/dbm-services/common/dbha/ha-module/dbmodule/redis/twemproxy_detect.go +++ b/dbm-services/common/dbha/ha-module/dbmodule/redis/twemproxy_detect.go @@ -12,12 +12,12 @@ import ( "dbm-services/common/dbha/ha-module/util" ) -// TwemproxyDetectInstance TODO +// TwemproxyDetectInstance twemproxy detect instance type TwemproxyDetectInstance struct { RedisDetectBase } -// Detection TODO +// Detection detect twemproxy instance func (ins *TwemproxyDetectInstance) Detection() error { err := ins.DoTwemproxyDetection() if err == nil && ins.Status == constvar.DBCheckSuccess { @@ -96,7 +96,7 @@ func (ins *TwemproxyDetectInstance) DoTwemproxyDetection() error { } } -// Serialization TODO +// Serialization serialize detect instance func (ins *TwemproxyDetectInstance) Serialization() ([]byte, error) { response := RedisDetectResponse{ BaseDetectDBResponse: ins.NewDBResponse(), @@ -111,25 +111,29 @@ func (ins *TwemproxyDetectInstance) Serialization() ([]byte, error) { return resByte, nil } -// ShowDetectionInfo TODO +// ShowDetectionInfo show detect instance information func (ins *TwemproxyDetectInstance) ShowDetectionInfo() string { str := fmt.Sprintf("ip:%s, port:%d, status:%s, DBType:%s", ins.Ip, ins.Port, ins.Status, ins.DBType) return str } -// NewTwemproxyDetectInstance TODO +// NewTwemproxyDetectInstance create twemproxy detect ins, +// +// used by FetchDBCallback func NewTwemproxyDetectInstance(ins *RedisDetectInfoFromCmDB, conf *config.Config) *TwemproxyDetectInstance { return &TwemproxyDetectInstance{ - RedisDetectBase: *GetDetectBaseByInfo(ins, constvar.Twemproxy, conf), + RedisDetectBase: *GetDetectBaseByInfo(ins, constvar.TwemproxyMetaType, conf), } } -// NewTwemproxyDetectInstanceFromRsp TODO +// NewTwemproxyDetectInstanceFromRsp create twemproxy detect ins, +// +// used by gm/DeserializeCallback func NewTwemproxyDetectInstanceFromRsp(ins *RedisDetectResponse, conf *config.Config) *TwemproxyDetectInstance { return &TwemproxyDetectInstance{ - RedisDetectBase: *GetDetectBaseByRsp(ins, constvar.Twemproxy, conf), + RedisDetectBase: *GetDetectBaseByRsp(ins, constvar.TwemproxyMetaType, conf), } } diff --git a/dbm-services/common/dbha/ha-module/dbmodule/redis/twemproxy_switch.go b/dbm-services/common/dbha/ha-module/dbmodule/redis/twemproxy_switch.go index c58386c1a8..ce6ecb79ec 100644 --- a/dbm-services/common/dbha/ha-module/dbmodule/redis/twemproxy_switch.go +++ b/dbm-services/common/dbha/ha-module/dbmodule/redis/twemproxy_switch.go @@ -7,19 +7,19 @@ import ( "dbm-services/common/dbha/ha-module/log" ) -// TwemproxySwitch TODO +// TwemproxySwitch twemproxy switch instance type TwemproxySwitch struct { RedisProxySwitchInfo } -// CheckSwitch TODO +// CheckSwitch nothing to check func (ins *TwemproxySwitch) CheckSwitch() (bool, error) { return true, nil } -// DoSwitch TODO +// DoSwitch kick twemproxy from gateway func (ins *TwemproxySwitch) DoSwitch() error { - ins.ReportLogs(constvar.SWITCH_INFO, + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("handle twemproxy switch[%s:%d]", ins.Ip, ins.Port)) err := ins.KickOffDns() cErr := ins.KickOffClb() @@ -27,24 +27,24 @@ func (ins *TwemproxySwitch) DoSwitch() error { if err != nil { tpErrLog := fmt.Sprintf("Twemproxy kick dns failed,err:%s", err.Error()) log.Logger.Errorf("%s info:%s", tpErrLog, ins.ShowSwitchInstanceInfo()) - ins.ReportLogs(constvar.SWITCH_FAIL, tpErrLog) + ins.ReportLogs(constvar.FailResult, tpErrLog) return err } if cErr != nil { tpErrLog := fmt.Sprintf("Twemproxy kick clb failed,err:%s", cErr.Error()) log.Logger.Errorf("%s info:%s", tpErrLog, ins.ShowSwitchInstanceInfo()) - ins.ReportLogs(constvar.SWITCH_FAIL, tpErrLog) + ins.ReportLogs(constvar.FailResult, tpErrLog) return cErr } if pErr != nil { tpErrLog := fmt.Sprintf("Twemproxy kick polaris failed,err:%s", pErr.Error()) log.Logger.Errorf("%s info:%s", tpErrLog, ins.ShowSwitchInstanceInfo()) - ins.ReportLogs(constvar.SWITCH_FAIL, tpErrLog) + ins.ReportLogs(constvar.FailResult, tpErrLog) return pErr } succLog := fmt.Sprintf("Twemproxy do switch ok,dns[%t] clb[%t], polaris[%t]", ins.ApiGw.DNSFlag, ins.ApiGw.CLBFlag, ins.ApiGw.PolarisFlag) - ins.ReportLogs(constvar.SWITCH_INFO, succLog) + ins.ReportLogs(constvar.InfoResult, succLog) return nil } @@ -53,12 +53,12 @@ func (ins *TwemproxySwitch) RollBack() error { return nil } -// UpdateMetaInfo TODO +// UpdateMetaInfo nothing to update func (ins *TwemproxySwitch) UpdateMetaInfo() error { return nil } -// ShowSwitchInstanceInfo TODO +// ShowSwitchInstanceInfo show switch instance information func (ins *TwemproxySwitch) ShowSwitchInstanceInfo() string { format := `<%s#%d IDC:%s Status:%s App:%s ClusterType:%s MachineType:%s Cluster:%s> switch` str := fmt.Sprintf( diff --git a/dbm-services/common/dbha/ha-module/dbmodule/register.go b/dbm-services/common/dbha/ha-module/dbmodule/register.go index 580ff8805d..fd8cf91597 100644 --- a/dbm-services/common/dbha/ha-module/dbmodule/register.go +++ b/dbm-services/common/dbha/ha-module/dbmodule/register.go @@ -3,10 +3,10 @@ package dbmodule import ( "dbm-services/common/dbha/ha-module/config" "dbm-services/common/dbha/ha-module/constvar" - "dbm-services/common/dbha/ha-module/dbmodule/mysql" + "dbm-services/common/dbha/ha-module/dbmodule/dbmysql" "dbm-services/common/dbha/ha-module/dbmodule/redis" + "dbm-services/common/dbha/ha-module/dbmodule/riak" "dbm-services/common/dbha/ha-module/dbutil" - "dbm-services/common/dbha/ha-module/types" ) // FetchDBCallback Agent将从cmdb获取的db实例信息转换为DataBaseDetect用于探测 @@ -20,45 +20,51 @@ type GetSwitchInstanceInformation func(instance []interface{}, conf *config.Conf // Callback TODO type Callback struct { - FetchDBCallback FetchDBCallback - DeserializeCallback DeserializeCallback + //Agent call this to fetch need detect instance list + FetchDBCallback FetchDBCallback + //GDM call this to get need doubleCheck instance(report by agent) + DeserializeCallback DeserializeCallback + //GQA call this to generate switch instance GetSwitchInstanceInformation GetSwitchInstanceInformation } -// DBCallbackMap TODO -var DBCallbackMap map[types.DBType]Callback +// DBCallbackMap map for agent handler different dbType +var DBCallbackMap map[string]Callback +// TODO map key try to instead of cluster type func init() { - DBCallbackMap = map[types.DBType]Callback{} - DBCallbackMap[constvar.MySQL] = Callback{ - FetchDBCallback: mysql.NewMySQLInstanceByCmDB, - DeserializeCallback: mysql.DeserializeMySQL, - GetSwitchInstanceInformation: mysql.NewMySQLSwitchInstance, - } - DBCallbackMap[constvar.MySQLProxy] = Callback{ - FetchDBCallback: mysql.NewMySQLProxyInstanceByCmDB, - DeserializeCallback: mysql.DeserializeMySQLProxy, - GetSwitchInstanceInformation: mysql.NewMySQLProxySwitchInstance, + DBCallbackMap = map[string]Callback{} + //TenDBHA used + DBCallbackMap[constvar.DetectTenDBHA] = Callback{ + FetchDBCallback: dbmysql.NewMySQLClusterByCmDB, + DeserializeCallback: dbmysql.DeserializeMySQL, + GetSwitchInstanceInformation: dbmysql.NewMySQLSwitchInstance, } - DBCallbackMap[constvar.TendisCache] = Callback{ - FetchDBCallback: redis.NewRedisInstanceByCmdb, - DeserializeCallback: redis.DeserializeRedis, - GetSwitchInstanceInformation: redis.NewRedisSwitchInstance, + //TenDBCluster used + DBCallbackMap[constvar.DetectTenDBCluster] = Callback{ + FetchDBCallback: dbmysql.NewSpiderClusterByCmDB, + DeserializeCallback: dbmysql.DeserializeMySQL, + GetSwitchInstanceInformation: dbmysql.NewMySQLSwitchInstance, } - DBCallbackMap[constvar.Twemproxy] = Callback{ - FetchDBCallback: redis.NewTwemproxyInstanceByCmdb, - DeserializeCallback: redis.DeserializeTwemproxy, - GetSwitchInstanceInformation: redis.NewTwemproxySwitchInstance, + + //TwemproxyRedisInstance used + DBCallbackMap[constvar.DetectRedis] = Callback{ + FetchDBCallback: redis.RedisClusterNewIns, + DeserializeCallback: redis.RedisClusterDeserialize, + GetSwitchInstanceInformation: redis.RedisClusterNewSwitchIns, } - DBCallbackMap[constvar.Predixy] = Callback{ - FetchDBCallback: redis.NewPredixyInstanceByCmdb, - DeserializeCallback: redis.DeserializePredixy, - GetSwitchInstanceInformation: redis.NewPredixySwitchInstance, + + //PredixyTendisplusCluster used + DBCallbackMap[constvar.DetectTendisplus] = Callback{ + FetchDBCallback: redis.TendisClusterNewIns, + DeserializeCallback: redis.TendisClusterDeserialize, + GetSwitchInstanceInformation: redis.TendisClusterNewSwitchIns, } - DBCallbackMap[constvar.Tendisplus] = Callback{ - FetchDBCallback: redis.NewTendisplusInstanceByCmdb, - DeserializeCallback: redis.DeserializeTendisplus, - GetSwitchInstanceInformation: redis.NewTendisplusSwitchInstance, + + DBCallbackMap[constvar.Riak] = Callback{ + FetchDBCallback: riak.NewRiakInstanceByCmDB, + DeserializeCallback: riak.DeserializeRiak, + GetSwitchInstanceInformation: riak.NewRiakSwitchInstance, } } diff --git a/dbm-services/common/dbha/ha-module/dbmodule/riak/riak_callback.go b/dbm-services/common/dbha/ha-module/dbmodule/riak/riak_callback.go new file mode 100644 index 0000000000..7b0ebcf389 --- /dev/null +++ b/dbm-services/common/dbha/ha-module/dbmodule/riak/riak_callback.go @@ -0,0 +1,137 @@ +package riak + +import ( + "dbm-services/common/dbha/ha-module/client" + "dbm-services/common/dbha/ha-module/config" + "dbm-services/common/dbha/ha-module/constvar" + "dbm-services/common/dbha/ha-module/dbutil" + "dbm-services/common/dbha/ha-module/log" + "encoding/json" + "fmt" + "strconv" +) + +// RiakInstanceInfoDetail 实例信息 +type RiakInstanceInfoDetail struct { + IP string `json:"ip"` + Port int `json:"port"` + BKIdcCityID int `json:"bk_idc_city_id"` + InstanceRole string `json:"instance_role"` + Status string `json:"status"` + Cluster string `json:"cluster"` + BKBizID int `json:"bk_biz_id"` + ClusterType string `json:"cluster_type"` + MachineType string `json:"machine_type"` +} + +// NewRiakInstanceByCmDB unmarshal cmdb instances to agent detect instance struct +func NewRiakInstanceByCmDB(instances []interface{}, Conf *config.Config) ([]dbutil.DataBaseDetect, error) { + var ( + err error + unmarshalIns []*RiakDetectInstanceInfoFromCmDB + ret []dbutil.DataBaseDetect + ) + + unmarshalIns, err = UnMarshalRiakInstanceByCmdb(instances, constvar.Riak, constvar.Riak) + + if err != nil { + return nil, err + } + + // cmdb的数据结构转换为agent用来探测的数据结构 + for _, uIns := range unmarshalIns { + ret = append(ret, NewRiakDetectInstanceForAgent(uIns, Conf)) + } + + return ret, err +} + +// DeserializeRiak 反序列化从Agent上报上来的故障实例 +func DeserializeRiak(jsonInfo []byte, conf *config.Config) (dbutil.DataBaseDetect, error) { + response := RiakDetectResponse{} + err := json.Unmarshal(jsonInfo, &response) + if err != nil { + log.Logger.Errorf("json unmarshal failed. jsoninfo:\n%s\n, err:%s", string(jsonInfo), err.Error()) + return nil, err + } + var ret dbutil.DataBaseDetect + // gm将agent上报的数据结构转换为gdm通道接收的数据结构 + ret = NewRiakDetectInstanceForGdm(&response, constvar.Riak, conf) + return ret, nil +} + +// NewRiakSwitchInstance unmarshal cmdb instances to switch instance struct +func NewRiakSwitchInstance(instances []interface{}, conf *config.Config) ([]dbutil.DataBaseSwitch, error) { + var ret []dbutil.DataBaseSwitch + for _, v := range instances { + ins := RiakInstanceInfoDetail{} + rawData, err := json.Marshal(v) + if err != nil { + return nil, fmt.Errorf("marshal instance info failed:%s", err.Error()) + } + if err = json.Unmarshal(rawData, &ins); err != nil { + return nil, fmt.Errorf("unmarshal instance info failed:%s", err.Error()) + } + + // 用于切换的实例信息 + swIns := RiakSwitch{ + BaseSwitch: dbutil.BaseSwitch{ + Ip: ins.IP, + Port: ins.Port, + IDC: strconv.Itoa(ins.BKIdcCityID), + Status: ins.Status, + App: strconv.Itoa(ins.BKBizID), + ClusterType: ins.ClusterType, + MetaType: ins.MachineType, + Cluster: ins.Cluster, + CmDBClient: client.NewCmDBClient(&conf.DBConf.CMDB, conf.GetCloudId()), + HaDBClient: client.NewHaDBClient(&conf.DBConf.HADB, conf.GetCloudId()), + }, + Role: ins.InstanceRole, + } + ret = append(ret, &swIns) + } + return ret, nil +} + +// UnMarshalRiakInstanceByCmdb convert cmdb instance info to RiakDetectInstanceInfoFromCmDB +func UnMarshalRiakInstanceByCmdb(instances []interface{}, + uClusterType string, uMetaType string) ([]*RiakDetectInstanceInfoFromCmDB, error) { + var ( + ret []*RiakDetectInstanceInfoFromCmDB + ) + cache := map[string]*RiakDetectInstanceInfoFromCmDB{} + + for _, v := range instances { + ins := RiakInstanceInfoDetail{} + rawData, err := json.Marshal(v) + if err != nil { + return nil, fmt.Errorf("marshal instance info failed:%s", err.Error()) + } + if err = json.Unmarshal(rawData, &ins); err != nil { + return nil, fmt.Errorf("unmarshal instance info failed:%s", err.Error()) + } + if ins.ClusterType != uClusterType || ins.MachineType != uMetaType || + (ins.Status != constvar.RUNNING && ins.Status != constvar.AVAILABLE) { + continue + } + cacheIns, ok := cache[ins.IP] + //only need detect the minimum port instance + if !ok || ok && ins.Port < cacheIns.Port { + cache[ins.IP] = &RiakDetectInstanceInfoFromCmDB{ + Ip: ins.IP, + Port: ins.Port, + App: strconv.Itoa(ins.BKBizID), + ClusterType: ins.ClusterType, + MetaType: ins.MachineType, + Cluster: ins.Cluster, + } + } + } + + for _, cacheIns := range cache { + ret = append(ret, cacheIns) + } + + return ret, nil +} diff --git a/dbm-services/common/dbha/ha-module/dbmodule/riak/riak_detect.go b/dbm-services/common/dbha/ha-module/dbmodule/riak/riak_detect.go new file mode 100644 index 0000000000..abe469e941 --- /dev/null +++ b/dbm-services/common/dbha/ha-module/dbmodule/riak/riak_detect.go @@ -0,0 +1,159 @@ +package riak + +import ( + "encoding/json" + "fmt" + "math/rand" + "strings" + "time" + + "dbm-services/common/dbha/ha-module/config" + "dbm-services/common/dbha/ha-module/constvar" + "dbm-services/common/dbha/ha-module/dbutil" + "dbm-services/common/dbha/ha-module/log" + "dbm-services/common/dbha/ha-module/types" + "dbm-services/common/dbha/ha-module/util" + + "gorm.io/gorm" +) + +// RiakDetectInstance riak instance detect struct +type RiakDetectInstance struct { + dbutil.BaseDetectDB + User string + Pass string + Timeout int + realDB *gorm.DB +} + +// RiakDetectResponse riak instance response struct +type RiakDetectResponse struct { + dbutil.BaseDetectDBResponse +} + +// RiakDetectInstanceInfoFromCmDB riak instance detect struct in cmdb +type RiakDetectInstanceInfoFromCmDB struct { + Ip string + Port int + App string + ClusterType string + MetaType string + Cluster string +} + +// NewRiakDetectInstanceForAgent convert cmdb info to detect info +func NewRiakDetectInstanceForAgent(ins *RiakDetectInstanceInfoFromCmDB, conf *config.Config) *RiakDetectInstance { + return &RiakDetectInstance{ + BaseDetectDB: dbutil.BaseDetectDB{ + Ip: ins.Ip, + Port: ins.Port, + App: ins.App, + DBType: types.DBType(ins.ClusterType), + ReporterTime: time.Unix(0, 0), + ReportInterval: conf.AgentConf.ReportInterval + rand.Intn(20), + Status: constvar.DBCheckSuccess, + Cluster: ins.Cluster, + }, + Timeout: conf.DBConf.Riak.Timeout, + } +} + +// NewRiakDetectInstanceForGdm convert api response info into detect info +func NewRiakDetectInstanceForGdm(ins *RiakDetectResponse, dbType string, conf *config.Config) *RiakDetectInstance { + return &RiakDetectInstance{ + BaseDetectDB: dbutil.BaseDetectDB{ + Ip: ins.DBIp, + Port: ins.DBPort, + App: ins.App, + DBType: types.DBType(dbType), + ReporterTime: time.Unix(0, 0), + ReportInterval: conf.AgentConf.ReportInterval + rand.Intn(20), + Status: types.CheckStatus(ins.Status), + Cluster: ins.Cluster, + }, + Timeout: conf.DBConf.Riak.Timeout, + } +} + +// Detection TODO +// return error: +// +// not nil: check db failed or do ssh failed +// nil: check db success +func (m *RiakDetectInstance) Detection() error { + recheck := 1 + var riakErr error + for i := 0; i <= recheck; i++ { + // 设置缓冲为1防止没有接收者导致阻塞,即Detection已经超时返回 + errChan := make(chan error, 2) + // 这里存在资源泄露的可能,因为不能主动kill掉协程,所以如果这个协程依然阻塞在连接riak,但是 + // 这个函数已经超时返回了,那么这个协程因为被阻塞一直没被释放,直到Riak连接超时,如果阻塞的时间 + // 大于下次探测该实例的时间间隔,则创建协程频率大于释放协程频率,可能会导致oom。可以考虑在Riak + // 客户端连接设置超时时间来防止。 + go m.CheckRiak(errChan) + select { + case riakErr = <-errChan: + if riakErr != nil { + log.Logger.Warnf("check riak failed:%s. ip:%s, port:%d, app:%s", + riakErr.Error(), m.Ip, m.Port, m.App) + // 公共的gmm.go的Process函数仅当SSHCheckFailed状态是才会进入gqa、gcm的切换步骤 + // 不论是riak实例访问不了还是机器访问不了,都希望gcm更改实例状态为不可用 + // 因此 m.Status = constvar.SSHCheckFailed 替代 m.Status = constvar.DBCheckFailed + m.Status = constvar.SSHCheckFailed + } else { + m.Status = constvar.DBCheckSuccess + return nil + } + case <-time.After(time.Second * time.Duration(m.Timeout)): + riakErr = fmt.Errorf("connect Riak timeout recheck:%d", recheck) + log.Logger.Warnf(riakErr.Error()) + m.Status = constvar.SSHCheckFailed + } + } + return riakErr +} + +// CheckRiak check whether riak alive +func (m *RiakDetectInstance) CheckRiak(errChan chan error) { + query := fmt.Sprintf(`curl -s --connect-timeout %d -m %d http://%s:%d/types/default/buckets/test/keys/1000`, + m.Timeout, m.Timeout, m.Ip, constvar.RiakHttpPort) + insert := fmt.Sprintf( + `%s -X PUT -H 'Content-Type: application/json' -d '{name: "DBATeam", members: 31}'`, query) + _, err := util.ExecShellCommand(false, insert) + if err != nil { + log.Logger.Warnf("riak insert heartbeat [ %s ] failed. ip:%s, port:%d, err:%s", + insert, m.Ip, m.Port, err.Error()) + errChan <- err + return + } + stdout, err := util.ExecShellCommand(false, query) + if err != nil { + log.Logger.Warnf("riak query heartbeat [ %s ] failed. ip:%s, port:%d, err:%s", + query, m.Ip, m.Port, err.Error()) + errChan <- err + return + } else if strings.Contains(stdout, "not found") { + err = fmt.Errorf("riak query heartbeat [ %s ] not found", query) + log.Logger.Warnf("riak query heartbeat [ %s ] not found. ip:%s, port:%d, err:%s", + query, m.Ip, m.Port, err.Error()) + errChan <- err + return + } + errChan <- nil +} + +// Serialization serialize riak instance info +func (m *RiakDetectInstance) Serialization() ([]byte, error) { + response := RiakDetectResponse{ + BaseDetectDBResponse: m.NewDBResponse(), + } + + resByte, err := json.Marshal(&response) + + if err != nil { + log.Logger.Errorf("riak serialization failed. err:%s", err.Error()) + return []byte{}, err + } + + return resByte, nil +} diff --git a/dbm-services/common/dbha/ha-module/dbmodule/riak/riak_switch.go b/dbm-services/common/dbha/ha-module/dbmodule/riak/riak_switch.go new file mode 100644 index 0000000000..be1c739231 --- /dev/null +++ b/dbm-services/common/dbha/ha-module/dbmodule/riak/riak_switch.go @@ -0,0 +1,58 @@ +package riak + +import ( + "dbm-services/common/dbha/ha-module/constvar" + "dbm-services/common/dbha/ha-module/dbutil" + "dbm-services/common/dbha/ha-module/log" + "fmt" +) + +// RiakSwitch defined riak switch struct +type RiakSwitch struct { + dbutil.BaseSwitch + Role string +} + +// GetRole get mysql role type +func (ins *RiakSwitch) GetRole() string { + return ins.Role +} + +// ShowSwitchInstanceInfo show mysql instance's switch info +func (ins *RiakSwitch) ShowSwitchInstanceInfo() string { + str := fmt.Sprintf("<%s#%d IDC:%s Role:%s Status:%s Bzid:%s ClusterType:%s MachineType:%s>", + ins.Ip, ins.Port, ins.IDC, ins.Role, ins.Status, ins.App, ins.ClusterType, + ins.MetaType) + return str +} + +// CheckSwitch check before switch +func (ins *RiakSwitch) CheckSwitch() (bool, error) { + var err error + if ins.Role == constvar.Riak { + ins.ReportLogs(constvar.InfoResult, "instance riak, needn't check") + return false, nil + } else { + err = fmt.Errorf("info:{%s} unknown role", ins.ShowSwitchInstanceInfo()) + log.Logger.Error(err) + ins.ReportLogs(constvar.FailResult, "instance unknown role") + return false, err + } + ins.ReportLogs(constvar.InfoResult, "riak check switch ok") + return false, nil +} + +// DoSwitch do switch +func (ins *RiakSwitch) DoSwitch() error { + return nil +} + +// RollBack do switch rollback +func (ins *RiakSwitch) RollBack() error { + return nil +} + +// UpdateMetaInfo swap master, slave 's meta info in cmdb +func (ins *RiakSwitch) UpdateMetaInfo() error { + return nil +} diff --git a/dbm-services/common/dbha/ha-module/dbutil/db_detect.go b/dbm-services/common/dbha/ha-module/dbutil/db_detect.go index 18864bb13a..fe27bdbeb6 100644 --- a/dbm-services/common/dbha/ha-module/dbutil/db_detect.go +++ b/dbm-services/common/dbha/ha-module/dbutil/db_detect.go @@ -12,13 +12,45 @@ import ( "golang.org/x/crypto/ssh" ) +// SlaveInfo defined slave switch info +type SlaveInfo struct { + Ip string `json:"ip"` + Port int `json:"port"` + IsStandBy bool `json:"is_stand_by"` + Status string `json:"status"` + BinlogFile string + BinlogPosition string +} + +// DBInstanceInfoDetail instance detail info from cmdb api +type DBInstanceInfoDetail struct { + IP string `json:"ip"` + Port int `json:"port"` + AdminPort int `json:"admin_port"` + BKIdcCityID int `json:"bk_idc_city_id"` + InstanceRole string `json:"instance_role"` + //only TenDBCluster's spider node used + SpiderRole string `json:"spider_role"` + Status string `json:"status"` + Cluster string `json:"cluster"` + BKBizID int `json:"bk_biz_id"` + ClusterType string `json:"cluster_type"` + MachineType string `json:"machine_type"` + Receiver []SlaveInfo `json:"receiver"` + ProxyInstanceSet []ProxyInfo `json:"proxyinstance_set"` + BindEntry BindEntry `json:"bind_entry"` +} + // DataBaseDetect interface type DataBaseDetect interface { Detection() error + // Serialization agent call this to serializa instance info, and then send to gdm Serialization() ([]byte, error) NeedReporter() bool GetType() types.DBType + // GetDetectType agent send detect type to gm, gm use this key to find callback func + GetDetectType() string GetStatus() types.CheckStatus GetAddress() (string, int) GetApp() string @@ -35,18 +67,23 @@ type BaseDetectDB struct { ReporterTime time.Time ReportInterval int Status types.CheckStatus - Cluster string - SshInfo Ssh + //cluster name + Cluster string + //cluster type name + ClusterType string + SshInfo Ssh } -// BaseDetectDBResponse detect response struct +// BaseDetectDBResponse agent do detect and response type BaseDetectDBResponse struct { - AgentIp string `json:"agent_ip"` - DBIp string `json:"db_ip"` - DBPort int `json:"db_port"` - App string `json:"app"` - Status string `json:"status"` - Cluster string `json:"cluster"` + AgentIp string `json:"agent_ip"` + DBIp string `json:"db_ip"` + DBPort int `json:"db_port"` + DBType string `json:"db_type"` + App string `json:"app"` + Status string `json:"status"` + Cluster string `json:"cluster"` + ClusterType string `json:"cluster_type"` } // Ssh detect configure @@ -95,7 +132,7 @@ func (b *BaseDetectDB) DoSSH(shellStr string) error { return nil } -// NeedReporter decide whether need report detect result +// NeedReporter decide whether need report detect result to HADB func (b *BaseDetectDB) NeedReporter() bool { var need bool if b.Status == constvar.DBCheckSuccess { @@ -123,6 +160,12 @@ func (b *BaseDetectDB) GetType() types.DBType { return b.DBType } +// GetDetectType return detect type +// prefer to use cluster name, but consider compatibility with currently dbType +func (b *BaseDetectDB) GetDetectType() string { + return string(b.DBType) +} + // GetStatus return status func (b *BaseDetectDB) GetStatus() types.CheckStatus { return b.Status @@ -159,11 +202,13 @@ func (b *BaseDetectDB) ReturnSshInteractive() ssh.KeyboardInteractiveChallenge { // NewDBResponse init db response struct, use to unmarshal func (b *BaseDetectDB) NewDBResponse() BaseDetectDBResponse { return BaseDetectDBResponse{ - AgentIp: util.LocalIp, - DBIp: b.Ip, - DBPort: b.Port, - App: b.App, - Status: string(b.Status), - Cluster: b.Cluster, + AgentIp: util.LocalIp, + DBIp: b.Ip, + DBPort: b.Port, + App: b.App, + Status: string(b.Status), + Cluster: b.Cluster, + DBType: string(b.DBType), + ClusterType: b.ClusterType, } } diff --git a/dbm-services/common/dbha/ha-module/dbutil/db_switch.go b/dbm-services/common/dbha/ha-module/dbutil/db_switch.go index 704384b810..115ed04090 100644 --- a/dbm-services/common/dbha/ha-module/dbutil/db_switch.go +++ b/dbm-services/common/dbha/ha-module/dbutil/db_switch.go @@ -1,9 +1,12 @@ package dbutil import ( + "fmt" "time" "dbm-services/common/dbha/ha-module/client" + "dbm-services/common/dbha/ha-module/config" + "dbm-services/common/dbha/ha-module/constvar" "dbm-services/common/dbha/ha-module/log" ) @@ -14,6 +17,7 @@ type DataBaseSwitch interface { ShowSwitchInstanceInfo() string RollBack() error UpdateMetaInfo() error + DeleteNameService(entry BindEntry) error GetAddress() (string, int) GetIDC() string @@ -31,11 +35,35 @@ type DataBaseSwitch interface { ReportLogs(result string, comment string) bool } +// PolarisInfo polaris detail info, response by cmdb api +type PolarisInfo struct { + Service string `json:"polaris_name"` + Token string `json:"polaris_token"` + L5 string `json:"polaris_l5"` +} + +// CLBInfo clb detail info, response by cmdb api +type ClbInfo struct { + Region string `json:"clb_region"` + LoadBalanceId string `json:"clb_id"` + ListenId string `json:"listener_id"` +} + +// DnsInfo dns detail info, response by cmdb api +type DnsInfo struct { + DomainName string `json:"domain"` + //master_entry, slave_entry + EntryRole string `json:"entry_role"` + BindIps []string `json:"bind_ips"` + BindPort int `json:"bind_port"` + ForwardEntryId int `json:"forward_entry_id"` +} + // BindEntry TODO type BindEntry struct { - Dns []interface{} + Dns []DnsInfo Polaris []interface{} - CLB []interface{} + Clb []interface{} } // ProxyInfo TODO @@ -54,12 +82,17 @@ type BaseSwitch struct { Status string App string ClusterType string - MetaType string - SwitchUid uint - Cluster string - CmDBClient *client.CmDBClient - HaDBClient *client.HaDBClient - Infos map[string]interface{} + //machine type in cmdb api response + MetaType string + SwitchUid uint + Cluster string + CmDBClient *client.CmDBClient + //if not init, gcm may report log abnormal + HaDBClient *client.HaDBClient + //extra info, used through SetInfo/GetInfo method + Infos map[string]interface{} + //config info in yaml + Config *config.Config } // GetAddress TODO @@ -136,7 +169,112 @@ func (ins *BaseSwitch) GetInfo(infoKey string) (bool, interface{}) { } } -// ReportLogs TODO +// DeleteNameService delete broken-down ip from entry +func (ins *BaseSwitch) DeleteNameService(entry BindEntry) error { + //flag refer to whether release name-service success + var ( + dnsFlag = true + clbFlag = true + polarisFlag = true + ) + conf := ins.Config + if entry.Dns != nil { + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("try to release dns entry")) + dnsClient := client.NewNameServiceClient(&conf.DNS.BindConf, conf.GetCloudId()) + for _, dns := range entry.Dns { + for _, ip := range dns.BindIps { + if ip == ins.Ip { + if err := dnsClient.DeleteDomain(dns.DomainName, ins.GetApp(), ins.Ip, dns.BindPort); err != nil { + ins.ReportLogs(constvar.FailResult, fmt.Sprintf("delete ip[%s] from domain[%s] failed:%s", + ip, dns.DomainName, err.Error())) + dnsFlag = false + } + break + } + } + } + if dnsFlag { + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("release dns entry success")) + } + } + + if entry.Clb != nil { + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("try to release clb entry")) + /*clbClient := client.NewNameServiceClient(&conf.DNS.ClbConf, conf.GetCloudId()) + for _, clb := range entry.Clb { + addr := fmt.Sprintf("%s:%d", ins.Ip, ins.Port) + ips, err := clbClient.ClbGetTargets( + clb.Region, clb.LoadBalanceId, clb.ListenId, + ) + if err != nil { + ins.ReportLogs(constvar.FailResult, + fmt.Sprintf("get Clb[%s:%s:%s] Targets failed:%s", + clb.Region, clb.LoadBalanceId, clb.ListenId, err.Error())) + continue + } + + for _, ip := range ips { + if ip != addr { + continue + } + + err := clbClient.ClbDeRegister( + clb.Region, clb.LoadBalanceId, clb.ListenId, addr, + ) + if err != nil { + ins.ReportLogs(constvar.FailResult, + fmt.Sprintf("delte %s from clb[%s:%s:%s] failed:%s", + addr, clb.Region, clb.LoadBalanceId, clb.ListenId, err.Error())) + clbFlag = false + } + break + } + }*/ + } + + if entry.Polaris != nil { + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("try to release polaris entry")) + /*polarisClient := client.NewNameServiceClient(&conf.DNS.PolarisConf, conf.GetCloudId()) + for _, pinfo := range entry.Polaris { + addr := fmt.Sprintf("%s:%d", ins.Ip, ins.Port) + ips, err := polarisClient.GetPolarisTargets(pinfo.Service) + if err != nil { + ins.ReportLogs(constvar.FailResult, + fmt.Sprintf("get Polaris[%s:%s] Targets failed,err:%s", + pinfo.Service, pinfo.Token, err.Error())) + continue + } + + for _, ip := range ips { + if ip != addr { + continue + } + + err := polarisClient.PolarisUnBindTarget( + pinfo.Service, pinfo.Token, addr) + if err != nil { + ins.ReportLogs(constvar.FailResult, + fmt.Sprintf("delete [%s] from polaris %s:%s failed:%s", + addr, pinfo.Service, pinfo.Token, err.Error())) + polarisFlag = false + } + break + } + }*/ + } + + if !(dnsFlag && clbFlag && polarisFlag) { + return fmt.Errorf("release broken-down ip from all entry failed") + } + + ins.ReportLogs(constvar.InfoResult, fmt.Sprintf("release all cluster entry success")) + return nil +} + +// ReportLogs report switch logs to hadb +// Input param +// result: constvar.FailResult, etc. in constvar +// comment: switch detail info func (ins *BaseSwitch) ReportLogs(result string, comment string) bool { log.Logger.Infof(comment) if nil == ins.HaDBClient { diff --git a/dbm-services/common/dbha/ha-module/dbutil/dbutil.go b/dbm-services/common/dbha/ha-module/dbutil/dbutil.go index d060e68705..c4b712da87 100644 --- a/dbm-services/common/dbha/ha-module/dbutil/dbutil.go +++ b/dbm-services/common/dbha/ha-module/dbutil/dbutil.go @@ -1,2 +1,21 @@ // Package dbutil TODO package dbutil + +import ( + "database/sql" + + _ "github.com/go-sql-driver/mysql" + + "dbm-services/common/dbha/ha-module/log" +) + +// ConnMySQL connParam format: user:password@(ip:port)/dbName, %s:%s@(%s:%d)/%s +func ConnMySQL(connParam string) (*sql.DB, error) { + db, err := sql.Open("mysql", connParam) + if err != nil { + log.Logger.Errorf("connect mysql failed. err:%s", err.Error()) + return nil, nil + } + + return db, nil +} diff --git a/dbm-services/common/dbha/ha-module/errno/code.go b/dbm-services/common/dbha/ha-module/errno/code.go deleted file mode 100644 index 313c555d7e..0000000000 --- a/dbm-services/common/dbha/ha-module/errno/code.go +++ /dev/null @@ -1,47 +0,0 @@ -package errno - -var ( - // OK TODO - OK = &Errno{Code: 0, Message: "OK"} - - // ErrInterval TODO - // types error, prefix is 100 - ErrInterval = &Errno{Code: 10001, Message: "Internal server error", CNMessage: "内部未知错误"} - // ErrHttpStatusCode TODO - ErrHttpStatusCode = &Errno{Code: 10002, Message: "Invalid http status code", CNMessage: "http请求状态码不对"} - // ErrHttpResponse TODO - ErrHttpResponse = &Errno{Code: 10003, Message: "Invalid http response", CNMessage: "http请求返回异常"} - // ErrInvokeApi TODO - ErrInvokeApi = &Errno{Code: 10004, Message: "Invoke api failed", CNMessage: "调用api出错"} - // ErrJSONMarshal TODO - ErrJSONMarshal = &Errno{Code: 10005, Message: "Error occurred while marshal the data to JSON.", - CNMessage: "序列化JSON数据出错"} - // ErrJSONUnmarshal TODO - ErrJSONUnmarshal = &Errno{Code: 10006, Message: "Error occurred while unmarshal the JSON to data model.", - CNMessage: "反序列号JSON数据出错"} - // ErrGetInstanceInfo TODO - ErrGetInstanceInfo = &Errno{Code: 10007, Message: "Get instance info failed", CNMessage: "获取实例信息失败"} - // ErrAppNotFound TODO - ErrAppNotFound = &Errno{Code: 10008, Message: "Get app info failed", CNMessage: "获取业务信息失败"} - - // api error, prefix is 200 - - // ErrMultiMaster TODO - // mysql error, prefix is 300 - ErrMultiMaster = &Errno{Code: 30001, Message: "Multi master found", CNMessage: "同一主机同时存在实例角色master和slave"} - // ErrSwitchNumUnMatched TODO - // dead host's instance num not equal to its switch number - ErrSwitchNumUnMatched = &Errno{Code: 30002, Message: "instances number is %d, switch number is %d, unmatched", - CNMessage: "实例个数%d与切换个数%d不匹配"} - // ErrRemoteQuery TODO - ErrRemoteQuery = &Errno{Code: 30003, Message: "do remote query failed", CNMessage: "调用remoteQuery失败"} - // ErrRemoteExecute TODO - ErrRemoteExecute = &Errno{Code: 30004, Message: "do remote execute failed", CNMessage: "调用remoteExecute失败"} - // ErrIOTreadState TODO - ErrIOTreadState = &Errno{Code: 30005, Message: "slave IO_THREAD is not ok", CNMessage: "IO_THREAD异常"} - // ErrSQLTreadState TODO - ErrSQLTreadState = &Errno{Code: 30006, Message: "slave SQL_THREAD is not ok", CNMessage: "SQL_THREAD异常"} - // ErrSlaveStatus TODO - ErrSlaveStatus = &Errno{Code: 30007, Message: "get slave status abnormal", CNMessage: "获取slave status异常"} - // proxy error, prefix is 400 -) diff --git a/dbm-services/common/dbha/ha-module/errno/errno.go b/dbm-services/common/dbha/ha-module/errno/errno.go deleted file mode 100644 index b61565926a..0000000000 --- a/dbm-services/common/dbha/ha-module/errno/errno.go +++ /dev/null @@ -1,102 +0,0 @@ -// Package errno TODO -package errno - -import ( - "fmt" -) - -// Errno struct -type Errno struct { - Code int - Message string - CNMessage string -} - -// Lang TODO -var Lang = "en_US" - -// Error 用于错误处理 -// get string error -func (err Errno) Error() string { - switch Lang { - case "zh_CN": - return err.CNMessage - case "en_US": - return err.Message - default: - return err.Message - } -} - -// Addf error info according to format -func (err Errno) Addf(format string, args ...interface{}) error { - return err.Add(fmt.Sprintf(format, args...)) -} - -// Errorf format -func (err Errno) Errorf(args ...interface{}) error { - switch Lang { - case "zh_CN": - err.CNMessage = fmt.Sprintf(err.CNMessage, args...) - case "en_US": - err.Message = fmt.Sprintf(err.Message, args...) - default: - err.Message = fmt.Sprintf(err.Message, args...) - } - - return err -} - -// Add error info -func (err Errno) Add(message string) error { - switch Lang { - case "zh_CN": - err.CNMessage += message - case "en_US": - err.Message += message - default: - err.Message += message - } - return err -} - -// Err or with errno -type Err struct { - Errno - Err error -} - -// New error -func New(errno *Errno, err error) *Err { - return &Err{Errno: *errno, Err: err} -} - -// SetMsg TODO -// set error message -func (err Err) SetMsg(message string) error { - err.Message = message - return err -} - -// SetCNMsg TODO -// set cn error message -func (err Err) SetCNMsg(cnMessage string) error { - err.CNMessage = cnMessage - return err -} - -// Error 用于错误处理 -// get error string -func (err Err) Error() string { - message := err.Message - switch Lang { - case "zh_CN": - message = err.CNMessage - case "en_US": - message = err.Message - err.Message += message - default: - message = err.Message - } - return fmt.Sprintf("Err - code: %d, message: %s, error: %s", err.Code, message, err.Err.Error()) -} diff --git a/dbm-services/common/dbha/ha-module/gm/connection.go b/dbm-services/common/dbha/ha-module/gm/connection.go index 3889f699a6..5a52067891 100644 --- a/dbm-services/common/dbha/ha-module/gm/connection.go +++ b/dbm-services/common/dbha/ha-module/gm/connection.go @@ -10,7 +10,6 @@ import ( "dbm-services/common/dbha/ha-module/config" "dbm-services/common/dbha/ha-module/dbmodule" "dbm-services/common/dbha/ha-module/log" - "dbm-services/common/dbha/ha-module/types" ) type parseStatus int @@ -46,7 +45,7 @@ const MaxBodyLength int = 128 * 1024 // Package TODO type Package struct { Header string - DBType string + DetectType string BodyLength int Body []byte } @@ -124,19 +123,20 @@ func (conn *AgentConnection) parse(readLen int) error { conn.status = ParseType case ParseType: if conn.Buffer[i] == '\r' { - _, ok := dbmodule.DBCallbackMap[types.DBType(conn.netPackage.DBType)] + _, ok := dbmodule.DBCallbackMap[conn.netPackage.DetectType] if !ok { - err = fmt.Errorf("parse failed, can't find dbtype, status ParseType, index %d", i) + err = fmt.Errorf("parse failed, can't find dbtype:%s, status ParseType, index %d", + conn.netPackage.DetectType, i) log.Logger.Errorf(err.Error()) break } conn.status = ParseTypeLF - } else if conn.Buffer[i] != '\r' && len(conn.netPackage.DBType) > MaxDBTypeLength { + } else if conn.Buffer[i] != '\r' && len(conn.netPackage.DetectType) > MaxDBTypeLength { err = fmt.Errorf("parse failed, len(DBType) > MaxDBtypeLen, status ParseType, index %d", i) log.Logger.Errorf(err.Error()) break } else { - conn.netPackage.DBType += string(conn.Buffer[i]) + conn.netPackage.DetectType += string(conn.Buffer[i]) } case ParseTypeLF: if conn.Buffer[i] != '\n' { @@ -181,7 +181,7 @@ func (conn *AgentConnection) parse(readLen int) error { // unpack success // replay ok log.Logger.Infof("process net package success. Type:%s, Body:%s", - conn.netPackage.DBType, conn.netPackage.Body) + conn.netPackage.DetectType, conn.netPackage.Body) n, err := conn.NetConnection.Write([]byte("OK")) if err != nil { log.Logger.Error("write failed. agent ip:", conn.Ip, " port:", conn.Port) @@ -206,7 +206,7 @@ func (conn *AgentConnection) parse(readLen int) error { func (conn *AgentConnection) resetPackage() { conn.netPackage.Header = "" - conn.netPackage.DBType = "" + conn.netPackage.DetectType = "" conn.netPackage.BodyLength = 0 conn.netPackage.Body = []byte{} conn.status = Idle @@ -215,9 +215,9 @@ func (conn *AgentConnection) resetPackage() { // processPackage 将一个完整的包处理并传给gdm func (conn *AgentConnection) processPackage() error { var err error - cb, ok := dbmodule.DBCallbackMap[types.DBType(conn.netPackage.DBType)] + cb, ok := dbmodule.DBCallbackMap[conn.netPackage.DetectType] if !ok { - err = fmt.Errorf("can't find %s instance callback", conn.netPackage.DBType) + err = fmt.Errorf("can't find %s instance callback", conn.netPackage.DetectType) log.Logger.Errorf(err.Error()) return err } diff --git a/dbm-services/common/dbha/ha-module/gm/gcm.go b/dbm-services/common/dbha/ha-module/gm/gcm.go index 748321c8a7..70a64c1999 100644 --- a/dbm-services/common/dbha/ha-module/gm/gcm.go +++ b/dbm-services/common/dbha/ha-module/gm/gcm.go @@ -2,6 +2,7 @@ package gm import ( "fmt" + "strconv" "time" "dbm-services/common/dbha/ha-module/client" @@ -26,9 +27,8 @@ type GCM struct { } // NewGCM init new gcm -func NewGCM(conf *config.Config, ch chan dbutil.DataBaseSwitch, reporter *HAReporter) (*GCM, error) { - var err error - gcm := &GCM{ +func NewGCM(conf *config.Config, ch chan dbutil.DataBaseSwitch, reporter *HAReporter) *GCM { + return &GCM{ GQAChan: ch, Conf: conf, AllowedChecksumMaxOffset: conf.GMConf.GCM.AllowedChecksumMaxOffset, @@ -36,16 +36,9 @@ func NewGCM(conf *config.Config, ch chan dbutil.DataBaseSwitch, reporter *HARepo AllowedSlaveDelayMax: conf.GMConf.GCM.AllowedSlaveDelayMax, ExecSlowKBytes: conf.GMConf.GCM.ExecSlowKBytes, reporter: reporter, + CmDBClient: client.NewCmDBClient(&conf.DBConf.CMDB, conf.GetCloudId()), + HaDBClient: client.NewHaDBClient(&conf.DBConf.HADB, conf.GetCloudId()), } - gcm.CmDBClient, err = client.NewCmDBClient(&conf.DBConf.CMDB, conf.GetCloudId()) - if err != nil { - return nil, err - } - gcm.HaDBClient, err = client.NewHaDBClient(&conf.DBConf.HADB, conf.GetCloudId()) - if err != nil { - return nil, err - } - return gcm, nil } // Run gcm run main entry @@ -62,12 +55,6 @@ func (gcm *GCM) Run() { } } -// PopInstance pop instance from GQA chan -func (gcm *GCM) PopInstance() dbutil.DataBaseSwitch { - switchInstance := <-gcm.GQAChan - return switchInstance -} - // Process gcm process instance switch func (gcm *GCM) Process(switchInstance dbutil.DataBaseSwitch) { go func(switchInstance dbutil.DataBaseSwitch) { @@ -78,6 +65,7 @@ func (gcm *GCM) Process(switchInstance dbutil.DataBaseSwitch) { // DoSwitchSingle gcm do instance switch func (gcm *GCM) DoSwitchSingle(switchInstance dbutil.DataBaseSwitch) { var err error + switchQueueInfo := &client.SwitchQueue{} // 这里先将实例获取锁设为unavailable,再插入switch_queue。原因是如果先插switch_queue,如果其他gm同时更新,则会有多条 // switch_queue记录,则更新switch_queue会同时更新多条记录,因为我们没有无法区分哪条记录是哪个gm插入的 @@ -85,7 +73,7 @@ func (gcm *GCM) DoSwitchSingle(switchInstance dbutil.DataBaseSwitch) { err = gcm.SetUnavailableAndLockInstance(switchInstance) if err != nil { switchFail := "set instance to unavailable failed:" + err.Error() - switchInstance.ReportLogs(constvar.SWITCH_FAIL, switchFail) + switchInstance.ReportLogs(constvar.FailResult, switchFail) monitor.MonitorSendSwitch(switchInstance, switchFail, false) return } @@ -99,10 +87,11 @@ func (gcm *GCM) DoSwitchSingle(switchInstance dbutil.DataBaseSwitch) { monitor.MonitorSendSwitch(switchInstance, switchFail, false) return } - switchInstance.ReportLogs(constvar.CHECK_SWITCH_INFO, "set instance unavailable success") + //only after insert switch queue, unique switch uid generated + switchInstance.ReportLogs(constvar.InfoResult, "set instance unavailable success") for i := 0; i < 1; i++ { - switchInstance.ReportLogs(constvar.CHECK_SWITCH_INFO, "start check switch") + switchInstance.ReportLogs(constvar.InfoResult, "do pre-check before switch") var needContinue bool needContinue, err = switchInstance.CheckSwitch() @@ -113,12 +102,13 @@ func (gcm *GCM) DoSwitchSingle(switchInstance dbutil.DataBaseSwitch) { err = fmt.Errorf("check switch failed:%s", err.Error()) break } + switchInstance.ReportLogs(constvar.InfoResult, "pre-check ok") if !needContinue { break } - switchInstance.ReportLogs(constvar.SWITCH_INFO, "start do switch") + switchInstance.ReportLogs(constvar.InfoResult, "start do switch") err = switchInstance.DoSwitch() if err != nil { log.Logger.Errorf("do switch failed. err:%s, info{%s}", err.Error(), @@ -126,7 +116,8 @@ func (gcm *GCM) DoSwitchSingle(switchInstance dbutil.DataBaseSwitch) { err = fmt.Errorf("do switch failed:%s", err.Error()) break } - switchInstance.ReportLogs(constvar.SWITCH_INFO, "do switch success. try to update meta info") + switchInstance.ReportLogs(constvar.InfoResult, "do switch success") + switchInstance.ReportLogs(constvar.InfoResult, "last step, try to update meta info") log.Logger.Infof("do update meta info. info{%s}", switchInstance.ShowSwitchInstanceInfo()) err = switchInstance.UpdateMetaInfo() @@ -136,17 +127,14 @@ func (gcm *GCM) DoSwitchSingle(switchInstance dbutil.DataBaseSwitch) { err = fmt.Errorf("do update meta info failed:%s", err.Error()) break } - switchInstance.ReportLogs(constvar.SWITCH_INFO, "update meta info success") + switchInstance.ReportLogs(constvar.InfoResult, "update meta info success") } if err != nil { monitor.MonitorSendSwitch(switchInstance, err.Error(), false) log.Logger.Errorf("switch instance failed. info:{%s}", switchInstance.ShowSwitchInstanceInfo()) - updateErr := gcm.UpdateSwitchQueue(switchInstance, err.Error(), constvar.SWITCH_FAIL) - if updateErr != nil { - log.Logger.Errorf("update switch queue failed. err:%s, info{%s}", updateErr.Error(), - switchInstance.ShowSwitchInstanceInfo()) - } + switchQueueInfo.SwitchResult = err.Error() + switchQueueInfo.Status = constvar.SwitchFailed gcm.InsertSwitchLogs(switchInstance, false, err.Error()) rollbackErr := switchInstance.RollBack() @@ -160,21 +148,60 @@ func (gcm *GCM) DoSwitchSingle(switchInstance dbutil.DataBaseSwitch) { monitor.MonitorSendSwitch(switchInstance, switchOk, true) gcm.InsertSwitchLogs(switchInstance, true, switchOk) - updateErr := gcm.UpdateSwitchQueue(switchInstance, "switch_done", constvar.SWITCH_SUCC) - if updateErr != nil { - log.Logger.Errorf("update Switch queue failed. err:%s", updateErr.Error()) - return - } + switchQueueInfo.SwitchResult = "switch done" + switchQueueInfo.Status = constvar.SwitchSuccess + } + + switchQueueInfo.Uid = switchInstance.GetSwitchUid() + if ok, slaveIp := switchInstance.GetInfo(constvar.SlaveIpKey); ok { + _, slavePort := switchInstance.GetInfo(constvar.SlavePortKey) + switchQueueInfo.SlaveIP = slaveIp.(string) + switchQueueInfo.SlavePort = slavePort.(int) + } + updateErr := gcm.UpdateSwitchQueue(switchQueueInfo) + if updateErr != nil { + log.Logger.Errorf("update Switch queue failed. err:%s", updateErr.Error()) + return } } // InsertSwitchQueue insert switch info to tb_mon_switch_queue func (gcm *GCM) InsertSwitchQueue(instance dbutil.DataBaseSwitch) error { + log.Logger.Debugf("switch instance info:%#v", instance) ip, port := instance.GetAddress() - uid, err := gcm.HaDBClient.InsertSwitchQueue( - ip, port, instance.GetIDC(), time.Now(), instance.GetApp(), - instance.GetClusterType(), instance.GetCluster(), - ) + confirmTime := time.Now() + if ok, value := instance.GetInfo(constvar.DoubleCheckTimeKey); ok { + if t, ok := value.(time.Time); ok { + confirmTime = t + } + } + doubleCheckInfo := "unknown" + if ok, value := instance.GetInfo(constvar.DoubleCheckInfoKey); ok { + doubleCheckInfo = value.(string) + } + + currentTime := time.Now() + req := &client.SwitchQueueRequest{ + DBCloudToken: gcm.Conf.DBConf.HADB.BKConf.BkToken, + BKCloudID: gcm.Conf.GetCloudId(), + Name: constvar.InsertSwitchQueue, + SetArgs: &client.SwitchQueue{ + IP: ip, + Port: port, + Idc: instance.GetIDC(), + App: instance.GetApp(), + ConfirmCheckTime: &confirmTime, + DbType: instance.GetMetaType(), + Cloud: strconv.Itoa(gcm.Conf.GetCloudId()), + Cluster: instance.GetCluster(), + Status: constvar.SwitchStart, + SwitchStartTime: ¤tTime, + DbRole: instance.GetRole(), + ConfirmResult: doubleCheckInfo, + }, + } + + uid, err := gcm.HaDBClient.InsertSwitchQueue(req) if err != nil { log.Logger.Errorf("insert switch queue failed. err:%s", err.Error()) return err @@ -190,10 +217,10 @@ func (gcm *GCM) InsertSwitchLogs(instance dbutil.DataBaseSwitch, result bool, re curr := time.Now() info := instance.ShowSwitchInstanceInfo() if result { - resultDetail = constvar.SWITCH_SUCC + resultDetail = constvar.SuccessResult comment = fmt.Sprintf("%s %s success", curr.Format("2006-01-02 15:04:05"), info) } else { - resultDetail = constvar.SWITCH_FAIL + resultDetail = constvar.FailResult comment = fmt.Sprintf( "%s %s failed,err:%s", curr.Format("2006-01-02 15:04:05"), info, resultInfo, ) @@ -221,42 +248,21 @@ func (gcm *GCM) SetUnavailableAndLockInstance(instance dbutil.DataBaseSwitch) er } // UpdateSwitchQueue update switch result -func (gcm *GCM) UpdateSwitchQueue(instance dbutil.DataBaseSwitch, confirmResult string, switchResult string) error { - var ( - confirmStr string - slaveIp string - slavePort int - ) - if ok, dcInfo := instance.GetInfo(constvar.SWITCH_INFO_DOUBLECHECK); ok { - confirmStr = dcInfo.(string) - } else { - confirmStr = confirmResult +func (gcm *GCM) UpdateSwitchQueue(switchInfo *client.SwitchQueue) error { + req := &client.SwitchQueueRequest{ + DBCloudToken: gcm.Conf.DBConf.HADB.BKConf.BkToken, + BKCloudID: gcm.Conf.GetCloudId(), + Name: constvar.UpdateSwitchQueue, + QueryArgs: &client.SwitchQueue{ + Uid: switchInfo.Uid, + }, + SetArgs: switchInfo, } - if ok, slaveIpInfo := instance.GetInfo(constvar.SWITCH_INFO_SLAVE_IP); ok { - slaveIp = slaveIpInfo.(string) - } else { - slaveIp = "N/A" - } - - if ok, slavePortInfo := instance.GetInfo(constvar.SWITCH_INFO_SLAVE_PORT); ok { - slavePort = slavePortInfo.(int) - } else { - slavePort = 0 - } - - ip, port := instance.GetAddress() - if err := gcm.HaDBClient.UpdateSwitchQueue( - instance.GetSwitchUid(), ip, port, - constvar.UNAVAILABLE, - slaveIp, - slavePort, - confirmStr, - switchResult, - instance.GetRole(), - ); err != nil { + if err := gcm.HaDBClient.UpdateSwitchQueue(req); err != nil { log.Logger.Errorf("update switch queue failed. err:%s", err.Error()) return err } + return nil } diff --git a/dbm-services/common/dbha/ha-module/gm/gdm.go b/dbm-services/common/dbha/ha-module/gm/gdm.go index f30a02a923..e5c4cf9b8b 100644 --- a/dbm-services/common/dbha/ha-module/gm/gdm.go +++ b/dbm-services/common/dbha/ha-module/gm/gdm.go @@ -26,8 +26,8 @@ type GDM struct { // NewGDM init gdm func NewGDM(conf *config.Config, ch chan DoubleCheckInstanceInfo, - reporter *HAReporter) (*GDM, error) { - gdm := &GDM{ + reporter *HAReporter) *GDM { + return &GDM{ AgentChan: make(chan DoubleCheckInstanceInfo, 10), GMMChan: ch, ListenPort: conf.GMConf.ListenPort, @@ -38,7 +38,6 @@ func NewGDM(conf *config.Config, ch chan DoubleCheckInstanceInfo, Conf: conf, reporter: reporter, } - return gdm, nil } // Run gdm main entry @@ -106,7 +105,7 @@ func (gdm *GDM) listenAndDoAccept() { log.Logger.Errorf("accept socket failed. err:%s", err.Error()) continue } else { - log.Logger.Infof("gdm accept success con: %v agent ip: %v\n", conn, conn.RemoteAddr().String()) + log.Logger.Infof("gdm accept success, agent ip: %v\n", conn.RemoteAddr().String()) } agentConn := AgentConnection{ NetConnection: conn, diff --git a/dbm-services/common/dbha/ha-module/gm/gm.go b/dbm-services/common/dbha/ha-module/gm/gm.go index b61ae923ec..7b35f5a184 100644 --- a/dbm-services/common/dbha/ha-module/gm/gm.go +++ b/dbm-services/common/dbha/ha-module/gm/gm.go @@ -25,7 +25,8 @@ type DoubleCheckInstanceInfo struct { db dbutil.DataBaseDetect ReceivedTime time.Time ConfirmTime time.Time - ResultInfo string + //double check result + ResultInfo string } // ModuleReportInfo module info @@ -52,8 +53,7 @@ type GM struct { } // NewGM init new gm -func NewGM(conf *config.Config) (*GM, error) { - var err error +func NewGM(conf *config.Config) *GM { gdmToGmmChan := make(chan DoubleCheckInstanceInfo, 100) gmmToGqaChan := make(chan DoubleCheckInstanceInfo, 100) gqaToGcmChan := make(chan dbutil.DataBaseSwitch, 100) @@ -61,55 +61,28 @@ func NewGM(conf *config.Config) (*GM, error) { Conf: conf, reportChan: make(chan ModuleReportInfo, 100), lastReportTime: time.Now(), + HaDBClient: client.NewHaDBClient(&conf.DBConf.HADB, conf.GetCloudId()), } - gm.gdm, err = NewGDM(conf, gdmToGmmChan, &HAReporter{ + haReporter := &HAReporter{ gm: gm, lastReportTime: time.Now(), - }) - if err != nil { - log.Logger.Errorf("gdm init failed. err:%s", err.Error()) - return nil, err - } - gm.gmm, err = NewGMM(gm.gdm, conf, gdmToGmmChan, gmmToGqaChan, &HAReporter{ - gm: gm, - lastReportTime: time.Now(), - }) - if err != nil { - log.Logger.Errorf("gmm init failed. err:%s", err.Error()) - return nil, err - } - gm.gqa, err = NewGQA(gm.gdm, conf, gmmToGqaChan, gqaToGcmChan, &HAReporter{ - gm: gm, - lastReportTime: time.Now(), - }) - if err != nil { - log.Logger.Errorf("gqa init failed. err:%s", err.Error()) - return nil, err - } - gm.gcm, err = NewGCM(conf, gqaToGcmChan, &HAReporter{ - gm: gm, - lastReportTime: time.Now(), - }) - if err != nil { - log.Logger.Errorf("gcm init failed. err:%s", err.Error()) - return nil, err - } - gm.HaDBClient, err = client.NewHaDBClient(&conf.DBConf.HADB, conf.GetCloudId()) - if err != nil { - return nil, err } - return gm, nil + gm.gdm = NewGDM(conf, gdmToGmmChan, haReporter) + gm.gmm = NewGMM(gm.gdm, conf, gdmToGmmChan, gmmToGqaChan, haReporter) + gm.gqa = NewGQA(gm.gdm, conf, gmmToGqaChan, gqaToGcmChan, haReporter) + gm.gcm = NewGCM(conf, gqaToGcmChan, haReporter) + return gm } // Run gm work main entry func (gm *GM) Run() error { - if err := gm.HaDBClient.RegisterDBHAInfo(util.LocalIp, gm.Conf.GMConf.ListenPort, "gm", - gm.Conf.GMConf.City, gm.Conf.GMConf.Campus, ""); err != nil { + if err := gm.HaDBClient.RegisterDBHAInfo(util.LocalIp, gm.Conf.GMConf.ListenPort, constvar.GM, + gm.Conf.GMConf.City, gm.Conf.GMConf.Campus, "N/A"); err != nil { return err } if err := gm.HaDBClient.RegisterDBHAInfo(util.LocalIp, gm.Conf.GMConf.ListenPort, constvar.GDM, - gm.Conf.GMConf.City, gm.Conf.GMConf.Campus, ""); err != nil { + gm.Conf.GMConf.City, gm.Conf.GMConf.Campus, "N/A"); err != nil { log.Logger.Errorf("GM register gcm module failed,err:%s", err.Error()) return err } @@ -119,7 +92,7 @@ func (gm *GM) Run() error { }() if err := gm.HaDBClient.RegisterDBHAInfo(util.LocalIp, gm.Conf.GMConf.ListenPort, constvar.GMM, - gm.Conf.GMConf.City, gm.Conf.GMConf.Campus, ""); err != nil { + gm.Conf.GMConf.City, gm.Conf.GMConf.Campus, "N/A"); err != nil { log.Logger.Errorf("GM register gcm module failed,err:%s", err.Error()) return err } @@ -129,7 +102,7 @@ func (gm *GM) Run() error { }() if err := gm.HaDBClient.RegisterDBHAInfo(util.LocalIp, gm.Conf.GMConf.ListenPort, constvar.GQA, - gm.Conf.GMConf.City, gm.Conf.GMConf.Campus, ""); err != nil { + gm.Conf.GMConf.City, gm.Conf.GMConf.Campus, "N/A"); err != nil { log.Logger.Errorf("GM register gcm module failed,err:%s", err.Error()) return err } @@ -139,7 +112,7 @@ func (gm *GM) Run() error { }() if err := gm.HaDBClient.RegisterDBHAInfo(util.LocalIp, gm.Conf.GMConf.ListenPort, constvar.GCM, - gm.Conf.GMConf.City, gm.Conf.GMConf.Campus, ""); err != nil { + gm.Conf.GMConf.City, gm.Conf.GMConf.Campus, "N/A"); err != nil { log.Logger.Errorf("GM register gcm module failed,err:%s", err.Error()) return err } @@ -195,7 +168,7 @@ func (gm *GM) CheckReportMyself() { now := time.Now() nextReport := gm.lastReportTime.Add(time.Duration(gm.Conf.GMConf.ReportInterval) * time.Second) if now.After(nextReport) { - log.Logger.Debugf("GM report myself by check, lastReport:%s, now:%s, nextReport:%s", + log.Logger.Debugf("GM report itself, lastReport:%s, now:%s, nextReport:%s", gm.lastReportTime.Format("2006-01-02 15:04:05"), now.Format("2006-01-02 15:04:05"), nextReport.Format("2006-01-02 15:04:05")) diff --git a/dbm-services/common/dbha/ha-module/gm/gmm.go b/dbm-services/common/dbha/ha-module/gm/gmm.go index 29849a57d4..a69b55d49e 100644 --- a/dbm-services/common/dbha/ha-module/gm/gmm.go +++ b/dbm-services/common/dbha/ha-module/gm/gmm.go @@ -23,20 +23,15 @@ type GMM struct { // NewGMM new gmm obeject func NewGMM(gdm *GDM, conf *config.Config, gdmCh, - gqaCh chan DoubleCheckInstanceInfo, reporter *HAReporter) (*GMM, error) { - var err error - gmm := &GMM{ - GDMChan: gdmCh, - GQAChan: gqaCh, - gdm: gdm, - Conf: conf, - reporter: reporter, + gqaCh chan DoubleCheckInstanceInfo, reporter *HAReporter) *GMM { + return &GMM{ + GDMChan: gdmCh, + GQAChan: gqaCh, + gdm: gdm, + Conf: conf, + reporter: reporter, + HaDBClient: client.NewHaDBClient(&conf.DBConf.HADB, conf.GetCloudId()), } - gmm.HaDBClient, err = client.NewHaDBClient(&conf.DBConf.HADB, conf.GetCloudId()) - if err != nil { - return nil, err - } - return gmm, nil } // Run gmm main entry @@ -80,7 +75,7 @@ func (gmm *GMM) Process(instance DoubleCheckInstanceInfo) { "db check failed. no need to switch in machine level", ) } - // AUTHCheckFailed also need double check and process base on the result of double check. + // AUTHCheckFailed also need double-check and process base on the result of double check. case constvar.SSHCheckFailed, constvar.AUTHCheckFailed: { // double check go func(doubleCheckInstance DoubleCheckInstanceInfo) { @@ -92,7 +87,7 @@ func (gmm *GMM) Process(instance DoubleCheckInstanceInfo) { ip, port, "gmm", - "double check success: db check success.", + "double check success: db check ok.", ) case constvar.SSHCheckSuccess: { @@ -101,7 +96,7 @@ func (gmm *GMM) Process(instance DoubleCheckInstanceInfo) { ip, port, "gmm", - fmt.Sprintf("double check success: db check failed, ssh check success. dbcheck err:%s", err), + fmt.Sprintf("double check success: db check failed, ssh check ok. dbcheck err:%s", err), ) } case constvar.SSHCheckFailed: @@ -114,7 +109,7 @@ func (gmm *GMM) Process(instance DoubleCheckInstanceInfo) { ) content := fmt.Sprintf("double check failed: ssh check failed. sshcheck err:%s", err) monitor.MonitorSendDetect( - doubleCheckInstance.db, constvar.DBHA_EVENT_DOUBLE_CHECK_SSH, content, + doubleCheckInstance.db, constvar.DBHAEventDoubleCheckSSH, content, ) doubleCheckInstance.ResultInfo = content // reporter GQA @@ -133,7 +128,7 @@ func (gmm *GMM) Process(instance DoubleCheckInstanceInfo) { ) content := fmt.Sprintf("double check failed: ssh authenticate failed. sshcheck err:%s", err) monitor.MonitorSendDetect( - doubleCheckInstance.db, constvar.DBHA_EVENT_DOUBLE_CHECK_AUTH, content, + doubleCheckInstance.db, constvar.DBHAEventDoubleCheckAuth, content, ) } default: diff --git a/dbm-services/common/dbha/ha-module/gm/gqa.go b/dbm-services/common/dbha/ha-module/gm/gqa.go index 67a56feee3..b0f45c2aa3 100644 --- a/dbm-services/common/dbha/ha-module/gm/gqa.go +++ b/dbm-services/common/dbha/ha-module/gm/gqa.go @@ -33,9 +33,8 @@ type GQA struct { // NewGQA init GQA object func NewGQA(gdm *GDM, conf *config.Config, gmmCh chan DoubleCheckInstanceInfo, - gcmCh chan dbutil.DataBaseSwitch, reporter *HAReporter) (*GQA, error) { - var err error - gqa := &GQA{ + gcmCh chan dbutil.DataBaseSwitch, reporter *HAReporter) *GQA { + return &GQA{ GMMChan: gmmCh, GCMChan: gcmCh, gdm: gdm, @@ -48,16 +47,9 @@ func NewGQA(gdm *GDM, conf *config.Config, AllSwitchLimit: conf.GMConf.GQA.AllHostSwitchLimit, SingleSwitchIDCLimit: conf.GMConf.GQA.SingleSwitchIDC, reporter: reporter, + CmDBClient: client.NewCmDBClient(&conf.DBConf.CMDB, conf.GetCloudId()), + HaDBClient: client.NewHaDBClient(&conf.DBConf.HADB, conf.GetCloudId()), } - gqa.CmDBClient, err = client.NewCmDBClient(&conf.DBConf.CMDB, conf.GetCloudId()) - if err != nil { - return nil, err - } - gqa.HaDBClient, err = client.NewHaDBClient(&conf.DBConf.HADB, conf.GetCloudId()) - if err != nil { - return nil, err - } - return gqa, nil } // Run GQA main entry @@ -76,12 +68,6 @@ func (gqa *GQA) Run() { } } -// PopInstance pop instance from gmm -func (gqa *GQA) PopInstance() []dbutil.DataBaseSwitch { - instance := <-gqa.GMMChan - return gqa.PreProcess(instance) -} - // PreProcess fetch instance detail info for process func (gqa *GQA) PreProcess(instance DoubleCheckInstanceInfo) []dbutil.DataBaseSwitch { ip, port := instance.db.GetAddress() @@ -106,6 +92,7 @@ func (gqa *GQA) PushInstance2Next(ins dbutil.DataBaseSwitch) { // Process decide whether instance allow next switch func (gqa *GQA) Process(cmdbInfos []dbutil.DataBaseSwitch) { if nil == cmdbInfos { + log.Logger.Debugf("no instance neeed to process, skip") return } @@ -223,9 +210,9 @@ func (gqa *GQA) getAllInstanceFromCMDB( log.Logger.Infof("gqa get mysql instance number:%d", len(instances)) } - cb, ok := dbmodule.DBCallbackMap[instance.db.GetType()] + cb, ok := dbmodule.DBCallbackMap[instance.db.GetDetectType()] if !ok { - err = fmt.Errorf("can't find %s instance callback", instance.db.GetType()) + err = fmt.Errorf("can't find %s instance callback", instance.db.GetDetectType()) log.Logger.Errorf(err.Error()) return nil, err } @@ -238,11 +225,13 @@ func (gqa *GQA) getAllInstanceFromCMDB( if ret == nil { log.Logger.Errorf("gqa get switch instance is nil") } else { - log.Logger.Errorf("gqa get switch instance num:%d", len(ret)) + log.Logger.Infof("gqa get switch instance num:%d", len(ret)) } + log.Logger.Errorf("need process instances detail:%#v", ret) for _, sins := range ret { - sins.SetInfo(constvar.SWITCH_INFO_DOUBLECHECK, instance.ResultInfo) + sins.SetInfo(constvar.DoubleCheckInfoKey, instance.ResultInfo) + sins.SetInfo(constvar.DoubleCheckTimeKey, instance.ConfirmTime) } return ret, nil } diff --git a/dbm-services/common/dbha/ha-module/go.mod b/dbm-services/common/dbha/ha-module/go.mod index 786ee88636..83578d3df4 100644 --- a/dbm-services/common/dbha/ha-module/go.mod +++ b/dbm-services/common/dbha/ha-module/go.mod @@ -3,35 +3,37 @@ module dbm-services/common/dbha/ha-module go 1.19 require ( - github.com/go-playground/validator/v10 v10.12.0 + github.com/go-playground/validator/v10 v10.14.1 github.com/go-redis/redis/v8 v8.11.5 github.com/go-sql-driver/mysql v1.7.1 github.com/natefinch/lumberjack v2.0.0+incompatible + github.com/pkg/errors v0.9.1 go.uber.org/zap v1.24.0 - golang.org/x/crypto v0.8.0 + golang.org/x/crypto v0.11.0 gopkg.in/yaml.v2 v2.4.0 gorm.io/driver/mysql v1.5.0 gorm.io/gorm v1.25.0 ) require ( - github.com/BurntSushi/toml v1.2.1 // indirect + github.com/BurntSushi/toml v1.3.2 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect - github.com/kr/pretty v0.3.0 // indirect - github.com/leodido/go-urn v1.2.3 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/rogpeppe/go-internal v1.8.0 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/leodido/go-urn v1.2.4 // indirect + github.com/stretchr/testify v1.8.4 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/goleak v1.1.12 // indirect go.uber.org/multierr v1.8.0 // indirect - golang.org/x/sys v0.7.0 // indirect - golang.org/x/text v0.9.0 // indirect + golang.org/x/net v0.13.0 // indirect + golang.org/x/sys v0.10.0 // indirect + golang.org/x/text v0.11.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect ) diff --git a/dbm-services/common/dbha/ha-module/go.sum b/dbm-services/common/dbha/ha-module/go.sum index ffd3882646..34ac3fb3e3 100644 --- a/dbm-services/common/dbha/ha-module/go.sum +++ b/dbm-services/common/dbha/ha-module/go.sum @@ -1,5 +1,5 @@ -github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= -github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -11,13 +11,15 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.12.0 h1:E4gtWgxWxp8YSxExrQFv5BpCahla0PVF2oTTEYaWQGI= -github.com/go-playground/validator/v10 v10.12.0/go.mod h1:hCAPuzYvKdP33pxWa+2+6AIKXEKqjIUyqsNCtbsSJrA= +github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k= +github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= @@ -29,14 +31,14 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.2.3 h1:6BE2vPT0lqoz3fmOesHZiaiFh7889ssCo2GMvLCfiuA= -github.com/leodido/go-urn v1.2.3/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM= github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= @@ -47,9 +49,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= -github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -57,8 +58,9 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= @@ -71,15 +73,16 @@ go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= -golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= +golang.org/x/net v0.13.0 h1:Nvo8UFsZ8X3BhAC9699Z1j7XQ3rsZnUUm7jfBEk1ueY= +golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -88,14 +91,14 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= +golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -107,7 +110,6 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= diff --git a/dbm-services/common/dbha/ha-module/ha.yaml b/dbm-services/common/dbha/ha-module/ha.yaml index 3578e34f51..d2f3e0122b 100644 --- a/dbm-services/common/dbha/ha-module/ha.yaml +++ b/dbm-services/common/dbha/ha-module/ha.yaml @@ -52,8 +52,8 @@ db_conf: bk_conf: bk_token: "xxxx" mysql: - user: "mysql-conn-user" - pass: "mysql-conn-pass" + user: "dbmysql-conn-user" + pass: "dbmysql-conn-pass" proxy_user: "proxy-conn-user" proxy_pass: "proxy-conn-pass" timeout: 10 @@ -77,10 +77,20 @@ dns: timeout: 10 bk_conf: bk_token: "xxxx" + clb_conf: + host: "clb-host" + port: 80 + url_pre: "/api/nameservice/clb" + timeout: 10 + polaris_conf: + host: "polaris-host" + port: 80 + url_pre: "/api/nameservice/polaris" + timeout: 10 ssh: port: 36000 - user: "mysql" - pass: "mysql-user-pass" + user: "dbmysql" + pass: "dbmysql-user-pass" dest: "agent" timeout: 10 monitor: diff --git a/dbm-services/common/dbha/ha-module/log/log.go b/dbm-services/common/dbha/ha-module/log/log.go index 788b633d01..c4d4c05184 100644 --- a/dbm-services/common/dbha/ha-module/log/log.go +++ b/dbm-services/common/dbha/ha-module/log/log.go @@ -87,15 +87,15 @@ func getStdoutWriter() zapcore.WriteSyncer { } func getLogLevel(logLevel string) zapcore.Level { - if logLevel == constvar.LOG_DEBUG { + if logLevel == constvar.LogDebug { return zapcore.DebugLevel - } else if logLevel == constvar.LOG_INFO { + } else if logLevel == constvar.LogInfo { return zapcore.InfoLevel - } else if logLevel == constvar.LOG_ERROR { + } else if logLevel == constvar.LogError { return zapcore.ErrorLevel - } else if logLevel == constvar.LOG_PANIC { + } else if logLevel == constvar.LogPanic { return zapcore.PanicLevel - } else if logLevel == constvar.LOG_FATAL { + } else if logLevel == constvar.LogFatal { return zapcore.FatalLevel } else { return zapcore.DebugLevel @@ -105,17 +105,17 @@ func getLogLevel(logLevel string) zapcore.Level { func getLogFileConfig(logConf config.LogConfig) (int, int, int) { logMaxSize := logConf.LogMaxSize if logConf.LogMaxSize == 0 { - logMaxSize = constvar.LOG_DEF_SIZE + logMaxSize = constvar.LogDefSize } logMaxAge := logConf.LogMaxAge if logMaxAge == 0 { - logMaxAge = constvar.LOG_DEF_AGE + logMaxAge = constvar.LogDefAge } logMaxBackups := logConf.LogMaxBackups if logMaxBackups == 0 { - logMaxBackups = constvar.LOG_DEF_BACKUPS + logMaxBackups = constvar.LogDefBackups } return logMaxSize, logMaxAge, logMaxBackups diff --git a/dbm-services/common/dbha/ha-module/monitor/monitor.go b/dbm-services/common/dbha/ha-module/monitor/monitor.go index 2530d402b1..e41fd1de5d 100644 --- a/dbm-services/common/dbha/ha-module/monitor/monitor.go +++ b/dbm-services/common/dbha/ha-module/monitor/monitor.go @@ -80,7 +80,7 @@ func MonitorSendDetect(ins dbutil.DataBaseDetect, eventName string, content stri // MonitorSend send dbha monitor information func MonitorSend(content string, info MonitorInfo) error { addDimension := make(map[string]interface{}) - if info.MonitorInfoType == constvar.MONITOR_INFO_SWITCH { + if info.MonitorInfoType == constvar.MonitorInfoSwitch { addDimension["role"] = info.Switch.Role addDimension["bzid"] = info.Switch.Bzid addDimension["server_ip"] = info.Switch.ServerIp @@ -100,34 +100,41 @@ func GetMonitorInfoBySwitch(ins dbutil.DataBaseSwitch, succ bool) MonitorInfo { switch ins.GetMetaType() { case constvar.RedisMetaType, constvar.TwemproxyMetaType: if succ { - eventName = constvar.DBHA_EVENT_REDIS_SWITCH_SUCC + eventName = constvar.DBHAEventRedisSwitchSucc } else { - eventName = constvar.DBHA_EVENT_REDIS_SWITCH_ERR + eventName = constvar.DBHAEventRedisSwitchErr } case constvar.PredixyMetaType, constvar.TendisplusMetaType: if succ { - eventName = constvar.DBHA_EVENT_REDIS_SWITCH_SUCC + eventName = constvar.DBHAEventRedisSwitchSucc } else { - eventName = constvar.DBHA_EVENT_REDIS_SWITCH_ERR + eventName = constvar.DBHAEventRedisSwitchErr } - case constvar.MySQLMetaType, constvar.MySQLProxyMetaType: + case constvar.TenDBStorageType, constvar.TenDBProxyType, + constvar.TenDBClusterStorageType, constvar.TenDBClusterProxyType: if succ { - eventName = constvar.DBHA_EVENT_MYSQL_SWITCH_SUCC + eventName = constvar.DBHAEventMysqlSwitchSucc } else { - eventName = constvar.DBHA_EVENT_MYSQL_SWITCH_ERR + eventName = constvar.DBHAEventMysqlSwitchErr + } + case constvar.Riak: + if succ { + eventName = constvar.DBHAEventRiakSwitchSucc + } else { + eventName = constvar.DBHAEventRiakSwitchErr } default: if succ { - eventName = constvar.DBHA_EVENT_MYSQL_SWITCH_SUCC + eventName = constvar.DBHAEventMysqlSwitchSucc } else { - eventName = constvar.DBHA_EVENT_MYSQL_SWITCH_ERR + eventName = constvar.DBHAEventMysqlSwitchErr } } addr, port := ins.GetAddress() return MonitorInfo{ EventName: eventName, - MonitorInfoType: constvar.MONITOR_INFO_SWITCH, + MonitorInfoType: constvar.MonitorInfoSwitch, Switch: SwitchMonitor{ ServerIp: addr, ServerPort: port, @@ -146,7 +153,7 @@ func GetMonitorInfoByDetect(ins dbutil.DataBaseDetect, eventName string) Monitor addr, port := ins.GetAddress() return MonitorInfo{ EventName: eventName, - MonitorInfoType: constvar.MONITOR_INFO_DETECT, + MonitorInfoType: constvar.MonitorInfoDetect, Detect: DetectMonitor{ ServerIp: addr, ServerPort: port, diff --git a/dbm-services/common/dbha/ha-module/test/MySQL_test.go b/dbm-services/common/dbha/ha-module/test/MySQL_test.go index 42289eb39e..22a09cdfbd 100644 --- a/dbm-services/common/dbha/ha-module/test/MySQL_test.go +++ b/dbm-services/common/dbha/ha-module/test/MySQL_test.go @@ -6,17 +6,17 @@ import ( "time" "dbm-services/common/dbha/ha-module/constvar" - "dbm-services/common/dbha/ha-module/dbmodule/mysql" + "dbm-services/common/dbha/ha-module/dbmodule/dbmysql" "dbm-services/common/dbha/ha-module/dbutil" ) -func newTestInstance() *mysql.MySQLDetectInstance { - return &mysql.MySQLDetectInstance{ +func newTestInstance() *dbmysql.MySQLDetectInstance { + return &dbmysql.MySQLDetectInstance{ BaseDetectDB: dbutil.BaseDetectDB{ Ip: "xxxx", Port: 40000, App: "test", - DBType: constvar.MySQL, + DBType: constvar.DetectTenDBHA, ReporterTime: time.Unix(0, 0), ReportInterval: 10, Status: constvar.DBCheckSuccess, diff --git a/dbm-services/common/dbha/ha-module/test/agent_test.go b/dbm-services/common/dbha/ha-module/test/agent_test.go index a0851b6023..724459bfc2 100644 --- a/dbm-services/common/dbha/ha-module/test/agent_test.go +++ b/dbm-services/common/dbha/ha-module/test/agent_test.go @@ -19,7 +19,7 @@ func TestAgentNetTransfor(t *testing.T) { return } ch := make(chan gm.DoubleCheckInstanceInfo, 0) - gdm, _ := gm.NewGDM(GlobalConfig, ch, nil) + gdm := gm.NewGDM(GlobalConfig, ch, nil) go func() { gdm.Run() }() @@ -31,7 +31,6 @@ func TestAgentNetTransfor(t *testing.T) { d = dbIns agentIns := agent.MonitorAgent{ City: "sz", - Type: "M", LastFetchInsTime: time.Unix(0, 0), } ip, _ := d.GetAddress() diff --git a/dbm-services/common/dbha/ha-module/test/client_test.go b/dbm-services/common/dbha/ha-module/test/client_test.go index ed727b2ff8..96e05e981e 100644 --- a/dbm-services/common/dbha/ha-module/test/client_test.go +++ b/dbm-services/common/dbha/ha-module/test/client_test.go @@ -40,7 +40,7 @@ func TestGetInstanceByCity(t *testing.T) { fmt.Printf("get instance failed. err:%s", err.Error()) t.FailNow() } - dbs, err := dbmodule.DBCallbackMap["tendbha"].FetchDBCallback(rawList, GlobalConfig) + dbs, err := dbmodule.DBCallbackMap[constvar.DetectTenDBHA].FetchDBCallback(rawList, GlobalConfig) for _, info := range dbs { ip, port := info.GetAddress() fmt.Printf("%s, %d, %s, %s, %s\n", ip, port, info.GetType(), info.GetStatus(), info.GetApp()) @@ -58,7 +58,7 @@ func TestGetInstanceByIp(t *testing.T) { fmt.Printf("get instance failed. err:%s", err.Error()) t.FailNow() } - list, err := dbmodule.DBCallbackMap["tendbha"].GetSwitchInstanceInformation(inf, nil) + list, err := dbmodule.DBCallbackMap[constvar.DetectTenDBHA].GetSwitchInstanceInformation(inf, nil) if err != nil { fmt.Printf("get switch instance failed. err:%s", err.Error()) t.FailNow() diff --git a/dbm-services/common/dbha/ha-module/util/timezone.go b/dbm-services/common/dbha/ha-module/util/timezone.go index 1f95e1bfd3..1726678599 100644 --- a/dbm-services/common/dbha/ha-module/util/timezone.go +++ b/dbm-services/common/dbha/ha-module/util/timezone.go @@ -11,9 +11,9 @@ import ( // InitTimezone TODO func InitTimezone(tzConf config.TimezoneConfig) { switch tzConf.Local { - case constvar.TZ_UTC: + case constvar.TZUTC: SetTimezoneToUTC() - case constvar.TZ_CST: + case constvar.TZCST: SetTimezoneToCST() default: SetTimezoneToCST() diff --git a/dbm-services/common/dbha/ha-module/util/util.go b/dbm-services/common/dbha/ha-module/util/util.go index 03490d3fbd..cc7c4fdb88 100644 --- a/dbm-services/common/dbha/ha-module/util/util.go +++ b/dbm-services/common/dbha/ha-module/util/util.go @@ -2,10 +2,11 @@ package util import ( - "errors" + "bytes" "fmt" "hash/crc32" "net" + "os/exec" "reflect" "runtime" "strings" @@ -13,6 +14,8 @@ import ( "dbm-services/common/dbha/ha-module/constvar" "dbm-services/common/dbha/ha-module/log" + + "github.com/pkg/errors" ) // LocalIp component local ip @@ -106,15 +109,15 @@ func CRC32(str string) uint32 { // status: api lack password and the password is invalid func CheckRedisErrIsAuthFail(err error) bool { errInfo := err.Error() - if strings.Contains(errInfo, constvar.REDIS_PASSWORD_INVALID) { + if strings.Contains(errInfo, constvar.RedisPasswordInvalid) { // this case is the status of the password is invalid, // rediscache tendisplus twemproxy and predixy match this case return true - } else if strings.Contains(errInfo, constvar.REDIS_PASSWORD_LACK) { + } else if strings.Contains(errInfo, constvar.RedisPasswordLack) { // this case is the status of lack password, // rediscache tendisplus twemproxy match this case, predixy un-match return true - } else if strings.Contains(errInfo, constvar.PREDIXY_PASSWORD_LACK) { + } else if strings.Contains(errInfo, constvar.PredixyPasswordLack) { // this case is the status of lack password // predixy match this case return true @@ -129,9 +132,32 @@ func CheckRedisErrIsAuthFail(err error) bool { func CheckSSHErrIsAuthFail(err error) bool { errInfo := err.Error() // ssh lack password or password is invalid will return the same error - if strings.Contains(errInfo, constvar.SSH_PASSWORD_LACK_OR_INVALID) { + if strings.Contains(errInfo, constvar.SSHPasswordLackORInvalid) { return true } else { return false } } + +// ExecShellCommand 执行 shell 命令 +// 如果有 err, 返回 stderr; 如果没有 err 返回的是 stdout +func ExecShellCommand(isSudo bool, param string) (stdoutStr string, err error) { + if isSudo { + param = "sudo " + param + } + cmd := exec.Command("bash", "-c", param) + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + err = cmd.Run() + + if err != nil { + return stderr.String(), errors.WithMessage(err, stderr.String()) + } + + if len(stderr.String()) > 0 { + err = fmt.Errorf("execute shell command(%s) error:%s", param, stderr.String()) + return stderr.String(), err + } + return stdout.String(), nil +} diff --git a/dbm-services/common/dbha/hadb-api/Dockerfile b/dbm-services/common/dbha/hadb-api/Dockerfile index 7b492a6b21..69e7f65ce9 100644 --- a/dbm-services/common/dbha/hadb-api/Dockerfile +++ b/dbm-services/common/dbha/hadb-api/Dockerfile @@ -7,9 +7,9 @@ RUN mkdir ${BASEDIR} COPY hadb-api ${BASEDIR}/ -RUN groupadd -r mysql && useradd -r -g mysql mysql \ - && /usr/bin/install -m 0777 -o mysql -g root -d ${BASEDIR}\ - && chown -R mysql ${BASEDIR} \ +RUN groupadd -r dbmysql && useradd -r -g dbmysql dbmysql \ + && /usr/bin/install -m 0777 -o dbmysql -g root -d ${BASEDIR}\ + && chown -R dbmysql ${BASEDIR} \ && chmod a+x ${BASEDIR}/hadb-api \ && chmod a+x ${BASEDIR} diff --git a/dbm-services/common/go-pubpkg/backupclient/backupclient.go b/dbm-services/common/go-pubpkg/backupclient/backupclient.go new file mode 100644 index 0000000000..2217226cfc --- /dev/null +++ b/dbm-services/common/go-pubpkg/backupclient/backupclient.go @@ -0,0 +1,127 @@ +// Package backupclient TODO +package backupclient + +import ( + "encoding/json" + "path/filepath" + "strings" + + "github.com/pkg/errors" + + "dbm-services/common/go-pubpkg/cmutil" +) + +const ( + // BackupClientPath TODO + BackupClientPath = "/usr/local/backup_client/bin/backup_client" + // AuthFilePath TODO + AuthFilePath = ".cosinfo.toml" +) + +// BackupClient TODO +type BackupClient struct { + ClientPath string + AuthFile string + FileTag string + + registerArgs []string + queryArgs []string +} + +// RegisterResp TODO +type RegisterResp struct { + TaskId string `json:"task_id"` +} + +// QueryResp TODO +type QueryResp struct { + TaskId string `json:"task_id"` + Status int `json:"status"` + StatusMsg string `json:"status_msg"` +} + +// New 初始一个 backup_client 命令 +// 默认使用 /usr/local/backup_client/bin/backup_client --auth-file $HOME/.cosinfo.toml +func New(clientPath string, authFile string, fileTag string) (*BackupClient, error) { + if clientPath == "" { + clientPath = BackupClientPath + } + if !cmutil.FileExists(clientPath) { + return nil, errors.Errorf("backup_client path not found:%s", clientPath) + } + + b := &BackupClient{ + ClientPath: clientPath, + AuthFile: authFile, + FileTag: fileTag, + } + if b.FileTag == "" { + return nil, errors.New("file_tag is required") + } + b.registerArgs = []string{b.ClientPath, "register", "--tag", b.FileTag} + if b.AuthFile != "" { + if !cmutil.FileExists(b.AuthFile) { + return nil, errors.Errorf("auth-file not found:%s", clientPath) + } + b.registerArgs = append(b.registerArgs, "--auth-file", b.AuthFile) + } + b.queryArgs = []string{b.ClientPath, "query"} + return b, nil +} + +func (b *BackupClient) register(filePath string) (backupTaskId string, err error) { + if !filepath.IsAbs(filePath) { + return "", errors.Errorf("file %s need absolute path", filePath) + } + registerArgs := append(b.registerArgs, "-f", filePath) + stdoutStr, stderrStr, err := cmutil.ExecCommand(false, "", registerArgs[0], registerArgs[1:]...) + if err != nil { + return "", errors.Wrapf(err, "register cmd failed %v with %s", registerArgs, stderrStr) + } + if strings.Count(stdoutStr, "-") == 3 && len(stdoutStr) < 80 { + // 这里粗略判断是否是合法的 task_id + return stdoutStr, nil + } else { + return "", errors.Errorf("illegal backup_task_id %s for %s", stdoutStr, filePath) + } +} + +// Upload upload 异步的,调用 register 来本地注册任务 +func (b *BackupClient) Upload(filePath string) (backupTaskId string, err error) { + return b.register(filePath) +} + +// Query TODO +func (b *BackupClient) Query(backupTaskId string) (uploadStatus int, err error) { + queryArgs := append(b.queryArgs, "--task-id", backupTaskId) + stdout, stderr, err := cmutil.ExecCommandReturnBytes(false, "", queryArgs[0], queryArgs[1:]...) + if err != nil { + return 0, errors.Wrapf(err, "query cmd failed %v with %s", queryArgs, string(stderr)) + } + return 4, nil + var resp QueryResp + if err := json.Unmarshal(stdout, &resp); err != nil { + return 0, errors.Wrapf(err, "parse query response %s", string(stdout)) + } + return resp.Status, err +} + +// Query2 TODO +func (b *BackupClient) Query2(backupTaskId string) (status int, statusMsg string, err error) { + queryArgs := append(b.queryArgs, "--task-id", backupTaskId) + stdout, stderr, err := cmutil.ExecCommandReturnBytes(false, "", queryArgs[0], queryArgs[1:]...) + if err != nil { + return 0, "", errors.Wrapf(err, "query cmd failed %v with %s", queryArgs, string(stderr)) + } + // [{"TaskID":"xxxxx-xxx-1512-0","Status":4,"Message":"upload success"}] + type NewResp struct { + TaskID string `json:"TaskID"` + Status int `json:"Status"` + Message string `json:"Message"` + } + resp := []NewResp{} + if err := json.Unmarshal(stdout, &resp); err != nil { + return 0, "", errors.Wrapf(err, "BackupClient parse query response %s fail", string(stdout)) + } + return resp[0].Status, resp[0].Message, err +} diff --git a/dbm-services/common/go-pubpkg/backupclient/types.go b/dbm-services/common/go-pubpkg/backupclient/types.go new file mode 100644 index 0000000000..bfcf0e7751 --- /dev/null +++ b/dbm-services/common/go-pubpkg/backupclient/types.go @@ -0,0 +1,40 @@ +package backupclient + +// CosAuth cos bucket 信息 +type CosAuth struct { + CosServer string `mapstructure:"cos_server" json:"cos_server" toml:"cos_server"` + Region string `mapstructure:"region" json:"region" toml:"region"` + SecretId string `mapstructure:"secret_id" json:"secret_id" toml:"secret_id"` + SecretKey string `mapstructure:"secret_key" json:"secret_key" toml:"secret_key"` + BucketName string `mapstructure:"bucket_name" json:"bucket_name" toml:"bucket_name"` +} + +// AppAttr 机器业务属性 +type AppAttr struct { + BkBizId int `mapstructure:"bk_biz_id" json:"bk_biz_id" toml:"bk_biz_id"` + BkCloudId int `mapstructure:"bk_cloud_id" json:"bk_cloud_id" toml:"bk_cloud_id"` +} + +// CosInfo cosinfo.toml +type CosInfo struct { + Cos *CosAuth `toml:"cos_auth" json:"cos_auth"` + AppAttr *AppAttr `toml:"app_attr" json:"app_attr"` +} + +type CosClientConfig struct { + Base BaseLimit `toml:"coslimits" json:"coslimit" mapstructure:"coslimits"` + Cfg UploadConfig `toml:"cfg" json:"cfg" mapstructure:"cfg"` +} + +type UploadConfig struct { + // FileTagAllowed 允许的 file tag 列表 + FileTagAllowed string `mapstructure:"file_tag_allowed" json:"file_tag_allowed" toml:"file_tag_allowed"` + // NetAddr 本机内网ip地址 + NetAddr string `mapstructure:"net_addr" json:"net_addr" toml:"net_addr"` +} + +type BaseLimit struct { + BlockSize int `mapstructure:"chunk_size" json:"block_size" toml:"chunk_size"` + LocalTotalLimit int `mapstructure:"local_total_limit" json:"local_total_limit" toml:"local_total_limit"` + LocalFileLimit int `mapstructure:"local_file_limit" json:"local_file_limit" toml:"local_file_limit"` +} diff --git a/dbm-services/common/go-pubpkg/bkrepo/bkrepo.go b/dbm-services/common/go-pubpkg/bkrepo/bkrepo.go new file mode 100644 index 0000000000..6ec146598b --- /dev/null +++ b/dbm-services/common/go-pubpkg/bkrepo/bkrepo.go @@ -0,0 +1,335 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +// Package bkrepo TODO +package bkrepo + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "log" + "mime/multipart" + "net/http" + "net/url" + "os" + "path" + "path/filepath" + "strconv" + "strings" + + util "dbm-services/common/go-pubpkg/cmutil" + "dbm-services/common/go-pubpkg/logger" +) + +/* + API: GET /generic/{project}/{repo}/{path}?download=true + API 名称: download + 功能说明: + + 中文:下载通用制品文件 + English:download generic file + 请求体 此接口请求体为空 +*/ + +// BkRepoClient TODO +type BkRepoClient struct { + Client *http.Client + BkRepoProject string + BkRepoPubBucket string + BkRepoEndpoint string + BkRepoUser string + BkRepoPwd string +} + +// BkRepoRespone TODO +type BkRepoRespone struct { + Code int `json:"code"` + Message string `json:"message"` + Data json.RawMessage `json:"data"` + RequestId string `json:"request_id"` +} + +// getBaseUrl TODO +// +// @receiver b +func (b *BkRepoClient) getBaseUrl() string { + u, err := url.Parse(b.BkRepoEndpoint) + if err != nil { + log.Fatal(err) + } + r, err := url.Parse(path.Join(u.Path, "generic", b.BkRepoProject, b.BkRepoPubBucket)) + if err != nil { + log.Fatal(err) + } + uri := u.ResolveReference(r).String() + logger.Info("uri:%s", uri) + return uri +} + +// Upload TODO +func (b *BkRepoClient) Upload(filepath, filname, repopath string) (*BkRepoRespone, error) { + return UploadDirectToBkRepo(filepath, b.getBaseUrl()+path.Join("/", repopath, filname), b.BkRepoUser, b.BkRepoPwd) +} + +// Download 从制品库下载文件 +// +// @receiver b +func (b *BkRepoClient) Download(sqlpath, filename, downloaddir string) (err error) { + uri := b.getBaseUrl() + path.Join("/", sqlpath, filename) + "?download=true" + logger.Info("The download url is %s", uri) + req, err := http.NewRequest(http.MethodGet, uri, nil) + if err != nil { + return err + } + if strings.Contains(filename, "..") { + return fmt.Errorf("%s there is a risk of path crossing", filename) + } + fileAbPath, err := filepath.Abs(path.Join(downloaddir, filename)) + if err != nil { + return err + } + f, err := os.Create(fileAbPath) + if err != nil { + return err + } + req.SetBasicAuth(b.BkRepoUser, b.BkRepoPwd) + resp, err := b.Client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + bs, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + return fmt.Errorf("respone code is %d,respone body is :%s", resp.StatusCode, string(bs)) + } + size, err := io.Copy(f, resp.Body) + if err != nil { + return err + } + logger.GetLogger().Info(fmt.Sprintf("Downloaded a file %s with size %d", filename, size)) + fileNodeInfo, err := b.QueryFileNodeInfo(sqlpath, filename) + if err != nil { + return err + } + logger.Info("node detail %v", fileNodeInfo) + if size != int64(fileNodeInfo.Size) { + bs, _ := os.ReadFile(fileAbPath) + return fmt.Errorf("body:%s,current file:%s source file size is inconsistent,current file is:%d,bkrepo file is:%d", + string(bs), filename, size, + fileNodeInfo.Size) + } + + currentFileMd5, err := util.GetFileMd5(fileAbPath) + if err != nil { + return err + } + if currentFileMd5 != fileNodeInfo.Md5 { + return fmt.Errorf("current file:%s source file md5 is inconsistent,current file is:%s,bkrepo file is:%s", filename, + currentFileMd5, + fileNodeInfo.Md5) + } + return nil +} + +// FileNodeInfo TODO +type FileNodeInfo struct { + Name string `json:"name"` + Sha256 string `json:"sha256"` + Md5 string `json:"md5"` + Size int `json:"size"` + Metadata map[string]string `json:"metadata"` +} + +// QueryFileNodeInfo TODO +// QueryMetaData 查询文件元数据信息 +// +// @receiver b +func (b *BkRepoClient) QueryFileNodeInfo(filepath, filename string) (realData FileNodeInfo, err error) { + var baseResp BkRepoRespone + uri, err := url.JoinPath(b.BkRepoEndpoint, "repository/api/node/detail/", b.BkRepoProject, b.BkRepoPubBucket, filepath, + filename) + if err != nil { + logger.Error("url join path failed %s", err.Error()) + return + } + logger.Info("query node detail url %s", uri) + req, err := http.NewRequest(http.MethodGet, uri, nil) + if err != nil { + return FileNodeInfo{}, err + } + req.SetBasicAuth(b.BkRepoUser, b.BkRepoPwd) + resp, err := b.Client.Do(req) + if err != nil { + return FileNodeInfo{}, err + } + defer resp.Body.Close() + if err = json.NewDecoder(resp.Body).Decode(&baseResp); err != nil { + return FileNodeInfo{}, err + } + if baseResp.Code != 0 { + return FileNodeInfo{}, fmt.Errorf("bkrepo Return Code: %d,Messgae:%s", baseResp.Code, baseResp.Message) + } + if err = json.Unmarshal([]byte(baseResp.Data), &realData); err != nil { + return FileNodeInfo{}, err + } + return +} + +// UploadRespData TODO +type UploadRespData struct { + Sha256 string `json:"sha256"` + Md5 string `json:"md5"` + Size int64 `json:"size"` + FullPath string `json:"fullPath"` + CreateBy string `json:"createBy"` + CreateDate string `json:"createdDate"` + LastModifiedBy string `json:"lastModifiedBy"` + LastModifiedDate string `json:"lastModifiedDate"` + Folder bool `json:"folder"` // 是否为文件夹 + Path string `json:"path"` + Name string `json:"name"` + ProjectId string `json:"projectId"` + RepoName string `json:"repoName"` +} + +// FileServerInfo 文件服务器 +type FileServerInfo struct { + URL string `json:"url"` // 制品库地址 + Bucket string `json:"bucket"` // 目标bucket + Password string `json:"password"` // 制品库 password + Username string `json:"username"` // 制品库 username + Project string `json:"project"` // 制品库 project +} + +func newfileUploadRequest(uri string, params map[string]string, paramName, path string) (*http.Request, error) { + file, err := os.Open(path) + if err != nil { + return nil, err + } + defer file.Close() + + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + part, err := writer.CreateFormFile(paramName, filepath.Base(path)) + if err != nil { + return nil, err + } + _, err = io.Copy(part, file) + if err != nil { + return nil, err + } + for key, val := range params { + _ = writer.WriteField(key, val) + } + err = writer.Close() + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPut, uri, body) + req.Header.Set("Content-Type", writer.FormDataContentType()) + return req, err +} + +// UploadDirectToBkRepo TODO +func UploadDirectToBkRepo(filepath string, targetURL string, username string, password string) (*BkRepoRespone, error) { + logger.Info("start upload files from %s to %s", filepath, targetURL) + bodyBuf := bytes.NewBufferString("") + bodyWriter := multipart.NewWriter(bodyBuf) + fh, err := os.Open(filepath) + if err != nil { + logger.Info("error opening file") + return nil, err + } + boundary := bodyWriter.Boundary() + closeBuf := bytes.NewBufferString("") + + requestReader := io.MultiReader(bodyBuf, fh, closeBuf) + fi, err := fh.Stat() + if err != nil { + fmt.Printf("Error Stating file: %s", filepath) + return nil, err + } + req, err := http.NewRequest("PUT", targetURL, requestReader) + if err != nil { + return nil, err + } + req.SetBasicAuth(username, password) + // Set headers for multipart, and Content Length + req.Header.Set("Content-Type", "multipart/form-data; boundary="+boundary) + // 文件是否可以被覆盖,默认false + req.Header.Set("X-BKREPO-OVERWRITE", "True") + // 文件默认保留半年 + req.Header.Set("X-BKREPO-EXPIRES", "183") + req.ContentLength = fi.Size() + int64(bodyBuf.Len()) + int64(closeBuf.Len()) + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + b, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + return nil, fmt.Errorf("返回码非200 http code: %d,body:%s\n", resp.StatusCode, string(b)) + } + var baseResp BkRepoRespone + if err = json.NewDecoder(resp.Body).Decode(&baseResp); err != nil { + return nil, err + } + return &baseResp, err +} + +// UploadFile 上传文件到蓝盾制品库 +// filepath: 本地需要上传文件的路径 +// targetURL: 仓库文件完整路径 +func UploadFile(filepath, targetURL, username, password string, BkCloudId int, DBCloudToken string) (*BkRepoRespone, + error) { + logger.Info("start upload files from %s to %s", filepath, targetURL) + if BkCloudId == 0 { + return UploadDirectToBkRepo(filepath, targetURL, username, password) + } + req, err := newfileUploadRequest( + targetURL, map[string]string{ + "bk_cloud_id": strconv.Itoa(BkCloudId), + "db_cloud_token": DBCloudToken, + }, "file", filepath, + ) + if err != nil { + logger.Error("new request failed %s", err.Error()) + return nil, err + } + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + body := &bytes.Buffer{} + _, err = body.ReadFrom(resp.Body) + if err != nil { + logger.Error("read from body failed %s", err.Error()) + return nil, err + } + logger.Info("respone body:%s", body.String()) + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("返回码非200 %d,Message:%s", resp.StatusCode, body.String()) + } + var baseResp BkRepoRespone + if err = json.NewDecoder(body).Decode(&baseResp); err != nil { + return nil, err + } + return &baseResp, err +} diff --git a/dbm-services/common/go-pubpkg/bkrepo/bkrepo_test.go b/dbm-services/common/go-pubpkg/bkrepo/bkrepo_test.go new file mode 100644 index 0000000000..11dbc6fd88 --- /dev/null +++ b/dbm-services/common/go-pubpkg/bkrepo/bkrepo_test.go @@ -0,0 +1,46 @@ +package bkrepo_test + +import ( + "net/http" + "testing" + + "dbm-services/common/go-pubpkg/bkrepo" +) + +func TestDownload(t *testing.T) { + t.Log("start ...") + b := &bkrepo.BkRepoClient{ + Client: &http.Client{ + Transport: &http.Transport{}, + }, + BkRepoProject: "", + BkRepoPubBucket: "", + BkRepoUser: "", + BkRepoPwd: "", + BkRepoEndpoint: "", + } + err := b.Download("/dbbackup/latest", "dbbackup_2.2.48.tar.gz", "/data/") + if err != nil { + t.Fatalf(err.Error()) + } + t.Log("ending ...") +} + +func TestQueryMeta(t *testing.T) { + t.Log("start ...") + b := &bkrepo.BkRepoClient{ + Client: &http.Client{ + Transport: &http.Transport{}, + }, + BkRepoProject: "", + BkRepoPubBucket: "", + BkRepoUser: "", + BkRepoPwd: "", + BkRepoEndpoint: "", + } + d, err := b.QueryFileNodeInfo("/dbbackup/latest", "dbbackup_2.2.48.tar.gz") + if err != nil { + t.Fatalf(err.Error()) + } + t.Log("ending ...", d) +} diff --git a/dbm-services/common/go-pubpkg/cc.v3/client.go b/dbm-services/common/go-pubpkg/cc.v3/client.go index 44b03e0e44..ad4091c9ca 100644 --- a/dbm-services/common/go-pubpkg/cc.v3/client.go +++ b/dbm-services/common/go-pubpkg/cc.v3/client.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package cc import ( @@ -8,6 +18,7 @@ import ( "io" "log" "net/http" + "net/url" "time" "github.com/google/go-querystring/query" @@ -35,23 +46,29 @@ type Client struct { // client for apiservers client *http.Client // Blueking secret - secret Secret + secret Secret + secretHeader string timeout time.Duration } // Secret TODO type Secret struct { - BKAppCode string - BKAppSecret string - BKUsername string + BKAppCode string `json:"bk_app_code"` + BKAppSecret string `json:"bk_app_secret"` + BKUsername string `json:"bk_username"` } // NewClient return new client func NewClient(apiserver string, secret Secret) (*Client, error) { + b, err := json.Marshal(secret) + if err != nil { + return nil, err + } cli := &Client{ - apiserver: apiserver, - secret: secret, + apiserver: apiserver, + secret: secret, + secretHeader: string(b), } tr := &http.Transport{} cli.client = &http.Client{ @@ -66,19 +83,16 @@ func (c *Client) Timeout(duration time.Duration) { } // Do main handler -func (c *Client) Do(method, url string, params interface{}) (*Response, error) { - object, err := Accessor(params) - if err != nil { - return nil, err - } - // set auth... - object.SetSecret(c.secret) - - body, err := json.Marshal(object) +func (c *Client) Do(method, uri string, params interface{}) (result *Response, err error) { + var fullURL string + body, err := json.Marshal(params) if err != nil { return nil, fmt.Errorf("RequestErr - %v", err) } - fullURL := c.apiserver + url + if fullURL, err = url.JoinPath(c.apiserver, uri); err != nil { + return nil, err + } + log.Println(fullURL, string(body)) req, err := http.NewRequest(method, fullURL, bytes.NewReader(body)) if err != nil { return nil, fmt.Errorf("RequestErr - new request failed: %v", err) @@ -90,10 +104,12 @@ func (c *Client) Do(method, url string, params interface{}) (*Response, error) { req = req.WithContext(ctx) } // Set Header + req.Header.Set("X-Bkapi-Accept-Code-Type", "int") + req.Header.Set("X-Bkapi-Authorization", c.secretHeader) if method == "GET" { - q, _ := query.Values(object) + q, _ := query.Values(params) log.Println("encode: ", q.Encode()) req.URL.RawQuery = q.Encode() } @@ -111,7 +127,7 @@ func (c *Client) Do(method, url string, params interface{}) (*Response, error) { if err != nil { return nil, fmt.Errorf("HttpCodeErr - Code: %v, io read all failed %s", resp.StatusCode, err.Error()) } - result := &Response{} + result = &Response{} err = json.Unmarshal(b, result) if err != nil { return nil, err diff --git a/dbm-services/common/go-pubpkg/cc.v3/list_agent_info.go b/dbm-services/common/go-pubpkg/cc.v3/list_agent_info.go new file mode 100644 index 0000000000..838d9d96cf --- /dev/null +++ b/dbm-services/common/go-pubpkg/cc.v3/list_agent_info.go @@ -0,0 +1,61 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package cc + +import ( + "encoding/json" + "net/http" +) + +// ListAgentState TODO +type ListAgentState struct { + client *Client + Url string +} + +// NewListAgentState TODO +// NewListBizHosts returns a new ListBizHosts server +func NewListAgentState(client *Client) *ListAgentState { + return &ListAgentState{ + client: client, + Url: "/api/v2/cluster/list_agent_info", + } +} + +// ListAgentInfoParam TODO +type ListAgentInfoParam struct { + // 最大支持1000个ID的查询 + AgentIdList []string `json:"agent_id_list"` +} + +// ListAgentInfoRespone TODO +type ListAgentInfoRespone struct { + BkAgentId string `json:"bk_agent_id"` + BkCloudID int `json:"bk_cloud_id"` + BkHostIp string `json:"bk_host_ip"` + Version string `json:"version"` + RunMode int `json:"run_mode"` + StatusCode int `json:"status_code"` +} + +// QueryListAgentInfo 查询主机gseAgent转态 +func (h *ListAgentState) QueryListAgentInfo(param *ListAgentInfoParam) ([]ListAgentInfoRespone, *Response, error) { + resp, err := h.client.Do(http.MethodPost, h.Url, param) + if err != nil { + return nil, resp, err + } + var result []ListAgentInfoRespone + err = json.Unmarshal(resp.Data, &result) + if err != nil { + return nil, resp, err + } + return result, resp, nil +} diff --git a/dbm-services/common/go-pubpkg/cc.v3/schema.go b/dbm-services/common/go-pubpkg/cc.v3/schema.go index 3d5f0b1679..1c7eaaa61d 100644 --- a/dbm-services/common/go-pubpkg/cc.v3/schema.go +++ b/dbm-services/common/go-pubpkg/cc.v3/schema.go @@ -1,44 +1,19 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package cc import ( "encoding/json" - "fmt" ) -// SecretMeta 定义了SetSecret接口 -// 每个请求CC接口的struct都包括了BaseSecret信息 -// 所以这里在调用时统一设置 -type SecretMeta interface { - SetSecret(secret Secret) -} - -// Accessor 统一处理Secret信息 -func Accessor(obj interface{}) (SecretMeta, error) { - switch t := obj.(type) { - case SecretMeta: - return t, nil - default: - return nil, fmt.Errorf("TypeErr: %v", t) - } -} - -// BaseSecret TODO -// CC接口验证信息 -type BaseSecret struct { - BKAppCode string `json:"bk_app_code" url:"bk_app_code"` - BKAppSecret string `json:"bk_app_secret" url:"bk_app_secret"` - BKUsername string `json:"bk_username" url:"bk_username"` - BKSupplierAccount string `json:"bk_supplier_account" url:"bk_supplier_account"` -} - -// SetSecret 设置Secret信息 -func (s *BaseSecret) SetSecret(secret Secret) { - s.BKAppCode = secret.BKAppCode - s.BKAppSecret = secret.BKAppSecret - s.BKUsername = secret.BKUsername - s.BKSupplierAccount = "tencent" -} - // Host TODO // CC主机属性 // 字段名与CCDeviceInfo的一致,主要方便做映射 @@ -98,6 +73,7 @@ type Host struct { BkCpu int `json:"bk_cpu,omitempty"` BkMem int `json:"bk_mem,omitempty"` BkDisk int `json:"bk_disk"` + BkAgentId string `json:"bk_agent_id,omitempty"` SvrTypeName string `json:"svr_type_name"` BKBSInfos []*CMDBInfo `json:"bk_bs_info"` } @@ -173,7 +149,6 @@ const ( // ResourceWatchParam 资源监听输入参数 type ResourceWatchParam struct { - BaseSecret `json:",inline"` // 事件类型: create(新增)/update(更新)/delete(删除) BKEventTypes []string `json:"bk_event_types"` // 返回的事件中需要返回的字段列表, 不能置空 @@ -206,7 +181,6 @@ type BKEvent struct { // ListHostRelationParam 主机拓扑信息查询输入参数 type ListHostRelationParam struct { - BaseSecret `json:",inline"` // 要查询的主机列表,最大长度为500,若不为空,则page字段不生效 BKHostIds []int `json:"bk_host_ids"` // 要返回的主机字段列表,不能为空 @@ -223,7 +197,6 @@ type ListHostRelationParam struct { // FindHostBizRelationParam 接口主机拓扑信息查询输入参数, BKHostIds的json为单数 type FindHostBizRelationParam struct { - BaseSecret `json:",inline"` // 要查询的主机列表,最大长度为500,若不为空,则page字段不生效 BKHostIds []int `json:"bk_host_id"` // 要返回的主机字段列表,不能为空 @@ -249,7 +222,6 @@ type FindHostBizRelationResp struct { // GetHostBaseInfoParam TODO type GetHostBaseInfoParam struct { - BaseSecret `json:",inline"` BkHostID int `json:"bk_host_id" url:"bk_host_id"` BkSupplierAccount string `json:"bk_supplier_account" url:"bk_supplier_account"` } @@ -296,9 +268,8 @@ type HostRelationInfo struct { // BizSetParam 业务与Set信息查询输入参数 type BizSetParam struct { - BaseSecret `json:",inline"` - BKBizId int `json:"bk_biz_id"` - Fields []string `json:"fields"` + BKBizId int `json:"bk_biz_id"` + Fields []string `json:"fields"` // 分页信息 Page BKPage `json:"page"` } @@ -311,9 +282,8 @@ type BizSetResponse struct { // BizModuleParam 业务与模块信息查询输入参数 type BizModuleParam struct { - BaseSecret `json:",inline"` - BKBizId int `json:"bk_biz_id"` - BKSetId int `json:"bk_set_id"` + BKBizId int `json:"bk_biz_id"` + BKSetId int `json:"bk_set_id"` // 模块属性列表,控制返回结果的模块信息里有哪些字段 Fields []string `json:"fields"` // 分页信息 @@ -396,8 +366,7 @@ type Biz struct { // BizParam 查询业务入参 type BizParam struct { - BaseSecret `json:",inline"` - Fields []string `json:"fields"` + Fields []string `json:"fields"` // 分页信息 Page BKPage `json:"page"` Condition map[string]interface{} `json:"condition"` @@ -435,7 +404,6 @@ type HostOri struct { // ListBizHostsParam TODO type ListBizHostsParam struct { - BaseSecret `json:",inline"` Page BKPage `json:"page"` BkBizId int `json:"bk_biz_id"` BkSetIds []int `json:"bk_set_ids"` @@ -457,14 +425,12 @@ type BizResponse struct { // TransferHostParam 转移主机模块输入参数 type TransferHostParam struct { - BaseSecret `json:",inline"` - From BKFrom `json:"bk_from"` - To BKTo `json:"bk_to"` + From BKFrom `json:"bk_from"` + To BKTo `json:"bk_to"` } // TransferHostModuleParam 同业务下转移模块,支持转移到多个模块 type TransferHostModuleParam struct { - BaseSecret `json:",inline"` BkBizID int `json:"bk_biz_id"` BkHostID []int `json:"bk_host_id"` BkModuleID []int `json:"bk_module_id"` @@ -473,7 +439,6 @@ type TransferHostModuleParam struct { // CloneHostPropertyParam 克隆主机属性输入参数 type CloneHostPropertyParam struct { - BaseSecret `json:",inline"` BkBizId int `json:"bk_biz_id"` BkOrgHostId int `json:"bk_org_id"` BkDstHostId int `json:"bk_dst_id"` @@ -481,7 +446,6 @@ type CloneHostPropertyParam struct { // CloneHostSvcInsParam 克隆实例信息输入参数 type CloneHostSvcInsParam struct { - BaseSecret `json:",inline"` BkBizId int `json:"bk_biz_id"` BkModuleIds []int `json:"bk_module_ids"` SrcHostId int `json:"src_host_id"` @@ -510,7 +474,6 @@ type BKTo struct { // UpdateHostParam 修改主机属性输入参数(例如主备负责人) type UpdateHostParam struct { - BaseSecret `json:",inline"` // 主机IP InnerIPs []string `json:"-"` // 主机固定号 @@ -523,7 +486,6 @@ type UpdateHostParam struct { // HostsWithoutBizListParam 根据过滤条件查询主机信息 type HostsWithoutBizListParam struct { - BaseSecret `json:",inline"` // 查询条件 HostPropertyFilter HostPropertyFilter `json:"host_property_filter"` // 分页 @@ -562,8 +524,7 @@ const ( // BizInternalModulesParam 查询业务的内置模块入参 type BizInternalModulesParam struct { - BaseSecret `json:",inline" url:",inline"` - BKBizId int `json:"bk_biz_id" url:"bk_biz_id"` + BKBizId int `json:"bk_biz_id" url:"bk_biz_id"` } // BizInternalModuleResponse 查询业务的内置模块返回值 @@ -580,8 +541,7 @@ type BizInternalModuleResponse struct { // BizTopoTreeParam 查询业务拓扑信息入参 type BizTopoTreeParam struct { - BaseSecret `json:",inline" url:",inline"` - BKBizId int `json:"bk_biz_id" url:"bk_biz_id"` + BKBizId int `json:"bk_biz_id" url:"bk_biz_id"` } // TopoTreeNode TODO @@ -598,13 +558,11 @@ type TopoTreeNode struct { // BizLocationParam TODO type BizLocationParam struct { - BaseSecret `json:",inline" url:",inline"` - BKBizIds []int `json:"bk_biz_ids" url:"bk_biz_ids"` + BKBizIds []int `json:"bk_biz_ids" url:"bk_biz_ids"` } // HostLocationParam TODO type HostLocationParam struct { - BaseSecret `json:",inline" url:",inline"` BkHostList []BkHostList `json:"bk_host_list"` } @@ -645,7 +603,6 @@ type FindHostBizRelationResponse struct { // DeptParam 部门信息 type DeptParam struct { - BaseSecret `json:",inline"` // 部门ID DeptId string `json:"dept_id"` } @@ -662,8 +619,7 @@ type DeptResponse struct { // BizCreateSetParam TODO type BizCreateSetParam struct { - BaseSecret `json:",inline"` - BkBizID int `json:"bk_biz_id"` + BkBizID int `json:"bk_biz_id"` Data struct { BkParentID int `json:"bk_parent_id"` @@ -674,7 +630,6 @@ type BizCreateSetParam struct { // BizDeleteSetParam TODO type BizDeleteSetParam struct { - BaseSecret `json:",inline"` BkBizID int `json:"bk_biz_id"` BkBizSetID int `json:"bk_set_id"` } @@ -693,8 +648,7 @@ type BizSensitive struct { // BizSensitiveParam 查询业务敏感信息入参 type BizSensitiveParam struct { - BaseSecret `json:",inline"` - Fields []string `json:"fields"` + Fields []string `json:"fields"` // 分页信息 Page BKPage `json:"page"` BkBizIds []int `json:"bk_biz_ids"` @@ -708,14 +662,12 @@ type BizSensitiveResponse struct { // SyncHostInfoFromCmpyParam 同步公司cmdb更新信息入参 type SyncHostInfoFromCmpyParam struct { - BaseSecret `json:",inline"` - BkHostIds []int `json:"bk_host_ids"` + BkHostIds []int `json:"bk_host_ids"` } // AddHostInfoFromCmpyParam 同步公司cmdb新增信息入参 type AddHostInfoFromCmpyParam struct { - BaseSecret `json:",inline"` - SvrIds []int `json:"svr_ids"` + SvrIds []int `json:"svr_ids"` } // CreateModuleParam 参数 diff --git a/dbm-services/common/go-pubpkg/cc.v3/utils/utils.go b/dbm-services/common/go-pubpkg/cc.v3/utils/utils.go index 8e95215f1a..00414f8001 100644 --- a/dbm-services/common/go-pubpkg/cc.v3/utils/utils.go +++ b/dbm-services/common/go-pubpkg/cc.v3/utils/utils.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + // Package utils TODO package utils diff --git a/dbm-services/common/go-pubpkg/cc.v3/utils/utils_test.go b/dbm-services/common/go-pubpkg/cc.v3/utils/utils_test.go deleted file mode 100644 index 1357fe10f2..0000000000 --- a/dbm-services/common/go-pubpkg/cc.v3/utils/utils_test.go +++ /dev/null @@ -1,52 +0,0 @@ -package utils - -import ( - "fmt" - "reflect" - "testing" - - "github.com/stretchr/testify/assert" -) - -type testObject struct { - c1 string `json:"c1,omitempty"` - c2 int `json:"c2"` - c3 *c3 `json:"c3,omitempty"` - c4 map[string]string `json:"c4"` - c5 []int `json:"c5"` -} - -type c3 struct { - x1 string `json:"x1"` - x2 []x2 `json:"x2,omitempty"` - x3 []string `json:"x3"` -} - -type x2 struct { - y1 string `json:"y1"` - y2 map[string]int `json:"y2,omitempty"` - y3 *int `json:"y3"` -} - -func TestGetStructTagName(t *testing.T) { - cases := []struct { - name string - object interface{} - expectedResult []string - }{ - { - name: "ptr", - object: &testObject{}, - expectedResult: []string{"c1", "c2", "x1", "y1", "y2", "y3", "x2", "x3", "c3", "c4", "c5"}, - }, - { - name: "struct", - object: testObject{}, - expectedResult: []string{"c1", "c2", "x1", "y1", "y2", "y3", "x2", "x3", "c3", "c4", "c5"}, - }, - } - for _, tc := range cases { - fields := GetStructTagName(reflect.TypeOf(tc.object)) - assert.Equal(t, tc.expectedResult, fields, fmt.Sprintf("case: %+v", tc.name)) - } -} diff --git a/dbm-services/common/go-pubpkg/cmutil/command.go b/dbm-services/common/go-pubpkg/cmutil/command.go index a03c8961c0..4d4432361e 100644 --- a/dbm-services/common/go-pubpkg/cmutil/command.go +++ b/dbm-services/common/go-pubpkg/cmutil/command.go @@ -3,7 +3,9 @@ package cmutil import ( "bytes" "fmt" + "os" "os/exec" + "strings" "github.com/pkg/errors" ) @@ -26,9 +28,54 @@ func ExecShellCommand(isSudo bool, param string) (stdoutStr string, err error) { } if len(stderr.String()) > 0 { - err = fmt.Errorf("execute shell command(%s) error:%s", param, stderr.String()) + err = fmt.Errorf("execute shell command(%s) has stderr:%s", param, stderr.String()) return stderr.String(), err } return stdout.String(), nil } + +// ExecCommand bash=true: bash -c 'cmdName args', bash=false: ./cmdName args list +// ExecCommand(false, "df", "-k /data") will get `df '-k /data'` error command. you need change it to (false, "df", "-k", "/data") or (true, "df -k /data") +// bash=false need PATH +// cwd is the command work dir +// return stdout, stderr ,err +func ExecCommand(bash bool, cwd string, cmdName string, args ...string) (string, string, error) { + stdout, stderr, err := ExecCommandReturnBytes(bash, cwd, cmdName, args...) + stdoutStr := strings.TrimSpace(string(stdout)) + stderrStr := strings.TrimSpace(string(stderr)) + + return stdoutStr, stderrStr, err +} + +// ExecCommandReturnBytes run exec.Command +// return stdout, stderr ,err +func ExecCommandReturnBytes(bash bool, cwd string, cmdName string, args ...string) ([]byte, []byte, error) { + var cmd *exec.Cmd + if bash { + cmdStr := fmt.Sprintf(`%s %s`, cmdName, strings.Join(args, " ")) + cmd = exec.Command("bash", "-c", cmdStr) + } else { + if cmdName == "" { + return nil, nil, errors.Errorf("command name should not be empty:%v", args) + } + // args should be list + cmd = exec.Command(cmdName, args...) + } + cmd.Env = append(cmd.Env, fmt.Sprintf( + "PATH=%s:/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin", os.Getenv("PATH")), + fmt.Sprintf("LD_LIBRARY_PATH=%s", os.Getenv("LD_LIBRARY_PATH"))) + + if cwd != "" { + cmd.Dir = cwd + } + //logger.Info("PATH:%s cmd.Env:%v", os.Getenv("PATH"), cmd.Env) + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + if err := cmd.Run(); err != nil { + //logger.Error("stdout:%s, stderr:%s, cmd:%s", stdout.String(), stderr.String(), cmd.String()) + return stdout.Bytes(), stderr.Bytes(), errors.Wrap(err, cmd.String()) + } + return stdout.Bytes(), stderr.Bytes(), nil +} diff --git a/dbm-services/common/go-pubpkg/cmutil/dir_size.go b/dbm-services/common/go-pubpkg/cmutil/dir_size.go new file mode 100644 index 0000000000..a0fe94af19 --- /dev/null +++ b/dbm-services/common/go-pubpkg/cmutil/dir_size.go @@ -0,0 +1,21 @@ +package cmutil + +import ( + "os" + "path/filepath" +) + +// DirSize get directory size like du +func DirSize(path string) (int64, error) { + var totalSize int64 + err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() { + totalSize += info.Size() + } + return err + }) + return totalSize, err +} diff --git a/dbm-services/common/go-pubpkg/cmutil/file.go b/dbm-services/common/go-pubpkg/cmutil/file.go index 65041b8a06..efcc4a9979 100644 --- a/dbm-services/common/go-pubpkg/cmutil/file.go +++ b/dbm-services/common/go-pubpkg/cmutil/file.go @@ -70,3 +70,12 @@ func GetFileSize(path string) int64 { } return f.Size() } + +// OSCopyFile os cp file +func OSCopyFile(srcFile, dstFile string) error { + _, errStr, err := ExecCommand(true, "", "cp", "-p", srcFile, dstFile) + if err != nil { + return errors.New(errStr) + } + return nil +} diff --git a/dbm-services/common/go-pubpkg/cmutil/osinfo.go b/dbm-services/common/go-pubpkg/cmutil/osinfo.go new file mode 100644 index 0000000000..c82f49d266 --- /dev/null +++ b/dbm-services/common/go-pubpkg/cmutil/osinfo.go @@ -0,0 +1,118 @@ +package cmutil + +import ( + "strings" + + "github.com/pkg/errors" + "github.com/shirou/gopsutil/v3/cpu" + "github.com/shirou/gopsutil/v3/disk" + "github.com/shirou/gopsutil/v3/mem" +) + +// MemoryInfo return memory info +type MemoryInfo struct { + Total uint64 `json:"total"` + Free uint64 `json:"free"` + Shared uint64 `json:"shared"` + Buffer uint64 `json:"buffer"` + SwapTotal uint64 `json:"swap_total"` + SwapFree uint64 `json:"swap_free"` +} + +// GetMemoryInfo Get Memory Info +func GetMemoryInfo() (*MemoryInfo, error) { + memStat, err := mem.VirtualMemory() + if err != nil { + return nil, errors.Wrap(err, "fail to get memory") + } + memInfo := MemoryInfo{ + Total: memStat.Total, + Free: memStat.Free, + Shared: memStat.Shared, + Buffer: memStat.Buffers, + SwapTotal: memStat.SwapTotal, + SwapFree: memStat.SwapFree, + } + return &memInfo, nil +} + +// DiskPartInfo return disk partition info +type DiskPartInfo struct { + Device string `json:"device"` + Mountpoint string `json:"mountpoint"` + Fstype string `json:"fstype"` + + Path string `json:"path"` + Total uint64 `json:"total"` + // Free 不包括 fs reserved block 部分 + Free uint64 `json:"free"` + // Used truly used, not include reserved + Used uint64 `json:"used"` + // Reserved = Total - Free - Used + Reserved uint64 `json:"reserved"` + UsedPercent float64 `json:"used_percent"` + InodesTotal uint64 `json:"inodes_total"` + InodesUsed uint64 `json:"inodes_used"` + InodesUsedPercent float64 `json:"inodes_used_percent"` +} + +// GetDiskPartInfo 获取目录的信息 +// 空间使用,挂载设备。比如 path = /data/dbbak/123,获取的是目录对应的挂载设备的信息 +func GetDiskPartInfo(path string, noCheckDevice bool) (*DiskPartInfo, error) { + info := DiskPartInfo{Path: path} + if !noCheckDevice { + // 获取目录对应的挂载点 + osStatArgs := []string{"--format", "%m", path} + if stdout, stderr, err := ExecCommand(false, "", "stat", osStatArgs...); err != nil { + return nil, errors.Wrapf(err, "stat to get path mount %s", stderr) + } else { + info.Mountpoint = strings.TrimSpace(stdout) + } + // get more mountpoint device info + partInfo, err := disk.Partitions(false) + if err != nil { + return nil, errors.Wrap(err, "get disk partitions") + } + for _, p := range partInfo { + if p.Mountpoint == info.Mountpoint { + info.Device = p.Device + info.Fstype = p.Fstype + } + } + if info.Device == "" { + return nil, errors.Errorf("fail to get device(mounted %s) for path %s", info.Mountpoint, info.Path) + } + if info.Mountpoint != info.Path { + // use du to get directory used size? + } + } + + // 获取挂载点的分区使用信息 + pathInfo, err := disk.Usage(path) + if err != nil { + return nil, errors.Wrap(err, "get disk info") + } + info.Total = pathInfo.Total + info.Free = pathInfo.Free + info.Used = pathInfo.Used + info.UsedPercent = pathInfo.UsedPercent + info.Reserved = pathInfo.Total - pathInfo.Used - pathInfo.Free + info.InodesTotal = pathInfo.InodesTotal + info.InodesUsed = pathInfo.InodesUsed + info.InodesUsedPercent = float64(100.0*pathInfo.InodesUsed) / float64(pathInfo.InodesTotal) + return &info, nil +} + +// CPUInfo return cpu processor info +type CPUInfo struct { + CoresLogical int `json:"cores_logical"` +} + +// GetCPUInfo Get CPU Info +func GetCPUInfo() (*CPUInfo, error) { + cores, err := cpu.Counts(true) + if err != nil { + return nil, errors.Wrap(err, "cpu.Counts") + } + return &CPUInfo{CoresLogical: cores}, nil +} diff --git a/dbm-services/common/go-pubpkg/cmutil/pflag_enum.go b/dbm-services/common/go-pubpkg/cmutil/pflag_enum.go new file mode 100644 index 0000000000..534daad1e1 --- /dev/null +++ b/dbm-services/common/go-pubpkg/cmutil/pflag_enum.go @@ -0,0 +1,97 @@ +package cmutil + +import ( + "errors" + "fmt" + + "github.com/spf13/pflag" +) + +/* 使用示例 +formatOpt, err := cmutil.NewPflagEnum("format", "table", []string{"table", "json"}) +// judge err +spiderQueryCmd.Flags().Var(formatOpt, formatOpt.Name(),fmt.Sprintf("output format, allowed %v", formatOpt.Choices())) +err = formatOpt.SetChoices(spiderQueryCmd.Flags()) +// judge err +_ = viper.BindPFlag("query.format", spiderQueryCmd.Flags().Lookup("format")) +*/ + +// PflagEnum 自定义的 pflag 类型,选项必须从事允许的值列表里面选择 +type PflagEnum struct { + // name flag 选项名称 + name string + // valueDefault 默认值 + valueDefault string + // choices 可选项 + choices []string + + value string + flag *pflag.Flag +} + +func NewPflagEnum(name string, value string, choices []string) (*PflagEnum, error) { + // validate valueDefault is in choices + isValueDefaultOk := false + for _, c := range choices { + if c == value { + isValueDefaultOk = true + break + } + } + if !isValueDefaultOk { + return nil, fmt.Errorf("default value %s is not one of %v", value, choices) + } + return &PflagEnum{ + name: name, + valueDefault: value, + choices: choices, + }, nil +} + +// SetChoices 要先通过 yourCmd.Flags().Var 注册 name,才能调用 SetChoices +func (e *PflagEnum) SetChoices(flagSet *pflag.FlagSet) error { + if e.name == "" { + return errors.New("flag name cannot be empty, please register it using yourCmd.Flags().Var") + } + e.flag = flagSet.Lookup(e.name) + if e.flag == nil { + return errors.New("unknown choices error, may lookup name error") + } + return flagSet.SetAnnotation(e.name, "choices", e.choices) +} + +// String is used both by fmt.Print and by Cobra in help text +func (e *PflagEnum) String() string { + return e.value +} + +// Set must have pointer receiver so it doesn't change the value of a copy +func (e *PflagEnum) Set(v string) error { + + choices, ok := e.flag.Annotations["choices"] + if !ok { + return errors.New("no choices given") + } + for _, c := range choices { + if c == v { + e.value = v + return nil + } + } + return fmt.Errorf(`must be one of %v`, choices) +} + +// Type is only used in help text +func (e *PflagEnum) Type() string { + return "PflagEnum" +} + +// Name return name +func (e *PflagEnum) Name() string { + return e.name +} + +// Choices return choices +func (e *PflagEnum) Choices() []string { + return e.choices +} diff --git a/dbm-services/common/go-pubpkg/cmutil/slice.go b/dbm-services/common/go-pubpkg/cmutil/slice.go index 126feed90c..40b6ade003 100644 --- a/dbm-services/common/go-pubpkg/cmutil/slice.go +++ b/dbm-services/common/go-pubpkg/cmutil/slice.go @@ -1,8 +1,17 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package cmutil import ( "fmt" - "reflect" "strconv" "strings" @@ -33,10 +42,15 @@ func StringsHas(ss []string, val string) bool { return false } +// UniqueInts Returns unique items in a slice +func UniqueInts(slice []int) []int { + return RemoveDuplicate(slice) +} + // RemoveDuplicate 通过map主键唯一的特性过滤重复元素 -func RemoveDuplicate(arr []string) []string { - resArr := make([]string, 0) - tmpMap := make(map[string]struct{}) +func RemoveDuplicate[T int | string](arr []T) []T { + resArr := make([]T, 0) + tmpMap := make(map[T]struct{}) for _, val := range arr { // 判断主键为val的map是否存在 if _, ok := tmpMap[val]; !ok { @@ -44,7 +58,6 @@ func RemoveDuplicate(arr []string) []string { tmpMap[val] = struct{}{} } } - return resArr } @@ -87,36 +100,16 @@ func SplitGroup(laxiconid []string, subGroupLength int64) [][]string { } // RemoveEmpty 过滤掉空字符串 -func RemoveEmpty(input []string) []string { +func CleanStrElems(elems []string) []string { var result []string - for _, item := range input { + for _, item := range elems { if strings.TrimSpace(item) != "" { - result = append(result, item) + result = append(result, strings.TrimSpace(item)) } } return result } -// RemoveDuplicateIntElement TODO -func RemoveDuplicateIntElement(arry []int) []int { - result := make([]int, 0, len(arry)) - temp := map[int]struct{}{} - for _, item := range arry { - if _, ok := temp[item]; !ok { - temp[item] = struct{}{} - result = append(result, item) - } - } - return result -} - -// ArryToInterfaceArry TODO -func ArryToInterfaceArry(arrys ...interface{}) []interface{} { - var result []interface{} - result = append(result, arrys...) - return result -} - // ElementNotInArry TODO func ElementNotInArry(ele string, arry []string) bool { if len(arry) <= 0 { @@ -169,38 +162,14 @@ func ArrayInGroupsOf(arr []string, num int64) [][]string { return segments } -// HasElem TODO -func HasElem(elem interface{}, slice interface{}) bool { - defer func() { - if err := recover(); err != nil { - fmt.Println("HasElem error", err) - } - }() - arrV := reflect.ValueOf(slice) - if arrV.Kind() == reflect.Slice || arrV.Kind() == reflect.Array { - for i := 0; i < arrV.Len(); i++ { - // XXX - panics if slice element points to an unexported struct field - // see https://golang.org/pkg/reflect/#Value.Interface - if reflect.DeepEqual(arrV.Index(i).Interface(), elem) { - return true - } +func HasElem[T int | string](elem T, elems []T) bool { + if len(elems) <= 0 { + return true + } + for _, v := range elems { + if elem == v { + return true } } return false } - -// UniqueInts Returns unique items in a slice -func UniqueInts(slice []int) []int { - // create a map with all the values as key - uniqMap := make(map[int]struct{}) - for _, v := range slice { - uniqMap[v] = struct{}{} - } - - // turn the map keys into a slice - uniqSlice := make([]int, 0, len(uniqMap)) - for v := range uniqMap { - uniqSlice = append(uniqSlice, v) - } - return uniqSlice -} diff --git a/dbm-services/common/go-pubpkg/errno/30000_dbha_code.go b/dbm-services/common/go-pubpkg/errno/30000_dbha_code.go new file mode 100644 index 0000000000..8827ad5f70 --- /dev/null +++ b/dbm-services/common/go-pubpkg/errno/30000_dbha_code.go @@ -0,0 +1,35 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package errno + +var ( + // dbha code start 30000 + + // ErrMultiMaster TODO + // mysql error, prefix is 300 + ErrMultiMaster = &Errno{Code: 30001, Message: "Multi master found", CNMessage: "同一主机同时存在实例角色master和slave"} + // ErrSwitchNumUnMatched TODO + // dead host's instance num not equal to its switch number + ErrSwitchNumUnMatched = &Errno{Code: 30002, Message: "instances number is %d, switch number is %d, unmatched", + CNMessage: "实例个数%d与切换个数%d不匹配"} + // ErrRemoteQuery TODO + ErrRemoteQuery = &Errno{Code: 30003, Message: "do remote query failed", CNMessage: "调用remoteQuery失败"} + // ErrRemoteExecute TODO + ErrRemoteExecute = &Errno{Code: 30004, Message: "do remote execute failed", CNMessage: "调用remoteExecute失败"} + // ErrIOTreadState TODO + ErrIOTreadState = &Errno{Code: 30005, Message: "slave IO_THREAD is not ok", CNMessage: "IO_THREAD异常"} + // ErrSQLTreadState TODO + ErrSQLTreadState = &Errno{Code: 30006, Message: "slave SQL_THREAD is not ok", CNMessage: "SQL_THREAD异常"} + // ErrSlaveStatus TODO + ErrSlaveStatus = &Errno{Code: 30007, Message: "get slave status abnormal", CNMessage: "获取slave status异常"} + // proxy error, prefix is 400 + +) diff --git a/dbm-services/common/go-pubpkg/errno/50000_dbpartition_code.go b/dbm-services/common/go-pubpkg/errno/50000_dbpartition_code.go new file mode 100644 index 0000000000..2209cd48a2 --- /dev/null +++ b/dbm-services/common/go-pubpkg/errno/50000_dbpartition_code.go @@ -0,0 +1,32 @@ +package errno + +var ( + // NoTableMatched TODO + NoTableMatched = Errno{Code: 52019, Message: "no table matched", CNMessage: "找不到匹配的表"} + // ClusterIdIsEmpty TODO + ClusterIdIsEmpty = Errno{Code: 52020, Message: "cluster_id can't be empty", + CNMessage: "cluster_id不能为空"} + // CheckPartitionFailed TODO + CheckPartitionFailed = Errno{Code: 52021, Message: "partition check failed", CNMessage: "分区检查失败"} + // PartitionConfigNotExisted TODO + PartitionConfigNotExisted = Errno{Code: 52022, Message: "Partition config not existed ", CNMessage: "分区配置不存在"} + // PartOfPartitionConfigsNotExisted TODO + PartOfPartitionConfigsNotExisted = Errno{Code: 52023, Message: "part of artition configs not existed ", + CNMessage: "部分分区配置不存在"} + // NotSupportedClusterType TODO + NotSupportedClusterType = Errno{Code: 52024, Message: "this instance type is not supportted by partition", + CNMessage: "不支持的实例类型"} + // ConfigIdIsEmpty TODO + ConfigIdIsEmpty = Errno{Code: 52025, Message: "partition config id can't be empty", + CNMessage: "partition config id 不能为空"} + // GetPartitionSqlFail TODO + GetPartitionSqlFail = Errno{Code: 52027, Message: "get partition sql failed", CNMessage: "获取分区语句失败"} + // ExecutePartitionFail TODO + ExecutePartitionFail = Errno{Code: 52028, Message: "execute partition failed", CNMessage: "执行分区失败"} + // NothingToDo TODO + NothingToDo = Errno{Code: 52029, Message: "nothing to do", CNMessage: "没有需要执行的操作"} + // DomainNotExists TODO + DomainNotExists = Errno{Code: 52030, Message: "domain not exists", CNMessage: "域名不存在"} + NotSupportedPartitionType = Errno{Code: 52031, Message: "not supported partition type", CNMessage: "不支持的分区类型"} + WrongPartitionNameFormat = Errno{Code: 52032, Message: "wrong partition name format ", CNMessage: "分区名格式错误"} +) diff --git a/dbm-services/common/go-pubpkg/errno/50000_dbpriv_code.go b/dbm-services/common/go-pubpkg/errno/50000_dbpriv_code.go new file mode 100644 index 0000000000..f8f54d3786 --- /dev/null +++ b/dbm-services/common/go-pubpkg/errno/50000_dbpriv_code.go @@ -0,0 +1,80 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package errno + +var ( + // dbpriv code start 50000 + + // PasswordNotConsistent TODO + PasswordNotConsistent = Errno{Code: 51008, + Message: "user is exist,but the new password is not consistent with the old password, should be consistent", + CNMessage: "账号已存在,但是新密码与旧密码不一致,需要保持一致"} + // GrantPrivilegesFail TODO + GrantPrivilegesFail = Errno{Code: 51009, Message: "Grant Privileges Fail", CNMessage: "授权执行失败"} + // GrantPrivilegesSuccess TODO + GrantPrivilegesSuccess = Errno{Code: 0, Message: "Grant Privileges success", CNMessage: "授权执行成功"} + // GrantPrivilegesParameterCheckFail TODO + GrantPrivilegesParameterCheckFail = Errno{Code: 51010, Message: "Parameter of Grant Privileges Check Fail", + CNMessage: "授权单据的参数检查失败"} + // ErrPswNotIdentical TODO + ErrPswNotIdentical = Errno{Code: 51000, + Message: "Password is not identical to the password of existed account rules, " + + "same accounts should use same password.", + CNMessage: "密码与已存在的账号规则中的密码不同,相同账号的密码需要保持一致!"} + // AccountRuleExisted TODO + AccountRuleExisted = Errno{Code: 51001, Message: "Account rule of user on this db is existed ", + CNMessage: "用户对此DB授权的账号规则已存在"} + // AccountExisted TODO + AccountExisted = Errno{Code: 51002, Message: "Account is existed ", CNMessage: "账号已存在"} + // AccountNotExisted TODO + AccountNotExisted = Errno{Code: 51003, Message: "Account not existed ", CNMessage: "账号不存在"} + // PasswordConsistentWithAccountName TODO + PasswordConsistentWithAccountName = Errno{Code: 51019, Message: "Password should be different from account name ", + CNMessage: "账号与密码不能相同"} + // PasswordOrAccountNameNull TODO + PasswordOrAccountNameNull = Errno{Code: 51020, Message: "Password or account name should not be empty ", + CNMessage: "账号与密码不能为空"} + // AccountIdNull TODO + AccountIdNull = Errno{Code: 51021, Message: "Account ID should not be empty", + CNMessage: "账号ID不能为空"} + // DbNameNull TODO + DbNameNull = Errno{Code: 51022, Message: "Database name should not be empty", + CNMessage: "数据库名称不能为空"} + // AccountRuleIdNull TODO + AccountRuleIdNull = Errno{Code: 51022, Message: "Account rule should not be empty", + CNMessage: "账号规则ID不能为空"} + // PrivNull TODO + PrivNull = Errno{Code: 51022, Message: "No privilege was chosen", CNMessage: "未选择权限"} + // AccountRuleNotExisted TODO + AccountRuleNotExisted = Errno{Code: 51004, Message: "Account rule not existed ", CNMessage: "账号规则不存在"} + // OnlyOneDatabaseAllowed TODO + OnlyOneDatabaseAllowed = Errno{Code: 51005, + Message: "Only one database allowed, database name should not contain space", CNMessage: "只允许填写一个数据库,数据库名称不能包含空格"} + // ErrMysqlInstanceStruct TODO + ErrMysqlInstanceStruct = Errno{Code: 51006, Message: "Not either tendbha or orphan structure", + CNMessage: "不符合tendbha或者orphan的集群结构"} + // GenerateEncryptedPasswordErr TODO + GenerateEncryptedPasswordErr = Errno{Code: 51007, Message: "Generate Encrypted Password Err", + CNMessage: "创建账号,生成加密的密码时发生错误"} + // ClonePrivilegesFail TODO + ClonePrivilegesFail = Errno{Code: 51013, Message: "Clone privileges fail", CNMessage: "克隆权限失败"} + // ClonePrivilegesCheckFail TODO + ClonePrivilegesCheckFail = Errno{Code: 51014, Message: "Clone privileges check fail", CNMessage: "克隆权限检查失败"} + // NoPrivilegesNothingToDo TODO + NoPrivilegesNothingToDo = Errno{Code: 51015, Message: "no privileges,nothing to do", CNMessage: "没有权限需要克隆"} + // IpPortFormatError TODO + IpPortFormatError = Errno{Code: 51017, Message: "format not in 'ip:port' format", + CNMessage: "格式不是ip:port的格式"} + // CloudIdRequired TODO + CloudIdRequired = Errno{Code: 51019, Message: "bk_cloud_id is required", CNMessage: "bk_cloud_id不能为空"} + // ClusterTypeIsEmpty TODO + ClusterTypeIsEmpty = Errno{Code: 51021, Message: "Cluster type can't be empty", CNMessage: "cluster type不能为空"} +) diff --git a/dbm-services/common/go-pubpkg/errno/60000_dbresource_code.go b/dbm-services/common/go-pubpkg/errno/60000_dbresource_code.go new file mode 100644 index 0000000000..233e6ffd7f --- /dev/null +++ b/dbm-services/common/go-pubpkg/errno/60000_dbresource_code.go @@ -0,0 +1,26 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package errno + +var ( + // dbresource code start 60000 + + // ErrResourceinsufficient TODO + ErrResourceinsufficient = Errno{Code: 60001, Message: "resource insufficient", CNMessage: "资源不足"} + // ErrResourceLock TODO + ErrResourceLock = Errno{Code: 60002, Message: "failed to acquire resource lock", CNMessage: "获取资源锁失败"} + // ErrApplyResourceParamCheck TODO + ErrApplyResourceParamCheck = Errno{Code: 60003, Message: "failed to check the parameters of the applied resource", + CNMessage: "申请资源参数合法性检查不通过"} + // ErresourceLockReturn TODO + ErresourceLockReturn = Errno{Code: 6004, CNMessage: "锁定机器,返回机器失败", + Message: "failed to lock the machine and return the machine"} +) diff --git a/dbm-services/common/go-pubpkg/errno/code.go b/dbm-services/common/go-pubpkg/errno/code.go new file mode 100644 index 0000000000..95b7407e4d --- /dev/null +++ b/dbm-services/common/go-pubpkg/errno/code.go @@ -0,0 +1,101 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package errno + +var ( + // OK TODO + OK = Errno{Code: 0, Message: "", CNMessage: ""} + + // InternalServerError TODO + InternalServerError = Errno{Code: 10001, Message: "Internal server error", CNMessage: "服务器内部错误。"} + // ErrBind TODO + ErrBind = Errno{Code: 10002, Message: "Error occurred while binding the request body to the struct.", + CNMessage: "请求参数发生错误。"} + // ErrReadEntity TODO + ErrReadEntity = Errno{Code: 10003, Message: "Error occurred while parsing the request parameter.", + CNMessage: "Error occurred while parsing the request parameter."} + // ErrJSONUnmarshal TODO + ErrJSONUnmarshal = Errno{Code: 10004, Message: "Error occurred while Unmarshaling the JSON to data model.", + CNMessage: "json反序列化失败"} + // ErrInputParameter TODO + ErrInputParameter = Errno{Code: 10005, Message: "input pramater error.", CNMessage: "输入参数错误"} + // ErrErrInvalidParam TODO + ErrErrInvalidParam = Errno{Code: 10005, Message: "parameter validity check failed", + CNMessage: "参数合法性检查不通过"} + + // ErrBytesToMap TODO + ErrBytesToMap = Errno{Code: 50307, Message: "Error occurred while converting bytes to map.", + CNMessage: "Error occurred while converting bytes to map."} + + // ErrString2Int TODO + ErrString2Int = Errno{Code: 10110, Message: "Error occurred while convert string to int."} + // ErrGetJSONArray TODO + ErrGetJSONArray = Errno{Code: 10111, Message: "Get simplejson Array error.", CNMessage: ""} + // ErrConvert2Map TODO + ErrConvert2Map = Errno{Code: 10112, Message: "Error occurred while converting the data to Map.", + CNMessage: "Error occurred while converting the data to Map."} + // ErrJSONMarshal TODO + ErrJSONMarshal = Errno{Code: 10113, Message: "Error occurred while marshaling the data to JSON.", + CNMessage: "json序列化失败"} + // ErrTypeAssertion TODO + ErrTypeAssertion = Errno{Code: 10114, Message: "Error occurred while doing type assertion.", CNMessage: "类型断言失败"} + // ErrParameterRequired TODO + ErrParameterRequired = Errno{Code: 10115, Message: "Input paramter required"} + + // ErrorJsonToMap TODO + ErrorJsonToMap = Errno{Code: 10114, Message: "Error occured while converting json to Map.", + CNMessage: "Json 转为 Map 出现错误!"} + + // ErrDBQuery TODO + ErrDBQuery = Errno{Code: 10201, Message: "DB Query error.", CNMessage: "查询DB错误!"} + // ErrModelFunction TODO + ErrModelFunction = Err{Errno: Errno{Code: 10202, Message: "Error occured while invoking model function.", + CNMessage: "调用 DB model 方法发生错误!"}, Err: nil} + + // ErrDoNotHavePrivs TODO + ErrDoNotHavePrivs = Errno{Code: 10301, Message: "User don't have Privs.", CNMessage: "此用户没有权限"} + // ErrUserIsEmpty TODO + ErrUserIsEmpty = Errno{Code: 10302, Message: "User can't be empty.", CNMessage: "user 不能为空!"} + + // StartBiggerThanEndTime TODO + StartBiggerThanEndTime = Errno{Code: 10060, Message: "Start time is bigger than end time.", CNMessage: "开始时间大于结束时间"} + + // RepeatedIpExistSystem TODO + RepeatedIpExistSystem = Errno{Code: 10070, CNMessage: "存在重复IP", Message: "there is a duplicate ip"} + + // ErrInvokeAPI TODO + ErrInvokeAPI = Errno{Code: 15000, Message: "Error occurred while invoking API", CNMessage: "调用 API 发生错误!"} + + // ErrRecordNotFound TODO + ErrRecordNotFound = Errno{Code: 404, Message: "There is no records in db.", CNMessage: "数据库未找到对应的记录!"} + + // ErrValidation TODO + ErrValidation = Errno{Code: 20001, Message: "Validation failed."} + // ErrDatabase TODO + ErrDatabase = Errno{Code: 20002, Message: "Database error."} + // ErrToken TODO + ErrToken = Errno{Code: 20003, Message: "Error occurred while signing the JSON web token."} + + // ErrEncrypt TODO + // user errors + ErrEncrypt = Errno{Code: 20101, Message: "Error occurred while encrypting the user password."} + // ErrUserNotFound TODO + ErrUserNotFound = Errno{Code: 20102, Message: "The user was not found."} + // ErrTokenInvalid TODO + ErrTokenInvalid = Errno{Code: 20103, Message: "The token was invalid."} + // ErrPasswordIncorrect TODO + ErrPasswordIncorrect = Errno{Code: 20104, Message: "The password was incorrect."} + + // BkBizIdIsEmpty TODO + BkBizIdIsEmpty = Errno{Code: 51012, Message: "bk_biz_id can't be empty", CNMessage: "bk_biz_id不能为空"} + // InstanceNotExists TODO + InstanceNotExists = Errno{Code: 51018, Message: "instance not exists", CNMessage: "实例不存在"} +) diff --git a/dbm-services/common/go-pubpkg/errno/errno.go b/dbm-services/common/go-pubpkg/errno/errno.go new file mode 100644 index 0000000000..cc7e659d3b --- /dev/null +++ b/dbm-services/common/go-pubpkg/errno/errno.go @@ -0,0 +1,135 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +// Package errno TODO +package errno + +import ( + "fmt" +) + +// Errno TODO +type Errno struct { + Code int + Message string + CNMessage string +} + +const lang = "zh_CN" + +// Error 用于错误处理 +func (err Errno) Error() string { + switch lang { + case "zh_CN": + return err.CNMessage + case "en_US": + return err.Message + default: + return err.CNMessage + } +} + +// Addf TODO +func (err Errno) Addf(format string, args ...interface{}) error { + return err.Add(fmt.Sprintf(format, args...)) +} + +// Add TODO +func (err Errno) Add(message string) error { + switch lang { + case "zh_CN": + err.CNMessage = fmt.Sprintf("[%s]: %s", err.CNMessage, message) + return err + case "en_US": + err.Message = fmt.Sprintf("[%s]: %s", err.Message, message) + return err + default: + err.CNMessage = fmt.Sprintf("[%s]: %s", err.CNMessage, message) + return err + } +} + +// AddBefore TODO +func (err Errno) AddBefore(message string) error { + switch lang { + case "zh_CN": + err.CNMessage = message + err.CNMessage + return err + case "en_US": + err.Message = message + err.Message + return err + default: + err.CNMessage = message + err.CNMessage + return err + } +} + +// AddErr TODO +func (err Errno) AddErr(xerr error) error { + message := xerr.Error() + if xerr == nil { + message = "error is nil" + } + return err.Add(message) +} + +// Err represents an error +type Err struct { + Errno + Err error +} + +// New TODO +func New(errno Errno, err error) *Err { + return &Err{Errno: errno, Err: err} +} + +// SetMsg TODO +func (err Err) SetMsg(message string) error { + err.Message = message + return err +} + +// SetCNMsg TODO +func (err Err) SetCNMsg(cnMessage string) error { + err.CNMessage = cnMessage + return err +} + +// Addf TODO +func (err Err) Addf(format string, args ...interface{}) error { + return err.Add(fmt.Sprintf(format, args...)) +} + +// DecodeErr TODO +func DecodeErr(err error) (int, string) { + + var CN bool = true + + if err == nil { + return OK.Code, OK.Message + } + switch typed := err.(type) { + case Err: + if CN { + return typed.Code, typed.CNMessage + } else { + return typed.Code, typed.Message + } + case Errno: + if CN { + return typed.Code, typed.CNMessage + } else { + return typed.Code, typed.Message + } + default: + } + return InternalServerError.Code, err.Error() +} diff --git a/dbm-services/common/go-pubpkg/go.mod b/dbm-services/common/go-pubpkg/go.mod index 8f6bfba3d0..e501d98171 100644 --- a/dbm-services/common/go-pubpkg/go.mod +++ b/dbm-services/common/go-pubpkg/go.mod @@ -13,9 +13,11 @@ require ( github.com/juju/ratelimit v1.0.2 github.com/pkg/errors v0.9.1 github.com/robfig/cron/v3 v3.0.1 + github.com/shirou/gopsutil/v3 v3.23.8 github.com/spf13/cast v1.5.0 + github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.15.0 - github.com/stretchr/testify v1.8.2 + github.com/stretchr/testify v1.8.4 go.uber.org/zap v1.24.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 ) @@ -23,24 +25,30 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/leodido/go-urn v1.2.3 // indirect github.com/lib/pq v1.10.0 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-sqlite3 v1.14.16 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/pelletier/go-toml/v2 v2.0.7 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/rogpeppe/go-internal v1.8.0 // indirect + github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/spf13/afero v1.9.5 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.4.2 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + github.com/yusufpapurcu/wmi v1.2.3 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/goleak v1.1.12 // indirect go.uber.org/multierr v1.8.0 // indirect golang.org/x/crypto v0.8.0 // indirect - golang.org/x/sys v0.7.0 // indirect + golang.org/x/sys v0.11.0 // indirect golang.org/x/text v0.9.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/dbm-services/common/go-pubpkg/go.sum b/dbm-services/common/go-pubpkg/go.sum index 9dbbe34a8d..8352a2e457 100644 --- a/dbm-services/common/go-pubpkg/go.sum +++ b/dbm-services/common/go-pubpkg/go.sum @@ -62,6 +62,8 @@ github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbS github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= @@ -110,7 +112,9 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -156,6 +160,8 @@ github.com/leodido/go-urn v1.2.3/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNa github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.0 h1:Zx5DJFEYQXio93kgXnQ09fXNiUKsqv4OUEu2UtGcB1E= github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= @@ -171,12 +177,20 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/shirou/gopsutil/v3 v3.23.8 h1:xnATPiybo6GgdRoC4YoGnxXZFRc3dqQTGi73oLvvBrE= +github.com/shirou/gopsutil/v3 v3.23.8/go.mod h1:7hmCaBn+2ZwaZOr6jmPBZDfawwMGuo1id3C6aM8EDqQ= +github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= +github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= +github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= +github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= @@ -198,15 +212,22 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= +github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -327,6 +348,7 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -347,6 +369,7 @@ golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -356,8 +379,9 @@ golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/dbm-services/common/go-pubpkg/mysqlcomm/spider.go b/dbm-services/common/go-pubpkg/mysqlcomm/spider.go new file mode 100644 index 0000000000..8d72e4fc3b --- /dev/null +++ b/dbm-services/common/go-pubpkg/mysqlcomm/spider.go @@ -0,0 +1,7 @@ +package mysqlcomm + +// GetTdbctlPortBySpider 根据 spider master 端口,获取 tdbctl 端口 +// tdbctl port = spider_port + 1000 +func GetTdbctlPortBySpider(spiderPort int) int { + return spiderPort + 1000 +} diff --git a/dbm-services/common/go-pubpkg/netutil/netutil.go b/dbm-services/common/go-pubpkg/netutil/netutil.go new file mode 100644 index 0000000000..7f7b69ea16 --- /dev/null +++ b/dbm-services/common/go-pubpkg/netutil/netutil.go @@ -0,0 +1,24 @@ +package netutil + +import ( + "fmt" + "net" +) + +// GetAllIpAddr 获取本机所有ip地址 +func GetAllIpAddr() []string { + addrs, err := net.InterfaceAddrs() + if err != nil { + fmt.Println(err) + return nil + } + var ipList []string + for _, address := range addrs { + if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { + if ipnet.IP.To4() != nil { + ipList = append(ipList, ipnet.IP.String()) + } + } + } + return ipList +} diff --git a/dbm-services/common/go-pubpkg/reportlog/report.go b/dbm-services/common/go-pubpkg/reportlog/report.go index 57bf6257cd..329760a3ce 100644 --- a/dbm-services/common/go-pubpkg/reportlog/report.go +++ b/dbm-services/common/go-pubpkg/reportlog/report.go @@ -46,6 +46,7 @@ func (r *Reporter) Println(v interface{}) { // NewReporter init reporter for logFile path func NewReporter(reportDir, filename string, logOpt *LoggerOption) (*Reporter, error) { + logFilePath := filepath.Join(reportDir, filename) var reporter *Reporter = &Reporter{ log: &log.Logger{}, } @@ -56,6 +57,14 @@ func NewReporter(reportDir, filename string, logOpt *LoggerOption) (*Reporter, e return nil, errors.Wrap(err, "create report path") } } + if !cmutil.FileExists(logFilePath) { + // lumberjack 默认创建的文件权限是 600 + if f, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY, 0644); err != nil { + return nil, err + } else { + f.Close() + } + } /* statusFile := "binlog_status.log" statusLogger := &lumberjack.Logger{ @@ -72,8 +81,9 @@ func NewReporter(reportDir, filename string, logOpt *LoggerOption) (*Reporter, e if logOpt == nil { logOpt = defaultLoggerOpt() } + resultLogger := &lumberjack.Logger{ - Filename: filepath.Join(reportDir, filename), + Filename: logFilePath, MaxSize: logOpt.MaxSize, MaxBackups: logOpt.MaxBackups, MaxAge: logOpt.MaxAge, diff --git a/dbm-services/go.work b/dbm-services/go.work index 48d511c0e4..1d5c2dba71 100644 --- a/dbm-services/go.work +++ b/dbm-services/go.work @@ -1,12 +1,14 @@ go 1.19 use ( + riak/db-tools/dbactuator + riak/db-tools/riak-monitor bigdata/db-tools/dbactuator + common/db-config common/db-resource common/dbha/ha-module common/dbha/hadb-api common/go-pubpkg - common/db-config mysql/db-partition mysql/db-priv mysql/db-remote-service @@ -21,4 +23,5 @@ use ( redis/db-tools/dbactuator redis/db-tools/dbmon redis/redis-dts + common/celery-service ) diff --git a/dbm-services/go.work.sum b/dbm-services/go.work.sum index 7f38722bdd..19ee46992d 100644 --- a/dbm-services/go.work.sum +++ b/dbm-services/go.work.sum @@ -4,52 +4,79 @@ cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+ cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= cloud.google.com/go v0.105.0 h1:DNtEKRBAAzeS4KyIory52wWHuClNaXJ5x1F7xa4q+5Y= cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= +cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= +cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= cloud.google.com/go/accessapproval v1.5.0 h1:/nTivgnV/n1CaAeo+ekGexTYUsKEU9jUVkoY5359+3Q= cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= +cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= cloud.google.com/go/accesscontextmanager v1.4.0 h1:CFhNhU7pcD11cuDkQdrE6PQJgv0EXNKNv06jIzbLlCU= cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE= +cloud.google.com/go/accesscontextmanager v1.7.0/go.mod h1:CEGLewx8dwa33aDAZQujl7Dx+uYhS0eay198wB/VumQ= cloud.google.com/go/aiplatform v1.27.0 h1:DBi3Jk9XjCJ4pkkLM4NqKgj3ozUL1wq4l+d3/jTGXAI= cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg= +cloud.google.com/go/aiplatform v1.37.0/go.mod h1:IU2Cv29Lv9oCn/9LkFiiuKfwrRTq+QQMbW+hPCxJGZw= cloud.google.com/go/analytics v0.12.0 h1:NKw6PpQi6V1O+KsjuTd+bhip9d0REYu4NevC45vtGp8= cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= +cloud.google.com/go/analytics v0.19.0/go.mod h1:k8liqf5/HCnOUkbawNtrWWc+UAzyDlW89doe8TtoDsE= cloud.google.com/go/apigateway v1.4.0 h1:IIoXKR7FKrEAQhMTz5hK2wiDz2WNFHS7eVr/L1lE/rM= cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc= +cloud.google.com/go/apigateway v1.5.0/go.mod h1:GpnZR3Q4rR7LVu5951qfXPJCHquZt02jf7xQx7kpqN8= cloud.google.com/go/apigeeconnect v1.4.0 h1:AONoTYJviyv1vS4IkvWzq69gEVdvHx35wKXc+e6wjZQ= cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04= +cloud.google.com/go/apigeeconnect v1.5.0/go.mod h1:KFaCqvBRU6idyhSNyn3vlHXc8VMDJdRmwDF6JyFRqZ8= +cloud.google.com/go/apigeeregistry v0.6.0/go.mod h1:BFNzW7yQVLZ3yj0TKcwzb8n25CFBri51GVGOEUcgQsc= +cloud.google.com/go/apikeys v0.6.0/go.mod h1:kbpXu5upyiAlGkKrJgQl8A0rKNNJ7dQ377pdroRSSi8= cloud.google.com/go/appengine v1.5.0 h1:lmG+O5oaR9xNwaRBwE2XoMhwQHsHql5IoiGr1ptdDwU= cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak= +cloud.google.com/go/appengine v1.7.1/go.mod h1:IHLToyb/3fKutRysUlFO0BPt5j7RiQ45nrzEJmKTo6E= cloud.google.com/go/area120 v0.6.0 h1:TCMhwWEWhCn8d44/Zs7UCICTWje9j3HuV6nVGMjdpYw= cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= +cloud.google.com/go/area120 v0.7.1/go.mod h1:j84i4E1RboTWjKtZVWXPqvK5VHQFJRF2c1Nm69pWm9k= cloud.google.com/go/artifactregistry v1.9.0 h1:3d0LRAU1K6vfqCahhl9fx2oGHcq+s5gftdix4v8Ibrc= cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc= +cloud.google.com/go/artifactregistry v1.13.0/go.mod h1:uy/LNfoOIivepGhooAUpL1i30Hgee3Cu0l4VTWHUC08= cloud.google.com/go/asset v1.10.0 h1:aCrlaLGJWTODJX4G56ZYzJefITKEWNfbjjtHSzWpxW0= cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY= +cloud.google.com/go/asset v1.13.0/go.mod h1:WQAMyYek/b7NBpYq/K4KJWcRqzoalEsxz/t/dTk4THw= cloud.google.com/go/assuredworkloads v1.9.0 h1:hhIdCOowsT1GG5eMCIA0OwK6USRuYTou/1ZeNxCSRtA= cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= +cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= cloud.google.com/go/automl v1.8.0 h1:BMioyXSbg7d7xLibn47cs0elW6RT780IUWr42W8rp2Q= cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM= +cloud.google.com/go/automl v1.12.0/go.mod h1:tWDcHDp86aMIuHmyvjuKeeHEGq76lD7ZqfGLN6B0NuU= cloud.google.com/go/baremetalsolution v0.4.0 h1:g9KO6SkakcYPcc/XjAzeuUrEOXlYPnMpuiaywYaGrmQ= cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI= +cloud.google.com/go/baremetalsolution v0.5.0/go.mod h1:dXGxEkmR9BMwxhzBhV0AioD0ULBmuLZI8CdwalUxuss= cloud.google.com/go/batch v0.4.0 h1:1jvEBY55OH4Sd2FxEXQfxGExFWov1A/IaRe+Z5Z71Fw= cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE= +cloud.google.com/go/batch v0.7.0/go.mod h1:vLZN95s6teRUqRQ4s3RLDsH8PvboqBK+rn1oevL159g= cloud.google.com/go/beyondcorp v0.3.0 h1:w+4kThysgl0JiKshi2MKDCg2NZgOyqOI0wq2eBZyrzA= cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8= +cloud.google.com/go/beyondcorp v0.5.0/go.mod h1:uFqj9X+dSfrheVp7ssLTaRHd2EHqSL4QZmH4e8WXGGU= cloud.google.com/go/bigquery v1.44.0 h1:Wi4dITi+cf9VYp4VH2T9O41w0kCW0uQTELq2Z6tukN0= cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc= +cloud.google.com/go/bigquery v1.50.0/go.mod h1:YrleYEh2pSEbgTBZYMJ5SuSr0ML3ypjRB1zgf7pvQLU= cloud.google.com/go/billing v1.7.0 h1:Xkii76HWELHwBtkQVZvqmSo9GTr0O+tIbRNnMcGdlg4= cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y= +cloud.google.com/go/billing v1.13.0/go.mod h1:7kB2W9Xf98hP9Sr12KfECgfGclsH3CQR0R08tnRlRbc= cloud.google.com/go/binaryauthorization v1.4.0 h1:pL70vXWn9TitQYXBWTK2abHl2JHLwkFRjYw6VflRqEA= cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk= +cloud.google.com/go/binaryauthorization v1.5.0/go.mod h1:OSe4OU1nN/VswXKRBmciKpo9LulY41gch5c68htf3/Q= cloud.google.com/go/certificatemanager v1.4.0 h1:tzbR4UHBbgsewMWUD93JHi8EBi/gHBoSAcY1/sThFGk= cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590= +cloud.google.com/go/certificatemanager v1.6.0/go.mod h1:3Hh64rCKjRAX8dXgRAyOcY5vQ/fE1sh8o+Mdd6KPgY8= cloud.google.com/go/channel v1.9.0 h1:pNuUlZx0Jb0Ts9P312bmNMuH5IiFWIR4RUtLb70Ke5s= cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk= +cloud.google.com/go/channel v1.12.0/go.mod h1:VkxCGKASi4Cq7TbXxlaBezonAYpp1GCnKMY6tnMQnLU= cloud.google.com/go/cloudbuild v1.4.0 h1:TAAmCmAlOJ4uNBu6zwAjwhyl/7fLHHxIEazVhr3QBbQ= cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA= +cloud.google.com/go/cloudbuild v1.9.0/go.mod h1:qK1d7s4QlO0VwfYn5YuClDGg2hfmLZEb4wQGAbIgL1s= cloud.google.com/go/clouddms v1.4.0 h1:UhzHIlgFfMr6luVYVNydw/pl9/U5kgtjCMJHnSvoVws= cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk= +cloud.google.com/go/clouddms v1.5.0/go.mod h1:QSxQnhikCLUw13iAbffF2CZxAER3xDGNHjsTAkQJcQA= cloud.google.com/go/cloudtasks v1.8.0 h1:faUiUgXjW8yVZ7XMnKHKm1WE4OldPBUWWfIRN/3z1dc= cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI= +cloud.google.com/go/cloudtasks v1.10.0/go.mod h1:NDSoTLkZ3+vExFEWu2UJV1arUyzVDAiZtdWcsUyNwBs= cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= @@ -61,205 +88,302 @@ cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE= cloud.google.com/go/compute v1.14.0 h1:hfm2+FfxVmnRlh6LpB7cg1ZNU+5edAHmW679JePztk0= cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= +cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= +cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/contactcenterinsights v1.4.0 h1:tTQLI/ZvguUf9Hv+36BkG2+/PeC8Ol1q4pBW+tgCx0A= cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= +cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= cloud.google.com/go/container v1.7.0 h1:nbEK/59GyDRKKlo1SqpohY1TK8LmJ2XNcvS9Gyom2A0= cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo= +cloud.google.com/go/container v1.15.0/go.mod h1:ft+9S0WGjAyjDggg5S06DXj+fHJICWg8L7isCQe9pQA= cloud.google.com/go/containeranalysis v0.6.0 h1:2824iym832ljKdVpCBnpqm5K94YT/uHTVhNF+dRTXPI= cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= +cloud.google.com/go/containeranalysis v0.9.0/go.mod h1:orbOANbwk5Ejoom+s+DUCTTJ7IBdBQJDcSylAx/on9s= cloud.google.com/go/datacatalog v1.8.0 h1:6kZ4RIOW/uT7QWC5SfPfq/G8sYzr/v+UOmOAxy4Z1TE= cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM= +cloud.google.com/go/datacatalog v1.13.0/go.mod h1:E4Rj9a5ZtAxcQJlEBTLgMTphfP11/lNaAshpoBgemX8= cloud.google.com/go/dataflow v0.7.0 h1:CW3541Fm7KPTyZjJdnX6NtaGXYFn5XbFC5UcjgALKvU= cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= +cloud.google.com/go/dataflow v0.8.0/go.mod h1:Rcf5YgTKPtQyYz8bLYhFoIV/vP39eL7fWNcSOyFfLJE= cloud.google.com/go/dataform v0.5.0 h1:vLwowLF2ZB5J5gqiZCzv076lDI/Rd7zYQQFu5XO1PSg= cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0= +cloud.google.com/go/dataform v0.7.0/go.mod h1:7NulqnVozfHvWUBpMDfKMUESr+85aJsC/2O0o3jWPDE= cloud.google.com/go/datafusion v1.5.0 h1:j5m2hjWovTZDTQak4MJeXAR9yN7O+zMfULnjGw/OOLg= cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w= +cloud.google.com/go/datafusion v1.6.0/go.mod h1:WBsMF8F1RhSXvVM8rCV3AeyWVxcC2xY6vith3iw3S+8= cloud.google.com/go/datalabeling v0.6.0 h1:dp8jOF21n/7jwgo/uuA0RN8hvLcKO4q6s/yvwevs2ZM= cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= +cloud.google.com/go/datalabeling v0.7.0/go.mod h1:WPQb1y08RJbmpM3ww0CSUAGweL0SxByuW2E+FU+wXcM= cloud.google.com/go/dataplex v1.4.0 h1:cNxeA2DiWliQGi21kPRqnVeQ5xFhNoEjPRt1400Pm8Y= cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A= +cloud.google.com/go/dataplex v1.6.0/go.mod h1:bMsomC/aEJOSpHXdFKFGQ1b0TDPIeL28nJObeO1ppRs= cloud.google.com/go/dataproc v1.8.0 h1:gVOqNmElfa6n/ccG/QDlfurMWwrK3ezvy2b2eDoCmS0= cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI= +cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= cloud.google.com/go/dataqna v0.6.0 h1:gx9jr41ytcA3dXkbbd409euEaWtofCVXYBvJz3iYm18= cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= +cloud.google.com/go/dataqna v0.7.0/go.mod h1:Lx9OcIIeqCrw1a6KdO3/5KMP1wAmTc0slZWwP12Qq3c= cloud.google.com/go/datastore v1.10.0 h1:4siQRf4zTiAVt/oeH4GureGkApgb2vtPQAtOmhpqQwE= cloud.google.com/go/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM= +cloud.google.com/go/datastore v1.11.0/go.mod h1:TvGxBIHCS50u8jzG+AW/ppf87v1of8nwzFNgEZU1D3c= cloud.google.com/go/datastream v1.5.0 h1:PgIgbhedBtYBU6POGXFMn2uSl9vpqubc3ewTNdcU8Mk= cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4= +cloud.google.com/go/datastream v1.7.0/go.mod h1:uxVRMm2elUSPuh65IbZpzJNMbuzkcvu5CjMqVIUHrww= cloud.google.com/go/deploy v1.5.0 h1:kI6dxt8Ml0is/x7YZjLveTvR7YPzXAUD/8wQZ2nH5zA= cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s= +cloud.google.com/go/deploy v1.8.0/go.mod h1:z3myEJnA/2wnB4sgjqdMfgxCA0EqC3RBTNcVPs93mtQ= cloud.google.com/go/dialogflow v1.19.0 h1:HYHVOkoxQ9bSfNIelSZYNAtUi4CeSrCnROyOsbOqPq8= cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0= +cloud.google.com/go/dialogflow v1.32.0/go.mod h1:jG9TRJl8CKrDhMEcvfcfFkkpp8ZhgPz3sBGmAUYJ2qE= cloud.google.com/go/dlp v1.7.0 h1:9I4BYeJSVKoSKgjr70fLdRDumqcUeVmHV4fd5f9LR6Y= cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q= +cloud.google.com/go/dlp v1.9.0/go.mod h1:qdgmqgTyReTz5/YNSSuueR8pl7hO0o9bQ39ZhtgkWp4= cloud.google.com/go/documentai v1.10.0 h1:jfq09Fdjtnpnmt/MLyf6A3DM3ynb8B2na0K+vSXvpFM= cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4= +cloud.google.com/go/documentai v1.18.0/go.mod h1:F6CK6iUH8J81FehpskRmhLq/3VlwQvb7TvwOceQ2tbs= cloud.google.com/go/domains v0.7.0 h1:pu3JIgC1rswIqi5romW0JgNO6CTUydLYX8zyjiAvO1c= cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= +cloud.google.com/go/domains v0.8.0/go.mod h1:M9i3MMDzGFXsydri9/vW+EWz9sWb4I6WyHqdlAk0idE= cloud.google.com/go/edgecontainer v0.2.0 h1:hd6J2n5dBBRuAqnNUEsKWrp6XNPKsaxwwIyzOPZTokk= cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= +cloud.google.com/go/edgecontainer v1.0.0/go.mod h1:cttArqZpBB2q58W/upSG++ooo6EsblxDIolxa3jSjbY= cloud.google.com/go/errorreporting v0.3.0 h1:kj1XEWMu8P0qlLhm3FwcaFsUvXChV/OraZwA70trRR0= cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= cloud.google.com/go/essentialcontacts v1.4.0 h1:b6csrQXCHKQmfo9h3dG/pHyoEh+fQG1Yg78a53LAviY= cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8= +cloud.google.com/go/essentialcontacts v1.5.0/go.mod h1:ay29Z4zODTuwliK7SnX8E86aUF2CTzdNtvv42niCX0M= cloud.google.com/go/eventarc v1.8.0 h1:AgCqrmMMIcel5WWKkzz5EkCUKC3Rl5LNMMYsS+LvsI0= cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw= +cloud.google.com/go/eventarc v1.11.0/go.mod h1:PyUjsUKPWoRBCHeOxZd/lbOOjahV41icXyUY5kSTvVY= cloud.google.com/go/filestore v1.4.0 h1:yjKOpzvqtDmL5AXbKttLc8j0hL20kuC1qPdy5HPcxp0= cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI= +cloud.google.com/go/filestore v1.6.0/go.mod h1:di5unNuss/qfZTw2U9nhFqo8/ZDSc466dre85Kydllg= cloud.google.com/go/firestore v1.9.0 h1:IBlRyxgGySXu5VuW0RgGFlTtLukSnNkpDiEOMkQkmpA= cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= cloud.google.com/go/functions v1.9.0 h1:35tgv1fQOtvKqH/uxJMzX3w6usneJ0zXpsFr9KAVhNE= cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08= +cloud.google.com/go/functions v1.13.0/go.mod h1:EU4O007sQm6Ef/PwRsI8N2umygGqPBS/IZQKBQBcJ3c= cloud.google.com/go/gaming v1.8.0 h1:97OAEQtDazAJD7yh/kvQdSCQuTKdR0O+qWAJBZJ4xiA= cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM= +cloud.google.com/go/gaming v1.9.0/go.mod h1:Fc7kEmCObylSWLO334NcO+O9QMDyz+TKC4v1D7X+Bc0= cloud.google.com/go/gkebackup v0.3.0 h1:4K+jiv4ocqt1niN8q5Imd8imRoXBHTrdnJVt/uFFxF4= cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo= +cloud.google.com/go/gkebackup v0.4.0/go.mod h1:byAyBGUwYGEEww7xsbnUTBHIYcOPy/PgUWUtOeRm9Vg= cloud.google.com/go/gkeconnect v0.6.0 h1:zAcvDa04tTnGdu6TEZewaLN2tdMtUOJJ7fEceULjguA= cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= +cloud.google.com/go/gkeconnect v0.7.0/go.mod h1:SNfmVqPkaEi3bF/B3CNZOAYPYdg7sU+obZ+QTky2Myw= cloud.google.com/go/gkehub v0.10.0 h1:JTcTaYQRGsVm+qkah7WzHb6e9sf1C0laYdRPn9aN+vg= cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= +cloud.google.com/go/gkehub v0.12.0/go.mod h1:djiIwwzTTBrF5NaXCGv3mf7klpEMcST17VBTVVDcuaw= cloud.google.com/go/gkemulticloud v0.4.0 h1:8F1NhJj8ucNj7lK51UZMtAjSWTgP1zO18XF6vkfiPPU= cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI= +cloud.google.com/go/gkemulticloud v0.5.0/go.mod h1:W0JDkiyi3Tqh0TJr//y19wyb1yf8llHVto2Htf2Ja3Y= cloud.google.com/go/grafeas v0.2.0 h1:CYjC+xzdPvbV65gi6Dr4YowKcmLo045pm18L0DhdELM= cloud.google.com/go/gsuiteaddons v1.4.0 h1:TGT2oGmO5q3VH6SjcrlgPUWI0njhYv4kywLm6jag0to= cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o= +cloud.google.com/go/gsuiteaddons v1.5.0/go.mod h1:TFCClYLd64Eaa12sFVmUyG62tk4mdIsI7pAnSXRkcFo= cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= +cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg= cloud.google.com/go/iam v0.8.0 h1:E2osAkZzxI/+8pZcxVLcDtAQx/u+hZXVryUaYQ5O0Kk= cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= +cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= cloud.google.com/go/iap v1.5.0 h1:BGEXovwejOCt1zDk8hXq0bOhhRu9haXKWXXXp2B4wBM= cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= +cloud.google.com/go/iap v1.7.1/go.mod h1:WapEwPc7ZxGt2jFGB/C/bm+hP0Y6NXzOYGjpPnmMS74= cloud.google.com/go/ids v1.2.0 h1:LncHK4HHucb5Du310X8XH9/ICtMwZ2PCfK0ScjWiJoY= cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY= +cloud.google.com/go/ids v1.3.0/go.mod h1:JBdTYwANikFKaDP6LtW5JAi4gubs57SVNQjemdt6xV4= cloud.google.com/go/iot v1.4.0 h1:Y9+oZT9jD4GUZzORXTU45XsnQrhxmDT+TFbPil6pRVQ= cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g= +cloud.google.com/go/iot v1.6.0/go.mod h1:IqdAsmE2cTYYNO1Fvjfzo9po179rAtJeVGUvkLN3rLE= cloud.google.com/go/kms v1.6.0 h1:OWRZzrPmOZUzurjI2FBGtgY2mB1WaJkqhw6oIwSj0Yg= cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= +cloud.google.com/go/kms v1.10.1/go.mod h1:rIWk/TryCkR59GMC3YtHtXeLzd634lBbKenvyySAyYI= cloud.google.com/go/language v1.8.0 h1:3Wa+IUMamL4JH3Zd3cDZUHpwyqplTACt6UZKRD2eCL4= cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8= +cloud.google.com/go/language v1.9.0/go.mod h1:Ns15WooPM5Ad/5no/0n81yUetis74g3zrbeJBE+ptUY= cloud.google.com/go/lifesciences v0.6.0 h1:tIqhivE2LMVYkX0BLgG7xL64oNpDaFFI7teunglt1tI= cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= +cloud.google.com/go/lifesciences v0.8.0/go.mod h1:lFxiEOMqII6XggGbOnKiyZ7IBwoIqA84ClvoezaA/bo= cloud.google.com/go/logging v1.6.1 h1:ZBsZK+JG+oCDT+vaxwqF2egKNRjz8soXiS6Xv79benI= cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw= +cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs= cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= +cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= cloud.google.com/go/managedidentities v1.4.0 h1:3Kdajn6X25yWQFhFCErmKSYTSvkEd3chJROny//F1A0= cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= +cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= cloud.google.com/go/maps v0.1.0 h1:kLReRbclTgJefw2fcCbdLPLhPj0U6UUWN10ldG8sdOU= cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI= +cloud.google.com/go/maps v0.7.0/go.mod h1:3GnvVl3cqeSvgMcpRlQidXsPYuDGQ8naBis7MVzpXsY= cloud.google.com/go/mediatranslation v0.6.0 h1:qAJzpxmEX+SeND10Y/4868L5wfZpo4Y3BIEnIieP4dk= cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= +cloud.google.com/go/mediatranslation v0.7.0/go.mod h1:LCnB/gZr90ONOIQLgSXagp8XUW1ODs2UmUMvcgMfI2I= cloud.google.com/go/memcache v1.7.0 h1:yLxUzJkZVSH2kPaHut7k+7sbIBFpvSh1LW9qjM2JDjA= cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY= +cloud.google.com/go/memcache v1.9.0/go.mod h1:8oEyzXCu+zo9RzlEaEjHl4KkgjlNDaXbCQeQWlzNFJM= cloud.google.com/go/metastore v1.8.0 h1:3KcShzqWdqxrDEXIBWpYJpOOrgpDj+HlBi07Grot49Y= cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI= +cloud.google.com/go/metastore v1.10.0/go.mod h1:fPEnH3g4JJAk+gMRnrAnoqyv2lpUCqJPWOodSaf45Eo= cloud.google.com/go/monitoring v1.8.0 h1:c9riaGSPQ4dUKWB+M1Fl0N+iLxstMbCktdEwYSPGDvA= cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= +cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw= cloud.google.com/go/networkconnectivity v1.7.0 h1:BVdIKaI68bihnXGdCVL89Jsg9kq2kg+II30fjVqo62E= cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8= +cloud.google.com/go/networkconnectivity v1.11.0/go.mod h1:iWmDD4QF16VCDLXUqvyspJjIEtBR/4zq5hwnY2X3scM= cloud.google.com/go/networkmanagement v1.5.0 h1:mDHA3CDW00imTvC5RW6aMGsD1bH+FtKwZm/52BxaiMg= cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4= +cloud.google.com/go/networkmanagement v1.6.0/go.mod h1:5pKPqyXjB/sgtvB5xqOemumoQNB7y95Q7S+4rjSOPYY= cloud.google.com/go/networksecurity v0.6.0 h1:qDEX/3sipg9dS5JYsAY+YvgTjPR63cozzAWop8oZS94= cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= +cloud.google.com/go/networksecurity v0.8.0/go.mod h1:B78DkqsxFG5zRSVuwYFRZ9Xz8IcQ5iECsNrPn74hKHU= cloud.google.com/go/notebooks v1.5.0 h1:AC8RPjNvel3ExgXjO1YOAz+teg9+j+89TNxa7pIZfww= cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0= +cloud.google.com/go/notebooks v1.8.0/go.mod h1:Lq6dYKOYOWUCTvw5t2q1gp1lAp0zxAxRycayS0iJcqQ= cloud.google.com/go/optimization v1.2.0 h1:7PxOq9VTT7TMib/6dMoWpMvWS2E4dJEvtYzjvBreaec= cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs= +cloud.google.com/go/optimization v1.3.1/go.mod h1:IvUSefKiwd1a5p0RgHDbWCIbDFgKuEdB+fPPuP0IDLI= cloud.google.com/go/orchestration v1.4.0 h1:39d6tqvNjd/wsSub1Bn4cEmrYcet5Ur6xpaN+SxOxtY= cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk= +cloud.google.com/go/orchestration v1.6.0/go.mod h1:M62Bevp7pkxStDfFfTuCOaXgaaqRAga1yKyoMtEoWPQ= cloud.google.com/go/orgpolicy v1.5.0 h1:erF5PHqDZb6FeFrUHiYj2JK2BMhsk8CyAg4V4amJ3rE= cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc= +cloud.google.com/go/orgpolicy v1.10.0/go.mod h1:w1fo8b7rRqlXlIJbVhOMPrwVljyuW5mqssvBtU18ONc= cloud.google.com/go/osconfig v1.10.0 h1:NO0RouqCOM7M2S85Eal6urMSSipWwHU8evzwS+siqUI= cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw= +cloud.google.com/go/osconfig v1.11.0/go.mod h1:aDICxrur2ogRd9zY5ytBLV89KEgT2MKB2L/n6x1ooPw= cloud.google.com/go/oslogin v1.7.0 h1:pKGDPfeZHDybtw48WsnVLjoIPMi9Kw62kUE5TXCLCN4= cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo= +cloud.google.com/go/oslogin v1.9.0/go.mod h1:HNavntnH8nzrn8JCTT5fj18FuJLFJc4NaZJtBnQtKFs= cloud.google.com/go/phishingprotection v0.6.0 h1:OrwHLSRSZyaiOt3tnY33dsKSedxbMzsXvqB21okItNQ= cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= +cloud.google.com/go/phishingprotection v0.7.0/go.mod h1:8qJI4QKHoda/sb/7/YmMQ2omRLSLYSu9bU0EKCNI+Lk= cloud.google.com/go/policytroubleshooter v1.4.0 h1:NQklJuOUoz1BPP+Epjw81COx7IISWslkZubz/1i0UN8= cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE= +cloud.google.com/go/policytroubleshooter v1.6.0/go.mod h1:zYqaPTsmfvpjm5ULxAyD/lINQxJ0DDsnWOP/GZ7xzBc= cloud.google.com/go/privatecatalog v0.6.0 h1:Vz86uiHCtNGm1DeC32HeG2VXmOq5JRYA3VRPf8ZEcSg= cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= +cloud.google.com/go/privatecatalog v0.8.0/go.mod h1:nQ6pfaegeDAq/Q5lrfCQzQLhubPiZhSaNhIgfJlnIXs= cloud.google.com/go/pubsub v1.27.1 h1:q+J/Nfr6Qx4RQeu3rJcnN48SNC0qzlYzSeqkPq93VHs= cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0= +cloud.google.com/go/pubsub v1.30.0/go.mod h1:qWi1OPS0B+b5L+Sg6Gmc9zD1Y+HaM0MdUr7LsupY1P4= cloud.google.com/go/pubsublite v1.5.0 h1:iqrD8vp3giTb7hI1q4TQQGj77cj8zzgmMPsTZtLnprM= cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg= +cloud.google.com/go/pubsublite v1.7.0/go.mod h1:8hVMwRXfDfvGm3fahVbtDbiLePT3gpoiJYJY+vxWxVM= cloud.google.com/go/recaptchaenterprise v1.3.1 h1:u6EznTGzIdsyOsvm+Xkw0aSuKFXQlyjGE9a4exk6iNQ= cloud.google.com/go/recaptchaenterprise/v2 v2.5.0 h1:UqzFfb/WvhwXGDF1eQtdHLrmni+iByZXY4h3w9Kdyv8= cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U= +cloud.google.com/go/recaptchaenterprise/v2 v2.7.0/go.mod h1:19wVj/fs5RtYtynAPJdDTb69oW0vNHYDBTbB4NvMD9c= cloud.google.com/go/recommendationengine v0.6.0 h1:6w+WxPf2LmUEqX0YyvfCoYb8aBYOcbIV25Vg6R0FLGw= cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= +cloud.google.com/go/recommendationengine v0.7.0/go.mod h1:1reUcE3GIu6MeBz/h5xZJqNLuuVjNg1lmWMPyjatzac= cloud.google.com/go/recommender v1.8.0 h1:9kMZQGeYfcOD/RtZfcNKGKtoex3DdoB4zRgYU/WaIwE= cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70= +cloud.google.com/go/recommender v1.9.0/go.mod h1:PnSsnZY7q+VL1uax2JWkt/UegHssxjUVVCrX52CuEmQ= cloud.google.com/go/redis v1.10.0 h1:/zTwwBKIAD2DEWTrXZp8WD9yD/gntReF/HkPssVYd0U= cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM= +cloud.google.com/go/redis v1.11.0/go.mod h1:/X6eicana+BWcUda5PpwZC48o37SiFVTFSs0fWAJ7uQ= cloud.google.com/go/resourcemanager v1.4.0 h1:NDao6CHMwEZIaNsdWy+tuvHaavNeGP06o1tgrR0kLvU= cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0= +cloud.google.com/go/resourcemanager v1.7.0/go.mod h1:HlD3m6+bwhzj9XCouqmeiGuni95NTrExfhoSrkC/3EI= cloud.google.com/go/resourcesettings v1.4.0 h1:eTzOwB13WrfF0kuzG2ZXCfB3TLunSHBur4s+HFU6uSM= cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg= +cloud.google.com/go/resourcesettings v1.5.0/go.mod h1:+xJF7QSG6undsQDfsCJyqWXyBwUoJLhetkRMDRnIoXA= cloud.google.com/go/retail v1.11.0 h1:N9fa//ecFUOEPsW/6mJHfcapPV0wBSwIUwpVZB7MQ3o= cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y= +cloud.google.com/go/retail v1.12.0/go.mod h1:UMkelN/0Z8XvKymXFbD4EhFJlYKRx1FGhQkVPU5kF14= cloud.google.com/go/run v0.3.0 h1:AWPuzU7Xtaj3Jf+QarDWIs6AJ5hM1VFQ+F6Q+VZ6OT4= cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo= +cloud.google.com/go/run v0.9.0/go.mod h1:Wwu+/vvg8Y+JUApMwEDfVfhetv30hCG4ZwDR/IXl2Qg= cloud.google.com/go/scheduler v1.7.0 h1:K/mxOewgHGeKuATUJNGylT75Mhtjmx1TOkKukATqMT8= cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44= +cloud.google.com/go/scheduler v1.9.0/go.mod h1:yexg5t+KSmqu+njTIh3b7oYPheFtBWGcbVUYF1GGMIc= cloud.google.com/go/secretmanager v1.9.0 h1:xE6uXljAC1kCR8iadt9+/blg1fvSbmenlsDN4fT9gqw= cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4= +cloud.google.com/go/secretmanager v1.10.0/go.mod h1:MfnrdvKMPNra9aZtQFvBcvRU54hbPD8/HayQdlUgJpU= cloud.google.com/go/security v1.10.0 h1:KSKzzJMyUoMRQzcz7azIgqAUqxo7rmQ5rYvimMhikqg= cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA= +cloud.google.com/go/security v1.13.0/go.mod h1:Q1Nvxl1PAgmeW0y3HTt54JYIvUdtcpYKVfIB8AOMZ+0= cloud.google.com/go/securitycenter v1.16.0 h1:QTVtk/Reqnx2bVIZtJKm1+mpfmwRwymmNvlaFez7fQY= cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk= +cloud.google.com/go/securitycenter v1.19.0/go.mod h1:LVLmSg8ZkkyaNy4u7HCIshAngSQ8EcIRREP3xBnyfag= cloud.google.com/go/servicecontrol v1.5.0 h1:ImIzbOu6y4jL6ob65I++QzvqgFaoAKgHOG+RU9/c4y8= cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s= +cloud.google.com/go/servicecontrol v1.11.1/go.mod h1:aSnNNlwEFBY+PWGQ2DoM0JJ/QUXqV5/ZD9DOLB7SnUk= cloud.google.com/go/servicedirectory v1.7.0 h1:f7M8IMcVzO3T425AqlZbP3yLzeipsBHtRza8vVFYMhQ= cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U= +cloud.google.com/go/servicedirectory v1.9.0/go.mod h1:29je5JjiygNYlmsGz8k6o+OZ8vd4f//bQLtvzkPPT/s= cloud.google.com/go/servicemanagement v1.5.0 h1:TpkCO5M7dhKSy1bKUD9o/sSEW/U1Gtx7opA1fsiMx0c= cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo= +cloud.google.com/go/servicemanagement v1.8.0/go.mod h1:MSS2TDlIEQD/fzsSGfCdJItQveu9NXnUniTrq/L8LK4= cloud.google.com/go/serviceusage v1.4.0 h1:b0EwJxPJLpavSljMQh0RcdHsUrr5DQ+Nelt/3BAs5ro= cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU= +cloud.google.com/go/serviceusage v1.6.0/go.mod h1:R5wwQcbOWsyuOfbP9tGdAnCAc6B9DRwPG1xtWMDeuPA= cloud.google.com/go/shell v1.4.0 h1:b1LFhFBgKsG252inyhtmsUUZwchqSz3WTvAIf3JFo4g= cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw= +cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+qE2f9A= cloud.google.com/go/spanner v1.41.0 h1:NvdTpRwf7DTegbfFdPjAWyD7bOVu0VeMqcvR9aCQCAc= cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= +cloud.google.com/go/spanner v1.45.0/go.mod h1:FIws5LowYz8YAE1J8fOS7DJup8ff7xJeetWEo5REA2M= cloud.google.com/go/speech v1.9.0 h1:yK0ocnFH4Wsf0cMdUyndJQ/hPv02oTJOxzi6AgpBy4s= cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco= +cloud.google.com/go/speech v1.15.0/go.mod h1:y6oH7GhqCaZANH7+Oe0BhgIogsNInLlz542tg3VqeYI= cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= cloud.google.com/go/storage v1.27.0 h1:YOO045NZI9RKfCj1c5A/ZtuuENUc8OAW+gHdGnDgyMQ= cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= +cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= +cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= cloud.google.com/go/storagetransfer v1.6.0 h1:fUe3OydbbvHcAYp07xY+2UpH4AermGbmnm7qdEj3tGE= cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= +cloud.google.com/go/storagetransfer v1.8.0/go.mod h1:JpegsHHU1eXg7lMHkvf+KE5XDJ7EQu0GwNJbbVGanEw= cloud.google.com/go/talent v1.4.0 h1:MrekAGxLqAeAol4Sc0allOVqUGO8j+Iim8NMvpiD7tM= cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA= +cloud.google.com/go/talent v1.5.0/go.mod h1:G+ODMj9bsasAEJkQSzO2uHQWXHHXUomArjWQQYkqK6c= cloud.google.com/go/texttospeech v1.5.0 h1:ccPiHgTewxgyAeCWgQWvZvrLmbfQSFABTMAfrSPLPyY= cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4= +cloud.google.com/go/texttospeech v1.6.0/go.mod h1:YmwmFT8pj1aBblQOI3TfKmwibnsfvhIBzPXcW4EBovc= cloud.google.com/go/tpu v1.4.0 h1:ztIdKoma1Xob2qm6QwNh4Xi9/e7N3IfvtwG5AcNsj1g= cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg= +cloud.google.com/go/tpu v1.5.0/go.mod h1:8zVo1rYDFuW2l4yZVY0R0fb/v44xLh3llq7RuV61fPM= cloud.google.com/go/trace v1.4.0 h1:qO9eLn2esajC9sxpqp1YKX37nXC3L4BfGnPS0Cx9dYo= cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y= +cloud.google.com/go/trace v1.9.0/go.mod h1:lOQqpE5IaWY0Ixg7/r2SjixMuc6lfTFeO4QGM4dQWOk= cloud.google.com/go/translate v1.4.0 h1:AOYOH3MspzJ/bH1YXzB+xTE8fMpn3mwhLjugwGXvMPI= cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg= +cloud.google.com/go/translate v1.7.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= cloud.google.com/go/video v1.9.0 h1:ttlvO4J5c1VGq6FkHqWPD/aH6PfdxujHt+muTJlW1Zk= cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw= +cloud.google.com/go/video v1.15.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= cloud.google.com/go/videointelligence v1.9.0 h1:RPFgVVXbI2b5vnrciZjtsUgpNKVtHO/WIyXUhEfuMhA= cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU= +cloud.google.com/go/videointelligence v1.10.0/go.mod h1:LHZngX1liVtUhZvi2uNS0VQuOzNi2TkY1OakiuoUOjU= cloud.google.com/go/vision v1.2.0 h1:/CsSTkbmO9HC8iQpxbK8ATms3OQaX3YQUeTMGCxlaK4= cloud.google.com/go/vision/v2 v2.5.0 h1:TQHxRqvLMi19azwm3qYuDbEzZWmiKJNTpGbkNsfRCik= cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E= +cloud.google.com/go/vision/v2 v2.7.0/go.mod h1:H89VysHy21avemp6xcf9b9JvZHVehWbET0uT/bcuY/0= cloud.google.com/go/vmmigration v1.3.0 h1:A2Tl2ZmwMRpvEmhV2ibISY85fmQR+Y5w9a0PlRz5P3s= cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g= +cloud.google.com/go/vmmigration v1.6.0/go.mod h1:bopQ/g4z+8qXzichC7GW1w2MjbErL54rk3/C843CjfY= cloud.google.com/go/vmwareengine v0.1.0 h1:JMPZaOT/gIUxVlTqSl/QQ32Y2k+r0stNeM1NSqhVP9o= cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208= +cloud.google.com/go/vmwareengine v0.3.0/go.mod h1:wvoyMvNWdIzxMYSpH/R7y2h5h3WFkx6d+1TIsP39WGY= cloud.google.com/go/vpcaccess v1.5.0 h1:woHXXtnW8b9gLFdWO9HLPalAddBQ9V4LT+1vjKwR3W8= cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8= +cloud.google.com/go/vpcaccess v1.6.0/go.mod h1:wX2ILaNhe7TlVa4vC5xce1bCnqE3AeH27RV31lnmZes= cloud.google.com/go/webrisk v1.7.0 h1:ypSnpGlJnZSXbN9a13PDmAYvVekBLnGKxQ3Q9SMwnYY= cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A= +cloud.google.com/go/webrisk v1.8.0/go.mod h1:oJPDuamzHXgUc+b8SiHRcVInZQuybnvEW72PqTc7sSg= cloud.google.com/go/websecurityscanner v1.4.0 h1:y7yIFg/h/mO+5Y5aCOtVAnpGUOgqCH5rXQ2Oc8Oq2+g= cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ= +cloud.google.com/go/websecurityscanner v1.5.0/go.mod h1:Y6xdCPy81yi0SQnDY1xdNTNpfY1oAgXUlcfN3B3eSng= cloud.google.com/go/workflows v1.9.0 h1:7Chpin9p50NTU8Tb7qk+I11U/IwVXmDhEoSsdccvInE= cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= +cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY= gioui.org v0.0.0-20210308172011-57750fc8a0a6 h1:K72hopUosKG3ntOPNG4OzzbuhxGuVf06fa2la1/H/Ho= github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8 h1:V8krnnfGj4pV65YLUm3C0/8bl7V5Nry2Pwvy3ru/wLc= @@ -293,6 +417,8 @@ github.com/alexflint/go-filemutex v1.1.0 h1:IAWuUuRYL2hETx5b8vCgwnD+xSdlsTQY6s2J github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo= github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg= github.com/apache/arrow/go/arrow v0.0.0-20211013220434-5962184e7a30 h1:HGREIyk0QRPt70R69Gm1JFHDgoiyYpCyuGE8E9k/nf0= +github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3 h1:ZSTrOEhiM5J5RFxEaFvMZVEAM1KvT1YzbEOwB2EAGjA= +github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 h1:G1bPvciwNyF7IUmKXNt9Ak3m6u9DE1rF+RmtIkBpVdA= github.com/armon/go-metrics v0.4.0 h1:yCQqn7dwca4ITXb+CbubHmedzaQYHhNhrEXLYUeEe8Q= @@ -334,7 +460,9 @@ github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXe github.com/bytedance/sonic v1.8.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo= github.com/census-instrumentation/opencensus-proto v0.3.0 h1:t/LhUZLVitR1Ow2YOnduCsavhwFUklBMoGVYUCqmCqk= +github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054 h1:uH66TXeswKn5PW5zdZ39xEwfS9an067BirqA+P4QaLI= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/checkpoint-restore/go-criu/v4 v4.1.0 h1:WW2B2uxx9KWF6bGlHqhm8Okiafwwx7Y2kcpn8lCpjgo= github.com/checkpoint-restore/go-criu/v5 v5.3.0 h1:wpFFOoomK3389ue2lAb0Boag6XPht5QYpipxmSNL4d8= github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= @@ -351,7 +479,9 @@ github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 h1:F1EaeKL/ta07PY/k9Os/UFtwERei2/XzGemhpGnBKNg= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 h1:hzAQntlaYRkVSFEfj9OTWlVV1H155FMD8BTKktLv0QI= +github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490 h1:KwaoQzs/WeUxxJqiJsZ4euOly1Az/IgZXXSxlD/UBNk= +github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/cockroach-go/v2 v2.1.1 h1:3XzfSMuUT0wBe1a3o5C0eOTcArhmmFAg2Jzh/7hhKqo= github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5 h1:xD/lrqdvwsc+O2bjSSi3YqY73Ke3LAiSCx49aCesA0E= @@ -411,7 +541,9 @@ github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1 h1:xvqufLtNVwAhN8NMyWklVgxnWohi+wtMGQMhtxexlm0= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/go-control-plane v0.11.0/go.mod h1:VnHyVMpzcLvCFt9yUz1UnCwHLhwx1WguiVDV7pTG/tI= github.com/envoyproxy/protoc-gen-validate v0.6.2 h1:JiO+kJTpmYGjEodY7O1Zk8oZcNz1+f30UtwtXoFUPzE= +github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= github.com/etcd-io/gofail v0.0.0-20180808172546-51ce9a71510a h1:QNEenQIsGDEEfFNSnN+h6hE1OwnHqTg7Dl9gEk1Cko4= github.com/evanphx/json-patch v4.11.0+incompatible h1:glyUF9yIYtMHzn8xaKw5rMhdWcwsYV8dZHIq5567/xs= github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= @@ -420,6 +552,7 @@ github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8S github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= github.com/form3tech-oss/jwt-go v3.2.5+incompatible h1:/l4kBbb4/vGSsdtB5nUe8L7B9mImVMaBPw9L/0TBHU8= github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= +github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsouza/fake-gcs-server v1.17.0 h1:OeH75kBZcZa3ZE+zz/mFdJ2btt9FgqfjI7gIh9+5fvk= github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa h1:RDBNVkRviHZtvDvId8XSGPu3rmpmSe+wKRcEWNgsfWU= github.com/gabriel-vasile/mimetype v1.4.0 h1:Cn9dkdYsMIu56tGho+fqzh7XmvY2YyGU0FnbhiOsEro= @@ -484,6 +617,9 @@ github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPh github.com/gogo/googleapis v1.4.0 h1:zgVt4UpGxcqVOw97aRGxT4svlcmdK35fynLNctY32zI= github.com/golang-jwt/jwt/v4 v4.1.0 h1:XUgk2Ex5veyVFVeLm0xhusUTQybEbexJXrvPNOKkSY0= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= +github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= +github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7 h1:2hRPrmiwPrp3fQX967rNJIhQPtiGXdlQWAxKbKw3VHA= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= @@ -494,13 +630,16 @@ github.com/google/go-containerregistry v0.5.1 h1:/+mFTs4AlwsJ/mJe8NDtKb7BxLtbZFp github.com/google/go-github/v39 v39.2.0 h1:rNNM311XtPOz5rDdsJXAp2o8F67X9FnROXTvto3aSnQ= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ= +github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= +github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= github.com/googleapis/enterprise-certificate-proxy v0.2.1 h1:RY7tHKZcRlk788d5WSo/e83gOyyy742E8GSs771ySpg= github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= @@ -508,6 +647,8 @@ github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqE github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= github.com/googleapis/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ= github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= +github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= +github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= github.com/googleapis/go-type-adapters v1.0.0 h1:9XdMn+d/G57qq1s8dNc5IesGCXHf6V2HZ2JwRxfA2tA= github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8 h1:tlyzajkF3030q6M8SvmJSemC9DTHL/xaMa18b65+JM4= @@ -524,6 +665,7 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4 github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= github.com/hashicorp/consul/api v1.18.0 h1:R7PPNzTCeN6VuQNDwwhZWJvzCtGSrNpJqfb22h3yH9g= github.com/hashicorp/consul/api v1.18.0/go.mod h1:owRRGJ9M5xReDC5nfT8FTJrNAPbT4NM6p/k+d03q2v4= +github.com/hashicorp/consul/api v1.20.0/go.mod h1:nR64eD44KQ59Of/ECwt2vUmIK2DKsDzAwTmwmLl8Wpo= github.com/hashicorp/consul/sdk v0.13.0 h1:lce3nFlpv8humJL8rNrrGHYSKc3q+Kxfeg3Ii1m6ZWU= github.com/hashicorp/consul/sdk v0.13.0/go.mod h1:0hs/l5fOVhJy/VdcoaNqUSi2AUs95eF5WKtv+EYIQqE= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= @@ -594,6 +736,7 @@ github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e h1:+lIPJOWl+jSiJOc70QXJ07+2eg2Jy2EC7Mi11BWujeM= github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/knz/go-libedit v1.10.1 h1:0pHpWtx9vcvC0xGZqEQlQdfSQs7WRlAjuPvk3fOZDCo= github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI= @@ -612,7 +755,6 @@ github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc github.com/mattn/go-ieproxy v0.0.1 h1:qiyop7gCflfhwCzGyeT0gro3sF9AIg9HU98JORTkqfI= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.2 h1:UnlwIPBGaTZfPQ6T1IGzPI0EkYAQmT9fAEJ/poFC63o= github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0= github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= @@ -707,12 +849,15 @@ github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFo github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1 h1:ZFfeKAhIQiiOrQaI3/znw0gOmYpO28Tcu1YaqMa/jtQ= github.com/sagikazarmark/crypt v0.9.0 h1:fipzMFW34hFUEc4D7fsLQFtE7yElkpgyS2zruedRdZk= github.com/sagikazarmark/crypt v0.9.0/go.mod h1:RnH7sEhxfdnPm1z+XMgSLjWTEIjyK4z2dw6+4vHTMuo= +github.com/sagikazarmark/crypt v0.10.0/go.mod h1:gwTNHQVoOS3xp9Xvz5LLR+1AauC5M6880z5NWzdhOyQ= github.com/sanity-io/litter v1.2.0 h1:DGJO0bxH/+C2EukzOSBmAlxmkhVMGqzvcx/rvySYw9M= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/sclevine/agouti v3.0.0+incompatible h1:8IBJS6PWz3uTlMP3YBIR5f+KAldcGuOeFkFbUWfBgK4= github.com/sclevine/spec v1.2.0 h1:1Jwdf9jSfDl9NVmt8ndHqbTZ7XCCPbh1jI3hkDBHVYA= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921 h1:58EBmR2dMNL2n/FnbQewK3D14nXr0V9CObDSvMJLq+Y= +github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= +github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371 h1:SWV2fHctRpRrp49VXJ6UZja7gU9QLHwRpIPBN89SKEo= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/vfsgen v0.0.0-20181020040650-a97a25d856ca h1:3fECS8atRjByijiI8yYiuwLwQ2ZxXobW7ua/8GRB3pI= @@ -744,6 +889,9 @@ github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5 h1:+UB2BJA852UkGH42H+Oee69djmxS3ANzl2b/JtT1YiA= github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA= +github.com/vmihailenco/msgpack v3.3.3+incompatible h1:wapg9xDUZDzGCNFlwc5SqI1rvcciqcxEHac4CYj89xI= +github.com/vmihailenco/msgpack/v4 v4.3.12 h1:07s4sz9IReOgdikxLTKNbBdqDMLsjPKXwvCazn8G65U= +github.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY= github.com/willf/bitset v1.1.11 h1:N7Z7E9UvjW+sGsEl7k/SJrvY2reP1A07MrGuCjIOjRE= github.com/xanzy/go-gitlab v0.15.0 h1:rWtwKTgEnXyNUGrOArN7yyc3THRkpYcKXIXia9abywQ= github.com/xdg-go/scram v1.0.2 h1:akYIkZ28e6A96dkWNJQu3nmCzH3YfwMPQExUYDaRv7w= @@ -758,18 +906,23 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= +github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b h1:FosyBZYxY34Wul7O/MSKey3txpPYyCqVO5ZyceuQJEI= github.com/zenazn/goji v0.9.0 h1:RSQQAbXGArQ0dIDEq+PI6WqN6if+5KHu6x2Cx/GXLTQ= gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b h1:7gd+rd8P3bqcn/96gOZa3F5dpJr/vEiDQYlNb/y2uNs= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489 h1:1JFLBqwIgdyHN1ZtgjTBwO+blA6gVOmZurpiMEsETKo= go.etcd.io/etcd/api/v3 v3.5.6 h1:Cy2qx3npLcYqTKqGJzMypnMv2tiRyifZJ17BlWIWA7A= go.etcd.io/etcd/api/v3 v3.5.6/go.mod h1:KFtNaxGDw4Yx/BA4iPPwevUTAuqcsPxzyX8PHydchN8= +go.etcd.io/etcd/api/v3 v3.5.9/go.mod h1:uyAal843mC8uUVSLWz6eHa/d971iDGnCRpmKd2Z+X8k= go.etcd.io/etcd/client/pkg/v3 v3.5.6 h1:TXQWYceBKqLp4sa87rcPs11SXxUA/mHwH975v+BDvLU= go.etcd.io/etcd/client/pkg/v3 v3.5.6/go.mod h1:ggrwbk069qxpKPq8/FKkQ3Xq9y39kbFR4LnKszpRXeQ= +go.etcd.io/etcd/client/pkg/v3 v3.5.9/go.mod h1:y+CzeSmkMpWN2Jyu1npecjB9BBnABxGM4pN8cGuJeL4= go.etcd.io/etcd/client/v2 v2.305.6 h1:fIDR0p4KMjw01MJMfUIDWdQbjo06PD6CeYM5z4EHLi0= go.etcd.io/etcd/client/v2 v2.305.6/go.mod h1:BHha8XJGe8vCIBfWBpbBLVZ4QjOIlfoouvOwydu63E0= +go.etcd.io/etcd/client/v2 v2.305.7/go.mod h1:GQGT5Z3TBuAQGvgPfhR7VPySu/SudxmEkRq9BgzFU6s= go.etcd.io/etcd/client/v3 v3.5.6 h1:coLs69PWCXE9G4FKquzNaSHrRyMCAXwF+IX1tAPVO8E= go.etcd.io/etcd/client/v3 v3.5.6/go.mod h1:f6GRinRMCsFVv9Ht42EyY7nfsVGwrNO0WEoS2pRKzQk= +go.etcd.io/etcd/client/v3 v3.5.9/go.mod h1:i/Eo5LrZ5IKqpbtpPDuaUnDOUv471oDg8cjQaUr2MbA= go.etcd.io/etcd/pkg/v3 v3.5.0 h1:ntrg6vvKRW26JRmHTE0iNlDgYK6JX3hg/4cD62X0ixk= go.etcd.io/etcd/raft/v3 v3.5.0 h1:kw2TmO3yFTgE+F0mdKkG7xMxkit2duBDa2Hu6D/HMlw= go.etcd.io/etcd/server/v3 v3.5.0 h1:jk8D/lwGEDlQU9kZXUFMSANkE22Sg5+mW27ip8xcF9E= @@ -800,12 +953,15 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEa go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/image v0.0.0-20210216034530-4410531fe030 h1:lP9pYkih3DUSC641giIXa2XqfTIbbbRr0w2EOTA7wHA= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= @@ -818,12 +974,14 @@ golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= @@ -831,7 +989,10 @@ golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7Lm golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= +golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -848,25 +1009,21 @@ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= -golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= -golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= +golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= +golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= -golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= @@ -888,8 +1045,13 @@ google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= +google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= google.golang.org/api v0.107.0 h1:I2SlFjD8ZWabaIFOfeEDg3pf0BHJDh6iYQ1ic3Yu/UU= google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= +google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0= +google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= +google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms= google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8 h1:Cpp2P6TPjujNoC5M2KHY6g7wfyLYfIWRZaSdIKfDasA= google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= @@ -917,6 +1079,13 @@ google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZV google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE= google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= +google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= @@ -926,28 +1095,24 @@ google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCD google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= +google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= +google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= +google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQfGwJD30Nv2jfUgzb5UcE= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/airbrake/gobrake.v2 v2.0.9 h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNatLo= gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/cheggaaa/pb.v1 v1.0.25 h1:Ev7yu1/f6+d+b3pi5vPdRPc6nNtP1umSfcWiEfRqv6I= gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= -gopkg.in/gcfg.v1 v1.2.3 h1:m8OOJ4ccYHnx2f4gQwpno8nAX5OGOh7RLaaz0pj3Ogs= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 h1:OAj3g0cR6Dx/R07QgQe8wkA9RNjB2u4i700xBkIT4e0= -gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= gopkg.in/go-playground/validator.v9 v9.29.1 h1:SvGtYmN60a5CVKTOzMSyfzWDeZRxRuGvRQyEAKbw1xc= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec h1:RlWgLqCMMIYYEVcAR5MDsuHlVkaIPDAF+5Dehzg8L5A= -gopkg.in/mgo.v2 v2.0.0-20160818020120-3f83fa500528 h1:/saqWwm73dLmuzbNhe92F0QsZ/KiFND+esHco2v1hiY= gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI= gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= gopkg.in/stretchr/testify.v1 v1.2.2 h1:yhQC6Uy5CqibAIlk1wlusa/MJ3iAN49/BsR/dCCKz3M= -gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= -gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/driver/mysql v1.4.3/go.mod h1:sSIebwZAVPiT+27jK9HIwvsqOGKx3YMPmrA3mBJR10c= gorm.io/driver/postgres v1.0.8 h1:PAgM+PaHOSAeroTjHkCHCBIHHoBIf9RgPWGo8dF2DA8= -gorm.io/driver/sqlite v1.3.1/go.mod h1:wJx0hJspfycZ6myN38x1O/AqLtNS6c5o9TndewFbELg= -gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools/v3 v3.1.0 h1:rVV8Tcg/8jHUkPUorwjaMTtemIMVXfIPKiOqnhEhakk= honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8= @@ -956,45 +1121,27 @@ k8s.io/code-generator v0.19.7 h1:kM/68Y26Z/u//TFc1ggVVcg62te8A2yQh57jBfD0FWQ= k8s.io/component-base v0.22.5 h1:U0eHqZm7mAFE42hFwYhY6ze/MmVaW00JpMrzVsQmzYE= k8s.io/cri-api v0.23.1 h1:0DHL/hpTf4Fp+QkUXFefWcp1fhjXr9OlNdY9X99c+O8= k8s.io/gengo v0.0.0-20201113003025-83324d819ded h1:JApXBKYyB7l9xx+DK7/+mFjC7A9Bt5A93FPvFD0HIFE= -k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/klog/v2 v2.30.0 h1:bUO6drIvCIsvZ/XFgfxoGFQU/a4Qkh0iAlvUR7vlHJw= k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c h1:jvamsI1tn9V0S8jicyX82qaFC0H/NKxv2e5mbqsgR80= k8s.io/kubernetes v1.13.0 h1:qTfB+u5M92k2fCCCVP2iuhgwwSOv1EkAkvQY1tQODD8= -k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b h1:wxEMGetGMur3J1xuGLQY7GEQYg9bZxKn3tKo5k/eYcs= modernc.org/b v1.0.0 h1:vpvqeyp17ddcQWF29Czawql4lDdABCDRbXRAS4+aF2o= -modernc.org/cc/v3 v3.32.4 h1:1ScT6MCQRWwvwVdERhGPsPq0f55J1/pFEOCiqM7zc78= -modernc.org/ccgo/v3 v3.9.2 h1:mOLFgduk60HFuPmxSix3AluTEh7zhozkby+e1VDo/ro= modernc.org/db v1.0.0 h1:2c6NdCfaLnshSvY7OU09cyAY0gYXUZj4lmg5ItHyucg= modernc.org/file v1.0.0 h1:9/PdvjVxd5+LcWUQIfapAWRGOkDLK90rloa8s/au06A= modernc.org/fileutil v1.0.0 h1:Z1AFLZwl6BO8A5NldQg/xTSjGLetp+1Ubvl4alfGx8w= -modernc.org/golex v1.0.0 h1:wWpDlbK8ejRfSyi0frMyhilD3JBvtcx2AdGDnU+JtsE= modernc.org/golex v1.0.1 h1:EYKY1a3wStt0RzHaH8mdSRNg78Ub0OHxYfCRWw35YtM= modernc.org/internal v1.0.0 h1:XMDsFDcBDsibbBnHB2xzljZ+B1yrOVLEFkKL2u15Glw= modernc.org/lex v1.0.0 h1:w0dxp18i1q+aSE7GkepvwzvVWTLoCIQ2oDgTFAV2JZU= modernc.org/lexer v1.0.0 h1:D2xE6YTaH7aiEC7o/+rbx6qTAEr1uY83peKwkamIdQ0= -modernc.org/libc v1.9.5 h1:zv111ldxmP7DJ5mOIqzRbza7ZDl3kh4ncKfASB2jIYY= modernc.org/lldb v1.0.0 h1:6vjDJxQEfhlOLwl4bhpwIz00uyFK4EmSYcbwqwbynsc= -modernc.org/mathutil v1.2.2 h1:+yFk8hBprV+4c0U9GjFtL+dV3N8hOJ8JCituQcMShFY= -modernc.org/memory v1.0.4 h1:utMBrFcpnQDdNsmM6asmyH/FM9TqLPS7XF7otpJmrwM= -modernc.org/opt v0.1.1 h1:/0RX92k9vwVeDXj+Xn23DKp2VJubL7k8qNffND6qn3A= modernc.org/parser v1.0.2 h1:/qHLDn1ezrcRk9/XbErYp84bPPM4+w0kIDuvMdRk6Vc= modernc.org/ql v1.0.0 h1:bIQ/trWNVjQPlinI6jdOQsi195SIturGo3mp5hsDqVU= modernc.org/scanner v1.0.1 h1:rmWBTztgQKLM2CYx0uTQGhAxgnrILDEOVXJsEq/I4Js= modernc.org/sortutil v1.1.0 h1:oP3U4uM+NT/qBQcbg/K2iqAX0Nx7B1b6YZtq3Gk/PjM= -modernc.org/sqlite v1.10.6 h1:iNDTQbULcm0IJAqrzCm2JcCqxaKRS94rJ5/clBMRmc8= -modernc.org/strutil v1.1.0 h1:+1/yCzZxY2pZwwrsbH+4T7BQMoLQ9QiBshRC9eicYsc= -modernc.org/tcl v1.5.2 h1:sYNjGr4zK6cDH74USl8wVJRrvDX6UOLpG0j4lFvR0W0= -modernc.org/tcl v1.15.2/go.mod h1:3+k/ZaEbKrC8ePv8zJWPtBSW0V7Gg9g8rkmhI1Kfs3c= -modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk= modernc.org/y v1.0.1 h1:+QT+MtLkwkvLkh3fYQq+YD5vw2s5paVE73jdl5R/Py8= -modernc.org/z v1.0.1 h1:WyIDpEpAIx4Hel6q/Pcgj/VhaQV5XPJ2I6ryIYbjnpc= -modernc.org/z v1.7.3/go.mod h1:Ipv4tsdxZRbQyLq9Q1M6gdbkxYzdlrciF2Hi/lS7nWE= modernc.org/zappy v1.0.0 h1:dPVaP+3ueIUv4guk8PuZ2wiUGcJ1WUVvIheeSSTD0yk= +nullprogram.com/x/optparse v1.0.0 h1:xGFgVi5ZaWOnYdac2foDT3vg0ZZC9ErXFV57mr4OHrI= rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE= rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4= rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY= rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22 h1:fmRfl9WJ4ApJn7LxNuED4m0t18qivVQOxP6aAYG9J6c= -sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sourcegraph.com/sourcegraph/appdash-data v0.0.0-20151005221446-73f23eafcf67 h1:e1sMhtVq9AfcEy8AXNb8eSg6gbzfdpYhoNqnPJa+GzI= diff --git a/dbm-services/mysql/db-partition/assests/migrations/000002_create_table.up.sql b/dbm-services/mysql/db-partition/assests/migrations/000002_create_table.up.sql index a4c9838035..6368c7f391 100644 --- a/dbm-services/mysql/db-partition/assests/migrations/000002_create_table.up.sql +++ b/dbm-services/mysql/db-partition/assests/migrations/000002_create_table.up.sql @@ -41,14 +41,15 @@ CREATE TABLE IF NOT EXISTS `mysql_partition_config` ( `partition_time_interval` int NOT NULL, `partition_type` int NOT NULL, `expire_time` int NOT NULL, + `time_zone` varchar(16) NOT NULL COMMENT '集群所在的时区', `creator` varchar(100) DEFAULT NULL, `updator` varchar(100) DEFAULT NULL, `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `update_time` timestamp NOT NULL DEFAULT '2000-01-01 00:00:00', - `phase` varchar(100) NOT NULL, + `phase` varchar(100) NOT NULL COMMENT 'online--在用;offline--停用', PRIMARY KEY (`id`), UNIQUE KEY `uniq` (`bk_biz_id`,`immute_domain`,`cluster_id`,`dblike`,`tblike`), - KEY `idx_cluster_id_phase` (`cluster_id`,`phase`) + KEY `idx_time_zone_phase` (`cluster_id`,`time_zone`,`phase`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; @@ -75,14 +76,15 @@ CREATE TABLE IF NOT EXISTS `spider_partition_config` ( `partition_time_interval` int NOT NULL, `partition_type` int NOT NULL, `expire_time` int NOT NULL, + `time_zone` varchar(16) NOT NULL COMMENT '集群所在的时区', `creator` varchar(100) DEFAULT NULL, `updator` varchar(100) DEFAULT NULL, `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `update_time` timestamp NOT NULL DEFAULT '2000-01-01 00:00:00', - `phase` varchar(100) NOT NULL, + `phase` varchar(100) NOT NULL COMMENT 'online--在用;offline--停用', PRIMARY KEY (`id`), UNIQUE KEY `uniq` (`bk_biz_id`,`immute_domain`,`cluster_id`,`dblike`,`tblike`), - KEY `idx_cluster_id_phase` (`cluster_id`,`phase`) + KEY `idx_time_zone_phase` (`cluster_id`,`time_zone`,`phase`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; @@ -104,12 +106,12 @@ CREATE TABLE IF NOT EXISTS `mysql_partition_cron_log` ( `bk_cloud_id` int NOT NULL, `time_zone` varchar(100) NOT NULL, `cron_date` varchar(100) NOT NULL, - `ticket_detail` json DEFAULT NULL, `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP, `check_info` text, `status` varchar(100) NOT NULL, PRIMARY KEY (`id`), KEY `idx_create_time` (`create_time`) + KEY `idx_cron_date_status (cron_date,status)` ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; @@ -130,12 +132,12 @@ CREATE TABLE IF NOT EXISTS `spider_partition_cron_log` ( `bk_cloud_id` int NOT NULL, `time_zone` varchar(100) NOT NULL, `cron_date` varchar(100) NOT NULL, - `ticket_detail` json DEFAULT NULL, `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP, `check_info` text, `status` varchar(100) NOT NULL, PRIMARY KEY (`id`), - KEY `idx_create_time` (`create_time`) + KEY `idx_create_time` (`create_time`), + KEY `idx_cron_date_status (cron_date,status)` ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; @@ -145,14 +147,15 @@ CREATE TABLE IF NOT EXISTS `spider_partition_cron_log` ( /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; -CREATE TABLE IF NOT EXISTS `partition_logs` ( +CREATE TABLE IF NOT EXISTS `manage_logs` ( `id` int(11) NOT NULL AUTO_INCREMENT, + `config_id` int(11) NOT NULL, `bk_biz_id` int(11) NOT NULL COMMENT '业务的 cmdb id', `operator` varchar(800) NOT NULL COMMENT '操作者', `para` longtext NOT NULL COMMENT '参数', `execute_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '执行时间', PRIMARY KEY (`id`), - KEY `bk_biz_id` (`bk_biz_id`,`operator`(10),`execute_time`) + KEY `bk_biz_id` (`bk_biz_id`,`config_id`,`operator`(10),`execute_time`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; diff --git a/dbm-services/mysql/db-partition/cron/cron.go b/dbm-services/mysql/db-partition/cron/cron.go index 062b4c6a47..b97f22886f 100644 --- a/dbm-services/mysql/db-partition/cron/cron.go +++ b/dbm-services/mysql/db-partition/cron/cron.go @@ -15,18 +15,19 @@ import ( "golang.org/x/exp/slog" ) +var CronList []*cron.Cron + // RegisterCron 注册定时任务 func RegisterCron() ([]*cron.Cron, error) { - cronList := make([]*cron.Cron, 24) timingHour := viper.GetString("cron.timing_hour") retryHour := viper.GetString("cron.retry_hour") if timingHour == "" || retryHour == "" { err := errors.New("cron.partition_hour or cron.retry_hour was not set") slog.Error("msg", "cron error", err) - return cronList, err + return CronList, err } - timing := fmt.Sprintf("2 %s * * * ", timingHour) - retry := fmt.Sprintf("2 %s * * * ", retryHour) + timing := fmt.Sprintf("02 %s * * * ", timingHour) + retry := fmt.Sprintf("02 %s * * * ", retryHour) fmt.Println(retry) var debug bool if strings.ToLower(strings.TrimSpace(viper.GetString("log.level"))) == "debug" { @@ -51,22 +52,23 @@ func RegisterCron() ([]*cron.Cron, error) { _, err := c.AddJob(timing, PartitionJob{CronType: Daily, ZoneOffset: offset, CronDate: date}) if err != nil { slog.Error("msg", "cron add daily job error", err) - return cronList, err + return CronList, err } _, err = c.AddJob(retry, PartitionJob{CronType: Retry, ZoneOffset: offset, CronDate: date}) if err != nil { slog.Error("msg", "cron add retry job error", err) - return cronList, err + return CronList, err } if offset == 0 { _, err = c.AddJob("@every 1s", PartitionJob{CronType: Heartbeat, ZoneOffset: offset, CronDate: date}) if err != nil { slog.Error("msg", "cron add heartbeat job error", err) - return cronList, err + return CronList, err } } - cronList = append(cronList, c) c.Start() + slog.Info("msg", zone, c.Entries()) + CronList = append(CronList, c) } - return cronList, nil + return CronList, nil } diff --git a/dbm-services/mysql/db-partition/cron/cron_basic_func.go b/dbm-services/mysql/db-partition/cron/cron_basic_func.go index baa82bbc05..a3ea73bc5e 100644 --- a/dbm-services/mysql/db-partition/cron/cron_basic_func.go +++ b/dbm-services/mysql/db-partition/cron/cron_basic_func.go @@ -1,9 +1,20 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package cron import ( "fmt" + "strings" - "dbm-services/mysql/db-partition/errno" + "dbm-services/common/go-pubpkg/errno" "dbm-services/mysql/db-partition/model" "dbm-services/mysql/db-partition/monitor" "dbm-services/mysql/db-partition/service" @@ -18,7 +29,9 @@ var Scheduler string // Run TODO func (m PartitionJob) Run() { var err error - Scheduler, err = util.ExecShellCommand(false, "hostname -I") + Scheduler, err = util.ExecShellCommand(false, `hostname -I`) + Scheduler = strings.Replace(Scheduler, " ", "", -1) + Scheduler = strings.Replace(Scheduler, "\n", "", -1) if err != nil { Scheduler = "0.0.0.0" } @@ -60,17 +73,19 @@ func (m PartitionJob) ExecutePartitionCron(clusterType string) { if err != nil { code, _ := errno.DecodeErr(err) if code == errno.NothingToDo.Code { - service.AddLog(item.ConfigId, item.BkBizId, item.ClusterId, *item.BkCloudId, 0, - item.ImmuteDomain, zone, m.CronDate, Scheduler, "{}", - errno.NothingToDo.Message, service.CheckSucceeded, item.ClusterType) + // 当天首次执行发现没有需要执行的sql,记录日志。重试没有执行的sql,不需要记录日志。 + if m.CronType == "daily" { + _ = service.AddLog(item.ConfigId, item.BkBizId, item.ClusterId, *item.BkCloudId, 0, + item.ImmuteDomain, zone, m.CronDate, Scheduler, errno.NothingToDo.Message, service.CheckSucceeded, + item.ClusterType) + } continue } else { dimension := monitor.NewPartitionEventDimension(item.BkBizId, *item.BkCloudId, item.ImmuteDomain) content := fmt.Sprintf("partition error. get partition sql fail: %s", err.Error()) monitor.SendEvent(monitor.PartitionEvent, dimension, content, "0.0.0.0") - service.AddLog(item.ConfigId, item.BkBizId, item.ClusterId, *item.BkCloudId, 0, - item.ImmuteDomain, zone, m.CronDate, Scheduler, "{}", - content, service.CheckFailed, item.ClusterType) + _ = service.AddLog(item.ConfigId, item.BkBizId, item.ClusterId, *item.BkCloudId, 0, + item.ImmuteDomain, zone, m.CronDate, Scheduler, content, service.CheckFailed, item.ClusterType) slog.Error(fmt.Sprintf("%v", *item), "get partition sql fail", err) continue } diff --git a/dbm-services/mysql/db-partition/errno/code.go b/dbm-services/mysql/db-partition/errno/code.go deleted file mode 100644 index abaa19d931..0000000000 --- a/dbm-services/mysql/db-partition/errno/code.go +++ /dev/null @@ -1,361 +0,0 @@ -package errno - -var ( - // OK TODO - // Common errors - // OK = Errno{Code: 0, Message: ""} - OK = Errno{Code: 0, Message: "", CNMessage: ""} - // SaveOK TODO - SaveOK = Errno{Code: 0, Message: "Bill save success!", CNMessage: "单据保存成功!"} - // CommitOK TODO - CommitOK = Errno{Code: 0, Message: "Bill commit success!", CNMessage: "单据提交成功!"} - // AuditOK TODO - AuditOK = Errno{Code: 0, Message: "Bill audit success!", CNMessage: "单据审核成功!"} - // RollbackOK TODO - RollbackOK = Errno{Code: 0, Message: "Bill rollback success!", CNMessage: "单据驳回成功!"} - // StopOK TODO - StopOK = Errno{Code: 0, Message: "Bill stop success!", CNMessage: "单据终止成功!"} - // ExecuteOK TODO - ExecuteOK = Errno{Code: 0, Message: "Bill execute success!", CNMessage: "单据执行成功!"} - // CommonOK TODO - CommonOK = Errno{Code: 0, Message: "", CNMessage: "通用成功描述"} - // JobUpdateOK TODO - JobUpdateOK = Errno{Code: 0, Message: "Job update success!", CNMessage: "Job 更新成功!"} - // SubjobUpdateOK TODO - SubjobUpdateOK = Errno{Code: 0, Message: "Subjob update success!", CNMessage: "Subjob 更新成功!"} - - // ErrRecordNotFound TODO - ErrRecordNotFound = Errno{Code: 404, Message: "There is no records in db.", CNMessage: "数据库未找到对应的记录!"} - - // CommonErr TODO - CommonErr = Errno{Code: 10000, Message: "common error!", CNMessage: "通用错误!"} - - // InternalServerError TODO - InternalServerError = Errno{Code: 10001, Message: "Internal server error", CNMessage: "服务器内部错误。"} - // ErrBind TODO - ErrBind = Errno{Code: 10002, Message: "Error occurred while binding the request body to the struct.", - CNMessage: "参数处理发生错误。"} - // ErrString2Int TODO - ErrString2Int = Errno{Code: 10010, Message: "Error occurred while convert string to int.", - CNMessage: "string 转化为 int 出错!"} - // ErrorJsonToMap TODO - ErrorJsonToMap = Errno{Code: 10030, Message: "Error occured while converting json to Map.", - CNMessage: "Json 转为 Map 出现错误!"} - // ErrorUIDBeZero TODO - ErrorUIDBeZero = Errno{Code: 10035, Message: "uid can not be 0!", CNMessage: "uid 不能为 0.!"} - // ErrRequestParam TODO - ErrRequestParam = Errno{Code: 10036, Message: "request parameter error!", CNMessage: "请求参数错误!"} - - // ErrTypeAssertion TODO - ErrTypeAssertion = Errno{Code: 10040, Message: "Error occurred while doing type assertion."} - // ErrParameterRequired TODO - ErrParameterRequired = Errno{Code: 10050, Message: "Input paramter required"} - // StartBiggerThanEndTime TODO - StartBiggerThanEndTime = Errno{Code: 10060, Message: "Start time is bigger than end time."} - - // ErrValidation TODO - ErrValidation = Errno{Code: 20001, Message: "Validation failed."} - // ErrDatabase TODO - ErrDatabase = Errno{Code: 20002, Message: "Database error."} - // ErrToken TODO - ErrToken = Errno{Code: 20003, Message: "Error occurred while signing the JSON web token."} - - // ErrEncrypt TODO - // user errors - ErrEncrypt = Errno{Code: 20101, Message: "Error occurred while encrypting the user password."} - // ErrUserNotFound TODO - ErrUserNotFound = Errno{Code: 20102, Message: "The user was not found."} - // ErrTokenInvalid TODO - ErrTokenInvalid = Errno{Code: 20103, Message: "The token was invalid."} - // ErrPasswordIncorrect TODO - ErrPasswordIncorrect = Errno{Code: 20104, Message: "The password was incorrect."} - // ErrDoNotHavePrivs TODO - ErrDoNotHavePrivs = Errno{Code: 20106, Message: "User don't have Privs."} - // ErrUserIsEmpty TODO - ErrUserIsEmpty = Errno{Code: 20110, Message: "User can't be empty.", CNMessage: "user 不能为空!"} - // ErrAppNameIsEmpty TODO - ErrAppNameIsEmpty = Errno{Code: 20115, Message: "App name can't be empty.", CNMessage: "业务名不能为空!"} - - // ErrCommonExecute TODO - ErrCommonExecute = Errno{Code: 20200, Message: "Error occured while invoking execute method.", - CNMessage: "调用 execute 出错!"} - - // ErrUserHaveNoProjectPriv TODO - ErrUserHaveNoProjectPriv = Errno{Code: 30000, Message: "User don't have project priv.", CNMessage: "没有 project 权限!"} - - // ErrGcsBillNotFound TODO - // gcsbill errors - ErrGcsBillNotFound = Errno{Code: 40000, Message: "Gcs bill was not found.", CNMessage: "单据不存在!"} - // ErrGCSBillTypeEmpty TODO - ErrGCSBillTypeEmpty = Errno{Code: 40001, Message: "Gcs bill type can not be empty.", CNMessage: "单据类型不能为空!"} - // InvalidGCSBillType TODO - InvalidGCSBillType = Errno{Code: 40002, Message: "Invalid Gcs bill type.", CNMessage: "无效的 GCS 单据类型!"} - // InvalidAuditLevel TODO - InvalidAuditLevel = Errno{Code: 40003, Message: "Invalid Bill Audit level.", CNMessage: "无效的单据审核级别!"} - - // CannotGetBillStatus TODO - CannotGetBillStatus = Errno{Code: 40004, Message: "Cann't get bill status.", CNMessage: `无法获取单据状态`} - // ErrGCSBillnotAuditable TODO - ErrGCSBillnotAuditable = Errno{Code: 40005, Message: "Current GCS bill is not in audit status now.", - CNMessage: `当前单据不在“待审核”状态!`} - // ErrGCSBillNotInExecute TODO - ErrGCSBillNotInExecute = Errno{Code: 40006, Message: "Bill is not in execute status.", CNMessage: `当前单据不在“待执行”状态!`} - // ErrGCSBillAudit TODO - ErrGCSBillAudit = Errno{Code: 40007, Message: "Audit bill error.", CNMessage: `审核单据出错。`} - - // ErrNotHaveBillCommitPriv TODO - ErrNotHaveBillCommitPriv = Errno{Code: 40008, Message: "user don't have bill commit priv", CNMessage: "用户没有提单权限!"} - - // ErrGetGCSDoneBills TODO - ErrGetGCSDoneBills = Errno{Code: 40009, Message: "Error occured while getting done bills.", - CNMessage: "获取个人已办事项出错!"} - // ErrBillAppIsEmpty TODO - ErrBillAppIsEmpty = Errno{Code: 40010, Message: "Gcs bill app can not be empty.", CNMessage: "单据的业务名不能为空!"} - // ErrGCSBillNoExecutePriv TODO - ErrGCSBillNoExecutePriv = Errno{Code: 40011, Message: "Only apply user and follower can execute the bill!", - CNMessage: "只有申请人或者关注人可以执行单据!"} - // ErrGetGCSBillModel TODO - ErrGetGCSBillModel = Errno{Code: 40012, Message: "Error occured while getting bill info", - CNMessage: "获取 Bill 详情出错"} - // ErrGetGCSBillTypes TODO - ErrGetGCSBillTypes = Errno{Code: 40014, Message: "Error occured while getting bill types", - CNMessage: "获取所有单据类型失败!"} - // ErrGCSBillCommit TODO - ErrGCSBillCommit = Err{Errno: Errno{Code: 40015, Message: "The bill can not be committed repeatly!", - CNMessage: "单据不能被重复提交!"}} - // ErrInvokeBillCommit TODO - ErrInvokeBillCommit = Err{Errno: Errno{Code: 40016, Message: "Error occured while committing gcs bills", - CNMessage: "单据提交时发生错误!"}} - // ErrInvokeBillExecute TODO - ErrInvokeBillExecute = Err{Errno: Errno{Code: 40017, Message: "Error occured while executing gcs bills", - CNMessage: "单据执行时发生错误!"}} - - // ErrGCSBillnotRollback TODO - ErrGCSBillnotRollback = Errno{Code: 40019, Message: "Bill is not auditable ,it can not be rollback.", - CNMessage: `非“待审核”单据不能被驳回!`} - // ErrGetGCSBills TODO - ErrGetGCSBills = Errno{Code: 40020, Message: "Error occured while getting gcs bills", CNMessage: "获取单据失败!"} - // ErrCloneUnfinishedBills TODO - ErrCloneUnfinishedBills = Errno{Code: 40022, Message: "Error occured while cloning unfinished gcs bills", - CNMessage: "不能克隆没有结束的单据!"} - // ErrFinishedBills TODO - ErrFinishedBills = Errno{Code: 40027, Message: "Error occured while finishing gcs bills", - CNMessage: `设置单据为“完成”状态时失败!`} - // ErrBillHaveTerminated TODO - ErrBillHaveTerminated = Errno{Code: 40028, Message: "Bill have terminated!", CNMessage: `单据已“终止”!`} - - // ErrNoStopPriv TODO - ErrNoStopPriv = Errno{Code: 40037, Message: "Don't have stop bill priv!", CNMessage: `用户没有“终止”单据权限!`} - // ErrGCSBillSave TODO - ErrGCSBillSave = Err{Errno: Errno{Code: 40042, Message: "Error occured while saving gcs bills!", - CNMessage: "单据保存失败!"}} - // ErrBillIsNotUncommit TODO - ErrBillIsNotUncommit = Err{Errno: Errno{Code: 40043, - Message: "Bill phase is not v_uncommit before committing the bill!", CNMessage: "单据提交之前,单据状态不是\"未提交\"!"}} - // ErrBillPreCommit TODO - ErrBillPreCommit = Err{Errno: Errno{Code: 40046, Message: "Error occured while invoking bill pre commit api:", - CNMessage: "调用单据的 PreCommit API 失败:"}} - // ErrBillAfterExecute TODO - ErrBillAfterExecute = Err{Errno: Errno{Code: 40050, Message: "Error occured while invoking after execute api!", - CNMessage: "调用单据的 AfterExecute API 失败!"}} - - // ErrTbBillInfoToBill TODO - ErrTbBillInfoToBill = Err{Errno: Errno{Code: 40055, Message: "Error occured while transfer TbBillInfo to Bill!", - CNMessage: "转换 Bill Model 失败"}} - - // ErrCreateGCSJob TODO - // job errors - ErrCreateGCSJob = Errno{Code: 40100, Message: "Error occured while creating the gcs job.", - CNMessage: "创建 GCS Job 失败!"} - // ErrGetJobQueue TODO - ErrGetJobQueue = Errno{Code: 40101, Message: "Error occured while get the gcs job queue.", - CNMessage: "获取 job 失败 !"} - // ErrGetJobQueueNotFound TODO - ErrGetJobQueueNotFound = Errno{Code: 40102, Message: "Job Queue Not Found.", CNMessage: "Job 不存在!"} - // ErrDeleteJobQueue TODO - ErrDeleteJobQueue = Errno{Code: 40103, Message: "Error occured while set the jobQueue to be deleted.", - CNMessage: "删除 Job 失败!"} - // ErrJobIDConvert2Int TODO - ErrJobIDConvert2Int = Errno{Code: 40104, Message: "Error occured while converting the jobID to int.", - CNMessage: "jobID 转换为int 出错!"} - // ErrSubjobIDConvert2Int TODO - ErrSubjobIDConvert2Int = Errno{Code: 40105, Message: "Error occured while converting the subjob_id to int.", - CNMessage: "subjobID 转换为int 出错!"} - - // ErrPutJobQueueParam TODO - ErrPutJobQueueParam = Errno{Code: 40106, Message: " param errors while puting a new JobQueue.", - CNMessage: "创建 Job 时参数错误!"} - // ErrJobQueueInputParam TODO - ErrJobQueueInputParam = Errno{Code: 40107, - Message: "Some parameters is required in EnJobQueue: app,name,input,tag_id", - CNMessage: "创建Job 时缺少下列参数:[app,name,input,tag_id]!"} - // ErrJobQueueV1InputParam TODO - ErrJobQueueV1InputParam = Errno{Code: 40107, - Message: "Some parameters is required in puting JobQueue: [app,name,distributions,payload,user]", - CNMessage: "创建/修改 Job 时缺少下列参数:[app,name,distributions,payload,user]!"} - // ErrJobQueueDistribution TODO - ErrJobQueueDistribution = Errno{Code: 40108, Message: "JobQueue distributions format is wrong.", - CNMessage: "创建 JobQueue 时 distributions 格式不正确!"} - // ErrCheckJobQueue TODO - ErrCheckJobQueue = Errno{Code: 40109, Message: "Error occured while checking JobQueue.", - CNMessage: "检查 JobQueue 出错!"} - // ErrJoqQueueIsNil TODO - ErrJoqQueueIsNil = Errno{Code: 40110, Message: "JobQueue is Nil", CNMessage: "返回的Job 内容为空!"} - // ErrCloneJoqQueues TODO - ErrCloneJoqQueues = Errno{Code: 40113, Message: "Error occured while cloning jobQueues", - CNMessage: "克隆 jobQueues 出错!"} - - // JobResultSuccess TODO - JobResultSuccess = Errno{Code: 0, Message: "success", CNMessage: "success"} - // JobResultRunning TODO - JobResultRunning = Errno{Code: 40114, Message: "running", CNMessage: "running"} - // JobResultFailed TODO - JobResultFailed = Errno{Code: 40115, Message: "fail", CNMessage: "fail"} - // JobResultOthers TODO - JobResultOthers = Errno{Code: 40116, Message: "other job status", CNMessage: "other job status"} - - // ErrGetJobFeedbacks TODO - // JobFeedback - ErrGetJobFeedbacks = Errno{Code: 40210, Message: "Error occured while getting the gcs job feedback.", - CNMessage: "获取 job feedback 信息失败!"} - // ErrCreateGCSJobFeedback TODO - ErrCreateGCSJobFeedback = Errno{Code: 40215, Message: "Error occured while creating the gcs jobFeedback.", - CNMessage: "创建 GCS jobFeedback 失败!"} - - // InvalidJobIDorSubjobID TODO - InvalidJobIDorSubjobID = Errno{Code: 40220, Message: "Invalid jobID or subJobID while getting the gcs job feedback.", - CNMessage: "jobID or subJobID 无效!"} - - // ErrorJobNameBeEmpty TODO - // JobDef errors - ErrorJobNameBeEmpty = Errno{Code: 40300, Message: "JobName can not be empty.", CNMessage: "JobName 不能为空!"} - // ErrorGetJobDef TODO - ErrorGetJobDef = Errno{Code: 40302, Message: "Error occured while getting the gcs job_def", - CNMessage: "获取 job_def 出现错误!"} - - // ErrorGetJobBlob TODO - // JobBlob errors - ErrorGetJobBlob = Errno{Code: 40302, Message: "Error occured while getting the gcs job_blob", - CNMessage: "获取 job_blob 出现错误!"} - - // ErrorGetSubJobQueue TODO - // subjob errors - ErrorGetSubJobQueue = Errno{Code: 40800, Message: "Error occured while getting the gcs subjob ", - CNMessage: "获取 subjob 出现错误!"} - // ErrCreateSubJobQueue TODO - ErrCreateSubJobQueue = Errno{Code: 40801, Message: "Error occured while creating the gcs subjobQueue.", - CNMessage: "创建 GCS subjobQueue 失败!"} - // ErrUpdateSubJobQueue TODO - ErrUpdateSubJobQueue = Errno{Code: 40802, Message: "Error occured while updating the gcs subjobQueue.", - CNMessage: "更新 GCS subjobQueue 失败!"} - - // SubJobUIDRequied TODO - SubJobUIDRequied = Errno{Code: 40804, Message: "Subjob uid is required!", CNMessage: "Subjob uid 是必填项.!"} - // ErrorUIDMustBeInt TODO - ErrorUIDMustBeInt = Errno{Code: 40808, Message: "Subjob uid must be int!", CNMessage: "Subjob uid 必须是 int 类型.!"} - // ErrSubjobQueueInputParam TODO - ErrSubjobQueueInputParam = Errno{Code: 40812, - Message: "Some parameters [JobID,Username,JobName,AtomjobList,JobInput] are not meet the demands in saving SubjobQueue", CNMessage: "保存 SubjobQueue 时缺少下列参数:[JobID,Username,JobName,AtomjobList,JobInput]!"} - // ErrJobFeedbackInputParam TODO - ErrJobFeedbackInputParam = Errno{Code: 40815, - Message: "Some parameters are not meet the demands in saving JobFeedback", CNMessage: "保存 JobFeedback 时参数不满足要求。"} - // ErrGetGCSApps TODO - // gcs app errors - ErrGetGCSApps = Errno{Code: 40900, Message: "Error occured while getting gcs apps", CNMessage: "获取 GCS App 出现错误!"} - // ErrGetCCApps TODO - ErrGetCCApps = Errno{Code: 40902, Message: "Error occured while getting cc apps", CNMessage: "获取 App 出现错误!"} - // ErrGetProjects TODO - ErrGetProjects = Errno{Code: 40905, Message: "Error occured while getting projects", CNMessage: "获取 projects 出现错误!"} - - // ErrDBTransaction TODO - // model operation errors - ErrDBTransaction = Errno{Code: 50200, Message: "DB Transaction error.", CNMessage: "DB 事务发生错误!"} - // ErrModelFunction TODO - ErrModelFunction = Err{Errno: Errno{Code: 50201, Message: "Error occured while invoking model function.", - CNMessage: "调用 DB model 方法发生错误!"}, Err: nil} - - // ErrSaveFlowAuditLog TODO - ErrSaveFlowAuditLog = Errno{Code: 50203, Message: "Error occured while saving Flow Audit Log.", - CNMessage: "存储单据审核日志记录出错!"} - - // ErrGetJSONArray TODO - // data handle error - ErrGetJSONArray = Errno{Code: 50300, Message: "Get simplejson Array error.", CNMessage: ""} - // ErrConvert2Map TODO - ErrConvert2Map = Errno{Code: 50301, Message: "Error occurred while converting the data to Map.", - CNMessage: "Error occurred while converting the data to Map."} - // ErrJSONMarshal TODO - ErrJSONMarshal = Errno{Code: 50302, Message: "Error occurred while marshaling the data to JSON.", - CNMessage: "Error occurred while marshaling the data to JSON."} - // ErrReadEntity TODO - ErrReadEntity = Errno{Code: 50303, Message: "Error occurred while parsing the request parameter.", - CNMessage: "Error occurred while parsing the request parameter."} - // ErrJSONUnmarshal TODO - ErrJSONUnmarshal = Errno{Code: 50304, Message: "Error occurred while Unmarshaling the JSON to data model.", - CNMessage: "Error occurred while Unmarshaling the JSON to data model."} - // ErrBytesToMap TODO - ErrBytesToMap = Errno{Code: 50307, Message: "Error occurred while converting bytes to map.", - CNMessage: "Error occurred while converting bytes to map."} - - // ErrUserIsNotDBA TODO - // user login and permission errors - ErrUserIsNotDBA = Errno{Code: 50500, Message: "User is not dba."} - // ErrNoSaveAndCommitPriv TODO - ErrNoSaveAndCommitPriv = Errno{Code: 50502, - Message: "User don't have gcs bill save and commit privs in this app.", CNMessage: "用户在当前 APP 上没有单据的保存和提交权限!"} - // ErrNoBillAduitPriv TODO - ErrNoBillAduitPriv = Errno{Code: 50504, Message: "User don't have gcs audit privs in this app.", - CNMessage: "用户在当前 APP 上没有单据的审核权限!"} - // ErrUserNotHaveBillRollbackPriv TODO - ErrUserNotHaveBillRollbackPriv = Errno{Code: 50506, Message: "User don't have gcs rollback privs in this app.", - CNMessage: "用户在当前 APP 上没有单据的驳回权限!"} - // ErrUserHasNoPermission TODO - ErrUserHasNoPermission = Errno{Code: 50508, Message: "User has no permission.", CNMessage: "当前用户没有权限!"} - // ErrUserNotHaveBillClonePriv TODO - ErrUserNotHaveBillClonePriv = Errno{Code: 50510, Message: "User don't have gcs bill clone privs in this app.", - CNMessage: "用户没有当前单据的克隆权限!"} - // ErrViewAppPriv TODO - ErrViewAppPriv = Errno{Code: 50515, Message: "User have no priv to view this app!", - CNMessage: "用户没有查看当前 APP 的权限!"} - - // ErrInvokeAPI TODO - ErrInvokeAPI = Errno{Code: 50601, Message: "Error occurred while invoking API", CNMessage: "调用 API 发生错误!"} - - // ErrSnedRTX TODO - // alarm errors - ErrSnedRTX = Errno{Code: 50800, Message: "Error occurred while sending RTX message to user.", - CNMessage: "发送 RTX 消息出现错误!"} - - // BkBizIdIsEmpty TODO - BkBizIdIsEmpty = Errno{Code: 51012, Message: "bk_biz_id can't be empty", CNMessage: "bk_biz_id不能为空"} - // InstanceNotExists TODO - InstanceNotExists = Errno{Code: 51018, Message: "instance not exists", CNMessage: "实例不存在"} - // NoTableMatched TODO - NoTableMatched = Errno{Code: 51019, Message: "no table matched", CNMessage: "找不到匹配的表"} - // ClusterIdIsEmpty TODO - ClusterIdIsEmpty = Errno{Code: 51020, Message: "cluster_id can't be empty", - CNMessage: "cluster_id不能为空"} - // CheckPartitionFailed TODO - CheckPartitionFailed = Errno{Code: 51021, Message: "partition check failed", CNMessage: "分区检查失败"} - // PartitionConfigNotExisted TODO - PartitionConfigNotExisted = Errno{Code: 51022, Message: "Partition config not existed ", CNMessage: "分区配置不存在"} - // PartOfPartitionConfigsNotExisted TODO - PartOfPartitionConfigsNotExisted = Errno{Code: 51023, Message: "part of artition configs not existed ", - CNMessage: "部分分区配置不存在"} - // NotSupportedClusterType TODO - NotSupportedClusterType = Errno{Code: 51024, Message: "this instance type is not supportted by partition", - CNMessage: "不支持的实例类型"} - // ConfigIdIsEmpty TODO - ConfigIdIsEmpty = Errno{Code: 51025, Message: "partition config id can't be empty", - CNMessage: "partition config id 不能为空"} - // CloudIdRequired TODO - CloudIdRequired = Errno{Code: 51026, Message: "bk_cloud_id is required", CNMessage: "bk_cloud_id不能为空"} - // GetPartitionSqlFail TODO - GetPartitionSqlFail = Errno{Code: 51027, Message: "get partition sql failed", CNMessage: "获取分区语句失败"} - // ExecutePartitionFail TODO - ExecutePartitionFail = Errno{Code: 51028, Message: "execute partition failed", CNMessage: "执行分区失败"} - // NothingToDo TODO - NothingToDo = Errno{Code: 51029, Message: "nothing to do", CNMessage: "没有需要执行的操作"} - // DomainNotExists TODO - DomainNotExists = Errno{Code: 51030, Message: "domain not exists", CNMessage: "域名不存在"} -) diff --git a/dbm-services/mysql/db-partition/errno/errno.go b/dbm-services/mysql/db-partition/errno/errno.go deleted file mode 100644 index fe2cf6d791..0000000000 --- a/dbm-services/mysql/db-partition/errno/errno.go +++ /dev/null @@ -1,133 +0,0 @@ -// Package errno TODO -package errno - -import ( - "fmt" - - "github.com/spf13/viper" -) - -// Errno TODO -type Errno struct { - Code int - Message string - CNMessage string -} - -var lang = viper.GetString("lang") - -// Error 用于错误处理 -func (err Errno) Error() string { - switch lang { - case "zh_CN": - return err.CNMessage - case "en_US": - return err.Message - default: - return err.CNMessage - } -} - -// Addf TODO -func (err Errno) Addf(format string, args ...interface{}) error { - return err.Add(fmt.Sprintf(format, args...)) -} - -// Add TODO -func (err Errno) Add(message string) error { - switch lang { - case "zh_CN": - err.CNMessage += message - return err - case "en_US": - err.Message += message - return err - default: - err.CNMessage += message - return err - } - return err -} - -// Err represents an error -type Err struct { - Errno - Err error -} - -// New TODO -func New(errno Errno, err error) *Err { - return &Err{Errno: errno, Err: err} -} - -// Add TODO -func (err Err) Add(message string) error { - switch lang { - case "zh_CN": - err.CNMessage += message - return err - case "en_US": - err.Message += message - return err - default: - err.CNMessage += message - return err - } - return err -} - -// SetMsg TODO -func (err Err) SetMsg(message string) error { - err.Message = message - return err -} - -// SetCNMsg TODO -func (err Err) SetCNMsg(cnMessage string) error { - err.CNMessage = cnMessage - return err -} - -// Addf TODO -func (err Err) Addf(format string, args ...interface{}) error { - return err.Add(fmt.Sprintf(format, args...)) -} - -// IsErrUserNotFound TODO -/* - func (err *Err) Error() string { - return fmt.Sprintf("Err - code: %d, message: %s, error: %s", err.Code, err.Message, err.Err) - } -*/ -func IsErrUserNotFound(err error) bool { - code, _ := DecodeErr(err) - return code == ErrUserNotFound.Code -} - -// DecodeErr TODO -func DecodeErr(err error) (int, string) { - - var CN bool = true - - if err == nil { - return OK.Code, OK.Message - } - - switch typed := err.(type) { - case Err: - if CN { - return typed.Code, typed.CNMessage - } else { - return typed.Code, typed.Message - } - case Errno: - if CN { - return typed.Code, typed.CNMessage - } else { - return typed.Code, typed.Message - } - default: - } - // lager.Logger.Errorf("%s", err) - return InternalServerError.Code, err.Error() -} diff --git a/dbm-services/mysql/db-partition/handler/handler.go b/dbm-services/mysql/db-partition/handler/handler.go index f28b63995c..6aebb5f7b4 100644 --- a/dbm-services/mysql/db-partition/handler/handler.go +++ b/dbm-services/mysql/db-partition/handler/handler.go @@ -6,8 +6,15 @@ import ( "fmt" "net/http" _ "runtime/debug" // debug TODO + "strconv" + "strings" + "time" - "dbm-services/mysql/db-partition/errno" + "dbm-services/mysql/db-partition/cron" + + cron_pkg "github.com/robfig/cron/v3" + + "dbm-services/common/go-pubpkg/errno" "dbm-services/mysql/db-partition/service" "github.com/gin-gonic/gin" @@ -39,6 +46,7 @@ func GetPartitionsConfig(r *gin.Context) { } slog.Info(fmt.Sprintf("bk_biz_id: %d, immute_domains: %s", input.BkBizId, input.ImmuteDomains)) lists, count, err := input.GetPartitionsConfig() + // ListResponse 返回信息 type ListResponse struct { Count int64 `json:"count"` Items interface{} `json:"items"` @@ -64,6 +72,7 @@ func GetPartitionLog(r *gin.Context) { return } lists, count, err := input.GetPartitionLog() + // ListResponse 返回信息 type ListResponse struct { Count int64 `json:"count"` Items interface{} `json:"items"` @@ -111,13 +120,18 @@ func CreatePartitionsConfig(r *gin.Context) { } slog.Info(fmt.Sprintf("bk_biz_id: %d, immute_domain: %s, creator: %s", input.BkBizId, input.ImmuteDomain, input.Creator)) - err := input.CreatePartitionsConfig() + err, configIDs := input.CreatePartitionsConfig() if err != nil { slog.Error(err.Error()) SendResponse(r, errors.New(fmt.Sprintf("添加分区配置失败!%s", err.Error())), nil) return } - SendResponse(r, nil, "分区配置信息创建成功!") + // 注意这里内部变量需要首字母大写,不然后面json无法访问 + data := struct { + ConfigIDs []int `json:"config_ids"` + Info string `json:"info"` + }{configIDs, "分区配置信息创建成功!"} + SendResponse(r, nil, data) return } @@ -182,6 +196,64 @@ func UpdatePartitionsConfig(r *gin.Context) { return } +// CreatePartitionLog 用于创建分区后马上执行分区规则,将执行单据的信息记录到日志表中 +func CreatePartitionLog(r *gin.Context) { + var input service.CreatePartitionCronLog + err := r.ShouldBind(&input) + if err != nil { + err = errno.ErrReadEntity.Add(err.Error()) + slog.Error(err.Error()) + SendResponse(r, err, nil) + return + } + // 计算单据处于集群时区的日期 + offsetStr := strings.Split(input.TimeZone, ":")[0] + offset, _ := strconv.Atoi(offsetStr) + offetSeconds := offset * 60 * 60 + name := fmt.Sprintf("UTC%s", offsetStr) + zone := time.FixedZone(name, offetSeconds) + date := time.Now().In(zone).Format("20060102") + err = service.AddLog(input.ConfigId, input.BkBizId, input.ClusterId, input.BkCloudId, input.TicketId, + input.ImmuteDomain, input.TimeZone, date, "from_ticket", "", + service.ExecuteAsynchronous, input.ClusterType) + if err != nil { + slog.Error(err.Error()) + SendResponse(r, err, nil) + return + } + SendResponse(r, nil, "插入分区日志成功") + return +} + +// CronEntries 查询定时任务 +func CronEntries(r *gin.Context) { + var entries []cron_pkg.Entry + for _, v := range cron.CronList { + entries = append(entries, v.Entries()...) + } + slog.Info("msg", "entries", entries) + SendResponse(r, nil, entries) + return +} + +// CronStop 关闭分区定时任务 +func CronStop(r *gin.Context) { + for _, v := range cron.CronList { + v.Stop() + } + SendResponse(r, nil, "关闭分区定时任务成功") + return +} + +// CronStart 开启分区定时任务 +func CronStart(r *gin.Context) { + for _, v := range cron.CronList { + v.Start() + } + SendResponse(r, nil, "开启分区定时任务成功") + return +} + // Response TODO type Response struct { Code int `json:"code"` @@ -196,7 +268,6 @@ func SendResponse(r *gin.Context, err error, data interface{}) { if ok { message += dataErr.Error() } - // always return http.StatusOK r.JSON(http.StatusOK, Response{ Code: code, diff --git a/dbm-services/mysql/db-partition/model/init_env.go b/dbm-services/mysql/db-partition/model/init_env.go index 500d8de46a..07c183d119 100644 --- a/dbm-services/mysql/db-partition/model/init_env.go +++ b/dbm-services/mysql/db-partition/model/init_env.go @@ -23,7 +23,6 @@ func InitEnv() { viper.BindEnv("cron.timing_hour", "CRON_TIMING_HOUR") viper.BindEnv("cron.retry_hour", "CRON_RETRY_HOUR") - viper.BindEnv("dbm_db_name", "DBM_DB_NAME") viper.BindEnv("db_remote_service", "DB_REMOTE_SERVICE") viper.BindEnv("db_meta_service", "DB_META_SERVICE") viper.BindEnv("dbm_ticket_service", "DBM_TICKET_SERVICE") diff --git a/dbm-services/mysql/db-partition/router/router.go b/dbm-services/mysql/db-partition/router/router.go index 84f537a779..1d227a46c7 100644 --- a/dbm-services/mysql/db-partition/router/router.go +++ b/dbm-services/mysql/db-partition/router/router.go @@ -18,4 +18,8 @@ func RegisterRouter(engine *gin.Engine) { p.POST("/disable_partition", handler.DisablePartition) p.POST("/enable_partition", handler.EnablePartition) p.POST("/update_conf", handler.UpdatePartitionsConfig) + p.POST("/create_log", handler.CreatePartitionLog) + p.POST("/cron_entries", handler.CronEntries) + p.POST("/cron_stop", handler.CronStop) + p.POST("/cron_start", handler.CronStart) } diff --git a/dbm-services/mysql/db-partition/service/check_partition.go b/dbm-services/mysql/db-partition/service/check_partition.go index 77e49b53cf..c1c873fe10 100644 --- a/dbm-services/mysql/db-partition/service/check_partition.go +++ b/dbm-services/mysql/db-partition/service/check_partition.go @@ -9,7 +9,7 @@ import ( "sync" "time" - "dbm-services/mysql/db-partition/errno" + "dbm-services/common/go-pubpkg/errno" "dbm-services/mysql/db-partition/model" "golang.org/x/exp/slog" @@ -39,7 +39,7 @@ func (m *Checker) DryRun() ([]PartitionObject, error) { case Tendbcluster: tbName = SpiderPartitionConfig default: - slog.Error(m.ClusterType, "error", errors.New("not supported db type")) + slog.Error(m.ClusterType, "error", errno.NotSupportedClusterType.Error()) return objects, errno.NotSupportedClusterType } if m.ConfigId == 0 { @@ -70,7 +70,6 @@ func (m *Checker) DryRun() ([]PartitionObject, error) { return objects, err } sqls, err = m.CheckPartitionConfigs(newConfigs, "mysql", 1) - // sqls, err = m.CheckPartitionConfigs(configs, "mysql", 1) if err != nil { slog.Error("msg", "CheckPartitionConfigs", err) return objects, err diff --git a/dbm-services/mysql/db-partition/service/check_partition_base_func.go b/dbm-services/mysql/db-partition/service/check_partition_base_func.go index 7e98983fb5..564d41411a 100644 --- a/dbm-services/mysql/db-partition/service/check_partition_base_func.go +++ b/dbm-services/mysql/db-partition/service/check_partition_base_func.go @@ -1,7 +1,6 @@ package service import ( - "encoding/json" "errors" "fmt" "regexp" @@ -10,12 +9,13 @@ import ( "sync" "time" - "dbm-services/mysql/db-partition/errno" + "golang.org/x/exp/slog" + + "dbm-services/common/go-pubpkg/errno" "dbm-services/mysql/db-partition/model" "dbm-services/mysql/db-partition/monitor" "github.com/spf13/viper" - "golang.org/x/exp/slog" ) // GetPartitionDbLikeTbLike TODO @@ -56,13 +56,15 @@ func (config *PartitionConfig) GetPartitionDbLikeTbLike(dbtype string, splitCnt return } AddString(&addSqls, sql) - sql, err = tb.GetDropPartitionSql() - if err != nil { - slog.Error("msg", "GetDropPartitionSql error", err) - AddString(&errs, err.Error()) - return + if tb.Phase == online { + sql, err = tb.GetDropPartitionSql() + if err != nil { + slog.Error("msg", "GetDropPartitionSql error", err) + AddString(&errs, err.Error()) + return + } + AddString(&dropSqls, sql) } - AddString(&dropSqls, sql) } else { sql, needSize, err = tb.GetInitPartitionSql(dbtype, splitCnt) if err != nil { @@ -97,26 +99,137 @@ func (config *PartitionConfig) GetDbTableInfo() (ptlist []ConfigDetail, err erro var queryRequest = QueryRequest{[]string{address}, []string{sql}, true, 30, config.BkCloudId} output, err = OneAddressExecuteSql(queryRequest) if err != nil { + slog.Error("GetDbTableInfo", sql, err.Error()) return nil, err } if len(output.CmdResults[0].TableData) == 0 { - return nil, errno.NoTableMatched.Add(fmt.Sprintf("db like: [%s] and table like: [%s]", config.DbLike, config.TbLike)) + return nil, errno.NoTableMatched.Add( + fmt.Sprintf("db like: [%s] and table like: [%s]", + strings.Replace(config.DbLike, "%", "%%", -1), config.TbLike)) } - fmt.Printf("output.CmdResults[0].TableData:%v\n", output.CmdResults[0].TableData) for _, row := range output.CmdResults[0].TableData { var partitioned bool + db := row["TABLE_SCHEMA"].(string) + tb := row["TABLE_NAME"].(string) if strings.Contains(row["CREATE_OPTIONS"].(string), "partitioned") { partitioned = true + //check分区字段、分区间隔 + sql = fmt.Sprintf("select PARTITION_EXPRESSION,PARTITION_METHOD,PARTITION_NAME from "+ + " information_schema.PARTITIONS where TABLE_SCHEMA like '%s' and TABLE_NAME like '%s' "+ + " order by PARTITION_DESCRIPTION asc limit 2;", + db, tb) + queryRequest = QueryRequest{[]string{address}, []string{sql}, true, 30, config.BkCloudId} + output, err = OneAddressExecuteSql(queryRequest) + if err != nil { + slog.Error("GetDbTableInfo", sql, err.Error()) + return nil, err + } + // 分区表至少会有一个分区 + for _, v := range output.CmdResults[0].TableData { + // 如果发现分区字段、分区间隔与规则不符合,需要重新做分区,页面调整了分区规则 + ok, errInner := CheckPartitionExpression(v["PARTITION_EXPRESSION"].(string), + v["PARTITION_METHOD"].(string), + config.PartitionColumn, config.PartitionType) + if errInner != nil { + slog.Error("CheckPartitionExpression", "error", errInner.Error()) + return nil, errInner + } else if !ok { + partitioned = false + break + } + } + if partitioned == true && len(output.CmdResults[0].TableData) == 2 { + ok, errInner := CalculateInterval(output.CmdResults[0].TableData[0]["PARTITION_NAME"].(string), + output.CmdResults[0].TableData[1]["PARTITION_NAME"].(string), config.PartitionTimeInterval) + if errInner != nil { + slog.Error("CalculateInterval", "error", errInner.Error()) + return nil, errInner + } else if !ok { + partitioned = false + } + } } - - partitionTable := ConfigDetail{PartitionConfig: *config, DbName: row["TABLE_SCHEMA"].(string), - TbName: row["TABLE_NAME"].(string), Partitioned: partitioned} + partitionTable := ConfigDetail{PartitionConfig: *config, DbName: db, + TbName: tb, Partitioned: partitioned} ptlist = append(ptlist, partitionTable) } slog.Info("finish getting all partition info") return ptlist, nil } +func CheckPartitionExpression(expression, method, column string, partitionType int) (bool, error) { + columnWithBackquote := fmt.Sprintf("`%s`", column) + switch partitionType { + case 0: + if (expression == fmt.Sprintf("to_days(%s)", column) || expression == + fmt.Sprintf("to_days(%s)", columnWithBackquote) || + expression == fmt.Sprintf("TO_DAYS(%s)", column) || + expression == fmt.Sprintf("TO_DAYS(%s)", columnWithBackquote)) && method == "RANGE" { + return true, nil + } + case 1: + if (expression == fmt.Sprintf("to_days(%s)", column) || expression == + fmt.Sprintf("to_days(%s)", columnWithBackquote) || + expression == fmt.Sprintf("TO_DAYS(%s)", column) || + expression == fmt.Sprintf("TO_DAYS(%s)", columnWithBackquote)) && method == "LIST" { + return true, nil + } + case 3: + if (expression == column || expression == columnWithBackquote) && method == "LIST" { + return true, nil + } + case 101: + if (expression == column || expression == columnWithBackquote) && method == "RANGE" { + return true, nil + } + case 4: + if (expression == column || expression == columnWithBackquote) && method == "RANGE COLUMNS" { + return true, nil + } + case 5: + if (expression == fmt.Sprintf("unix_timestamp(%s)", column) || expression == + fmt.Sprintf("unix_timestamp(%s)", columnWithBackquote) || + expression == fmt.Sprintf("UNIX_TIMESTAMP(%s)", column) || + expression == fmt.Sprintf("UNIX_TIMESTAMP(%s)", columnWithBackquote)) && method == "RANGE" { + return true, nil + } + default: + return true, errno.NotSupportedPartitionType + } + return false, nil +} + +func CalculateInterval(firstName, secondName string, interval int) (bool, error) { + reg := regexp.MustCompile(fmt.Sprintf("^%s$", "p[0-9]{8}")) + name := firstName + if !reg.MatchString(name) { + slog.Error("msg", "wrong name format", errno.WrongPartitionNameFormat.AddBefore(name)) + return true, errno.WrongPartitionNameFormat.AddBefore(name) + } + name = secondName + if !reg.MatchString(name) { + slog.Error("msg", "wrong name format", errno.WrongPartitionNameFormat.AddBefore(name)) + return true, errno.WrongPartitionNameFormat.AddBefore(name) + } + + firstName = strings.Replace(firstName, "p", "", -1) + secondName = strings.Replace(secondName, "p", "", -1) + t1, err := time.Parse("20060102", firstName) + if err != nil { + slog.Error("msg", "time parse error", err) + return true, err + } + t2, err := time.Parse("20060102", secondName) + if err != nil { + slog.Error("msg", "time parse error", err) + return true, err + } + if int(t2.Sub(t1).Hours()/24) != interval { + return false, nil + } + return true, nil +} + // GetDropPartitionSql 生成删除分区的sql func (m *ConfigDetail) GetDropPartitionSql() (string, error) { var sql, dropSql, fx string @@ -133,13 +246,14 @@ func (m *ConfigDetail) GetDropPartitionSql() (string, error) { case 3: fx = fmt.Sprintf(`DATE_FORMAT(date_sub(now(),interval %d day),'%%Y%%m%%d')`, reserve) case 101: + // 101类型分区,分区名和desc不相差一天,但是用了less than fx = fmt.Sprintf(`DATE_FORMAT(date_sub(now(),interval %d day),'%%Y%%m%%d')`, reserve-DiffOneDay) case 4: fx = fmt.Sprintf(`DATE_FORMAT(date_sub(now(),interval %d day),'\'%%Y-%%m-%%d\'')`, reserve-DiffOneDay) case 5: fx = fmt.Sprintf(`UNIX_TIMESTAMP(date_sub(curdate(),INTERVAL %d DAY))`, reserve-DiffOneDay) default: - return dropSql, errors.New("not supported partition type") + return dropSql, errno.NotSupportedPartitionType } sql = fmt.Sprintf("%s %s %s", base0, fx, base1) var queryRequest = QueryRequest{Addresses: []string{address}, Cmds: []string{sql}, Force: true, QueryTimeout: 30, @@ -205,14 +319,23 @@ func (m *ConfigDetail) GetInitPartitionSql(dbtype string, splitCnt int) (string, descFormat = "20060102" diff = 0 default: - return initSql, needSize, errors.New("不支持的分区类型") - } - - for i := -m.ReservedPartition; i < 15; i++ { - pname := time.Now().AddDate(0, 0, i*m.PartitionTimeInterval).Format("p20060102") - pdesc := time.Now().AddDate(0, 0, i*m.PartitionTimeInterval+diff).Format(descFormat) - palter := fmt.Sprintf(" partition %s values %s (%s)", pname, descKey, pdesc) - sqlPartitionDesc = append(sqlPartitionDesc, palter) + return initSql, needSize, errno.NotSupportedPartitionType + } + // 兼容历史遗留的PARTITION p20230325 VALUES LESS THAN (20230325)的格式,虽然是less than但是分区名和desc是同一天 + if m.PartitionType == 101 { + for i := -m.ReservedPartition + 1; i < m.ExtraPartition+1; i++ { + pname := time.Now().AddDate(0, 0, i*m.PartitionTimeInterval).Format("p20060102") + pdesc := time.Now().AddDate(0, 0, i*m.PartitionTimeInterval+diff).Format(descFormat) + palter := fmt.Sprintf(" partition %s values %s (%s)", pname, descKey, pdesc) + sqlPartitionDesc = append(sqlPartitionDesc, palter) + } + } else { + for i := -m.ReservedPartition; i < m.ExtraPartition; i++ { + pname := time.Now().AddDate(0, 0, i*m.PartitionTimeInterval).Format("p20060102") + pdesc := time.Now().AddDate(0, 0, i*m.PartitionTimeInterval+diff).Format(descFormat) + palter := fmt.Sprintf(" partition %s values %s (%s)", pname, descKey, pdesc) + sqlPartitionDesc = append(sqlPartitionDesc, palter) + } } // nohup /usr/bin/perl /data/dbbak/percona-toolkit-3.2.0/bin/pt-online-schema-change -uxxx -pxxx -S /data1/mysqldata/mysql.sock // --charset=utf8 --recursion-method=NONE --alter-foreign-keys-method=auto --alter "partition by xxx" @@ -291,6 +414,7 @@ func (m *ConfigDetail) GetAddPartitionSql() (string, error) { wantedName = "partition_description as WANTED_NAME" wantedNameIfOld = "DATE_FORMAT(now(),'%Y%m%d') as WANTED_NAME" case 101: + // 101类型分区,分区名和desc不相差一天,但是desc为今天,不能算在预留分区个数中,因为【less than 今天】存储的是历史数据,所以diff为1 diff = DiffOneDay descKey = "less than" fx = fmt.Sprintf(`DATE_FORMAT(date_add(now(),interval %d day),'%%Y%%m%%d')`, diff) @@ -313,12 +437,13 @@ func (m *ConfigDetail) GetAddPartitionSql() (string, error) { wantedDescIfOld = fmt.Sprintf(`UNIX_TIMESTAMP(DATE_ADD(curdate(),INTERVAL %d DAY)) as WANTED_DESC,`, diff) wantedNameIfOld = "DATE_FORMAT(now(),'%Y%m%d') as WANTED_NAME" default: - return addSql, errors.New("不支持的分区类型") + return addSql, errno.NotSupportedPartitionType } + // 可存储今日数据的分区是一个预留分区 vsql = fmt.Sprintf( "select count(*) as COUNT from INFORMATION_SCHEMA.PARTITIONS where TABLE_SCHEMA='%s' and TABLE_NAME='%s' "+ - "and PARTITION_DESCRIPTION> %s", m.DbName, m.TbName, fx) + "and partition_description>= %s", m.DbName, m.TbName, fx) var queryRequest = QueryRequest{Addresses: []string{address}, Cmds: []string{vsql}, Force: true, QueryTimeout: 30, BkCloudId: m.BkCloudId} output, err := OneAddressExecuteSql(queryRequest) @@ -365,7 +490,7 @@ func (m *ConfigDetail) GetAddPartitionSql() (string, error) { case 4: addSql, err = m.NewPartitionNameDescType4(begin, need, name, descKey) default: - return addSql, errors.New("不支持的分区类型") + return addSql, errno.NotSupportedPartitionType } addSql = fmt.Sprintf("alter table `%s`.`%s` add partition( %s", m.DbName, m.TbName, addSql) return addSql, nil @@ -430,23 +555,60 @@ func (m *ConfigDetail) NewPartitionNameDescType4(begin int, need int, name strin // GetSpiderBackends TODO func GetSpiderBackends(address string, bkCloudId int) (tableDataType, int, error) { var splitCnt int - vsql := "select HOST,PORT,replace(server_name,'SPT','') as SPLIT_NUM, SERVER_NAME, WRAPPER from mysql.servers " + - "where wrapper in ('mysql','TDBCTL') and (server_name like 'SPT%' or server_name like 'TDBCTL%') ;" - queryRequest := QueryRequest{Addresses: []string{address}, Cmds: []string{vsql}, Force: true, QueryTimeout: 30, + var tdbctlPrimary string + // 查询tdbctl + dbctlSql := "select HOST,PORT,server_name as SPLIT_NUM, SERVER_NAME, WRAPPER from mysql.servers " + + "where wrapper='TDBCTL' and server_name like 'TDBCTL%' ;" + getTdbctlPrimary := "tdbctl get primary;" + queryRequest := QueryRequest{Addresses: []string{address}, Cmds: []string{dbctlSql}, Force: true, QueryTimeout: 30, BkCloudId: bkCloudId} output, err := OneAddressExecuteSql(queryRequest) if err != nil { - return nil, splitCnt, fmt.Errorf("get spider info error: %s", err.Error()) + return nil, splitCnt, fmt.Errorf("execute [%s] get spider info error: %s", dbctlSql, err.Error()) } else if len(output.CmdResults[0].TableData) == 0 { - return nil, splitCnt, fmt.Errorf("no spider remote db or control spider found") + return nil, splitCnt, fmt.Errorf("no spider tdbctl found") } - vsql = - "select count(*) as COUNT from mysql.servers where WRAPPER='mysql' and SERVER_NAME like 'SPT%' group by host order by 1 desc limit 1;" - queryRequest = QueryRequest{Addresses: []string{address}, Cmds: []string{vsql}, Force: true, QueryTimeout: 30, + + // 查询tdbctl主节点 + for _, item := range output.CmdResults[0].TableData { + tdbctl := fmt.Sprintf("%s:%s", item["HOST"].(string), item["PORT"].(string)) + queryRequest = QueryRequest{Addresses: []string{tdbctl}, Cmds: []string{getTdbctlPrimary}, Force: true, + QueryTimeout: 30, BkCloudId: bkCloudId} + primary, err := OneAddressExecuteSql(queryRequest) + if err != nil { + slog.Warn(fmt.Sprintf("execute [%s] error: %s", getTdbctlPrimary, err.Error())) + continue + } + if len(primary.CmdResults[0].TableData) == 0 { + slog.Error(fmt.Sprintf("execute [%s] nothing return", getTdbctlPrimary)) + return nil, splitCnt, fmt.Errorf("execute [%s] nothing return", getTdbctlPrimary) + } + slog.Info("data:", primary.CmdResults[0].TableData) + tdbctlPrimary = primary.CmdResults[0].TableData[0]["SERVER_NAME"].(string) + break + } + if tdbctlPrimary == "" { + slog.Error(fmt.Sprintf("execute [%s] SERVER_NAME is null", getTdbctlPrimary)) + return nil, splitCnt, fmt.Errorf("execute [%s] SERVER_NAME is null", getTdbctlPrimary) + } + // 查询remote master各分片实例和tdbctl主节点 + splitSql := fmt.Sprintf("select HOST,PORT,replace(server_name,'SPT','') as SPLIT_NUM, SERVER_NAME, WRAPPER "+ + "from mysql.servers where wrapper in ('mysql','TDBCTL') and "+ + "(server_name like 'SPT%%' or server_name like '%s')", tdbctlPrimary) + queryRequest = QueryRequest{Addresses: []string{address}, Cmds: []string{splitSql}, Force: true, QueryTimeout: 30, + BkCloudId: bkCloudId} + output, err = OneAddressExecuteSql(queryRequest) + if err != nil { + return nil, splitCnt, fmt.Errorf("execute [%s] get spider remote and tdbctl master error: %s", splitSql, err.Error()) + } + // 查询一台remote机器上有多少个实例,用于评估存储空间 + cntSql := "select count(*) as COUNT from mysql.servers where WRAPPER='mysql' and " + + "SERVER_NAME like 'SPT%' group by host order by 1 desc limit 1;" + queryRequest = QueryRequest{Addresses: []string{address}, Cmds: []string{cntSql}, Force: true, QueryTimeout: 30, BkCloudId: bkCloudId} output1, err := OneAddressExecuteSql(queryRequest) if err != nil { - return nil, splitCnt, fmt.Errorf("get spider split count error: %s", err.Error()) + return nil, splitCnt, fmt.Errorf("execute [%s] get spider split count error: %s", cntSql, err.Error()) } splitCnt, _ = strconv.Atoi(output1.CmdResults[0].TableData[0]["COUNT"].(string)) return output.CmdResults[0].TableData, splitCnt, nil @@ -467,44 +629,34 @@ func CreatePartitionTicket(check Checker, objects []PartitionObject, zoneOffset content := fmt.Sprintf("partition error. create ticket fail: %s", err.Error()) monitor.SendEvent(monitor.PartitionEvent, dimension, content, "0.0.0.0") slog.Error("msg", fmt.Sprintf("create ticket fail: %v", ticket), err) - AddLog(check.ConfigId, check.BkBizId, check.ClusterId, *check.BkCloudId, 0, check.ImmuteDomain, zone, date, scheduler, - "{}", + _ = AddLog(check.ConfigId, check.BkBizId, check.ClusterId, *check.BkCloudId, + 0, check.ImmuteDomain, zone, date, scheduler, content, CheckFailed, check.ClusterType) return } - bytes, err := json.Marshal(ticket) - if err != nil { - bytes = []byte("{}") - slog.Error("msg", "ticket marshal failed", err) - } - AddLog(check.ConfigId, check.BkBizId, check.ClusterId, *check.BkCloudId, id, check.ImmuteDomain, - zone, date, scheduler, string(bytes), "", ExecuteAsynchronous, check.ClusterType) + _ = AddLog(check.ConfigId, check.BkBizId, check.ClusterId, *check.BkCloudId, id, check.ImmuteDomain, + zone, date, scheduler, "", ExecuteAsynchronous, check.ClusterType) } // NeedPartition TODO func NeedPartition(cronType string, clusterType string, zoneOffset int, cronDate string) ([]*Checker, error) { - var configTb, logTb, ticket string - var all, successed, doNothing []*Checker + var configTb, logTb string + var all, doNothing []*Checker switch clusterType { case Tendbha, Tendbsingle: configTb = MysqlPartitionConfig logTb = MysqlPartitionCronLogTable - ticket = MysqlPartition case Tendbcluster: configTb = SpiderPartitionConfig logTb = SpiderPartitionCronLogTable - ticket = SpiderPartition default: return nil, errors.New("不支持的db类型") } vzone := fmt.Sprintf("%+03d:00", zoneOffset) vsql := fmt.Sprintf( - "select conf.id as config_id, conf.bk_biz_id as bk_biz_id, conf.cluster_id as cluster_id,"+ - "conf.immute_domain as immute_domain, conf.port as port, conf.bk_cloud_id as bk_cloud_id,"+ - "cluster.cluster_type as cluster_type from `%s`.`%s` as conf,`%s`.db_meta_cluster "+ - "as cluster where conf.cluster_id=cluster.id and cluster.time_zone='%s' and "+ - "conf.phase='online' order by 2,3;", - viper.GetString("db.name"), configTb, viper.GetString("dbm_db_name"), vzone) + "select id as config_id, bk_biz_id, cluster_id, immute_domain, port, bk_cloud_id,"+ + " '%s' as cluster_type from `%s`.`%s` where time_zone='%s' order by 2,3;", + clusterType, viper.GetString("db.name"), configTb, vzone) slog.Info(vsql) err := model.DB.Self.Raw(vsql).Scan(&all).Error if err != nil { @@ -515,46 +667,20 @@ func NeedPartition(cronType string, clusterType string, zoneOffset int, cronDate if cronType == "daily" { return all, nil } - vsql = fmt.Sprintf( - "select conf.id as config_id from `%s`.`%s` as conf,`%s`.db_meta_cluster as cluster, "+ - "`%s`.`%s` as log,`%s`.ticket_ticket as ticket "+ - "where conf.cluster_id=cluster.id and conf.id=log.config_id and ticket.id=log.ticket_id "+ - "and cluster.time_zone='%s' and log.cron_date='%s' "+ - "and ticket.remark='auto partition' and ticket.ticket_type='%s' "+ - "and (ticket.status='SUCCEEDED' or ticket.status='RUNNING')", - viper.GetString("db.name"), configTb, viper.GetString("dbm_db_name"), - viper.GetString("db.name"), logTb, viper.GetString("dbm_db_name"), vzone, cronDate, ticket) - slog.Info(vsql) - err = model.DB.Self.Raw(vsql).Scan(&successed).Error - if err != nil { - slog.Error(vsql, "execute err", err) - return nil, err - } - slog.Info("successed", successed) - vsql = fmt.Sprintf("select conf.id as config_id from `%s`.`%s` as conf,`%s`.db_meta_cluster as cluster, "+ - "`%s`.`%s` as log where conf.cluster_id=cluster.id and conf.id=log.config_id "+ - "and cluster.time_zone='%s' and log.cron_date='%s' and log.status like '%s'", - viper.GetString("db.name"), configTb, viper.GetString("dbm_db_name"), - viper.GetString("db.name"), logTb, vzone, cronDate, CheckSucceeded) + vsql = fmt.Sprintf("select conf.id as config_id from `%s`.`%s` as conf,"+ + "`%s`.`%s` as log where conf.id=log.config_id "+ + "and conf.time_zone='%s' and log.cron_date='%s' and log.status like '%s'", + viper.GetString("db.name"), configTb, viper.GetString("db.name"), + logTb, vzone, cronDate, CheckSucceeded) slog.Info(vsql) err = model.DB.Self.Raw(vsql).Scan(&doNothing).Error if err != nil { slog.Error(vsql, "execute err", err) return nil, err } - slog.Info("doNothing", doNothing) var need []*Checker for _, item := range all { retryFlag := true - for _, ok := range successed { - if (*item).ConfigId == (*ok).ConfigId { - retryFlag = false - break - } - } - if retryFlag == false { - continue - } for _, ok := range doNothing { if (*item).ConfigId == (*ok).ConfigId { retryFlag = false @@ -565,7 +691,6 @@ func NeedPartition(cronType string, clusterType string, zoneOffset int, cronDate need = append(need, item) } } - slog.Info("need", need) return need, nil } @@ -597,8 +722,8 @@ func GetMaster(configs []*PartitionConfig, immuteDomain, clusterType string) ([] } // AddLog TODO -func AddLog(configId, bkBizId, clusterId, bkCloudId, ticketId int, immuteDomain, zone, date, scheduler, detailJson, - info, checkStatus, clusterType string) { +func AddLog(configId, bkBizId, clusterId, bkCloudId, ticketId int, immuteDomain, zone, date, scheduler, + info, checkStatus, clusterType string) error { tx := model.DB.Self.Begin() tb := MysqlPartitionCronLogTable if clusterType == Tendbcluster { @@ -606,13 +731,15 @@ func AddLog(configId, bkBizId, clusterId, bkCloudId, ticketId int, immuteDomain, } log := &PartitionCronLog{ConfigId: configId, BkBizId: bkBizId, ClusterId: clusterId, TicketId: ticketId, ImmuteDomain: immuteDomain, BkCloudId: bkCloudId, TimeZone: zone, CronDate: date, Scheduler: scheduler, - TicketDetail: detailJson, CheckInfo: info, Status: checkStatus} + CheckInfo: info, Status: checkStatus} err := tx.Debug().Table(tb).Create(log).Error if err != nil { tx.Rollback() - slog.Error("msg", "add con log failed", err) + slog.Error("msg", "add cron log failed", err) + return err } tx.Commit() + return nil } // AddInit TODO diff --git a/dbm-services/mysql/db-partition/service/check_partition_object.go b/dbm-services/mysql/db-partition/service/check_partition_object.go index 40ff1cd207..f59a9565e7 100644 --- a/dbm-services/mysql/db-partition/service/check_partition_object.go +++ b/dbm-services/mysql/db-partition/service/check_partition_object.go @@ -47,19 +47,22 @@ type PartitionCronLog struct { BkCloudId int `json:"bk_cloud_id" gorm:"column:bk_cloud_id"` TimeZone string `json:"time_zone" gorm:"column:time_zone"` CronDate string `json:"cron_date" grom:"column:cron_date"` - TicketDetail string `json:"ticket_detail" gorm:"column:ticket_detail"` CheckInfo string `json:"check_info" gorm:"column:check_info"` Status string `json:"status" gorm:"column:status"` } +type CreatePartitionCronLog struct { + PartitionCronLog + ClusterType string `json:"cluster_type"` +} + // PartitionLog TODO type PartitionLog struct { - Id int `json:"id"` - TicketId int `json:"ticket_id" gorm:"column:ticket_id"` - TicketStatus string `json:"ticket_status" gorm:"ticket_status"` - ExecuteTime time.Time `json:"execute_time" gorm:"execute_time"` - CheckInfo string `json:"check_info" gorm:"check_info"` - Status string `json:"status" gorm:"status"` + Id int `json:"id"` + TicketId int `json:"ticket_id" gorm:"column:ticket_id"` + ExecuteTime time.Time `json:"execute_time" gorm:"execute_time"` + CheckInfo string `json:"check_info" gorm:"check_info"` + Status string `json:"status" gorm:"status"` } // InitMessages TODO diff --git a/dbm-services/mysql/db-partition/service/db_meta_service.go b/dbm-services/mysql/db-partition/service/db_meta_service.go index b55c2a3f8d..e75afb9e82 100644 --- a/dbm-services/mysql/db-partition/service/db_meta_service.go +++ b/dbm-services/mysql/db-partition/service/db_meta_service.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package service import ( @@ -5,7 +15,7 @@ import ( "fmt" "net/http" - "dbm-services/mysql/db-partition/errno" + "dbm-services/common/go-pubpkg/errno" "dbm-services/mysql/db-partition/util" "github.com/spf13/viper" diff --git a/dbm-services/mysql/db-partition/service/execute_partition_object.go b/dbm-services/mysql/db-partition/service/execute_partition_object.go index 5ee4d0adb9..9a3458b3c9 100644 --- a/dbm-services/mysql/db-partition/service/execute_partition_object.go +++ b/dbm-services/mysql/db-partition/service/execute_partition_object.go @@ -1,52 +1,64 @@ +// Package service TODO package service import ( "time" ) -// PartitionConfig TODO +// PartitionConfig 分区配置表 type PartitionConfig struct { - ID int `json:"id" gorm:"column:id;primary_key;auto_increment"` - BkBizId int `json:"bk_biz_id" gorm:"column:bk_biz_id"` - ImmuteDomain string `json:"immute_domain" gorm:"column:immute_domain"` - Port int `json:"port" gorm:"column:port"` - BkCloudId int `json:"bk_cloud_id" gorm:"column:bk_cloud_id"` - ClusterId int `json:"cluster_id" gorm:"column:cluster_id"` - DbLike string `json:"dblike" gorm:"column:dblike"` - TbLike string `json:"tblike" gorm:"column:tblike"` - PartitionColumn string `json:"partition_columns" gorm:"column:partition_column"` - PartitionColumnType string `json:"partition_column_type" gorm:"column:partition_column_type"` - ReservedPartition int `json:"reserved_partition" gorm:"column:reserved_partition"` - ExtraPartition int `json:"extra_partition" gorm:"column:extra_partition"` - PartitionTimeInterval int `json:"partition_time_interval" gorm:"column:partition_time_interval"` - PartitionType int `json:"partition_type" gorm:"column:partition_type"` - ExpireTime int `json:"expire_time"` - Phase string `json:"phase" gorm:"column:phase"` - Creator string `json:"creator" gorm:"column:creator"` - Updator string `json:"updator" gorm:"column:updator"` - CreateTime time.Time `json:"create_time" gorm:"column:create_time"` - UpdateTime time.Time `json:"update_time" gorm:"column:update_time"` + ID int `json:"id" gorm:"column:id;primary_key;auto_increment"` + BkBizId int `json:"bk_biz_id" gorm:"column:bk_biz_id"` + ImmuteDomain string `json:"immute_domain" gorm:"column:immute_domain"` + Port int `json:"port" gorm:"column:port"` + BkCloudId int `json:"bk_cloud_id" gorm:"column:bk_cloud_id"` + ClusterId int `json:"cluster_id" gorm:"column:cluster_id"` + DbLike string `json:"dblike" gorm:"column:dblike"` + TbLike string `json:"tblike" gorm:"column:tblike"` + PartitionColumn string `json:"partition_columns" gorm:"column:partition_column"` + PartitionColumnType string `json:"partition_column_type" gorm:"column:partition_column_type"` + // 保留的分区个数 ReservedPartition := ExpireTime / PartitionTimeInterval + ReservedPartition int `json:"reserved_partition" gorm:"column:reserved_partition"` + ExtraPartition int `json:"extra_partition" gorm:"column:extra_partition"` + // 分区间隔 + PartitionTimeInterval int `json:"partition_time_interval" gorm:"column:partition_time_interval"` + PartitionType int `json:"partition_type" gorm:"column:partition_type"` + // 数据过期天数 + ExpireTime int `json:"expire_time"` + // 集群所在的时区 + TimeZone string `json:"time_zone"` + // 分区规则启用或者禁用 + Phase string `json:"phase" gorm:"column:phase"` + Creator string `json:"creator" gorm:"column:creator"` + Updator string `json:"updator" gorm:"column:updator"` + CreateTime time.Time `json:"create_time" gorm:"column:create_time"` + UpdateTime time.Time `json:"update_time" gorm:"column:update_time"` } -// PartitionConfigWithLog TODO +// PartitionConfigWithLog 分区配置以及执行日志 type PartitionConfigWithLog struct { PartitionConfig - ExecuteTime time.Time `json:"execute_time" gorm:"execute_time"` - TicketId int `json:"ticket_id" gorm:"ticket_id"` - Status string `json:"status" gorm:"status"` - TicketStatus string `json:"ticket_status" gorm:"ticket_status"` - CheckInfo string `json:"check_info" gorm:"check_info"` + // 这里故意设置为string而不是time.Time,因为当值为null会被转换为1-01-01 08:00:00 + ExecuteTime string `json:"execute_time" gorm:"execute_time"` + TicketId int `json:"ticket_id" gorm:"ticket_id"` + // 分区任务的状态 + Status string `json:"status" gorm:"status"` + // 分区单据的状态 + TicketStatus string `json:"ticket_status" gorm:"ticket_status"` + // 分区检查的结果 + CheckInfo string `json:"check_info" gorm:"check_info"` } -// ConfigDetail TODO +// ConfigDetail 具体到库表的分区配置 type ConfigDetail struct { PartitionConfig - DbName string `json:"dbname"` - TbName string `json:"tbname"` - Partitioned bool `json:"partitioned"` + DbName string `json:"dbname"` + TbName string `json:"tbname"` + // 是否已经分区 + Partitioned bool `json:"partitioned"` } -// Ticket TODO +// Ticket 分区单据 type Ticket struct { BkBizId int `json:"bk_biz_id"` TicketType string `json:"ticket_type"` @@ -54,18 +66,18 @@ type Ticket struct { Details Detail `json:"details"` } -// Details TODO +// Details 单据参数 type Details struct { Infos []Info `json:"infos"` Clusters ClustersResponse `json:"clusters"` } -// ClustersResponse TODO +// ClustersResponse 用于创建单据 type ClustersResponse struct { ClusterResponse map[string]ClusterResponse `json:"cluster_response"` } -// ClusterResponse TODO +// ClusterResponse 用于创建单据 type ClusterResponse struct { Id int `json:"id"` Creator string `json:"creator"` @@ -85,12 +97,12 @@ type ClusterResponse struct { ClusterTypeName string `json:"cluster_type_name"` } -// Detail TODO +// Detail 用于创建单据 type Detail struct { Infos []Info `json:"infos"` } -// Info TODO +// Info 用于创建单据 type Info struct { ConfigId int `json:"config_id"` ClusterId int `json:"cluster_id"` @@ -99,7 +111,7 @@ type Info struct { PartitionObjects []PartitionObject `json:"partition_objects"` } -// PartitionObject TODO +// PartitionObject 待执行的分区语句集合 type PartitionObject struct { Ip string `json:"ip"` Port int `json:"port"` @@ -107,81 +119,17 @@ type PartitionObject struct { ExecuteObjects []PartitionSql `json:"execute_objects"` } -// ExecResult TODO -type ExecResult struct { - IsSuccess bool - Sql string - Msg string +// PartitionLogs TODO +type PartitionLogs struct { + ID int `gorm:"column:id;primary_key;auto_increment` + BkBizId int `gorm:"column:bk_biz_id"` + Operator string `gorm:"column:operator"` + Para string `gorm:"column:para"` + ExecuteTime time.Time `gorm:"column:execute_time"` } -// Result TODO -type Result struct { - ExecResult - DbName string - Action string +// PartitionLogsParam TODO +type PartitionLogsParam struct { + Para string + Err error } - -/* -show create table mysql_partition_conf\G -*************************** 1. row *************************** - Table: mysql_partition_conf -Create Table: CREATE TABLE `mysql_partition_conf` ( - `ID` bigint(20) NOT NULL AUTO_INCREMENT, - `App` varchar(100) NOT NULL, - `Module` varchar(100) NOT NULL, - `Ip` varchar(100) NOT NULL, - `Port` int(11) NOT NULL, - `DbLike` varchar(100) NOT NULL, - `PartitionTableName` varchar(100) NOT NULL, - `PartitionColumn` varchar(100) DEFAULT NULL, - `PartitionColumnType` varchar(100) DEFAULT NULL, - `ReservedPartition` int(11) NOT NULL, - `ExtraPartition` int(11) NOT NULL, - `PartitionTimeInterval` int(11) NOT NULL, - `PartitionTimeWay` enum('DAY','MONTH') DEFAULT NULL, - `PartitionType` int(11) NOT NULL, - `IsExchange` int(11) NOT NULL DEFAULT '0', - `HeartBeat` datetime NOT NULL DEFAULT '2000-01-01 00:00:00', - `Alive` int(11) NOT NULL DEFAULT '3' COMMENT '1:success,2:failed,3:not execute,4+:others', - `DoSuccess` int(11) DEFAULT NULL, - `DoFailed` int(11) DEFAULT NULL, - `Creator` varchar(100) DEFAULT NULL, - `Updator` varchar(100) DEFAULT NULL, - `CreateTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `UpdateTime` timestamp NOT NULL DEFAULT '2000-01-01 00:00:00', - PRIMARY KEY (`ID`), - UNIQUE KEY `UK_MPT` (`App`,`Module`,`Ip`,`Port`,`DbLike`,`PartitionTableName`), - UNIQUE KEY `uk_IPDT` (`Ip`,`Port`,`DbLike`,`PartitionTableName`), - KEY `idx_app_alive` (`App`,`Alive`), - KEY `IDX_DT` (`DbLike`,`PartitionTableName`), - KEY `IDX_IDT` (`Ip`,`DbLike`,`PartitionTableName`) -) ENGINE=InnoDB AUTO_INCREMENT=74533 DEFAULT CHARSET=utf8mb4 -1 row in set (0.00 sec) - -select * from mysql_partition_conf limit 1\G -*************************** 1. row *************************** - ID: 25 - App: web - Module: web - Ip: gamedb.amspoint16.web.db - Port: 10000 - DbLike: dbcaccts - PartitionTableName: t_acct_water_0 - PartitionColumn: Fcreate_time - PartitionColumnType: timestamp - ReservedPartition: 30 - ExtraPartition: 14 -PartitionTimeInterval: 1 - PartitionTimeWay: DAY - PartitionType: 5 - IsExchange: 0 - HeartBeat: 2023-02-12 01:30:08 - Alive: 1 - DoSuccess: 2 - DoFailed: 0 - Creator: NULL - Updator: NULL - CreateTime: 0000-00-00 00:00:00 - UpdateTime: 2000-01-01 00:00:00 -1 row in set (0.00 sec) -*/ diff --git a/dbm-services/mysql/db-partition/service/manage_config.go b/dbm-services/mysql/db-partition/service/manage_config.go index 3d4d96bd3c..55551b83e5 100644 --- a/dbm-services/mysql/db-partition/service/manage_config.go +++ b/dbm-services/mysql/db-partition/service/manage_config.go @@ -1,6 +1,17 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package service import ( + "encoding/json" "errors" "fmt" "regexp" @@ -8,26 +19,28 @@ import ( "strings" "time" - "dbm-services/mysql/db-partition/errno" + "dbm-services/common/go-pubpkg/errno" "dbm-services/mysql/db-partition/model" - "github.com/spf13/viper" "golang.org/x/exp/slog" ) // GetPartitionsConfig TODO func (m *QueryParititionsInput) GetPartitionsConfig() ([]*PartitionConfigWithLog, int64, error) { allResults := make([]*PartitionConfigWithLog, 0) - var tbName string + var configTb, logTb string + // Cnt 用于返回匹配到的行数 type Cnt struct { Count int64 `gorm:"column:cnt"` } // 判断是mysql集群还是spider集群 switch strings.ToLower(m.ClusterType) { case Tendbha, Tendbsingle: - tbName = MysqlPartitionConfig + configTb = MysqlPartitionConfig + logTb = MysqlPartitionCronLogTable case Tendbcluster: - tbName = SpiderPartitionConfig + configTb = SpiderPartitionConfig + logTb = SpiderPartitionCronLogTable default: return nil, 0, errors.New("不支持的db类型") } @@ -35,41 +48,55 @@ func (m *QueryParititionsInput) GetPartitionsConfig() ([]*PartitionConfigWithLog if m.BkBizId > 0 { where = fmt.Sprintf("%s and config.bk_biz_id=%d", where, m.BkBizId) } + if len(m.Ids) != 0 { + var temp = make([]string, len(m.Ids)) + for k, id := range m.Ids { + temp[k] = strconv.FormatInt(id, 10) + } + ids := " and config.id in (" + strings.Join(temp, ",") + ") " + where = where + ids + } if len(m.ImmuteDomains) != 0 { dns := " and config.immute_domain in ('" + strings.Join(m.ImmuteDomains, "','") + "') " where = where + dns } if len(m.DbLikes) != 0 { - dblike := "and config.dblike in ('" + strings.Join(m.DbLikes, "','") + "') " + dblike := " and config.dblike in ('" + strings.Join(m.DbLikes, "','") + "') " where = where + dblike } if len(m.TbLikes) != 0 { - tblike := "and config.tblike in ('" + strings.Join(m.TbLikes, "','") + "') " + tblike := " and config.tblike in ('" + strings.Join(m.TbLikes, "','") + "') " where = where + tblike } cnt := Cnt{} - vsql := fmt.Sprintf("select count(*) as cnt from `%s`", tbName) - err := model.DB.Self.Debug().Table(tbName).Raw(vsql).Scan(&cnt).Error + vsql := fmt.Sprintf("select count(*) as cnt from `%s` as config where %s", configTb, where) + err := model.DB.Self.Debug().Raw(vsql).Scan(&cnt).Error if err != nil { slog.Error(vsql, "execute error", err) return nil, 0, err } + limitCondition := fmt.Sprintf("limit %d offset %d", m.Limit, m.Offset) - condition := fmt.Sprintf("%s %s", where, limitCondition) - // ticket_id NULL,规则没有被执行过 - vsql = fmt.Sprintf("SELECT config.*, d.create_time as execute_time, "+ - "d.ticket_id as ticket_id, d.ticket_status as ticket_status, d.check_info as check_info, "+ - "d.status as status FROM "+ - "%s AS config LEFT JOIN (SELECT c.*, ticket.status as ticket_status FROM "+ - "(SELECT a.* FROM partition_cron_log AS a, "+ + condition := fmt.Sprintf("%s order by config.id desc %s", where, limitCondition) + /* + 一、ticket_id是非0的整数,则表示,最近一次任务生成了分区单据,单据执行状态就是最近这次任务的状态,需要从dbm ticket_ticket表中获取状态。 + 二、ticket_id是0,表示最近一次任务没有生成分区单据,status状态表示最近这次任务的状态,FAILED或者SUCCEEDED。 + (1)FAILED: 可能因为dry_run获取分区语句失败等,check_info显示错误信息; + (2)SUCCEEDED: 表示dry_run成功,并且没有需要实施的分区操作,无需创建单据。 + 三、ticket_id是NULL,status是NULL,分区规则还没有执行过 + */ + vsql = fmt.Sprintf("SELECT config.*, logs.create_time as execute_time, "+ + "logs.ticket_id as ticket_id, logs.check_info as check_info, "+ + "logs.status as status FROM "+ + "%s AS config LEFT JOIN "+ + "(SELECT log.* FROM %s AS log, "+ "(SELECT inner_config.id AS config_id, MAX(inner_log.id) AS log_id FROM "+ "%s AS inner_config LEFT JOIN "+ - "partition_cron_log AS inner_log ON inner_config.id = inner_log.config_id where "+ + "%s AS inner_log ON inner_config.id = inner_log.config_id where "+ "inner_log.create_time > DATE_SUB(now(),interval 100 day) GROUP BY inner_config.id) "+ - "AS b WHERE a.id = b.log_id) AS c LEFT JOIN `%s`.ticket_ticket AS ticket "+ - "ON ticket.id = c.ticket_id) AS d ON config.id = d.config_id where %s;", tbName, tbName, - viper.GetString("dbm_db_name"), condition) - err = model.DB.Self.Debug().Table(tbName).Raw(vsql).Scan(&allResults).Error + "AS latest_log WHERE log.id = latest_log.log_id) AS logs ON config.id = logs.config_id where %s ", + configTb, logTb, configTb, logTb, condition) + err = model.DB.Self.Debug().Raw(vsql).Scan(&allResults).Error if err != nil { slog.Error(vsql, "execute error", err) return nil, 0, err @@ -80,35 +107,38 @@ func (m *QueryParititionsInput) GetPartitionsConfig() ([]*PartitionConfigWithLog // GetPartitionLog TODO func (m *QueryLogInput) GetPartitionLog() ([]*PartitionLog, int64, error) { allResults := make([]*PartitionLog, 0) - var tbName string + var logTb string switch strings.ToLower(m.ClusterType) { case Tendbha, Tendbsingle: - tbName = MysqlPartitionConfig + logTb = MysqlPartitionCronLogTable case Tendbcluster: - tbName = SpiderPartitionConfig + logTb = SpiderPartitionCronLogTable default: return nil, 0, errors.New("不支持的db类型") } + // Cnt 用于返回匹配到的行数 type Cnt struct { Count int64 `gorm:"column:cnt"` } - vsql := fmt.Sprintf("select logs.*,ticket.status as ticket_status from "+ - "(select config.id as id, log.ticket_id as ticket_id, log.create_time as execute_time, "+ - "log.check_info as check_info, log.status as status "+ - "from %s as config join partition_cron_log as log "+ - "where log.config_id=config.id and config.id=%d and log.create_time>"+ - "DATE_SUB(now(),interval 100 day)) as logs left join "+ - "`%s`.ticket_ticket as ticket on ticket.id=logs.ticket_id where "+ - "ticket.create_at > DATE_SUB(now(),interval 100 day)) "+ - "order by execute_time desc ", tbName, m.ConfigId, viper.GetString("dbm_db_name")) - err := model.DB.Self.Debug().Table(tbName).Raw(vsql).Scan(&allResults).Error + where := fmt.Sprintf(` config_id=%d and create_time> DATE_SUB(now(),interval 100 day) `, m.ConfigId) + if m.StartTime != "" && m.EndTime != "" { + where = fmt.Sprintf(` config_id=%d and create_time>'%s' and create_time<'%s' `, m.ConfigId, m.StartTime, m.EndTime) + } + limitCondition := fmt.Sprintf(" limit %d offset %d ", m.Limit, m.Offset) + cnt := Cnt{} + vsql := fmt.Sprintf("select count(*) as cnt from `%s` where %s", logTb, where) + err := model.DB.Self.Debug().Raw(vsql).Scan(&cnt).Error if err != nil { + slog.Error(vsql, "execute error", err) return nil, 0, err } - cnt := Cnt{} - countSQL := fmt.Sprintf("select count(*) as cnt from (%s) c", vsql) - err = model.DB.Self.Debug().Table(tbName).Raw(countSQL).Scan(&cnt).Error + + vsql = fmt.Sprintf("select id, ticket_id, create_time as execute_time, "+ + "check_info, status from %s where %s order by execute_time desc %s", + logTb, where, limitCondition) + err = model.DB.Self.Debug().Raw(vsql).Scan(&allResults).Error if err != nil { + slog.Error(vsql, "execute error", err) return nil, 0, err } return allResults, cnt.Count, nil @@ -150,7 +180,7 @@ func (m *DeletePartitionConfigByIds) DeletePartitionsConfig() error { } // CreatePartitionsConfig TODO -func (m *CreatePartitionsInput) CreatePartitionsConfig() error { +func (m *CreatePartitionsInput) CreatePartitionsConfig() (error, []int) { var tbName string switch strings.ToLower(m.ClusterType) { case Tendbha, Tendbsingle: @@ -158,28 +188,27 @@ func (m *CreatePartitionsInput) CreatePartitionsConfig() error { case Tendbcluster: tbName = SpiderPartitionConfig default: - return errors.New("错误的db类型") + return errors.New("错误的db类型"), []int{} } if len(m.PartitionColumn) == 0 { - return errors.New("请输入分区字段!") + return errors.New("请输入分区字段!"), []int{} } if len(m.DbLikes) == 0 || len(m.TbLikes) == 0 { - return errors.New("库表名不能为空!") + return errors.New("库表名不能为空!"), []int{} } if m.PartitionTimeInterval < 1 { - return errors.New("分区间隔不能小于1") + return errors.New("分区间隔不能小于1"), []int{} } if m.ExpireTime < m.PartitionTimeInterval { - return errors.New("过期时间必须不小于分区间隔") + return errors.New("过期时间必须不小于分区间隔"), []int{} } if m.ExpireTime%m.PartitionTimeInterval != 0 { - return errors.New("过期时间必须是分区间隔的整数倍") + return errors.New("过期时间必须是分区间隔的整数倍"), []int{} } - reservedPartition := m.ExpireTime / m.PartitionTimeInterval partitionType := 0 switch m.PartitionColumnType { @@ -190,26 +219,23 @@ func (m *CreatePartitionsInput) CreatePartitionsConfig() error { case "int": partitionType = 101 default: - return errors.New("请选择分区字段类型:datetime、timestamp或int") + return errors.New("请选择分区字段类型:datetime、timestamp或int"), []int{} } var errs []string warnings1, err := m.compareWithSameArray() if err != nil { - fmt.Println(err) - return err + return err, []int{} } - fmt.Println(warnings1) warnings2, err := m.compareWithExistDB(tbName) if err != nil { - fmt.Println(err) - return err + return err, []int{} } warnings := append(warnings1, warnings2...) if len(warnings) > 0 { - return errors.New(strings.Join(warnings, "\n")) + return errors.New(strings.Join(warnings, "\n")), []int{} } - + var configIDs []int for _, dblike := range m.DbLikes { for _, tblike := range m.TbLikes { partitionConfig := PartitionConfig{ @@ -227,6 +253,7 @@ func (m *CreatePartitionsInput) CreatePartitionsConfig() error { PartitionTimeInterval: m.PartitionTimeInterval, PartitionType: partitionType, ExpireTime: m.ExpireTime, + TimeZone: m.TimeZone, Creator: m.Creator, Updator: m.Updator, Phase: online, @@ -234,15 +261,16 @@ func (m *CreatePartitionsInput) CreatePartitionsConfig() error { UpdateTime: time.Now(), } result := model.DB.Self.Debug().Table(tbName).Create(&partitionConfig) + configIDs = append(configIDs, partitionConfig.ID) if result.Error != nil { errs = append(errs, result.Error.Error()) } } } if len(errs) > 0 { - return fmt.Errorf("errors: %s", strings.Join(errs, "\n")) + return fmt.Errorf("errors: %s", strings.Join(errs, "\n")), []int{} } - return nil + return nil, configIDs } // UpdatePartitionsConfig TODO @@ -278,6 +306,7 @@ func (m *CreatePartitionsInput) UpdatePartitionsConfig() error { reservedPartition := m.ExpireTime / m.PartitionTimeInterval partitionType := 0 + switch m.PartitionColumnType { case "datetime": partitionType = 0 @@ -291,37 +320,53 @@ func (m *CreatePartitionsInput) UpdatePartitionsConfig() error { var errs []string for _, dblike := range m.DbLikes { for _, tblike := range m.TbLikes { - update_condition := fmt.Sprintf("bk_biz_id=%d and immute_domain='%s' and dblike='%s' and tblike='%s'", - m.BkBizId, m.ImmuteDomain, dblike, tblike) - var update_column struct { - PartitionColumn string - PartitionColumnType string - ReservedPartition int - ExtraPartition int - PartitionTimeInterval int - PartitionType int - ExpireTime int - Creator string - Updator string + update_column_map := map[string]interface{}{ + "partition_column": m.PartitionColumn, + "partition_column_type": m.PartitionColumnType, + "reserved_partition": reservedPartition, + "extra_partition": extraTime, + "partition_time_interval": m.PartitionTimeInterval, + "partition_type": partitionType, + "expire_time": m.ExpireTime, + "updator": m.Updator, + "update_time": time.Now(), + } + result := model.DB.Self.Debug().Table(tbName). + Where( + "bk_biz_id=? and immute_domain=? and dblike=? and tblike=?", + m.BkBizId, m.ImmuteDomain, dblike, tblike). + Updates(update_column_map) + var para PartitionLogsParam + jString, jerr := json.Marshal(update_column_map) + if jerr != nil { + return jerr } - update_column.PartitionColumn = m.PartitionColumn - update_column.PartitionColumnType = m.PartitionColumnType - update_column.ReservedPartition = reservedPartition - update_column.ExtraPartition = extraTime - update_column.PartitionTimeInterval = m.PartitionTimeInterval - update_column.PartitionType = partitionType - update_column.ExpireTime = m.ExpireTime - update_column.Creator = m.Creator - update_column.Updator = m.Updator - result := model.DB.Self.Debug().Table(tbName).Where(update_condition).Updates(&update_column) + para.Para = string(jString) if result.Error != nil { errs = append(errs, result.Error.Error()) + para.Err = result.Error + } + var plog PartitionLogs + plog.BkBizId = m.BkBizId + plog.ExecuteTime = time.Now() + plog.Operator = m.Updator + + jString, jerr = json.Marshal(update_column_map) + if jerr != nil { + return jerr + } + plog.Para = string(jString) + res := model.DB.Self.Debug().Create(&plog) + if res.Error != nil { + return res.Error } } } + if len(errs) > 0 { return fmt.Errorf("errors: %s", strings.Join(errs, "\n")) } + return nil } @@ -414,7 +459,6 @@ func (m *CreatePartitionsInput) compareWithExistDB(tbName string) (warnings []st for i := 0; i < l; i++ { db := m.DbLikes[i] existRules, err := m.checkExistRules(tbName) - fmt.Println(existRules) if err != nil { return warnings, err } @@ -449,3 +493,11 @@ func (m *CreatePartitionsInput) checkExistRules(tbName string) (existRules []Exi } return existRules, nil } + +// CreateManageLog 记录操作日志,日志不对外 +func CreateManageLog(log ManageLog) { + err := model.DB.Self.Create(&log).Error + if err != nil { + slog.Error("create manage log err", err) + } +} diff --git a/dbm-services/mysql/db-partition/service/manage_config_object.go b/dbm-services/mysql/db-partition/service/manage_config_object.go index 4ec72c2803..090cfe8cd1 100644 --- a/dbm-services/mysql/db-partition/service/manage_config_object.go +++ b/dbm-services/mysql/db-partition/service/manage_config_object.go @@ -1,5 +1,7 @@ package service +import "dbm-services/mysql/db-partition/util" + // MysqlPartitionConfig TODO const MysqlPartitionConfig = "mysql_partition_config" @@ -11,12 +13,6 @@ const MysqlPartitionCronLogTable = "mysql_partition_cron_log" // SpiderPartitionCronLogTable TODO const SpiderPartitionCronLogTable = "spider_partition_cron_log" - -// MysqlPartition TODO -const MysqlPartition = "MYSQL_PARTITION" - -// SpiderPartition TODO -const SpiderPartition = "SPIDER_PARTITION" const online = "online" const offline = "offline" const extraTime = 15 @@ -31,6 +27,7 @@ type ExistRule struct { type QueryParititionsInput struct { ClusterType string `json:"cluster_type"` BkBizId int64 `json:"bk_biz_id"` + Ids []int64 `json:"ids"` ImmuteDomains []string `json:"immute_domains"` DbLikes []string `json:"dblikes"` TbLikes []string `json:"tblikes"` @@ -42,6 +39,10 @@ type QueryParititionsInput struct { type QueryLogInput struct { ClusterType string `json:"cluster_type"` ConfigId int64 `json:"config_id"` + Limit int `json:"limit"` + Offset int `json:"offset"` + StartTime string `json:"start_time"` + EndTime string `json:"end_time"` } // CreatePartitionsInput TODO @@ -58,6 +59,7 @@ type CreatePartitionsInput struct { PartitionColumnType string `json:"partition_column_type"` ExpireTime int `json:"expire_time"` // 分区过期时间 PartitionTimeInterval int `json:"partition_time_interval"` // 分区间隔 + TimeZone string `json:"time_zone"` Creator string `json:"creator"` Updator string `json:"updator"` } @@ -82,3 +84,13 @@ type EnablePartitionInput struct { Operator string `json:"operator"` Ids []int64 `json:"ids"` } + +// ManageLog 审计分区管理行为 +type ManageLog struct { + Id int64 `gorm:"column:id;primary_key;auto_increment" json:"id"` + ConfigId int64 `gorm:"column:config_id;not_null" json:"config_id"` + BkBizId int64 `gorm:"column:bk_biz_id;not_null" json:"bk_biz_id"` + Operator string `gorm:"column:operator" json:"operator"` + Para string `gorm:"column:para" json:"para"` + Time util.TimeFormat `gorm:"column:execute_time" json:"execute_time"` +} diff --git a/dbm-services/mysql/db-priv/errno/code.go b/dbm-services/mysql/db-priv/errno/code.go deleted file mode 100644 index bf50267490..0000000000 --- a/dbm-services/mysql/db-priv/errno/code.go +++ /dev/null @@ -1,407 +0,0 @@ -package errno - -var ( - // OK TODO - // Common errors - // OK = Errno{Code: 0, Message: ""} - OK = Errno{Code: 0, Message: "", CNMessage: ""} - // SaveOK TODO - SaveOK = Errno{Code: 0, Message: "Bill save success!", CNMessage: "单据保存成功!"} - // CommitOK TODO - CommitOK = Errno{Code: 0, Message: "Bill commit success!", CNMessage: "单据提交成功!"} - // AuditOK TODO - AuditOK = Errno{Code: 0, Message: "Bill audit success!", CNMessage: "单据审核成功!"} - // RollbackOK TODO - RollbackOK = Errno{Code: 0, Message: "Bill rollback success!", CNMessage: "单据驳回成功!"} - // StopOK TODO - StopOK = Errno{Code: 0, Message: "Bill stop success!", CNMessage: "单据终止成功!"} - // ExecuteOK TODO - ExecuteOK = Errno{Code: 0, Message: "Bill execute success!", CNMessage: "单据执行成功!"} - // CommonOK TODO - CommonOK = Errno{Code: 0, Message: "", CNMessage: "通用成功描述"} - // JobUpdateOK TODO - JobUpdateOK = Errno{Code: 0, Message: "Job update success!", CNMessage: "Job 更新成功!"} - // SubjobUpdateOK TODO - SubjobUpdateOK = Errno{Code: 0, Message: "Subjob update success!", CNMessage: "Subjob 更新成功!"} - - // ErrRecordNotFound TODO - ErrRecordNotFound = Errno{Code: 404, Message: "There is no records in db.", CNMessage: "数据库未找到对应的记录!"} - - // CommonErr TODO - CommonErr = Errno{Code: 10000, Message: "common error!", CNMessage: "通用错误!"} - - // InternalServerError TODO - InternalServerError = Errno{Code: 10001, Message: "Internal server error", CNMessage: "服务器内部错误。"} - // ErrBind TODO - ErrBind = Errno{Code: 10002, Message: "Error occurred while binding the request body to the struct.", - CNMessage: "参数处理发生错误。"} - // ErrString2Int TODO - ErrString2Int = Errno{Code: 10010, Message: "Error occurred while convert string to int.", - CNMessage: "string 转化为 int 出错!"} - // ErrorJsonToMap TODO - ErrorJsonToMap = Errno{Code: 10030, Message: "Error occured while converting json to Map.", - CNMessage: "Json 转为 Map 出现错误!"} - // ErrorUIDBeZero TODO - ErrorUIDBeZero = Errno{Code: 10035, Message: "uid can not be 0!", CNMessage: "uid 不能为 0.!"} - // ErrRequestParam TODO - ErrRequestParam = Errno{Code: 10036, Message: "request parameter error!", CNMessage: "请求参数错误!"} - - // ErrTypeAssertion TODO - ErrTypeAssertion = Errno{Code: 10040, Message: "Error occurred while doing type assertion."} - // ErrParameterRequired TODO - ErrParameterRequired = Errno{Code: 10050, Message: "Input paramter required"} - // StartBiggerThanEndTime TODO - StartBiggerThanEndTime = Errno{Code: 10060, Message: "Start time is bigger than end time."} - - // ErrValidation TODO - ErrValidation = Errno{Code: 20001, Message: "Validation failed."} - // ErrDatabase TODO - ErrDatabase = Errno{Code: 20002, Message: "Database error."} - // ErrToken TODO - ErrToken = Errno{Code: 20003, Message: "Error occurred while signing the JSON web token."} - - // ErrEncrypt TODO - // user errors - ErrEncrypt = Errno{Code: 20101, Message: "Error occurred while encrypting the user password."} - // ErrUserNotFound TODO - ErrUserNotFound = Errno{Code: 20102, Message: "The user was not found."} - // ErrTokenInvalid TODO - ErrTokenInvalid = Errno{Code: 20103, Message: "The token was invalid."} - // ErrPasswordIncorrect TODO - ErrPasswordIncorrect = Errno{Code: 20104, Message: "The password was incorrect."} - // ErrDoNotHavePrivs TODO - ErrDoNotHavePrivs = Errno{Code: 20106, Message: "User don't have Privs."} - // ErrUserIsEmpty TODO - ErrUserIsEmpty = Errno{Code: 20110, Message: "User can't be empty.", CNMessage: "user 不能为空!"} - // ErrAppNameIsEmpty TODO - ErrAppNameIsEmpty = Errno{Code: 20115, Message: "App name can't be empty.", CNMessage: "业务名不能为空!"} - - // ErrCommonExecute TODO - ErrCommonExecute = Errno{Code: 20200, Message: "Error occured while invoking execute method.", - CNMessage: "调用 execute 出错!"} - - // ErrUserHaveNoProjectPriv TODO - ErrUserHaveNoProjectPriv = Errno{Code: 30000, Message: "User don't have project priv.", CNMessage: "没有 project 权限!"} - - // ErrGcsBillNotFound TODO - // gcsbill errors - ErrGcsBillNotFound = Errno{Code: 40000, Message: "Gcs bill was not found.", CNMessage: "单据不存在!"} - // ErrGCSBillTypeEmpty TODO - ErrGCSBillTypeEmpty = Errno{Code: 40001, Message: "Gcs bill type can not be empty.", CNMessage: "单据类型不能为空!"} - // InvalidGCSBillType TODO - InvalidGCSBillType = Errno{Code: 40002, Message: "Invalid Gcs bill type.", CNMessage: "无效的 GCS 单据类型!"} - // InvalidAuditLevel TODO - InvalidAuditLevel = Errno{Code: 40003, Message: "Invalid Bill Audit level.", CNMessage: "无效的单据审核级别!"} - - // CannotGetBillStatus TODO - CannotGetBillStatus = Errno{Code: 40004, Message: "Cann't get bill status.", CNMessage: `无法获取单据状态`} - // ErrGCSBillnotAuditable TODO - ErrGCSBillnotAuditable = Errno{Code: 40005, Message: "Current GCS bill is not in audit status now.", - CNMessage: `当前单据不在“待审核”状态!`} - // ErrGCSBillNotInExecute TODO - ErrGCSBillNotInExecute = Errno{Code: 40006, Message: "Bill is not in execute status.", CNMessage: `当前单据不在“待执行”状态!`} - // ErrGCSBillAudit TODO - ErrGCSBillAudit = Errno{Code: 40007, Message: "Audit bill error.", CNMessage: `审核单据出错。`} - - // ErrNotHaveBillCommitPriv TODO - ErrNotHaveBillCommitPriv = Errno{Code: 40008, Message: "user don't have bill commit priv", CNMessage: "用户没有提单权限!"} - - // ErrGetGCSDoneBills TODO - ErrGetGCSDoneBills = Errno{Code: 40009, Message: "Error occured while getting done bills.", - CNMessage: "获取个人已办事项出错!"} - // ErrBillAppIsEmpty TODO - ErrBillAppIsEmpty = Errno{Code: 40010, Message: "Gcs bill app can not be empty.", CNMessage: "单据的业务名不能为空!"} - // ErrGCSBillNoExecutePriv TODO - ErrGCSBillNoExecutePriv = Errno{Code: 40011, Message: "Only apply user and follower can execute the bill!", - CNMessage: "只有申请人或者关注人可以执行单据!"} - // ErrGetGCSBillModel TODO - ErrGetGCSBillModel = Errno{Code: 40012, Message: "Error occured while getting bill info", - CNMessage: "获取 Bill 详情出错"} - // ErrGetGCSBillTypes TODO - ErrGetGCSBillTypes = Errno{Code: 40014, Message: "Error occured while getting bill types", - CNMessage: "获取所有单据类型失败!"} - // ErrGCSBillCommit TODO - ErrGCSBillCommit = Err{Errno: Errno{Code: 40015, Message: "The bill can not be committed repeatly!", - CNMessage: "单据不能被重复提交!"}} - // ErrInvokeBillCommit TODO - ErrInvokeBillCommit = Err{Errno: Errno{Code: 40016, Message: "Error occured while committing gcs bills", - CNMessage: "单据提交时发生错误!"}} - // ErrInvokeBillExecute TODO - ErrInvokeBillExecute = Err{Errno: Errno{Code: 40017, Message: "Error occured while executing gcs bills", - CNMessage: "单据执行时发生错误!"}} - - // ErrGCSBillnotRollback TODO - ErrGCSBillnotRollback = Errno{Code: 40019, Message: "Bill is not auditable ,it can not be rollback.", - CNMessage: `非“待审核”单据不能被驳回!`} - // ErrGetGCSBills TODO - ErrGetGCSBills = Errno{Code: 40020, Message: "Error occured while getting gcs bills", CNMessage: "获取单据失败!"} - // ErrCloneUnfinishedBills TODO - ErrCloneUnfinishedBills = Errno{Code: 40022, Message: "Error occured while cloning unfinished gcs bills", - CNMessage: "不能克隆没有结束的单据!"} - // ErrFinishedBills TODO - ErrFinishedBills = Errno{Code: 40027, Message: "Error occured while finishing gcs bills", - CNMessage: `设置单据为“完成”状态时失败!`} - // ErrBillHaveTerminated TODO - ErrBillHaveTerminated = Errno{Code: 40028, Message: "Bill have terminated!", CNMessage: `单据已“终止”!`} - - // ErrNoStopPriv TODO - ErrNoStopPriv = Errno{Code: 40037, Message: "Don't have stop bill priv!", CNMessage: `用户没有“终止”单据权限!`} - // ErrGCSBillSave TODO - ErrGCSBillSave = Err{Errno: Errno{Code: 40042, Message: "Error occured while saving gcs bills!", - CNMessage: "单据保存失败!"}} - // ErrBillIsNotUncommit TODO - ErrBillIsNotUncommit = Err{Errno: Errno{Code: 40043, - Message: "Bill phase is not v_uncommit before committing the bill!", CNMessage: "单据提交之前,单据状态不是\"未提交\"!"}} - // ErrBillPreCommit TODO - ErrBillPreCommit = Err{Errno: Errno{Code: 40046, Message: "Error occured while invoking bill pre commit api:", - CNMessage: "调用单据的 PreCommit API 失败:"}} - // ErrBillAfterExecute TODO - ErrBillAfterExecute = Err{Errno: Errno{Code: 40050, Message: "Error occured while invoking after execute api!", - CNMessage: "调用单据的 AfterExecute API 失败!"}} - - // ErrTbBillInfoToBill TODO - ErrTbBillInfoToBill = Err{Errno: Errno{Code: 40055, Message: "Error occured while transfer TbBillInfo to Bill!", - CNMessage: "转换 Bill Model 失败"}} - - // ErrCreateGCSJob TODO - // job errors - ErrCreateGCSJob = Errno{Code: 40100, Message: "Error occured while creating the gcs job.", - CNMessage: "创建 GCS Job 失败!"} - // ErrGetJobQueue TODO - ErrGetJobQueue = Errno{Code: 40101, Message: "Error occured while get the gcs job queue.", - CNMessage: "获取 job 失败 !"} - // ErrGetJobQueueNotFound TODO - ErrGetJobQueueNotFound = Errno{Code: 40102, Message: "Job Queue Not Found.", CNMessage: "Job 不存在!"} - // ErrDeleteJobQueue TODO - ErrDeleteJobQueue = Errno{Code: 40103, Message: "Error occured while set the jobQueue to be deleted.", - CNMessage: "删除 Job 失败!"} - // ErrJobIDConvert2Int TODO - ErrJobIDConvert2Int = Errno{Code: 40104, Message: "Error occured while converting the jobID to int.", - CNMessage: "jobID 转换为int 出错!"} - // ErrSubjobIDConvert2Int TODO - ErrSubjobIDConvert2Int = Errno{Code: 40105, Message: "Error occured while converting the subjob_id to int.", - CNMessage: "subjobID 转换为int 出错!"} - - // ErrPutJobQueueParam TODO - ErrPutJobQueueParam = Errno{Code: 40106, Message: " param errors while puting a new JobQueue.", - CNMessage: "创建 Job 时参数错误!"} - // ErrJobQueueInputParam TODO - ErrJobQueueInputParam = Errno{Code: 40107, - Message: "Some parameters is required in EnJobQueue: app,name,input,tag_id", - CNMessage: "创建Job 时缺少下列参数:[app,name,input,tag_id]!"} - // ErrJobQueueV1InputParam TODO - ErrJobQueueV1InputParam = Errno{Code: 40107, - Message: "Some parameters is required in puting JobQueue: [app,name,distributions,payload,user]", - CNMessage: "创建/修改 Job 时缺少下列参数:[app,name,distributions,payload,user]!"} - // ErrJobQueueDistribution TODO - ErrJobQueueDistribution = Errno{Code: 40108, Message: "JobQueue distributions format is wrong.", - CNMessage: "创建 JobQueue 时 distributions 格式不正确!"} - // ErrCheckJobQueue TODO - ErrCheckJobQueue = Errno{Code: 40109, Message: "Error occured while checking JobQueue.", - CNMessage: "检查 JobQueue 出错!"} - // ErrJoqQueueIsNil TODO - ErrJoqQueueIsNil = Errno{Code: 40110, Message: "JobQueue is Nil", CNMessage: "返回的Job 内容为空!"} - // ErrCloneJoqQueues TODO - ErrCloneJoqQueues = Errno{Code: 40113, Message: "Error occured while cloning jobQueues", - CNMessage: "克隆 jobQueues 出错!"} - - // JobResultSuccess TODO - JobResultSuccess = Errno{Code: 0, Message: "success", CNMessage: "success"} - // JobResultRunning TODO - JobResultRunning = Errno{Code: 40114, Message: "running", CNMessage: "running"} - // JobResultFailed TODO - JobResultFailed = Errno{Code: 40115, Message: "fail", CNMessage: "fail"} - // JobResultOthers TODO - JobResultOthers = Errno{Code: 40116, Message: "other job status", CNMessage: "other job status"} - - // ErrGetJobFeedbacks TODO - // JobFeedback - ErrGetJobFeedbacks = Errno{Code: 40210, Message: "Error occured while getting the gcs job feedback.", - CNMessage: "获取 job feedback 信息失败!"} - // ErrCreateGCSJobFeedback TODO - ErrCreateGCSJobFeedback = Errno{Code: 40215, Message: "Error occured while creating the gcs jobFeedback.", - CNMessage: "创建 GCS jobFeedback 失败!"} - - // InvalidJobIDorSubjobID TODO - InvalidJobIDorSubjobID = Errno{Code: 40220, Message: "Invalid jobID or subJobID while getting the gcs job feedback.", - CNMessage: "jobID or subJobID 无效!"} - - // ErrorJobNameBeEmpty TODO - // JobDef errors - ErrorJobNameBeEmpty = Errno{Code: 40300, Message: "JobName can not be empty.", CNMessage: "JobName 不能为空!"} - // ErrorGetJobDef TODO - ErrorGetJobDef = Errno{Code: 40302, Message: "Error occured while getting the gcs job_def", - CNMessage: "获取 job_def 出现错误!"} - - // ErrorGetJobBlob TODO - // JobBlob errors - ErrorGetJobBlob = Errno{Code: 40302, Message: "Error occured while getting the gcs job_blob", - CNMessage: "获取 job_blob 出现错误!"} - - // ErrorGetSubJobQueue TODO - // subjob errors - ErrorGetSubJobQueue = Errno{Code: 40800, Message: "Error occured while getting the gcs subjob ", - CNMessage: "获取 subjob 出现错误!"} - // ErrCreateSubJobQueue TODO - ErrCreateSubJobQueue = Errno{Code: 40801, Message: "Error occured while creating the gcs subjobQueue.", - CNMessage: "创建 GCS subjobQueue 失败!"} - // ErrUpdateSubJobQueue TODO - ErrUpdateSubJobQueue = Errno{Code: 40802, Message: "Error occured while updating the gcs subjobQueue.", - CNMessage: "更新 GCS subjobQueue 失败!"} - - // SubJobUIDRequied TODO - SubJobUIDRequied = Errno{Code: 40804, Message: "Subjob uid is required!", CNMessage: "Subjob uid 是必填项.!"} - // ErrorUIDMustBeInt TODO - ErrorUIDMustBeInt = Errno{Code: 40808, Message: "Subjob uid must be int!", CNMessage: "Subjob uid 必须是 int 类型.!"} - // ErrSubjobQueueInputParam TODO - ErrSubjobQueueInputParam = Errno{Code: 40812, - Message: "Some parameters [JobID,Username,JobName,AtomjobList,JobInput] " + - "are not meet the demands in saving SubjobQueue", - CNMessage: "保存 SubjobQueue 时缺少下列参数:[JobID,Username,JobName,AtomjobList,JobInput]!"} - // ErrJobFeedbackInputParam TODO - ErrJobFeedbackInputParam = Errno{Code: 40815, - Message: "Some parameters are not meet the demands in saving JobFeedback", CNMessage: "保存 JobFeedback 时参数不满足要求。"} - // ErrGetGCSApps TODO - // gcs app errors - ErrGetGCSApps = Errno{Code: 40900, Message: "Error occured while getting gcs apps", CNMessage: "获取 GCS App 出现错误!"} - // ErrGetCCApps TODO - ErrGetCCApps = Errno{Code: 40902, Message: "Error occured while getting cc apps", CNMessage: "获取 App 出现错误!"} - // ErrGetProjects TODO - ErrGetProjects = Errno{Code: 40905, Message: "Error occured while getting projects", CNMessage: "获取 projects 出现错误!"} - - // ErrDBTransaction TODO - // model operation errors - ErrDBTransaction = Errno{Code: 50200, Message: "DB Transaction error.", CNMessage: "DB 事务发生错误!"} - // ErrModelFunction TODO - ErrModelFunction = Err{Errno: Errno{Code: 50201, Message: "Error occured while invoking model function.", - CNMessage: "调用 DB model 方法发生错误!"}, Err: nil} - - // ErrSaveFlowAuditLog TODO - ErrSaveFlowAuditLog = Errno{Code: 50203, Message: "Error occured while saving Flow Audit Log.", - CNMessage: "存储单据审核日志记录出错!"} - - // ErrGetJSONArray TODO - // data handle error - ErrGetJSONArray = Errno{Code: 50300, Message: "Get simplejson Array error.", CNMessage: ""} - // ErrConvert2Map TODO - ErrConvert2Map = Errno{Code: 50301, Message: "Error occurred while converting the data to Map.", - CNMessage: "Error occurred while converting the data to Map."} - // ErrJSONMarshal TODO - ErrJSONMarshal = Errno{Code: 50302, Message: "Error occurred while marshaling the data to JSON.", - CNMessage: "Error occurred while marshaling the data to JSON."} - // ErrReadEntity TODO - ErrReadEntity = Errno{Code: 50303, Message: "Error occurred while parsing the request parameter.", - CNMessage: "Error occurred while parsing the request parameter."} - // ErrJSONUnmarshal TODO - ErrJSONUnmarshal = Errno{Code: 50304, Message: "Error occurred while Unmarshaling the JSON to data model.", - CNMessage: "Error occurred while Unmarshaling the JSON to data model."} - // ErrBytesToMap TODO - ErrBytesToMap = Errno{Code: 50307, Message: "Error occurred while converting bytes to map.", - CNMessage: "Error occurred while converting bytes to map."} - - // ErrUserIsNotDBA TODO - // user login and permission errors - ErrUserIsNotDBA = Errno{Code: 50500, Message: "User is not dba."} - // ErrNoSaveAndCommitPriv TODO - ErrNoSaveAndCommitPriv = Errno{Code: 50502, - Message: "User don't have gcs bill save and commit privs in this app.", CNMessage: "用户在当前 APP 上没有单据的保存和提交权限!"} - // ErrNoBillAduitPriv TODO - ErrNoBillAduitPriv = Errno{Code: 50504, Message: "User don't have gcs audit privs in this app.", - CNMessage: "用户在当前 APP 上没有单据的审核权限!"} - // ErrUserNotHaveBillRollbackPriv TODO - ErrUserNotHaveBillRollbackPriv = Errno{Code: 50506, Message: "User don't have gcs rollback privs in this app.", - CNMessage: "用户在当前 APP 上没有单据的驳回权限!"} - // ErrUserHasNoPermission TODO - ErrUserHasNoPermission = Errno{Code: 50508, Message: "User has no permission.", CNMessage: "当前用户没有权限!"} - // ErrUserNotHaveBillClonePriv TODO - ErrUserNotHaveBillClonePriv = Errno{Code: 50510, Message: "User don't have gcs bill clone privs in this app.", - CNMessage: "用户没有当前单据的克隆权限!"} - // ErrViewAppPriv TODO - ErrViewAppPriv = Errno{Code: 50515, Message: "User have no priv to view this app!", - CNMessage: "用户没有查看当前 APP 的权限!"} - - // ErrInvokeAPI TODO - ErrInvokeAPI = Errno{Code: 50601, Message: "Error occurred while invoking API", CNMessage: "调用 API 发生错误!"} - - // ErrSnedRTX TODO - // alarm errors - ErrSnedRTX = Errno{Code: 50800, Message: "Error occurred while sending RTX message to user.", - CNMessage: "发送 RTX 消息出现错误!"} - - // ErrPswNotIdentical TODO - // grant privileges errors - ErrPswNotIdentical = Errno{Code: 51000, - Message: "Password is not identical to the password of existed account rules, " + - "same accounts should use same password.", - CNMessage: "密码与已存在的账号规则中的密码不同,相同账号的密码需要保持一致!"} - // AccountRuleExisted TODO - AccountRuleExisted = Errno{Code: 51001, Message: "Account rule of user on this db is existed ", - CNMessage: "用户对此DB授权的账号规则已存在"} - // AccountExisted TODO - AccountExisted = Errno{Code: 51002, Message: "Account is existed ", CNMessage: "账号已存在"} - // AccountNotExisted TODO - AccountNotExisted = Errno{Code: 51003, Message: "Account not existed ", CNMessage: "账号不存在"} - // PasswordConsistentWithAccountName TODO - PasswordConsistentWithAccountName = Errno{Code: 51019, Message: "Password should be different from account name ", - CNMessage: "账号与密码不能相同"} - // PasswordOrAccountNameNull TODO - PasswordOrAccountNameNull = Errno{Code: 51020, Message: "Password or account name should not be empty ", - CNMessage: "账号与密码不能为空"} - // AccountIdNull TODO - AccountIdNull = Errno{Code: 51021, Message: "Account ID should not be empty", - CNMessage: "账号ID不能为空"} - // DbNameNull TODO - DbNameNull = Errno{Code: 51022, Message: "Database name should not be empty", - CNMessage: "数据库名称不能为空"} - // AccountRuleIdNull TODO - AccountRuleIdNull = Errno{Code: 51022, Message: "Account rule should not be empty", - CNMessage: "账号规则ID不能为空"} - // PrivNull TODO - PrivNull = Errno{Code: 51022, Message: "No privilege was chosen", CNMessage: "未选择权限"} - // AccountRuleNotExisted TODO - AccountRuleNotExisted = Errno{Code: 51004, Message: "Account rule not existed ", CNMessage: "账号规则不存在"} - // OnlyOneDatabaseAllowed TODO - OnlyOneDatabaseAllowed = Errno{Code: 51005, - Message: "Only one database allowed, database name should not contain space", CNMessage: "只允许填写一个数据库,数据库名称不能包含空格"} - // ErrMysqlInstanceStruct TODO - ErrMysqlInstanceStruct = Errno{Code: 51006, Message: "Not either tendbha or orphan structure", - CNMessage: "不符合tendbha或者orphan的集群结构"} - // GenerateEncryptedPasswordErr TODO - GenerateEncryptedPasswordErr = Errno{Code: 51007, Message: "Generate Encrypted Password Err", - CNMessage: "创建账号,生成加密的密码时发生错误"} - // PasswordNotConsistent TODO - PasswordNotConsistent = Errno{Code: 51008, - Message: "user is exist,but the new password is not consistent with the old password, should be consistent", - CNMessage: "账号已存在,但是新密码与旧密码不一致,需要保持一致"} - // GrantPrivilegesFail TODO - GrantPrivilegesFail = Errno{Code: 51009, Message: "Grant Privileges Fail", CNMessage: "授权执行失败"} - // GrantPrivilegesSuccess TODO - GrantPrivilegesSuccess = Errno{Code: 0, Message: "Grant Privileges success", CNMessage: "授权执行成功"} - // GrantPrivilegesParameterCheckFail TODO - GrantPrivilegesParameterCheckFail = Errno{Code: 51010, Message: "Parameter of Grant Privileges Check Fail", - CNMessage: "授权单据的参数检查失败"} - // ClonePrivilegesParameterCheckFail TODO - ClonePrivilegesParameterCheckFail = Errno{Code: 51011, Message: "Parameter of Clone Privileges Check Fail", - CNMessage: "克隆权限单据的参数检查失败"} - // BkBizIdIsEmpty TODO - BkBizIdIsEmpty = Errno{Code: 51012, Message: "bk_biz_id can't be empty", CNMessage: "bk_biz_id不能为空"} - // ClonePrivilegesFail TODO - ClonePrivilegesFail = Errno{Code: 51013, Message: "Clone privileges fail", CNMessage: "克隆权限失败"} - // ClonePrivilegesCheckFail TODO - ClonePrivilegesCheckFail = Errno{Code: 51014, Message: "Clone privileges check fail", CNMessage: "克隆权限检查失败"} - // NoPrivilegesNothingToDo TODO - NoPrivilegesNothingToDo = Errno{Code: 51015, Message: "no privileges,nothing to do", CNMessage: "没有权限需要克隆"} - // DomainNotExists TODO - DomainNotExists = Errno{Code: 51016, Message: "domain not exists", CNMessage: "域名不存在"} - // IpPortFormatError TODO - IpPortFormatError = Errno{Code: 51017, Message: "format not in 'ip:port' format", - CNMessage: "格式不是ip:port的格式"} - // InstanceNotExists TODO - InstanceNotExists = Errno{Code: 51018, Message: "instance not exists", CNMessage: "实例不存在"} - // CloudIdRequired TODO - CloudIdRequired = Errno{Code: 51019, Message: "bk_cloud_id is required", CNMessage: "bk_cloud_id不能为空"} - // NotSupportedClusterType TODO - NotSupportedClusterType = Errno{Code: 51020, Message: "not supported cluster type", CNMessage: "不支持此集群类型"} - // ClusterTypeIsEmpty TODO - ClusterTypeIsEmpty = Errno{Code: 51021, Message: "Cluster type can't be empty", CNMessage: "cluster type不能为空"} -) diff --git a/dbm-services/mysql/db-priv/errno/errno.go b/dbm-services/mysql/db-priv/errno/errno.go deleted file mode 100644 index d5d107291f..0000000000 --- a/dbm-services/mysql/db-priv/errno/errno.go +++ /dev/null @@ -1,133 +0,0 @@ -// Package errno TODO -package errno - -import ( - "fmt" - - "github.com/spf13/viper" -) - -// Errno TODO -type Errno struct { - Code int - Message string - CNMessage string -} - -var lang = viper.GetString("lang") - -// Error 用于错误处理 -func (err Errno) Error() string { - switch lang { - case "zh_CN": - return err.CNMessage - case "en_US": - return err.Message - default: - return err.CNMessage - } -} - -// Addf TODO -func (err Errno) Addf(format string, args ...interface{}) error { - return err.Add(fmt.Sprintf(format, args...)) -} - -// Add TODO -func (err Errno) Add(message string) error { - switch lang { - case "zh_CN": - err.CNMessage += message - return err - case "en_US": - err.Message += message - return err - default: - err.CNMessage += message - return err - } - return err -} - -// AddBefore TODO -func (err Errno) AddBefore(message string) error { - switch lang { - case "zh_CN": - err.CNMessage = message + err.CNMessage - return err - case "en_US": - err.Message = message + err.Message - return err - default: - err.CNMessage = message + err.CNMessage - return err - } - return err -} - -// Err TODO -type Err struct { - Errno - Err error -} - -// Add TODO -func (err Err) Add(message string) error { - switch lang { - case "zh_CN": - err.CNMessage += message - return err - case "en_US": - err.Message += message - return err - default: - err.CNMessage += message - return err - } - return err -} - -// SetMsg TODO -func (err Err) SetMsg(message string) error { - err.Message = message - return err -} - -// SetCNMsg TODO -func (err Err) SetCNMsg(cnMessage string) error { - err.CNMessage = cnMessage - return err -} - -// Addf TODO -func (err Err) Addf(format string, args ...interface{}) error { - return err.Add(fmt.Sprintf(format, args...)) -} - -// DecodeErr TODO -func DecodeErr(err error) (int, string) { - - var CN bool = true - - if err == nil { - return OK.Code, OK.Message - } - - switch typed := err.(type) { - case Err: - if CN { - return typed.Code, typed.CNMessage - } else { - return typed.Code, typed.Message - } - case Errno: - if CN { - return typed.Code, typed.CNMessage - } else { - return typed.Code, typed.Message - } - default: - } - fmt.Printf("%s", err.Error()) - return InternalServerError.Code, err.Error() -} diff --git a/dbm-services/mysql/db-priv/handler/account.go b/dbm-services/mysql/db-priv/handler/account.go index a04fd41255..f3822bf108 100644 --- a/dbm-services/mysql/db-priv/handler/account.go +++ b/dbm-services/mysql/db-priv/handler/account.go @@ -4,7 +4,7 @@ import ( "encoding/json" "io/ioutil" - "dbm-services/mysql/priv-service/errno" + "dbm-services/common/go-pubpkg/errno" "dbm-services/mysql/priv-service/service" "github.com/gin-gonic/gin" diff --git a/dbm-services/mysql/db-priv/handler/account_rule.go b/dbm-services/mysql/db-priv/handler/account_rule.go index 3be7809bb1..0ab11ef29c 100644 --- a/dbm-services/mysql/db-priv/handler/account_rule.go +++ b/dbm-services/mysql/db-priv/handler/account_rule.go @@ -4,7 +4,7 @@ import ( "encoding/json" "io/ioutil" - "dbm-services/mysql/priv-service/errno" + "dbm-services/common/go-pubpkg/errno" "dbm-services/mysql/priv-service/service" "github.com/gin-gonic/gin" diff --git a/dbm-services/mysql/db-priv/handler/add_priv.go b/dbm-services/mysql/db-priv/handler/add_priv.go index c5f0060682..513b6e0ede 100644 --- a/dbm-services/mysql/db-priv/handler/add_priv.go +++ b/dbm-services/mysql/db-priv/handler/add_priv.go @@ -4,7 +4,7 @@ import ( "encoding/json" "io/ioutil" - "dbm-services/mysql/priv-service/errno" + "dbm-services/common/go-pubpkg/errno" "dbm-services/mysql/priv-service/service" "github.com/gin-gonic/gin" diff --git a/dbm-services/mysql/db-priv/handler/clone_client_priv.go b/dbm-services/mysql/db-priv/handler/clone_client_priv.go index 8fa52ddf59..f9dab95dde 100644 --- a/dbm-services/mysql/db-priv/handler/clone_client_priv.go +++ b/dbm-services/mysql/db-priv/handler/clone_client_priv.go @@ -4,7 +4,7 @@ import ( "encoding/json" "io/ioutil" - "dbm-services/mysql/priv-service/errno" + "dbm-services/common/go-pubpkg/errno" "dbm-services/mysql/priv-service/service" "github.com/gin-gonic/gin" diff --git a/dbm-services/mysql/db-priv/handler/clone_instance_priv.go b/dbm-services/mysql/db-priv/handler/clone_instance_priv.go index c670791805..90d693aef0 100644 --- a/dbm-services/mysql/db-priv/handler/clone_instance_priv.go +++ b/dbm-services/mysql/db-priv/handler/clone_instance_priv.go @@ -4,7 +4,7 @@ import ( "encoding/json" "io/ioutil" - "dbm-services/mysql/priv-service/errno" + "dbm-services/common/go-pubpkg/errno" "dbm-services/mysql/priv-service/service" "github.com/gin-gonic/gin" diff --git a/dbm-services/mysql/db-priv/handler/public_key.go b/dbm-services/mysql/db-priv/handler/public_key.go index 6ec72ecf76..3e18eec82e 100644 --- a/dbm-services/mysql/db-priv/handler/public_key.go +++ b/dbm-services/mysql/db-priv/handler/public_key.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package handler import ( @@ -5,7 +15,7 @@ import ( "net/http" "os" - "dbm-services/mysql/priv-service/errno" + "dbm-services/common/go-pubpkg/errno" "github.com/gin-gonic/gin" "golang.org/x/exp/slog" diff --git a/dbm-services/mysql/db-priv/service/account.go b/dbm-services/mysql/db-priv/service/account.go index 0d033282d3..4efc5ccf1d 100644 --- a/dbm-services/mysql/db-priv/service/account.go +++ b/dbm-services/mysql/db-priv/service/account.go @@ -1,13 +1,14 @@ package service import ( - "dbm-services/mysql/priv-service/errno" - "dbm-services/mysql/priv-service/util" "fmt" "io/ioutil" "os" "strings" + "dbm-services/common/go-pubpkg/errno" + "dbm-services/mysql/priv-service/util" + "github.com/spf13/viper" "golang.org/x/exp/slog" ) diff --git a/dbm-services/mysql/db-priv/service/accout_rule.go b/dbm-services/mysql/db-priv/service/accout_rule.go index f62218ffd1..1aa29b5e77 100644 --- a/dbm-services/mysql/db-priv/service/accout_rule.go +++ b/dbm-services/mysql/db-priv/service/accout_rule.go @@ -1,12 +1,13 @@ package service import ( - "dbm-services/mysql/priv-service/errno" - "dbm-services/mysql/priv-service/util" errors2 "errors" "fmt" "strings" + "dbm-services/common/go-pubpkg/errno" + "dbm-services/mysql/priv-service/util" + "github.com/jinzhu/gorm" ) diff --git a/dbm-services/mysql/db-priv/service/add_priv.go b/dbm-services/mysql/db-priv/service/add_priv.go index 2635479458..c55076ba8f 100644 --- a/dbm-services/mysql/db-priv/service/add_priv.go +++ b/dbm-services/mysql/db-priv/service/add_priv.go @@ -1,13 +1,14 @@ package service import ( - "dbm-services/mysql/priv-service/errno" - "dbm-services/mysql/priv-service/util" "errors" "fmt" "strings" "sync" + "dbm-services/common/go-pubpkg/errno" + "dbm-services/mysql/priv-service/util" + "github.com/spf13/viper" "golang.org/x/exp/slog" ) @@ -142,7 +143,11 @@ func (m *PrivTaskPara) AddPriv(jsonPara string) error { return } } else if m.ClusterType == tendbcluster { - for _, spider := range instance.SpiderMaster { + var spiders []Proxy + // spider在spider-master和spider-slave节点添加权限的行为是一致的, + // 通过部署时spider-slave实例只读控制实际能执行的操作 + spiders = append(append(spiders, instance.SpiderMaster...), instance.SpiderSlave...) + for _, spider := range spiders { address = fmt.Sprintf("%s:%d", spider.IP, spider.Port) err = ImportBackendPrivilege(account, accountRule, address, proxyIPs, m.SourceIPs, instance.ClusterType, tendbhaMasterDomain, instance.BkCloudId) diff --git a/dbm-services/mysql/db-priv/service/add_priv_base_func.go b/dbm-services/mysql/db-priv/service/add_priv_base_func.go index 6c97b30c01..348f578aef 100644 --- a/dbm-services/mysql/db-priv/service/add_priv_base_func.go +++ b/dbm-services/mysql/db-priv/service/add_priv_base_func.go @@ -1,8 +1,6 @@ package service import ( - "dbm-services/mysql/priv-service/errno" - "dbm-services/mysql/priv-service/util" "encoding/json" errors2 "errors" "fmt" @@ -11,6 +9,9 @@ import ( "strings" "sync" + "dbm-services/common/go-pubpkg/errno" + "dbm-services/mysql/priv-service/util" + "github.com/asaskevich/govalidator" "github.com/jinzhu/gorm" "github.com/spf13/viper" diff --git a/dbm-services/mysql/db-priv/service/add_priv_object.go b/dbm-services/mysql/db-priv/service/add_priv_object.go index 72d799ccae..eec7b5eb92 100644 --- a/dbm-services/mysql/db-priv/service/add_priv_object.go +++ b/dbm-services/mysql/db-priv/service/add_priv_object.go @@ -54,9 +54,6 @@ type Instance struct { type Cluster struct { DbModuleId int64 `json:"db_module_id"` BkBizId string `json:"bk_biz_id"` - SpiderMaster []Proxy `json:"spider_master"` - SpiderSlave []Proxy `json:"spider_slave"` - SpiderMnt []Proxy `json:"spider_mnt"` Proxies []Proxy `json:"proxies"` Storages []Storage `json:"storages"` ClusterType string `json:"cluster_type"` diff --git a/dbm-services/mysql/db-priv/service/clone_client_priv.go b/dbm-services/mysql/db-priv/service/clone_client_priv.go index 875dda6837..027950b8c2 100644 --- a/dbm-services/mysql/db-priv/service/clone_client_priv.go +++ b/dbm-services/mysql/db-priv/service/clone_client_priv.go @@ -1,18 +1,18 @@ package service import ( - "dbm-services/mysql/priv-service/errno" - "dbm-services/mysql/priv-service/util" "fmt" "strings" "sync" + "dbm-services/common/go-pubpkg/errno" + "dbm-services/mysql/priv-service/util" + "github.com/spf13/viper" ) // CloneClientPrivDryRun 克隆客户端权限预检查 func (m *CloneClientPrivParaList) CloneClientPrivDryRun() error { - var errMsg []string var errMsgTemp []string if m.BkBizId == 0 { @@ -58,7 +58,6 @@ func (m *CloneClientPrivPara) CloneClientPriv(jsonPara string) error { if m.ClusterType == nil { ct := mysql m.ClusterType = &ct - // return errno.ClusterTypeIsEmpty } AddPrivLog(PrivLog{BkBizId: m.BkBizId, Operator: m.Operator, Para: jsonPara, Time: util.NowTimeFormat()}) @@ -68,19 +67,44 @@ func (m *CloneClientPrivPara) CloneClientPriv(jsonPara string) error { if errOuter != nil { return errOuter } + var tempClusters []Cluster var clusters []Cluster + var notExists []string for _, item := range resp { if item.BkCloudId == *m.BkCloudId { // mysql客户权限克隆,会克隆tendbha、tendbsingle集群内的权限 // spider客户权限克隆,会克隆tendbcluster集群内的权限 - if *m.ClusterType == tendbcluster && item.ClusterType == tendbcluster { - clusters = append(clusters, item) - } else if *m.ClusterType == mysql && (item.ClusterType == tendbha || - item.ClusterType == tendbsingle) { - clusters = append(clusters, item) + if (*m.ClusterType == tendbcluster && item.ClusterType == tendbcluster) || + (*m.ClusterType == mysql && (item.ClusterType == tendbha || + item.ClusterType == tendbsingle)) { + tempClusters = append(tempClusters, item) + } + } + } + + // 内部调用,标准运维的指定用户的客户端克隆功能 + if len(m.TargetInstances) > 0 { + for _, target := range m.TargetInstances { + exist := false + for _, temp := range tempClusters { + if target == temp.ImmuteDomain { + clusters = append(clusters, temp) + exist = true + break + } } + if exist == false { + notExists = append(notExists, target) + } + } + if len(notExists) > 0 { + return errno.DomainNotExists.AddBefore(strings.Join(notExists, ",")) } + } else { + clusters = make([]Cluster, len(tempClusters)) + clusters = tempClusters } + errMsg.errs = validateIP(m.SourceIp, m.TargetIp, m.BkCloudId) if len(errMsg.errs) > 0 { return errno.ClonePrivilegesFail.Add("\n" + strings.Join(errMsg.errs, "\n")) @@ -101,7 +125,7 @@ func (m *CloneClientPrivPara) CloneClientPriv(jsonPara string) error { if item.ClusterType == tendbha || item.ClusterType == tendbsingle { for _, storage := range item.Storages { address := fmt.Sprintf("%s:%d", storage.IP, storage.Port) - userGrants, err := GetRemotePrivilege(address, m.SourceIp, item.BkCloudId, machineTypeBackend) + userGrants, err := GetRemotePrivilege(address, m.SourceIp, item.BkCloudId, machineTypeBackend, m.User) if err != nil { AddError(&errMsg, address, err) return @@ -114,10 +138,9 @@ func (m *CloneClientPrivPara) CloneClientPriv(jsonPara string) error { } } } else { - spiders := append(append(item.SpiderMaster, item.SpiderSlave...), item.SpiderMnt...) - for _, spider := range spiders { + for _, spider := range item.Proxies { address := fmt.Sprintf("%s:%d", spider.IP, spider.Port) - userGrants, err := GetRemotePrivilege(address, m.SourceIp, item.BkCloudId, machineTypeSpider) + userGrants, err := GetRemotePrivilege(address, m.SourceIp, item.BkCloudId, machineTypeSpider, m.User) if err != nil { AddError(&errMsg, address, err) return @@ -133,7 +156,7 @@ func (m *CloneClientPrivPara) CloneClientPriv(jsonPara string) error { if item.ClusterType == tendbha { for _, proxy := range item.Proxies { address := fmt.Sprintf("%s:%d", proxy.IP, proxy.AdminPort) - proxyGrants, err := GetProxyPrivilege(address, m.SourceIp, item.BkCloudId) + proxyGrants, err := GetProxyPrivilege(address, m.SourceIp, item.BkCloudId, m.User) if err != nil { AddError(&errMsg, address, err) } diff --git a/dbm-services/mysql/db-priv/service/clone_client_priv_base_func.go b/dbm-services/mysql/db-priv/service/clone_client_priv_base_func.go index 7c2b537112..691ea657f0 100644 --- a/dbm-services/mysql/db-priv/service/clone_client_priv_base_func.go +++ b/dbm-services/mysql/db-priv/service/clone_client_priv_base_func.go @@ -1,12 +1,13 @@ package service import ( - "dbm-services/mysql/priv-service/errno" "fmt" "regexp" "strings" "sync" + "dbm-services/common/go-pubpkg/errno" + "github.com/asaskevich/govalidator" ) @@ -56,7 +57,7 @@ func ReplaceHostInProxyGrants(grants []string, sourceIp string, targetIp []strin } // GetProxyPrivilege 获取proxy白名单 -func GetProxyPrivilege(address string, host string, bkCloudId int64) ([]string, error) { +func GetProxyPrivilege(address string, host string, bkCloudId int64, specifiedUser string) ([]string, error) { var grants []string sql := "select * from user;" var queryRequest = QueryRequest{[]string{address}, []string{sql}, true, 30, bkCloudId} @@ -68,20 +69,25 @@ func GetProxyPrivilege(address string, host string, bkCloudId int64) ([]string, } usersResult := output.CmdResults[0].TableData if host == "" { + // 实例间克隆 for _, user := range usersResult { addUserSQL := fmt.Sprintf("refresh_users('%s','+')", user["user@ip"].(string)) grants = append(grants, addUserSQL) } } else { - regexp := regexp.MustCompile(fmt.Sprintf(".*@%s$", strings.ReplaceAll(host, ".", "\\."))) + // 客户端克隆 + re := regexp.MustCompile(fmt.Sprintf(".*@%s$", strings.ReplaceAll(host, ".", "\\."))) + // 客户端克隆并且指定了user + if specifiedUser != "" { + re = regexp.MustCompile(fmt.Sprintf("^%s@%s$", specifiedUser, strings.ReplaceAll(host, ".", "\\."))) + } for _, user := range usersResult { tmpUser := user["user@ip"].(string) - if regexp.MatchString(tmpUser) { + if re.MatchString(tmpUser) { addUserSQL := fmt.Sprintf("refresh_users('%s','+')", tmpUser) grants = append(grants, addUserSQL) } } - } return grants, nil } diff --git a/dbm-services/mysql/db-priv/service/clone_client_priv_object.go b/dbm-services/mysql/db-priv/service/clone_client_priv_object.go index ccda6790a2..8ae86f2e7a 100644 --- a/dbm-services/mysql/db-priv/service/clone_client_priv_object.go +++ b/dbm-services/mysql/db-priv/service/clone_client_priv_object.go @@ -19,6 +19,9 @@ type CloneClientPrivPara struct { TargetIp []string `json:"target_ip"` BkCloudId *int64 `json:"bk_cloud_id"` ClusterType *string `json:"cluster_type"` + // 如下User和TargetInstances是标准运维【指定user和集群的客户端权限克隆所需的参数】,TargetInstances为主域名 + User string `json:"user"` + TargetInstances []string `json:"target_instances"` } // String 打印CloneClientPrivPara diff --git a/dbm-services/mysql/db-priv/service/clone_instance_priv.go b/dbm-services/mysql/db-priv/service/clone_instance_priv.go index 0786ff0802..110943e058 100644 --- a/dbm-services/mysql/db-priv/service/clone_instance_priv.go +++ b/dbm-services/mysql/db-priv/service/clone_instance_priv.go @@ -1,10 +1,11 @@ package service import ( - "dbm-services/mysql/priv-service/errno" - "dbm-services/mysql/priv-service/util" "fmt" "strings" + + "dbm-services/common/go-pubpkg/errno" + "dbm-services/mysql/priv-service/util" ) // CloneInstancePrivDryRun 克隆实例权限预检查 @@ -60,7 +61,7 @@ func (m *CloneInstancePrivPara) CloneInstancePriv(jsonPara string) error { // 此处单集群instanceType是single if instanceType == machineTypeSingle || instanceType == machineTypeBackend || instanceType == machineTypeRemote || instanceType == machineTypeSpider { - userGrants, err := GetRemotePrivilege(m.Source.Address, "", *m.BkCloudId, instanceType) + userGrants, err := GetRemotePrivilege(m.Source.Address, "", *m.BkCloudId, instanceType, "") if err != nil { return err } else if len(userGrants) == 0 { @@ -92,7 +93,7 @@ func (m *CloneInstancePrivPara) CloneInstancePriv(jsonPara string) error { if err != nil { return errno.ClonePrivilegesFail.Add(err.Error()) } - proxyGrants, err := GetProxyPrivilege(m.Source.Address, "", *m.BkCloudId) + proxyGrants, err := GetProxyPrivilege(m.Source.Address, "", *m.BkCloudId, "") if err != nil { return err } else if len(proxyGrants) == 0 { diff --git a/dbm-services/mysql/db-priv/service/clone_instance_priv_base_func.go b/dbm-services/mysql/db-priv/service/clone_instance_priv_base_func.go index 6c2520b946..1ec7716371 100644 --- a/dbm-services/mysql/db-priv/service/clone_instance_priv_base_func.go +++ b/dbm-services/mysql/db-priv/service/clone_instance_priv_base_func.go @@ -1,13 +1,14 @@ package service import ( - "dbm-services/mysql/priv-service/util" "fmt" "regexp" "strconv" "strings" "sync" + "dbm-services/mysql/priv-service/util" + "github.com/pingcap/parser" "github.com/pingcap/parser/ast" _ "github.com/pingcap/tidb/types/parser_driver" // parser_driver TODO @@ -32,7 +33,8 @@ func (v *visitor) Leave(node ast.Node) (out ast.Node, ok bool) { } // GetRemotePrivilege 获取mysql上的授权语句 -func GetRemotePrivilege(address string, host string, bkCloudId int64, instanceType string) ([]UserGrant, error) { +func GetRemotePrivilege(address string, host string, bkCloudId int64, instanceType string, + user string) ([]UserGrant, error) { var version string var errOuter error var repsOuter oneAddressResult @@ -57,11 +59,15 @@ func GetRemotePrivilege(address string, host string, bkCloudId int64, instanceTy instanceType == machineTypeRemote) { needShowCreateUser = true } - selectUser := `select user,host from mysql.user` + selectUser := `select user,host from mysql.user where 1=1 ` if host != "" { - selectUser += fmt.Sprintf(` where host='%s'`, host) + selectUser += fmt.Sprintf(` and host='%s' `, host) + } + if user != "" { + selectUser += fmt.Sprintf(` and user='%s' `, user) } - queryRequestOuter := QueryRequest{[]string{address}, []string{selectUser}, true, 30, bkCloudId} + queryRequestOuter := QueryRequest{[]string{address}, []string{selectUser}, + true, 30, bkCloudId} repsOuter, errOuter = OneAddressExecuteSql(queryRequestOuter) if errOuter != nil { return nil, errOuter @@ -180,11 +186,14 @@ func (m *CloneInstancePrivPara) DealWithPrivileges(userGrants []UserGrant, insta if instanceType == machineTypeBackend || instanceType == machineTypeSingle || instanceType == machineTypeRemote { - if MySQLVersionParse(sourceVersion, "")/1000 == 8000 && MySQLVersionParse(targetVersion, "")/1000 == 5007 { + if MySQLVersionParse(sourceVersion, "")/1000 == 8000 && + MySQLVersionParse(targetVersion, "")/1000 == 5007 { mysql80Tomysql57 = true - } else if MySQLVersionParse(sourceVersion, "")/1000 == 5007 && MySQLVersionParse(targetVersion, "")/1000 == 5006 { + } else if MySQLVersionParse(sourceVersion, "")/1000 == 5007 && + MySQLVersionParse(targetVersion, "")/1000 == 5006 { mysql57Tomysql56 = true - } else if MySQLVersionParse(sourceVersion, "")/1000 < 8000 && MySQLVersionParse(targetVersion, "")/1000 >= 8000 { + } else if MySQLVersionParse(sourceVersion, "")/1000 < 8000 && + MySQLVersionParse(targetVersion, "")/1000 >= 8000 { mysql5Tomysql8 = true } } @@ -192,7 +201,8 @@ func (m *CloneInstancePrivPara) DealWithPrivileges(userGrants []UserGrant, insta wg := sync.WaitGroup{} errorChan := make(chan error, 1) finishChan := make(chan bool, 1) - var userExcluded = []string{"ADMIN", "mysql.session", "mysql.sys", "mysql.infoschema"} // Delete system user + // Delete system user + var userExcluded = []string{"ADMIN", "mysql.session", "mysql.sys", "mysql.infoschema", "tdbctl"} for _, row := range userGrants { wg.Add(1) go func(row UserGrant, targetIp, sourceIp string) { diff --git a/dbm-services/mysql/db-priv/service/db_meta_service.go b/dbm-services/mysql/db-priv/service/db_meta_service.go index 8a02f10eae..af0ac868cc 100644 --- a/dbm-services/mysql/db-priv/service/db_meta_service.go +++ b/dbm-services/mysql/db-priv/service/db_meta_service.go @@ -1,16 +1,17 @@ package service import ( - "dbm-services/mysql/priv-service/errno" - "dbm-services/mysql/priv-service/util" "encoding/json" "fmt" "net/http" + "dbm-services/common/go-pubpkg/errno" + "dbm-services/mysql/priv-service/util" + "golang.org/x/exp/slog" ) -const mysql string = "mysql" //包含tendbha和tendbsingle +const mysql string = "mysql" // 包含tendbha和tendbsingle const tendbha string = "tendbha" const tendbsingle string = "tendbsingle" const tendbcluster string = "tendbcluster" diff --git a/dbm-services/mysql/db-remote-service/go.mod b/dbm-services/mysql/db-remote-service/go.mod index 70fe4814b2..befb84aa9e 100644 --- a/dbm-services/mysql/db-remote-service/go.mod +++ b/dbm-services/mysql/db-remote-service/go.mod @@ -6,12 +6,11 @@ require ( github.com/gin-gonic/gin v1.9.0 github.com/go-redis/redis/v8 v8.11.5 github.com/go-sql-driver/mysql v1.7.1 - github.com/google/uuid v1.3.0 github.com/jmoiron/sqlx v1.3.5 github.com/pkg/errors v0.9.1 github.com/spf13/cobra v1.7.0 github.com/spf13/viper v1.15.0 - golang.org/x/exp v0.0.0-20230418202329-0354be287a23 + golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df gopkg.in/natefinch/lumberjack.v2 v2.2.1 ) diff --git a/dbm-services/mysql/db-remote-service/go.sum b/dbm-services/mysql/db-remote-service/go.sum index f92d956332..852a495fee 100644 --- a/dbm-services/mysql/db-remote-service/go.sum +++ b/dbm-services/mysql/db-remote-service/go.sum @@ -145,8 +145,6 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= @@ -273,8 +271,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230418202329-0354be287a23 h1:4NKENAGIctmZYLK9W+X1kDK8ObBFqOSCJM6WE7CvkJY= -golang.org/x/exp v0.0.0-20230418202329-0354be287a23/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME= +golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= diff --git a/dbm-services/mysql/db-remote-service/pkg/mysql_rpc/embed.go b/dbm-services/mysql/db-remote-service/pkg/mysql_rpc/embed.go index 59624de897..54e62acca8 100644 --- a/dbm-services/mysql/db-remote-service/pkg/mysql_rpc/embed.go +++ b/dbm-services/mysql/db-remote-service/pkg/mysql_rpc/embed.go @@ -3,6 +3,7 @@ package mysql_rpc import ( "context" "fmt" + "regexp" "strings" "time" @@ -40,18 +41,19 @@ func (c *MySQLRPCEmbed) MakeConnection(address string, user string, password str // ParseCommand mysql 解析命令 func (c *MySQLRPCEmbed) ParseCommand(command string) (*parser.ParseQueryBase, error) { - /* - 由于 tmysqlparser 和中控兼容性不好, 不再使用 tmysqlparser 解析 - 改回不那么精确的用 sql 首单词来区分下 - */ - firstWord := strings.Split(command, " ")[0] - slog.Info("parse command", - slog.String("command", command), - slog.String("first command word", firstWord)) + ///* + // 由于 tmysqlparser 和中控兼容性不好, 不再使用 tmysqlparser 解析 + // 改回不那么精确的用 sql 首单词来区分下 + //*/ + //pattern := regexp.MustCompile(`\s+`) + //firstWord := pattern.Split(command, -1)[0] + //slog.Info("parse command", + // slog.String("command", command), + // slog.String("first command word", firstWord)) return &parser.ParseQueryBase{ QueryId: 0, - Command: firstWord, + Command: command, //strings.ToLower(firstWord), ErrorCode: 0, ErrorMsg: "", }, nil @@ -59,13 +61,12 @@ func (c *MySQLRPCEmbed) ParseCommand(command string) (*parser.ParseQueryBase, er // IsQueryCommand mysql 解析命令 func (c *MySQLRPCEmbed) IsQueryCommand(pc *parser.ParseQueryBase) bool { - return slices.Index(genericDoQueryCommand, strings.ToLower(pc.Command)) >= 0 + return isQueryCommand(pc.Command) } // IsExecuteCommand mysql 解析命令 func (c *MySQLRPCEmbed) IsExecuteCommand(pc *parser.ParseQueryBase) bool { - return !c.IsQueryCommand(pc) - // return slices.Index(doExecuteParseCommands, pc.Command) >= 0 + return !isQueryCommand(pc.Command) } // User mysql 用户 @@ -77,3 +78,28 @@ func (c *MySQLRPCEmbed) User() string { func (c *MySQLRPCEmbed) Password() string { return config.RuntimeConfig.MySQLAdminPassword } + +func isQueryCommand(command string) bool { + pattern := regexp.MustCompile(`\s+`) + firstWord := strings.ToLower(pattern.Split(command, -1)[0]) + if firstWord == "tdbctl" { + return isTDBCTLQuery(command) + } else { + return slices.Index(genericDoQueryCommand, firstWord) >= 0 + } +} + +func isTDBCTLQuery(command string) bool { + splitPattern := regexp.MustCompile(`\s+`) + secondWord := strings.ToLower(splitPattern.Split(command, -1)[1]) + switch secondWord { + case "get", "show": + return true + case "connect": + catchPattern := regexp.MustCompile(`(?mi)^.*execute\s+['"](.*)['"]$`) + executeCmd := catchPattern.FindAllStringSubmatch(command, -1)[0][1] + return isQueryCommand(executeCmd) + default: + return false + } +} diff --git a/dbm-services/mysql/db-remote-service/pkg/parser/parser.go b/dbm-services/mysql/db-remote-service/pkg/parser/parser.go index 39a271d01d..52c5e23d83 100644 --- a/dbm-services/mysql/db-remote-service/pkg/parser/parser.go +++ b/dbm-services/mysql/db-remote-service/pkg/parser/parser.go @@ -1,19 +1,6 @@ // Package parser sql解析 package parser -import ( - "bytes" - "encoding/json" - "os" - "os/exec" - "path" - - "dbm-services/mysql/db-remote-service/pkg/config" - - "github.com/google/uuid" - "github.com/pkg/errors" -) - // ParseQueryBase query result base field type ParseQueryBase struct { QueryId int `json:"query_id"` @@ -22,60 +9,60 @@ type ParseQueryBase struct { ErrorMsg string `json:"error_msg"` } -// ParseResult parse result -type ParseResult struct { - Result []*ParseQueryBase `json:"result"` - MinMySQLVersion int `json:"min_mysql_version"` - MaxMySQLVersion int `json:"max_my_sql_version"` -} - -// Parse parser impl -func Parse(payLoad string) (*ParseResult, error) { - tempDir, err := os.MkdirTemp("", uuid.New().String()) - if err != nil { - return nil, err - } - defer func() { - _ = os.RemoveAll(tempDir) - }() - - inputFile, err := os.CreateTemp(tempDir, "tmp_input_") - if err != nil { - return nil, err - } - defer func() { - _ = inputFile.Close() - }() +//// ParseResult parse result +//type ParseResult struct { +// Result []*ParseQueryBase `json:"result"` +// MinMySQLVersion int `json:"min_mysql_version"` +// MaxMySQLVersion int `json:"max_my_sql_version"` +//} - _, err = inputFile.WriteString(payLoad) - if err != nil { - return nil, err - } - - var stdout, stderr bytes.Buffer - cmd := exec.Command( - config.RuntimeConfig.ParserBin, - "--sql-file", inputFile.Name(), - "--output-path", tempDir, - ) - cmd.Stdout = &stdout - cmd.Stderr = &stderr - - err = cmd.Run() - if err != nil { - return nil, errors.Wrap(err, stderr.String()) - } - - output, err := os.ReadFile(path.Join(tempDir, "tmysqlparse_out.json")) - if err != nil { - return nil, err - } - - var res ParseResult - err = json.Unmarshal(output, &res) - if err != nil { - return nil, err - } - - return &res, nil -} +//// Parse parser impl +//func Parse(payLoad string) (*ParseResult, error) { +// tempDir, err := os.MkdirTemp("", uuid.New().String()) +// if err != nil { +// return nil, err +// } +// defer func() { +// _ = os.RemoveAll(tempDir) +// }() +// +// inputFile, err := os.CreateTemp(tempDir, "tmp_input_") +// if err != nil { +// return nil, err +// } +// defer func() { +// _ = inputFile.Close() +// }() +// +// _, err = inputFile.WriteString(payLoad) +// if err != nil { +// return nil, err +// } +// +// var stdout, stderr bytes.Buffer +// cmd := exec.Command( +// config.RuntimeConfig.ParserBin, +// "--sql-file", inputFile.Name(), +// "--output-path", tempDir, +// ) +// cmd.Stdout = &stdout +// cmd.Stderr = &stderr +// +// err = cmd.Run() +// if err != nil { +// return nil, errors.Wrap(err, stderr.String()) +// } +// +// output, err := os.ReadFile(path.Join(tempDir, "tmysqlparse_out.json")) +// if err != nil { +// return nil, err +// } +// +// var res ParseResult +// err = json.Unmarshal(output, &res) +// if err != nil { +// return nil, err +// } +// +// return &res, nil +//} diff --git a/dbm-services/mysql/db-remote-service/pkg/rpc_core/execute_cmds_on_addr.go b/dbm-services/mysql/db-remote-service/pkg/rpc_core/execute_cmds_on_addr.go index 6129aa11a4..324b11521c 100644 --- a/dbm-services/mysql/db-remote-service/pkg/rpc_core/execute_cmds_on_addr.go +++ b/dbm-services/mysql/db-remote-service/pkg/rpc_core/execute_cmds_on_addr.go @@ -28,6 +28,9 @@ func (c *RPCWrapper) executeOneAddr(address string) (res []cmdResult, err error) slog.Error("get conn from db", err) return nil, err } + defer func() { + _ = conn.Close() + }() for idx, command := range c.commands { pc, err := c.ParseCommand(command) diff --git a/dbm-services/mysql/db-remote-service/pkg/service/handler_parser.go b/dbm-services/mysql/db-remote-service/pkg/service/handler_parser.go index 9f36782619..004ac07141 100644 --- a/dbm-services/mysql/db-remote-service/pkg/service/handler_parser.go +++ b/dbm-services/mysql/db-remote-service/pkg/service/handler_parser.go @@ -1,47 +1,39 @@ package service -import ( - "net/http" +//type parseRequest struct { +// Statements string `form:"statements" json:"statements"` +//} - "dbm-services/mysql/db-remote-service/pkg/parser" - - "github.com/gin-gonic/gin" -) - -type parseRequest struct { - Statements string `form:"statements" json:"statements"` -} - -// parseHandler parser 服务 -func parseHandler(c *gin.Context) { - var req parseRequest - if err := c.ShouldBindJSON(&req); err != nil { - c.JSON( - http.StatusBadRequest, gin.H{ - "code": 1, - "data": "", - "msg": err.Error(), - }, - ) - return - } - - resp, err := parser.Parse(req.Statements) - if err != nil { - c.JSON( - http.StatusInternalServerError, gin.H{ - "code": 1, - "data": "", - "msg": err.Error(), - }, - ) - } - - c.JSON( - http.StatusOK, gin.H{ - "code": 0, - "data": resp, - "msg": "", - }, - ) -} +//// parseHandler parser 服务 +//func parseHandler(c *gin.Context) { +// var req parseRequest +// if err := c.ShouldBindJSON(&req); err != nil { +// c.JSON( +// http.StatusBadRequest, gin.H{ +// "code": 1, +// "data": "", +// "msg": err.Error(), +// }, +// ) +// return +// } +// +// resp, err := parser.Parse(req.Statements) +// if err != nil { +// c.JSON( +// http.StatusInternalServerError, gin.H{ +// "code": 1, +// "data": "", +// "msg": err.Error(), +// }, +// ) +// } +// +// c.JSON( +// http.StatusOK, gin.H{ +// "code": 0, +// "data": resp, +// "msg": "", +// }, +// ) +//} diff --git a/dbm-services/mysql/db-remote-service/pkg/service/router.go b/dbm-services/mysql/db-remote-service/pkg/service/router.go index 664031d94e..9891ac8723 100644 --- a/dbm-services/mysql/db-remote-service/pkg/service/router.go +++ b/dbm-services/mysql/db-remote-service/pkg/service/router.go @@ -10,7 +10,7 @@ import ( func RegisterRouter(engine *gin.Engine) { mysqlGroup := engine.Group("/mysql") mysqlGroup.POST("/rpc", handler_rpc.MySQLRPCHandler) - mysqlGroup.POST("/parser", parseHandler) + //mysqlGroup.POST("/parser", parseHandler) proxyGroup := engine.Group("/proxy-admin") proxyGroup.POST("/rpc", handler_rpc.ProxyRPCHandler) diff --git a/dbm-services/mysql/db-simulation/Dockerfile b/dbm-services/mysql/db-simulation/Dockerfile index 8b73de7fea..be6c67ef5e 100644 --- a/dbm-services/mysql/db-simulation/Dockerfile +++ b/dbm-services/mysql/db-simulation/Dockerfile @@ -1,4 +1,4 @@ -FROM mirrors.tencent.com/build/blueking/tmysqlparse:3.0.8 +FROM mirrors.tencent.com/build/blueking/tmysqlparse:latest RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime RUN echo "Asia/Shanghai" > /etc/timezone diff --git a/dbm-services/mysql/db-simulation/README.md b/dbm-services/mysql/db-simulation/README.md new file mode 100644 index 0000000000..31948958a0 --- /dev/null +++ b/dbm-services/mysql/db-simulation/README.md @@ -0,0 +1,7 @@ + +### 库表名称规范主要规则 +- 不允许包含特殊的字符: `~¥$!@#%^&*()+={}\[\];:'"<>,.?/\\|\s` +- 只能由,`[0-9],[a-z],[A-Z],-,_` 组成 +- 只允许数字、大小写字母开头和结尾 +- 不允许选择包含系统(除test) +- 不允许包含MySQL关键字 \ No newline at end of file diff --git a/dbm-services/mysql/db-simulation/app/keyworld/keyworld.go b/dbm-services/mysql/db-simulation/app/keyworld/keyworld.go new file mode 100644 index 0000000000..22146d1438 --- /dev/null +++ b/dbm-services/mysql/db-simulation/app/keyworld/keyworld.go @@ -0,0 +1,27 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +// Package keyworld TODO +package keyworld + +import ( + "dbm-services/common/go-pubpkg/cmutil" +) + +// ALL_KEYWORD TODO +var ALL_KEYWORD []string + +func init() { + ALL_KEYWORD = append(ALL_KEYWORD, MySQL55_KEYWORD...) + ALL_KEYWORD = append(ALL_KEYWORD, MySQL56_KEYWORD...) + ALL_KEYWORD = append(ALL_KEYWORD, MySQL57_KEYWORD...) + ALL_KEYWORD = append(ALL_KEYWORD, MySQL80_KEYWORD...) + ALL_KEYWORD = cmutil.RemoveDuplicate(ALL_KEYWORD) +} diff --git a/dbm-services/mysql/db-simulation/app/keyworld/mysql55.go b/dbm-services/mysql/db-simulation/app/keyworld/mysql55.go new file mode 100644 index 0000000000..c659fa19e9 --- /dev/null +++ b/dbm-services/mysql/db-simulation/app/keyworld/mysql55.go @@ -0,0 +1,37 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package keyworld + +// MySQL55_KEYWORD TODO +var MySQL55_KEYWORD = []string{ + "ACCESSIBLE", "ADD", "ALL", "ALTER", "ANALYZE", "AND", "AS", "ASC", "ASENSITIVE", "BEFORE", "BETWEEN", "BIGINT", + "BINARY", "BLOB", "BOTH", "BY", "CALL", "CASCADE", "CASE", "CHANGE", "CHAR", "CHARACTER", "CHECK", "COLLATE", + "COLUMN", "CONDITION", "CONSTRAINT", "CONTINUE", "CONVERT", "CREATE", "CROSS", "CURRENT_DATE", "CURRENT_TIME", + "CURRENT_TIMESTAMP", "CURRENT_USER", "CURSOR", "DATABASE", "DATABASES", "DAY_HOUR", "DAY_MICROSECOND", "DAY_MINUTE", + "DAY_SECOND", "DEC", "DECIMAL", "DECLARE", "DEFAULT", "DELAYED", "DELETE", "DESC", "DESCRIBE", "DETERMINISTIC", + "DISTINCT", "DISTINCTROW", "DIV", "DOUBLE", "DROP", "DUAL", "EACH", "ELSE", "ELSEIF", "ENCLOSED", "ESCAPED", "EXISTS", + "EXIT", "EXPLAIN", "FALSE", "FETCH", "FLOAT", "FLOAT4", "FLOAT8", "FOR", "FORCE", "FOREIGN", "FROM", "FULLTEXT", + "GRANT", "GROUP", "HAVING", "HIGH_PRIORITY", "HOUR_MICROSECOND", "HOUR_MINUTE", "HOUR_SECOND", "IF", "IGNORE", "IN", + "INDEX", "INFILE", "INNER", "INOUT", "INSENSITIVE", "INSERT", "INT", "INT1", "INT2", "INT3", "INT4", "INT8", "INTEGER", + "INTERVAL", "INTO", "IS", "ITERATE", "JOIN", "KEY", "KEYS", "KILL", "LEADING", "LEAVE", "LEFT", "LIKE", "LIMIT", + "LINEAR", "LINES", "LOAD", "LOCALTIME", "LOCALTIMESTAMP", "LOCK", "LONG", "LONGBLOB", "LONGTEXT", "LOOP", + "LOW_PRIORITY", "MASTER_SSL_VERIFY_SERVER_CERT", "MATCH", "MAXVALUE", "MEDIUMBLOB", "MEDIUMINT", "MEDIUMTEXT", + "MIDDLEINT", "MINUTE_MICROSECOND", "MINUTE_SECOND", "MOD", "MODIFIES", "NATURAL", "NOT", "NO_WRITE_TO_BINLOG", "NULL", + "NUMERIC", "ON", "OPTIMIZE", "OPTION", "OPTIONALLY", "OR", "ORDER", "OUT", "OUTER", "OUTFILE", "PRECISION", "PRIMARY", + "PROCEDURE", "PURGE", "RANGE", "READ", "READS", "READ_WRITE", "REAL", "REFERENCES", "REGEXP", "RELEASE", "RENAME", + "REPEAT", "REPLACE", "REQUIRE", "RESTRICT", "RETURN", "REVOKE", "RIGHT", "RLIKE", "SCHEMA", "SCHEMAS", + "SECOND_MICROSECOND", "SELECT", "SENSITIVE", "SEPARATOR", "SET", "SHOW", "SMALLINT", "SPATIAL", "SPECIFIC", "SQL", + "SQLEXCEPTION", "SQLSTATE", "SQLWARNING", "SQL_BIG_RESULT", "SQL_CALC_FOUND_ROWS", "SQL_SMALL_RESULT", "SSL", + "STARTING", "STRAIGHT_JOIN", "TABLE", "TERMINATED", "THEN", "TINYBLOB", "TINYINT", "TINYTEXT", "TO", "TRAILING", + "TRIGGER", "TRUE", "UNDO", "UNION", "UNIQUE", "UNLOCK", "UNSIGNED", "UPDATE", "USAGE", "USE", "USING", "UTC_DATE", + "UTC_TIME", "UTC_TIMESTAMP", "VALUES", "VARBINARY", "VARCHAR", "VARCHARACTER", "VARYING", "WHEN", "WHERE", "WHILE", + "WITH", "WRITE", "XOR", "YEAR_MONTH", "ZEROFILL", +} diff --git a/dbm-services/mysql/db-simulation/app/keyworld/mysql56.go b/dbm-services/mysql/db-simulation/app/keyworld/mysql56.go new file mode 100644 index 0000000000..53d3b38aac --- /dev/null +++ b/dbm-services/mysql/db-simulation/app/keyworld/mysql56.go @@ -0,0 +1,81 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package keyworld + +// MySQL56_KEYWORD TODO +var MySQL56_KEYWORD = []string{"ACCESSIBLE", "ACTION", "ADD", "AFTER", "AGAINST", "AGGREGATE", + "ALGORITHM", "ALL", "ALTER", "ANALYSE", "ANALYZE", "AND", "ANY", "AS", "ASC", "ASCII", + "ASENSITIVE", "AT", "AUTHORS", "AUTOEXTEND_SIZE", "AUTO_INCREMENT", "AVG", "AVG_ROW_LENGTH", + "BACKUP", "BEFORE", "BEGIN", "BETWEEN", "BIGINT", "BINARY", "BINLOG", "BIT", "BLOB", "BLOCK", + "BOOL", "BOOLEAN", "BOTH", "BTREE", "BY", "BYTE", "CACHE", "CALL", "CASCADE", "CASCADED", "CASE", + "CATALOG_NAME", "CHAIN", "CHANGE", "CHANGED", "CHAR", "CHARACTER", "CHARSET", "CHECK", "CHECKSUM", + "CIPHER", "CLASS_ORIGIN", "CLIENT", "CLOSE", "COALESCE", "CODE", "COLLATE", "COLLATION", "COLUMN", + "COLUMNS", "COLUMN_FORMAT", "COLUMN_NAME", "COMMENT", "COMMIT", "COMMITTED", "COMPACT", "COMPLETION", + "COMPRESSED", "CONCURRENT", "CONDITION", "CONNECTION", "CONSISTENT", "CONSTRAINT", "CONSTRAINT_CATALOG", + "CONSTRAINT_NAME", "CONSTRAINT_SCHEMA", "CONTAINS", "CONTEXT", "CONTINUE", "CONTRIBUTORS", "CONVERT", "CPU", + "CREATE", "CROSS", "CUBE", "CURRENT", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_USER", "CURSOR", + "CURSOR_NAME", "DATA", "DATABASE", "DATABASES", "DATAFILE", "DATE", "DATETIME", "DAY", "DAY_HOUR", "DAY_MICROSECOND", + "DAY_MINUTE", "DAY_SECOND", "DEALLOCATE", "DEC", "DECIMAL", "DECLARE", "DEFAULT", "DEFAULT_AUTH", "DEFINER", + "DELAYED", "DELAY_KEY_WRITE", "DELETE", "DESC", "DESCRIBE", "DES_KEY_FILE", "DETERMINISTIC", "DIAGNOSTICS", + "DIRECTORY", "DISABLE", "DISCARD", "DISK", "DISTINCT", "DISTINCTROW", "DIV", "DO", "DOUBLE", "DROP", "DUAL", + "DUMPFILE", "DUPLICATE", "DYNAMIC", "EACH", "ELSE", "ELSEIF", "ENABLE", "ENCLOSED", "END", "ENDS", "ENGINE", "ENGINES", + "ENUM", "ERROR", "ERRORS", "ESCAPE", "ESCAPED", "EVENT", "EVENTS", "EVERY", "EXCHANGE", "EXECUTE", "EXISTS", "EXIT", + "EXPANSION", "EXPIRE", "EXPLAIN", "EXPORT", "EXTENDED", "EXTENT_SIZE", "FALSE", "FAST", "FAULTS", "FETCH", "FIELDS", + "FILE", "FIRST", "FIXED", "FLOAT", "FLOAT4", "FLOAT8", "FLUSH", "FOR", "FORCE", "FOREIGN", "FORMAT", "FOUND", "FROM", + "FULL", "FULLTEXT", "FUNCTION", "GENERAL", "GEOMETRY", "GEOMETRYCOLLECTION", "GET", "GET_FORMAT", "GLOBAL", "GRANT", + "GRANTS", "GROUP", "HANDLER", "HASH", "HAVING", "HELP", "HIGH_PRIORITY", "HOST", "HOSTS", "HOUR", "HOUR_MICROSECOND", + "HOUR_MINUTE", "HOUR_SECOND", "IDENTIFIED", "IF", "IGNORE", "IGNORE_SERVER_IDS", "IMPORT", "IN", "INDEX", "INDEXES", + "INFILE", "INITIAL_SIZE", "INNER", "INOUT", "INSENSITIVE", "INSERT", "INSERT_METHOD", "INSTALL", "INT", "INT1", "INT2", + "INT3", "INT4", "INT8", "INTEGER", "INTERVAL", "INTO", "INVOKER", "IO", "IO_AFTER_GTIDS", "IO_BEFORE_GTIDS", + "IO_THREAD", "IPC", "IS", "ISOLATION", "ISSUER", "ITERATE", "JOIN", "KEY", "KEYS", "KEY_BLOCK_SIZE", "KILL", + "LANGUAGE", "LAST", "LEADING", "LEAVE", "LEAVES", "LEFT", "LESS", "LEVEL", "LIKE", "LIMIT", "LINEAR", "LINES", + "LINESTRING", "LIST", "LOAD", "LOCAL", "LOCALTIME", "LOCALTIMESTAMP", "LOCK", "LOCKS", "LOGFILE", "LOGS", "LONG", + "LONGBLOB", "LONGTEXT", "LOOP", "LOW_PRIORITY", "MASTER", "MASTER_AUTO_POSITION", "MASTER_BIND", + "MASTER_CONNECT_RETRY", "MASTER_DELAY", "MASTER_HEARTBEAT_PERIOD", "MASTER_HOST", "MASTER_LOG_FILE", "MASTER_LOG_POS", + "MASTER_PASSWORD", "MASTER_PORT", "MASTER_RETRY_COUNT", "MASTER_SERVER_ID", "MASTER_SSL", "MASTER_SSL_CA", + "MASTER_SSL_CAPATH", "MASTER_SSL_CERT", "MASTER_SSL_CIPHER", "MASTER_SSL_CRL", "MASTER_SSL_CRLPATH", "MASTER_SSL_KEY", + "MASTER_SSL_VERIFY_SERVER_CERT", "MASTER_USER", "MATCH", "MAXVALUE", "MAX_CONNECTIONS_PER_HOUR", + "MAX_QUERIES_PER_HOUR", "MAX_ROWS", "MAX_SIZE", "MAX_UPDATES_PER_HOUR", "MAX_USER_CONNECTIONS", "MEDIUM", "MEDIUMBLOB", + "MEDIUMINT", "MEDIUMTEXT", "MEMORY", "MERGE", "MESSAGE_TEXT", "MICROSECOND", "MIDDLEINT", "MIGRATE", "MINUTE", + "MINUTE_MICROSECOND", "MINUTE_SECOND", "MIN_ROWS", "MOD", "MODE", "MODIFIES", "MODIFY", "MONTH", "MULTILINESTRING", + "MULTIPOINT", "MULTIPOLYGON", "MUTEX", "MYSQL_ERRNO", "NAME", "NAMES", "NATIONAL", "NATURAL", "NCHAR", "NDB", + "NDBCLUSTER", "NEW", "NEXT", "NO", "NODEGROUP", "NONE", "NOT", "NO_WAIT", "NO_WRITE_TO_BINLOG", "NULL", "NUMBER", + "NUMERIC", "NVARCHAR", "OFFSET", "OLD_PASSWORD", "ON", "ONE", "ONE_SHOT", "ONLY", "OPEN", "OPTIMIZE", "OPTION", + "OPTIONALLY", "OPTIONS", "OR", "ORDER", "OUT", "OUTER", "OUTFILE", "OWNER", "PACK_KEYS", "PAGE", "PARSER", + "PARTIAL", "PARTITION", "PARTITIONING", "PARTITIONS", "PASSWORD", "PHASE", "PLUGIN", "PLUGINS", "PLUGIN_DIR", + "POINT", "POLYGON", "PORT", "PRECISION", "PREPARE", "PRESERVE", "PREV", "PRIMARY", "PRIVILEGES", "PROCEDURE", + "PROCESSLIST", "PROFILE", "PROFILES", "PROXY", "PURGE", "QUARTER", "QUERY", "QUICK", "RANGE", "READ", + "READS", "READ_ONLY", "READ_WRITE", "REAL", "REBUILD", "RECOVER", "REDOFILE", "REDO_BUFFER_SIZE", "REDUNDANT", + "REFERENCES", "REGEXP", "RELAY", "RELAYLOG", "RELAY_LOG_FILE", "RELAY_LOG_POS", "RELAY_THREAD", "RELEASE", "RELOAD", + "REMOVE", "RENAME", "REORGANIZE", "REPAIR", "REPEAT", "REPEATABLE", "REPLACE", "REPLICATION", + "REQUIRE", "RESET", "RESIGNAL", "RESTORE", "RESTRICT", "RESUME", "RETURN", "RETURNED_SQLSTATE", "RETURNS", "REVERSE", + "REVOKE", "RIGHT", "RLIKE", "ROLLBACK", "ROLLUP", "ROUTINE", "ROW", "ROWS", "ROW_COUNT", "ROW_FORMAT", "RTREE", + "SAVEPOINT", "SCHEDULE", "SCHEMA", "SCHEMAS", "SCHEMA_NAME", "SECOND", "SECOND_MICROSECOND", + "SECURITY", "SELECT", "SENSITIVE", "SEPARATOR", "SERIAL", "SERIALIZABLE", "SERVER", "SESSION", "SET", "SHARE", + "SHOW", "SHUTDOWN", "SIGNAL", "SIGNED", "SIMPLE", "SLAVE", "SLOW", "SMALLINT", "SNAPSHOT", "SOCKET", "SOME", "SONAME", + "SOUNDS", "SOURCE", "SPATIAL", "SPECIFIC", "SQL", "SQLEXCEPTION", "SQLSTATE", "SQLWARNING", + "SQL_AFTER_GTIDS", "SQL_AFTER_MTS_GAPS", "SQL_BEFORE_GTIDS", "SQL_BIG_RESULT", "SQL_BUFFER_RESULT", "SQL_CACHE", + "SQL_CALC_FOUND_ROWS", "SQL_NO_CACHE", "SQL_SMALL_RESULT", + "SQL_THREAD", "SQL_TSI_DAY", "SQL_TSI_HOUR", "SQL_TSI_MINUTE", "SQL_TSI_MONTH", "SQL_TSI_QUARTER", "SQL_TSI_SECOND", + "SQL_TSI_WEEK", "SQL_TSI_YEAR", "SSL", "START", "STARTING", + "STARTS", "STATS_AUTO_RECALC", "STATS_PERSISTENT", "STATS_SAMPLE_PAGES", "STATUS", "STOP", "STORAGE", + "STRAIGHT_JOIN", "STRING", "SUBCLASS_ORIGIN", "SUBJECT", "SUBPARTITION", + "SUBPARTITIONS", "SUPER", "SUSPEND", "SWAPS", "SWITCHES", "TABLE", "TABLES", "TABLESPACE", "TABLE_CHECKSUM", + "TABLE_NAME", "TEMPORARY", "TEMPTABLE", "TERMINATED", + "TEXT", "THAN", "THEN", "TIME", "TIMESTAMP", "TIMESTAMPADD", "TIMESTAMPDIFF", "TINYBLOB", "TINYINT", "TINYTEXT", + "TO", "TRAILING", "TRANSACTION", "TRIGGER", "TRIGGERS", + "TRUE", "TRUNCATE", "TYPE", "TYPES", "UNCOMMITTED", "UNDEFINED", "UNDO", "UNDOFILE", "UNDO_BUFFER_SIZE", "UNICODE", + "UNINSTALL", "UNION", "UNIQUE", "UNKNOWN", "UNLOCK", + "UNSIGNED", "UNTIL", "UPDATE", "UPGRADE", "USAGE", "USE", "USER", "USER_RESOURCES", "USE_FRM", "USING", "UTC_DATE", + "UTC_TIME", "UTC_TIMESTAMP", "VALUE", "VALUES", + "VARBINARY", "VARCHAR", "VARCHARACTER", "VARIABLES", "VARYING", "VIEW", "WAIT", "WARNINGS", "WEEK", "WEIGHT_STRING", + "WHEN", "WHERE", "WHILE", "WITH", "WORK", "WRAPPER", "WRITE", "X509", "XA", "XML", "XOR", "YEAR", "YEAR_MONTH", + "ZEROFILL"} diff --git a/dbm-services/mysql/db-simulation/app/keyworld/mysql57.go b/dbm-services/mysql/db-simulation/app/keyworld/mysql57.go new file mode 100644 index 0000000000..18f42356c5 --- /dev/null +++ b/dbm-services/mysql/db-simulation/app/keyworld/mysql57.go @@ -0,0 +1,94 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package keyworld + +// MySQL57_KEYWORD TODO +var MySQL57_KEYWORD = []string{"ACCESSIBLE", "ACCOUNT", "ACTION", "ADD", "AFTER", "AGAINST", "AGGREGATE", "ALGORITHM", + "ALL", "ALTER", "ALWAYS", "ANALYSE", "ANALYZE", "AND", "ANY", "AS", "ASC", "ASCII", "ASENSITIVE", "AT", + "AUTOEXTEND_SIZE", "AUTO_INCREMENT", "AVG", "AVG_ROW_LENGTH", "BACKUP", "BEFORE", "BEGIN", "BETWEEN", "BIGINT", + "BINARY", "BINLOG", "BIT", "BLOB", "BLOCK", "BOOL", "BOOLEAN", "BOTH", "BTREE", "BY", "BYTE", "CACHE", "CALL", + "CASCADE", "CASCADED", "CASE", "CATALOG_NAME", "CHAIN", "CHANGE", "CHANGED", "CHANNEL", "CHAR", "CHARACTER", "CHARSET", + "CHECK", "CHECKSUM", "CIPHER", "CLASS_ORIGIN", "CLIENT", "CLOSE", "COALESCE", "CODE", "COLLATE", "COLLATION", "COLUMN", + "COLUMNS", "COLUMN_FORMAT", "COLUMN_NAME", "COMMENT", "COMMIT", "COMMITTED", "COMPACT", "COMPLETION", "COMPRESSED", + "COMPRESSION", "CONCURRENT", "CONDITION", "CONNECTION", "CONSISTENT", "CONSTRAINT", "CONSTRAINT_CATALOG", + "CONSTRAINT_NAME", "CONSTRAINT_SCHEMA", "CONTAINS", "CONTEXT", "CONTINUE", "CONVERT", "CPU", "CREATE", "CROSS", "CUBE", + "CURRENT", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_USER", "CURSOR", "CURSOR_NAME", "DATA", + "DATABASE", "DATABASES", "DATAFILE", "DATE", "DATETIME", "DAY", "DAY_HOUR", "DAY_MICROSECOND", "DAY_MINUTE", + "DAY_SECOND", "DEALLOCATE", "DEC", "DECIMAL", "DECLARE", "DEFAULT", "DEFAULT_AUTH", "DEFINER", "DELAYED", + "DELAY_KEY_WRITE", "DELETE", "DESC", "DESCRIBE", "DES_KEY_FILE", "DETERMINISTIC", "DIAGNOSTICS", "DIRECTORY", + "DISABLE", "DISCARD", "DISK", "DISTINCT", "DISTINCTROW", "DIV", "DO", "DOUBLE", "DROP", "DUAL", "DUMPFILE", + "DUPLICATE", "DYNAMIC", "EACH", "ELSE", "ELSEIF", "ENABLE", "ENCLOSED", "ENCRYPTION", "END", "ENDS", "ENGINE", + "ENGINES", "ENUM", "ERROR", "ERRORS", "ESCAPE", "ESCAPED", "EVENT", "EVENTS", "EVERY", "EXCHANGE", "EXECUTE", "EXISTS", + "EXIT", "EXPANSION", "EXPIRE", "EXPLAIN", "EXPORT", "EXTENDED", "EXTENT_SIZE", "FALSE", "FAST", "FAULTS", "FETCH", + "FIELDS", "FILE", "FILE_BLOCK_SIZE", "FILTER", "FIRST", "FIXED", "FLOAT", "FLOAT4", "FLOAT8", "FLUSH", "FOLLOWS", + "FOR", "FORCE", "FOREIGN", "FORMAT", "FOUND", "FROM", "FULL", "FULLTEXT", "FUNCTION", "GENERAL", "GENERATED", + "GEOMETRY", "GEOMETRYCOLLECTION", "GET", "GET_FORMAT", "GLOBAL", "GRANT", "GRANTS", "GROUP", "GROUP_REPLICATION", + "HANDLER", "HASH", "HAVING", "HELP", "HIGH_PRIORITY", "HOST", "HOSTS", "HOUR", "HOUR_MICROSECOND", "HOUR_MINUTE", + "HOUR_SECOND", "IDENTIFIED", "IF", "IGNORE", "IGNORE_SERVER_IDS", "IMPORT", "IN", "INDEX", "INDEXES", + "INFILE", "INITIAL_SIZE", "INNER", "INOUT", "INSENSITIVE", "INSERT", "INSERT_METHOD", "INSTALL", "INSTANCE", "INT", + "INT1", "INT2", "INT3", "INT4", "INT8", "INTEGER", "INTERVAL", "INTO", "INVOKER", "IO", "IO_AFTER_GTIDS", + "IO_BEFORE_GTIDS", "IO_THREAD", "IPC", "IS", "ISOLATION", "ISSUER", "ITERATE", "JOIN", "JSON", "KEY", "KEYS", + "KEY_BLOCK_SIZE", "KILL", "LANGUAGE", "LAST", "LEADING", "LEAVE", "LEAVES", "LEFT", "LESS", "LEVEL", "LIKE", "LIMIT", + "LINEAR", "LINES", "LINESTRING", "LIST", "LOAD", "LOCAL", "LOCALTIME", "LOCALTIMESTAMP", "LOCK", "LOCKS", "LOGFILE", + "LOGS", "LONG", "LONGBLOB", "LONGTEXT", "LOOP", "LOW_PRIORITY", "MASTER", "MASTER_AUTO_POSITION", "MASTER_BIND", + "MASTER_CONNECT_RETRY", "MASTER_DELAY", "MASTER_HEARTBEAT_PERIOD", "MASTER_HOST", "MASTER_LOG_FILE", "MASTER_LOG_POS", + "MASTER_PASSWORD", "MASTER_PORT", "MASTER_RETRY_COUNT", "MASTER_SERVER_ID", "MASTER_SSL", "MASTER_SSL_CA", + "MASTER_SSL_CAPATH", "MASTER_SSL_CERT", "MASTER_SSL_CIPHER", "MASTER_SSL_CRL", "MASTER_SSL_CRLPATH", "MASTER_SSL_KEY", + "MASTER_SSL_VERIFY_SERVER_CERT", "MASTER_TLS_VERSION", "MASTER_USER", "MATCH", "MAXVALUE", "MAX_CONNECTIONS_PER_HOUR", + "MAX_QUERIES_PER_HOUR", "MAX_ROWS", "MAX_SIZE", "MAX_STATEMENT_TIME", "MAX_UPDATES_PER_HOUR", "MAX_USER_CONNECTIONS", + "MEDIUM", "MEDIUMBLOB", "MEDIUMINT", "MEDIUMTEXT", "MEMORY", "MERGE", + "MESSAGE_TEXT", "MICROSECOND", "MIDDLEINT", "MIGRATE", "MINUTE", "MINUTE_MICROSECOND", "MINUTE_SECOND", "MIN_ROWS", + "MOD", "MODE", "MODIFIES", "MODIFY", + "MONTH", "MULTILINESTRING", "MULTIPOINT", "MULTIPOLYGON", "MUTEX", "MYSQL_ERRNO", "NAME", "NAMES", "NATIONAL", + "NATURAL", "NCHAR", "NDB", "NDBCLUSTER", + "NEVER", "NEW", "NEXT", "NO", "NODEGROUP", "NONBLOCKING", "NONE", "NOT", "NO_WAIT", "NO_WRITE_TO_BINLOG", "NULL", + "NUMBER", "NUMERIC", "NVARCHAR", "OFFSET", + "OLD_PASSWORD", "ON", "ONE", "ONLY", "OPEN", "OPTIMIZE", "OPTIMIZER_COSTS", "OPTION", "OPTIONALLY", "OPTIONS", "OR", + "ORDER", "OUT", "OUTER", "OUTFILE", + "OWNER", "PACK_KEYS", "PAGE", "PARSER", "PARSE_GCOL_EXPR", "PARTIAL", "PARTITION", "PARTITIONING", "PARTITIONS", + "PASSWORD", "PHASE", "PLUGIN", "PLUGINS", + "PLUGIN_DIR", "POINT", "POLYGON", "PORT", "PRECEDES", "PRECISION", "PREPARE", "PRESERVE", "PREV", "PRIMARY", + "PRIVILEGES", "PROCEDURE", "PROCESSLIST", + "PROFILE", "PROFILES", "PROXY", "PURGE", "QUARTER", "QUERY", "QUICK", "RANGE", "READ", "READS", "READ_ONLY", + "READ_WRITE", "REAL", "REBUILD", "RECOVER", + "REDOFILE", "REDO_BUFFER_SIZE", "REDUNDANT", "REFERENCES", "REGEXP", "RELAY", "RELAYLOG", "RELAY_LOG_FILE", + "RELAY_LOG_POS", "RELAY_THREAD", "RELEASE", + "RELOAD", "REMOVE", "RENAME", "REORGANIZE", "REPAIR", "REPEAT", "REPEATABLE", "REPLACE", "REPLICATE_DO_DB", + "REPLICATE_DO_TABLE", "REPLICATE_IGNORE_DB", + "REPLICATE_IGNORE_TABLE", "REPLICATE_REWRITE_DB", "REPLICATE_WILD_DO_TABLE", "REPLICATE_WILD_IGNORE_TABLE", + "REPLICATION", "REQUIRE", "RESET", "RESIGNAL", + "RESTORE", "RESTRICT", "RESUME", "RETURN", "RETURNED_SQLSTATE", "RETURNS", "REVERSE", "REVOKE", "RIGHT", "RLIKE", + "ROLLBACK", "ROLLUP", "ROTATE", "ROUTINE", + "ROW", "ROWS", "ROW_COUNT", "ROW_FORMAT", "RTREE", "SAVEPOINT", "SCHEDULE", "SCHEMA", "SCHEMAS", "SCHEMA_NAME", + "SECOND", "SECOND_MICROSECOND", "SECURITY", + "SELECT", "SENSITIVE", "SEPARATOR", "SERIAL", "SERIALIZABLE", "SERVER", "SESSION", "SET", "SHARE", "SHOW", + "SHUTDOWN", "SIGNAL", "SIGNED", "SIMPLE", "SLAVE", + "SLOW", "SMALLINT", "SNAPSHOT", "SOCKET", "SOME", "SONAME", "SOUNDS", "SOURCE", "SPATIAL", "SPECIFIC", "SQL", + "SQLEXCEPTION", "SQLSTATE", "SQLWARNING", + "SQL_AFTER_GTIDS", "SQL_AFTER_MTS_GAPS", "SQL_BEFORE_GTIDS", "SQL_BIG_RESULT", "SQL_BUFFER_RESULT", "SQL_CACHE", + "SQL_CALC_FOUND_ROWS", "SQL_NO_CACHE", + "SQL_SMALL_RESULT", "SQL_THREAD", "SQL_TSI_DAY", "SQL_TSI_HOUR", "SQL_TSI_MINUTE", "SQL_TSI_MONTH", + "SQL_TSI_QUARTER", "SQL_TSI_SECOND", "SQL_TSI_WEEK", + "SQL_TSI_YEAR", "SSL", "STACKED", "START", "STARTING", "STARTS", "STATS_AUTO_RECALC", "STATS_PERSISTENT", + "STATS_SAMPLE_PAGES", "STATUS", "STOP", "STORAGE", + "STORED", "STRAIGHT_JOIN", "STRING", "SUBCLASS_ORIGIN", "SUBJECT", "SUBPARTITION", "SUBPARTITIONS", "SUPER", + "SUSPEND", "SWAPS", "SWITCHES", "TABLE", "TABLES", + "TABLESPACE", "TABLE_CHECKSUM", "TABLE_NAME", "TEMPORARY", "TEMPTABLE", "TERMINATED", "TEXT", "THAN", "THEN", "TIME", + "TIMESTAMP", "TIMESTAMPADD", "TIMESTAMPDIFF", + "TINYBLOB", "TINYINT", "TINYTEXT", "TO", "TRAILING", "TRANSACTION", "TRIGGER", "TRIGGERS", "TRUE", "TRUNCATE", + "TYPE", "TYPES", "UNCOMMITTED", "UNDEFINED", "UNDO", + "UNDOFILE", "UNDO_BUFFER_SIZE", "UNICODE", "UNINSTALL", "UNION", "UNIQUE", "UNKNOWN", "UNLOCK", "UNSIGNED", "UNTIL", + "UPDATE", "UPGRADE", "USAGE", "USE", "USER", + "USER_RESOURCES", "USE_FRM", "USING", "UTC_DATE", "UTC_TIME", "UTC_TIMESTAMP", "VALIDATION", "VALUE", "VALUES", + "VARBINARY", "VARCHAR", "VARCHARACTER", "VARIABLES", + "VARYING", "VIEW", "VIRTUAL", "WAIT", "WARNINGS", "WEEK", "WEIGHT_STRING", "WHEN", "WHERE", "WHILE", "WITH", + "WITHOUT", "WORK", "WRAPPER", "WRITE", "X509", "XA", "XID", + "XML", "XOR", "YEAR", "YEAR_MONTH", "ZEROFILL"} diff --git a/dbm-services/mysql/db-simulation/app/keyworld/mysql80.go b/dbm-services/mysql/db-simulation/app/keyworld/mysql80.go new file mode 100644 index 0000000000..169eff6a18 --- /dev/null +++ b/dbm-services/mysql/db-simulation/app/keyworld/mysql80.go @@ -0,0 +1,115 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package keyworld + +// MySQL80_KEYWORD TODO +var MySQL80_KEYWORD = []string{"ACCESSIBLE", "ACCOUNT", "ACTION", "ACTIVE", "ADD", "ADMIN", "AFTER", "AGAINST", + "AGGREGATE", "ALGORITHM", "ALL", "ALTER", "ALWAYS", "ANALYSE", "ANALYZE", "AND", "ANY", "ARRAY", "AS", "ASC", "ASCII", + "ASENSITIVE", "AT", "ATTRIBUTE", "AUTHENTICATION", "AUTOEXTEND_SIZE", "AUTO_INCREMENT", "AVG", "AVG_ROW_LENGTH", + "BACKUP", "BEFORE", "BEGIN", "BETWEEN", "BIGINT", "BINARY", "BINLOG", "BIT", "BLOB", "BLOCK", "BOOL", "BOOLEAN", + "BOTH", "BTREE", "BUCKETS", "BULK", "BY", "BYTE", "CACHE", "CALL", "CASCADE", "CASCADED", "CASE", "CATALOG_NAME", + "CHAIN", "CHALLENGE_RESPONSE", "CHANGE", "CHANGED", "CHANNEL", "CHAR", "CHARACTER", "CHARSET", "CHECK", "CHECKSUM", + "CIPHER", "CLASS_ORIGIN", "CLIENT", "CLONE", "CLOSE", "COALESCE", "CODE", "COLLATE", "COLLATION", "COLUMN", "COLUMNS", + "COLUMN_FORMAT", "COLUMN_NAME", "COMMENT", "COMMIT", "COMMITTED", "COMPACT", "COMPLETION", "COMPONENT", "COMPRESSED", + "COMPRESSION", "CONCURRENT", "CONDITION", "CONNECTION", "CONSISTENT", "CONSTRAINT", "CONSTRAINT_CATALOG", + "CONSTRAINT_NAME", "CONSTRAINT_SCHEMA", "CONTAINS", "CONTEXT", "CONTINUE", "CONVERT", "CPU", "CREATE", "CROSS", "CUBE", + "CUME_DIST", "CURRENT", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_USER", "CURSOR", "CURSOR_NAME", + "DATA", "DATABASE", "DATABASES", "DATAFILE", "DATE", "DATETIME", "DAY", "DAY_HOUR", "DAY_MICROSECOND", "DAY_MINUTE", + "DAY_SECOND", "DEALLOCATE", "DEC", "DECIMAL", "DECLARE", "DEFAULT", "DEFAULT_AUTH", "DEFINER", "DEFINITION", "DELAYED", + "DELAY_KEY_WRITE", "DELETE", "DENSE_RANK", "DESC", "DESCRIBE", "DESCRIPTION", "DES_KEY_FILE", "DETERMINISTIC", + "DIAGNOSTICS", "DIRECTORY", "DISABLE", "DISCARD", "DISK", "DISTINCT", "DISTINCTROW", "DIV", "DO", "DOUBLE", "DROP", + "DUAL", "DUMPFILE", "DUPLICATE", "DYNAMIC", "EACH", "ELSE", "ELSEIF", "EMPTY", "ENABLE", "ENCLOSED", "ENCRYPTION", + "END", "ENDS", "ENFORCED", "ENGINE", "ENGINES", "ENGINE_ATTRIBUTE", "ENUM", "ERROR", "ERRORS", "ESCAPE", "ESCAPED", + "EVENT", "EVENTS", "EVERY", "EXCEPT", "EXCHANGE", "EXCLUDE", "EXECUTE", "EXISTS", "EXIT", "EXPANSION", "EXPIRE", + "EXPLAIN", "EXPORT", "EXTENDED", "EXTENT_SIZE", "FACTOR", "FAILED_LOGIN_ATTEMPTS", "FALSE", "FAST", "FAULTS", "FETCH", + "FIELDS", "FILE", "FILE_BLOCK_SIZE", "FILTER", "FINISH", "FIRST", "FIRST_VALUE", "FIXED", "FLOAT", "FLOAT4", + "FLOAT8", "FLUSH", "FOLLOWING", "FOLLOWS", "FOR", "FORCE", "FOREIGN", "FORMAT", "FOUND", "FROM", "FULL", "FULLTEXT", + "FUNCTION", "GENERAL", "GENERATE", "GENERATED", "GEOMCOLLECTION", "GEOMETRY", "GEOMETRYCOLLECTION", "GET", + "GET_FORMAT", "GET_MASTER_PUBLIC_KEY", "GET_SOURCE_PUBLIC_KEY", "GLOBAL", "GRANT", "GRANTS", "GROUP", "GROUPING", + "GROUPS", "GROUP_REPLICATION", "GTID_ONLY", "HANDLER", "HASH", "HAVING", "HELP", "HIGH_PRIORITY", "HISTOGRAM", + "HISTORY", "HOST", "HOSTS", "HOUR", "HOUR_MICROSECOND", "HOUR_MINUTE", "HOUR_SECOND", "IDENTIFIED", "IF", "IGNORE", + "IGNORE_SERVER_IDS", "IMPORT", "IN", "INACTIVE", "INDEX", "INDEXES", "INFILE", "INITIAL", + "INITIAL_SIZE", "INITIATE", "INNER", "INOUT", "INSENSITIVE", "INSERT", "INSERT_METHOD", "INSTALL", "INSTANCE", "INT", + "INT1", "INT2", "INT3", "INT4", "INT8", "INTEGER", "INTERSECT", "INTERVAL", "INTO", "INVISIBLE", "INVOKER", "IO", + "IO_AFTER_GTIDS", "IO_BEFORE_GTIDS", "IO_THREAD", "IPC", "IS", "ISOLATION", "ISSUER", "ITERATE", "JOIN", "JSON", + "JSON_TABLE", "JSON_VALUE", "KEY", "KEYRING", "KEYS", "KEY_BLOCK_SIZE", "KILL", "LAG", "LANGUAGE", "LAST", + "LAST_VALUE", "LATERAL", "LEAD", "LEADING", "LEAVE", "LEAVES", "LEFT", "LESS", "LEVEL", "LIKE", "LIMIT", "LINEAR", + "LINES", "LINESTRING", "LIST", "LOAD", "LOCAL", "LOCALTIME", "LOCALTIMESTAMP", "LOCK", "LOCKED", "LOCKS", "LOGFILE", + "LOGS", "LONG", "LONGBLOB", "LONGTEXT", "LOOP", "LOW_PRIORITY", "MASTER", "MASTER_AUTO_POSITION", "MASTER_BIND", + "MASTER_COMPRESSION_ALGORITHMS", "MASTER_CONNECT_RETRY", "MASTER_DELAY", "MASTER_HEARTBEAT_PERIOD", "MASTER_HOST", + "MASTER_LOG_FILE", "MASTER_LOG_POS", "MASTER_PASSWORD", "MASTER_PORT", "MASTER_PUBLIC_KEY_PATH", "MASTER_RETRY_COUNT", + "MASTER_SERVER_ID", "MASTER_SSL", "MASTER_SSL_CA", "MASTER_SSL_CAPATH", "MASTER_SSL_CERT", "MASTER_SSL_CIPHER", + "MASTER_SSL_CRL", "MASTER_SSL_CRLPATH", "MASTER_SSL_KEY", "MASTER_SSL_VERIFY_SERVER_CERT", "MASTER_TLS_CIPHERSUITES", + "MASTER_TLS_VERSION", "MASTER_USER", "MASTER_ZSTD_COMPRESSION_LEVEL", "MATCH", "MAXVALUE", "MAX_CONNECTIONS_PER_HOUR", + "MAX_QUERIES_PER_HOUR", "MAX_ROWS", "MAX_SIZE", "MAX_UPDATES_PER_HOUR", "MAX_USER_CONNECTIONS", "MEDIUM", + "MEDIUMBLOB", "MEDIUMINT", "MEDIUMTEXT", + "MEMBER", "MEMORY", "MERGE", "MESSAGE_TEXT", "MICROSECOND", "MIDDLEINT", "MIGRATE", "MINUTE", "MINUTE_MICROSECOND", + "MINUTE_SECOND", "MIN_ROWS", + "MOD", "MODE", "MODIFIES", "MODIFY", "MONTH", "MULTILINESTRING", "MULTIPOINT", "MULTIPOLYGON", "MUTEX", + "MYSQL_ERRNO", "NAME", "NAMES", "NATIONAL", + "NATURAL", "NCHAR", "NDB", "NDBCLUSTER", "NESTED", "NETWORK_NAMESPACE", "NEVER", "NEW", "NEXT", "NO", "NODEGROUP", + "NONE", "NOT", "NOWAIT", "NO_WAIT", + "NO_WRITE_TO_BINLOG", "NTH_VALUE", "NTILE", "NULL", "NULLS", "NUMBER", "NUMERIC", "NVARCHAR", "OF", "OFF", "OFFSET", + "OJ", "OLD", "ON", "ONE", "ONLY", + "OPEN", "OPTIMIZE", "OPTIMIZER_COSTS", "OPTION", "OPTIONAL", "OPTIONALLY", "OPTIONS", "OR", "ORDER", "ORDINALITY", + "ORGANIZATION", "OTHERS", "OUT", + "OUTER", "OUTFILE", "OVER", "OWNER", "PACK_KEYS", "PAGE", "PARSER", "PARTIAL", "PARTITION", "PARTITIONING", + "PARTITIONS", "PASSWORD", "PASSWORD_LOCK_TIME", + "PATH", "PERCENT_RANK", "PERSIST", "PERSIST_ONLY", "PHASE", "PLUGIN", "PLUGINS", "PLUGIN_DIR", "POINT", "POLYGON", + "PORT", "PRECEDES", "PRECEDING", "PRECISION", + "PREPARE", "PRESERVE", "PREV", "PRIMARY", "PRIVILEGES", "PRIVILEGE_CHECKS_USER", "PROCEDURE", "PROCESS", + "PROCESSLIST", "PROFILE", "PROFILES", "PROXY", + "PURGE", "QUARTER", "QUERY", "QUICK", "RANDOM", "RANGE", "RANK", "READ", "READS", "READ_ONLY", "READ_WRITE", "REAL", + "REBUILD", "RECOVER", "RECURSIVE", + "REDOFILE", "REDO_BUFFER_SIZE", "REDUNDANT", "REFERENCE", "REFERENCES", "REGEXP", "REGISTRATION", "RELAY", + "RELAYLOG", "RELAY_LOG_FILE", "RELAY_LOG_POS", + "RELAY_THREAD", "RELEASE", "RELOAD", "REMOTE", "REMOVE", "RENAME", "REORGANIZE", "REPAIR", "REPEAT", "REPEATABLE", + "REPLACE", "REPLICA", "REPLICAS", + "REPLICATE_DO_DB", "REPLICATE_DO_TABLE", "REPLICATE_IGNORE_DB", "REPLICATE_IGNORE_TABLE", "REPLICATE_REWRITE_DB", + "REPLICATE_WILD_DO_TABLE", "REPLICATE_WILD_IGNORE_TABLE", + "REPLICATION", "REQUIRE", "REQUIRE_ROW_FORMAT", "RESET", "RESIGNAL", "RESOURCE", "RESPECT", "RESTART", "RESTORE", + "RESTRICT", "RESUME", "RETAIN", "RETURN", + "RETURNED_SQLSTATE", "RETURNING", "RETURNS", "REUSE", "REVERSE", "REVOKE", "RIGHT", "RLIKE", "ROLE", "ROLLBACK", + "ROLLUP", "ROTATE", "ROUTINE", "ROW", "ROWS", + "ROW_COUNT", "ROW_FORMAT", "ROW_NUMBER", "RTREE", "SAVEPOINT", "SCHEDULE", "SCHEMA", "SCHEMAS", "SCHEMA_NAME", + "SECOND", "SECONDARY", "SECONDARY_ENGINE", + "SECONDARY_ENGINE_ATTRIBUTE", "SECONDARY_LOAD", "SECONDARY_UNLOAD", "SECOND_MICROSECOND", "SECURITY", "SELECT", + "SENSITIVE", "SEPARATOR", "SERIAL", "SERIALIZABLE", + "SERVER", "SESSION", "SET", "SHARE", "SHOW", "SHUTDOWN", "SIGNAL", "SIGNED", "SIMPLE", "SKIP", "SLAVE", "SLOW", + "SMALLINT", "SNAPSHOT", "SOCKET", "SOME", "SONAME", + "SOUNDS", "SOURCE", "SOURCE_AUTO_POSITION", "SOURCE_BIND", "SOURCE_COMPRESSION_ALGORITHMS", "SOURCE_CONNECT_RETRY", + "SOURCE_DELAY", "SOURCE_HEARTBEAT_PERIOD", + "SOURCE_HOST", "SOURCE_LOG_FILE", "SOURCE_LOG_POS", "SOURCE_PASSWORD", "SOURCE_PORT", "SOURCE_PUBLIC_KEY_PATH", + "SOURCE_RETRY_COUNT", "SOURCE_SSL", "SOURCE_SSL_CA", + "SOURCE_SSL_CAPATH", "SOURCE_SSL_CERT", "SOURCE_SSL_CIPHER", "SOURCE_SSL_CRL", "SOURCE_SSL_CRLPATH", + "SOURCE_SSL_KEY", "SOURCE_SSL_VERIFY_SERVER_CERT", + "SOURCE_TLS_CIPHERSUITES", "SOURCE_TLS_VERSION", "SOURCE_USER", "SOURCE_ZSTD_COMPRESSION_LEVEL", "SPATIAL", + "SPECIFIC", "SQL", "SQLEXCEPTION", "SQLSTATE", + "SQLWARNING", "SQL_AFTER_GTIDS", "SQL_AFTER_MTS_GAPS", "SQL_BEFORE_GTIDS", "SQL_BIG_RESULT", "SQL_BUFFER_RESULT", + "SQL_CACHE", "SQL_CALC_FOUND_ROWS", "SQL_NO_CACHE", + "SQL_SMALL_RESULT", "SQL_THREAD", "SQL_TSI_DAY", "SQL_TSI_HOUR", "SQL_TSI_MINUTE", "SQL_TSI_MONTH", + "SQL_TSI_QUARTER", "SQL_TSI_SECOND", "SQL_TSI_WEEK", "SQL_TSI_YEAR", + "SRID", "SSL", "STACKED", "START", "STARTING", "STARTS", "STATS_AUTO_RECALC", "STATS_PERSISTENT", + "STATS_SAMPLE_PAGES", "STATUS", "STOP", "STORAGE", "STORED", "STRAIGHT_JOIN", + "STREAM", "STRING", "SUBCLASS_ORIGIN", "SUBJECT", "SUBPARTITION", "SUBPARTITIONS", "SUPER", "SUSPEND", "SWAPS", + "SWITCHES", "SYSTEM", "TABLE", "TABLES", "TABLESPACE", + "TABLE_CHECKSUM", "TABLE_NAME", "TEMPORARY", "TEMPTABLE", "TERMINATED", "TEXT", "THAN", "THEN", "THREAD_PRIORITY", + "TIES", "TIME", "TIMESTAMP", "TIMESTAMPADD", + "TIMESTAMPDIFF", "TINYBLOB", "TINYINT", "TINYTEXT", "TLS", "TO", "TRAILING", "TRANSACTION", "TRIGGER", "TRIGGERS", + "TRUE", "TRUNCATE", "TYPE", "TYPES", "UNBOUNDED", + "UNCOMMITTED", "UNDEFINED", "UNDO", "UNDOFILE", "UNDO_BUFFER_SIZE", "UNICODE", "UNINSTALL", "UNION", "UNIQUE", + "UNKNOWN", "UNLOCK", "UNREGISTER", "UNSIGNED", "UNTIL", + "UPDATE", "UPGRADE", "URL", "USAGE", "USE", "USER", "USER_RESOURCES", "USE_FRM", "USING", "UTC_DATE", "UTC_TIME", + "UTC_TIMESTAMP", "VALIDATION", "VALUE", "VALUES", + "VARBINARY", "VARCHAR", "VARCHARACTER", "VARIABLES", "VARYING", "VCPU", "VIEW", "VIRTUAL", "VISIBLE", "WAIT", + "WARNINGS", "WEEK", "WEIGHT_STRING", "WHEN", "WHERE", + "WHILE", "WINDOW", "WITH", "WITHOUT", "WORK", "WRAPPER", "WRITE", "X509", "XA", "XID", "XML", "XOR", "YEAR", + "YEAR_MONTH", "ZEROFILL", "ZONE"} diff --git a/dbm-services/mysql/db-simulation/app/service/kubernets.go b/dbm-services/mysql/db-simulation/app/service/kubernets.go index 86adfdafd6..70e798b8c9 100644 --- a/dbm-services/mysql/db-simulation/app/service/kubernets.go +++ b/dbm-services/mysql/db-simulation/app/service/kubernets.go @@ -100,6 +100,7 @@ func (k *DbPodSets) getCreateClusterSqls() []string { ss = append(ss, fmt.Sprintf( "tdbctl create node wrapper 'TDBCTL' options(user 'root', password '%s', host 'localhost', port 26000);", k.BaseInfo.RootPwd)) + ss = append(ss, "tdbctl enable primary;") ss = append(ss, "tdbctl flush routing;") return ss } @@ -167,7 +168,8 @@ func (k *DbPodSets) CreateClusterPod() (err error) { }}, ImagePullPolicy: v1.PullIfNotPresent, Image: k.TdbCtlImage, - Args: []string{"mysqld", "--defaults-file=/etc/my.cnf", "--port=26000", "--tc-is-primary=1", + Args: []string{"mysqld", "--defaults-file=/etc/my.cnf", "--port=26000", "--tc-admin=1", + "--dbm-allow-standalone-primary", fmt.Sprintf("--character-set-server=%s", k.BaseInfo.Charset), "--user=mysql"}, diff --git a/dbm-services/mysql/db-simulation/app/service/simulation_task.go b/dbm-services/mysql/db-simulation/app/service/simulation_task.go index 53e1874a49..6168d0a5e4 100644 --- a/dbm-services/mysql/db-simulation/app/service/simulation_task.go +++ b/dbm-services/mysql/db-simulation/app/service/simulation_task.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package service import ( @@ -171,6 +181,7 @@ func run(task SimulationTask, tkType string) { return } }() + xlogger := task.getXlogger() // create Pod model.UpdatePhase(task.TaskId, model.Phase_CreatePod) defer func() { @@ -182,14 +193,15 @@ func run(task SimulationTask, tkType string) { } }() if err = createPod(task, tkType); err != nil { - logger.Error("create pod failed %s", err.Error()) + xlogger.Error("create pod failed %s", err.Error()) return } - so, se, err = task.SimulationRun(tkType) + so, se, err = task.SimulationRun(tkType, xlogger) if err != nil { - logger.Error("模拟执行失败%s", err.Error()) + xlogger.Error("simulation execution failed%s", err.Error()) return } + xlogger.Info("the simulation was executed successfully") } func createPod(task SimulationTask, tkType string) (err error) { @@ -205,7 +217,7 @@ func createPod(task SimulationTask, tkType string) (err error) { func (t *SimulationTask) getDbsExcludeSysDb() (err error) { alldbs, err := t.DbWork.ShowDatabases() if err != nil { - logger.Error("获取实例db list失败:%s", err.Error()) + logger.Error("failed to get instance db list:%s", err.Error()) return err } logger.Info("get all database is %v", alldbs) @@ -219,7 +231,8 @@ func (t *SimulationTask) getDbsExcludeSysDb() (err error) { } // SimulationRun TODO -func (t *SimulationTask) SimulationRun(containerName string) (sstdout, sstderr string, err error) { +func (t *SimulationTask) SimulationRun(containerName string, xlogger *logger.Logger) (sstdout, sstderr string, + err error) { logger.Info("will execute in %s", containerName) doneChan := make(chan struct{}) go func() { @@ -236,7 +249,7 @@ func (t *SimulationTask) SimulationRun(containerName string) (sstdout, sstderr s }() // 关闭协程 defer func() { doneChan <- struct{}{} }() - xlogger := logger.New(os.Stdout, true, logger.InfoLevel, t.getExtmap()) + // xlogger := t.getXlogger() // execute load schema model.UpdatePhase(t.TaskId, model.Phase_LoadSchema) stdout, stderr, err := t.DbPodSets.ExecuteInPod(t.GetLoadSchemaSQLCmd(t.Path, t.SchemaSQLFile), @@ -248,7 +261,7 @@ func (t *SimulationTask) SimulationRun(containerName string) (sstdout, sstderr s logger.Error("load database schema sql failed %s", err.Error()) return sstdout, sstderr, errors.Wrap(err, "[导入表结构失败]") } - logger.Info(stdout.String(), stderr.String()) + xlogger.Info(stdout.String(), stderr.String()) // load real databases if err = t.getDbsExcludeSysDb(); err != nil { logger.Error("getDbsExcludeSysDb faiked") @@ -278,14 +291,14 @@ func (t *SimulationTask) SimulationRun(containerName string) (sstdout, sstderr s sstderr += stderr.String() + "\n" if err != nil { if idx == 0 { - logger.Error("download file failed:%s", err.Error()) + xlogger.Error("download file failed:%s", err.Error()) return sstdout, sstderr, fmt.Errorf("download file %s failed:%s", e.SQLFile, err.Error()) } - logger.Error("%s[%s]:ExecuteInPod failed %s", e.SQLFile, realexcutedbs[idx-1], err.Error()) + xlogger.Error("%s[%s]:ExecuteInPod failed %s", e.SQLFile, realexcutedbs[idx-1], err.Error()) return sstdout, sstderr, fmt.Errorf("exec %s in %s failed:%s", e.SQLFile, realexcutedbs[idx-1], err.Error()) } - logger.Info("%s \n %s", stdout.String(), stderr.String()) + xlogger.Info("%s \n %s", stdout.String(), stderr.String()) } xlogger.Info("[end]-%s", e.SQLFile) } @@ -316,3 +329,8 @@ func (t *SimulationTask) getExtmap() map[string]string { "version_id": t.VersionId, } } + +// getXlogger TODO +func (t *SimulationTask) getXlogger() *logger.Logger { + return logger.New(os.Stdout, true, logger.InfoLevel, t.getExtmap()) +} diff --git a/dbm-services/mysql/db-simulation/app/syntax/builtin_rule.go b/dbm-services/mysql/db-simulation/app/syntax/builtin_rule.go new file mode 100644 index 0000000000..0360cde4ed --- /dev/null +++ b/dbm-services/mysql/db-simulation/app/syntax/builtin_rule.go @@ -0,0 +1,92 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package syntax + +import ( + "fmt" + "regexp" + + "dbm-services/mysql/db-simulation/app/keyworld" +) + +const ( + + // SpecialCharRegex = `[¥$!@#%^&*()+={}\[\];:'"<>,.?/\\| ]` + + // AllowWordRegex TODO + AllowWordRegex = `^[a-zA-Z0-9][a-zA-Z0-9_-]+[a-zA-Z0-9]$` + // SysReservesPrefixName TODO + +) + +var reAllowWord *regexp.Regexp + +// var mysql55WordMap map[string]struct{} +var mysql56WordMap map[string]struct{} +var mysql57WordMap map[string]struct{} +var mysql80WordMap map[string]struct{} +var defaultWordMap map[string]struct{} +var sysReservesPrefixNames []string + +func init() { + sysReservesPrefixNames = []string{"stage_truncate"} + reAllowWord = regexp.MustCompile(AllowWordRegex) + mysql56WordMap = sliceToMap(keyworld.MySQL56_KEYWORD) + mysql57WordMap = sliceToMap(keyworld.MySQL57_KEYWORD) + mysql80WordMap = sliceToMap(keyworld.MySQL80_KEYWORD) + defaultWordMap = sliceToMap(keyworld.ALL_KEYWORD) +} + +// KeyWordValidator TODO +func KeyWordValidator(ver, name string) (matched bool, msg string) { + var kwmap map[string]struct{} + switch ver { + case "mysql5.6": + kwmap = mysql56WordMap + case "mysql5.7": + kwmap = mysql57WordMap + case "mysql8.0": + kwmap = mysql80WordMap + default: + kwmap = defaultWordMap + } + if existInKeywords(name, kwmap) { + return true, name + " is mysql keyword" + } + return +} + +// SpecialCharValidator TODO +func SpecialCharValidator(name string) (matched bool, msg string) { + if !reAllowWord.MatchString(name) { + return true, "Only allowed " + AllowWordRegex + " characters " + } + for _, sysPrefix := range sysReservesPrefixNames { + re := regexp.MustCompile(fmt.Sprintf("^%s", sysPrefix)) + if re.MatchString(name) { + return true, "不允许以" + sysPrefix + "开头的关键字,前缀被系统占用" + } + } + return +} + +func existInKeywords(name string, keywordsmap map[string]struct{}) bool { + _, ok := keywordsmap[name] + return ok +} + +func sliceToMap(elems []string) map[string]struct{} { + m := make(map[string]struct{}) + for _, elem := range elems { + m[elem] = struct{}{} + } + return m +} diff --git a/dbm-services/mysql/db-simulation/app/syntax/builtin_rule_test.go b/dbm-services/mysql/db-simulation/app/syntax/builtin_rule_test.go new file mode 100644 index 0000000000..7cca689bc0 --- /dev/null +++ b/dbm-services/mysql/db-simulation/app/syntax/builtin_rule_test.go @@ -0,0 +1,22 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package syntax_test + +import ( + "testing" + + "dbm-services/mysql/db-simulation/app/syntax" +) + +func TestKeywordFilter(t *testing.T) { + t.Log("start test") + t.Log(syntax.KeyWordValidator("mysql-5.7", "call")) +} diff --git a/dbm-services/mysql/db-simulation/app/syntax/create_db_rule.go b/dbm-services/mysql/db-simulation/app/syntax/create_db_rule.go index c92138f426..70ad220788 100644 --- a/dbm-services/mysql/db-simulation/app/syntax/create_db_rule.go +++ b/dbm-services/mysql/db-simulation/app/syntax/create_db_rule.go @@ -1,14 +1,38 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package syntax +import "dbm-services/common/go-pubpkg/cmutil" + // Checker TODO func (c CreateDBResult) Checker(mysqlVersion string) (r *CheckerResult) { r = &CheckerResult{} // 检查库名规范 - // R.CreateTableRule.NormalizedName指明yaml文件中的键,根据键获得item 进而和 val比较 - etypesli, charsli := NameCheck(c.DbName, mysqlVersion) - for i, etype := range etypesli { - r.Parse(R.CreateTableRule.NormalizedName, etype, charsli[i]) + if R.BuiltInRule.TableNameSpecification.KeyWord { + r.ParseBultinBan(func() (bool, string) { + return KeyWordValidator(mysqlVersion, c.DbName) + }) + } + if R.BuiltInRule.TableNameSpecification.SpeicalChar { + r.ParseBultinBan(func() (bool, string) { + return SpecialCharValidator(c.DbName) + }) } + // 不允许包含系统库 + r.ParseBultinBan(func() (bool, string) { + if cmutil.HasElem(c.DbName, cmutil.GetGcsSystemDatabasesIgnoreTest(mysqlVersion)) { + return true, "不允许操作系统库" + c.DbName + } + return false, "" + }) return } diff --git a/dbm-services/mysql/db-simulation/app/syntax/create_table_rule.go b/dbm-services/mysql/db-simulation/app/syntax/create_table_rule.go index ce97d67911..ec4a6947d4 100644 --- a/dbm-services/mysql/db-simulation/app/syntax/create_table_rule.go +++ b/dbm-services/mysql/db-simulation/app/syntax/create_table_rule.go @@ -1,8 +1,17 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package syntax import ( "fmt" - "regexp" "strings" "dbm-services/common/go-pubpkg/cmutil" @@ -13,6 +22,16 @@ import ( // SpiderChecker TODO func (c CreateTableResult) SpiderChecker(spiderVersion string) (r *CheckerResult) { r = &CheckerResult{} + if R.BuiltInRule.TableNameSpecification.KeyWord { + r.ParseBultinBan(func() (bool, string) { + return KeyWordValidator(spiderVersion, c.TableName) + }) + } + if R.BuiltInRule.TableNameSpecification.SpeicalChar { + r.ParseBultinBan(func() (bool, string) { + return SpecialCharValidator(c.TableName) + }) + } r.Parse(SR.SpiderCreateTableRule.CreateTbLike, !c.IsCreateTableLike, "") r.Parse(SR.SpiderCreateTableRule.CreateWithSelect, !c.IsCreateTableSelect, "") r.Parse(SR.SpiderCreateTableRule.ColChasetNotEqTbChaset, c.ColCharsetEqTbCharset(), "") @@ -34,11 +53,15 @@ func (c CreateTableResult) Checker(mysqlVersion string) (r *CheckerResult) { r = &CheckerResult{} r.Parse(R.CreateTableRule.SuggestEngine, c.GetEngine(), "") r.Parse(R.CreateTableRule.SuggestBlobColumCount, c.BlobColumCount(), "") - // 检查表名规范 - // R.CreateTableRule.NormalizedName指明yaml文件中的键,根据键获得item 进而和 val比较 - etypesli, charsli := NameCheck(c.TableName, mysqlVersion) - for i, etype := range etypesli { - r.Parse(R.CreateTableRule.NormalizedName, etype, charsli[i]) + if R.BuiltInRule.TableNameSpecification.KeyWord { + r.ParseBultinBan(func() (bool, string) { + return KeyWordValidator(mysqlVersion, c.TableName) + }) + } + if R.BuiltInRule.TableNameSpecification.SpeicalChar { + r.ParseBultinBan(func() (bool, string) { + return SpecialCharValidator(c.TableName) + }) } return } @@ -89,7 +112,6 @@ func (c CreateTableResult) ShardKeyIsNotPrimaryKey() bool { if !strings.Contains(cmt, "shard_key") { return true } - logger.Info("will check xaxsasxaxax ") sk, err := util.ParseGetShardKeyForSpider(cmt) if err != nil { logger.Error("parse get shardkey %s", err.Error()) @@ -186,43 +208,3 @@ func (c CreateTableResult) ColCharsetEqTbCharset() bool { } return false } - -// NameCheck TODO -func NameCheck(name string, mysqlVersion string) (etypesli, charsli []string) { - reservesmap := getKewords(mysqlVersion) - etypesli = []string{} - charsli = []string{} - if _, ok := reservesmap[name]; ok { - etypesli = append(etypesli, "Keyword_exception") - charsli = append(charsli, fmt.Sprintf("。库表名中包含了MySQL关键字: %s。请避免使用这些关键字!", name)) - } - if regexp.MustCompile(`[¥$!@#%^&*()+={}\[\];:'"<>,.?/\\| ]`).MatchString(name) { - chars := regexp.MustCompile(`[¥$!@#%^&*()+={}\[\];:'"<>,.?/\\| ]`).FindAllString(name, -1) - etypesli = append(etypesli, "special_char") - charsli = append(charsli, fmt.Sprintf("。库表名中包含以下特殊字符: %s。请避免在库表名称中使用这些特殊字符!", chars)) - } - if regexp.MustCompile(`^[_]`).MatchString(name) { - etypesli = append(etypesli, "first_char_exception") - charsli = append(charsli, "。首字符不规范,请使用尽量字母或数字作为首字母") - } - return etypesli, charsli -} - -func getKewords(mysqlVersion string) (keywordsmap map[string]string) { - var keysli []string - switch mysqlVersion { - case "mysql5.6": - keysli = MySQL56_KEYWORD - case "mysql5.7": - keysli = MySQL57_KEYWORD - case "mysql8.0": - keysli = MySQL80_KEYWORD - default: - keysli = ALL_KEYWORD - } - keywordsmap = map[string]string{} - for _, key := range keysli { - keywordsmap[key] = "" - } - return keywordsmap -} diff --git a/dbm-services/mysql/db-simulation/app/syntax/mysql_keyword.go b/dbm-services/mysql/db-simulation/app/syntax/mysql_keyword.go deleted file mode 100644 index 17b48cc1c2..0000000000 --- a/dbm-services/mysql/db-simulation/app/syntax/mysql_keyword.go +++ /dev/null @@ -1,435 +0,0 @@ -package syntax - -// MySQL56_KEYWORD TODO -var MySQL56_KEYWORD = []string{"ACCESSIBLE", "ACTION", "ADD", "AFTER", "AGAINST", "AGGREGATE", - "ALGORITHM", "ALL", "ALTER", "ANALYSE", "ANALYZE", "AND", "ANY", "AS", "ASC", "ASCII", - "ASENSITIVE", "AT", "AUTHORS", "AUTOEXTEND_SIZE", "AUTO_INCREMENT", "AVG", "AVG_ROW_LENGTH", - "BACKUP", "BEFORE", "BEGIN", "BETWEEN", "BIGINT", "BINARY", "BINLOG", "BIT", "BLOB", "BLOCK", - "BOOL", "BOOLEAN", "BOTH", "BTREE", "BY", "BYTE", "CACHE", "CALL", "CASCADE", "CASCADED", "CASE", - "CATALOG_NAME", "CHAIN", "CHANGE", "CHANGED", "CHAR", "CHARACTER", "CHARSET", "CHECK", "CHECKSUM", - "CIPHER", "CLASS_ORIGIN", "CLIENT", "CLOSE", "COALESCE", "CODE", "COLLATE", "COLLATION", "COLUMN", - "COLUMNS", "COLUMN_FORMAT", "COLUMN_NAME", "COMMENT", "COMMIT", "COMMITTED", "COMPACT", "COMPLETION", - "COMPRESSED", "CONCURRENT", "CONDITION", "CONNECTION", "CONSISTENT", "CONSTRAINT", "CONSTRAINT_CATALOG", - "CONSTRAINT_NAME", "CONSTRAINT_SCHEMA", "CONTAINS", "CONTEXT", "CONTINUE", "CONTRIBUTORS", "CONVERT", "CPU", - "CREATE", "CROSS", "CUBE", "CURRENT", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_USER", "CURSOR", - "CURSOR_NAME", "DATA", "DATABASE", "DATABASES", "DATAFILE", "DATE", "DATETIME", "DAY", "DAY_HOUR", "DAY_MICROSECOND", - "DAY_MINUTE", "DAY_SECOND", "DEALLOCATE", "DEC", "DECIMAL", "DECLARE", "DEFAULT", "DEFAULT_AUTH", "DEFINER", - "DELAYED", "DELAY_KEY_WRITE", "DELETE", "DESC", "DESCRIBE", "DES_KEY_FILE", "DETERMINISTIC", "DIAGNOSTICS", - "DIRECTORY", "DISABLE", "DISCARD", "DISK", "DISTINCT", "DISTINCTROW", "DIV", "DO", "DOUBLE", "DROP", "DUAL", - "DUMPFILE", "DUPLICATE", "DYNAMIC", "EACH", "ELSE", "ELSEIF", "ENABLE", "ENCLOSED", "END", "ENDS", "ENGINE", "ENGINES", - "ENUM", "ERROR", "ERRORS", "ESCAPE", "ESCAPED", "EVENT", "EVENTS", "EVERY", "EXCHANGE", "EXECUTE", "EXISTS", "EXIT", - "EXPANSION", "EXPIRE", "EXPLAIN", "EXPORT", "EXTENDED", "EXTENT_SIZE", "FALSE", "FAST", "FAULTS", "FETCH", "FIELDS", - "FILE", "FIRST", "FIXED", "FLOAT", "FLOAT4", "FLOAT8", - "FLUSH", "FOR", "FORCE", "FOREIGN", "FORMAT", "FOUND", "FROM", "FULL", "FULLTEXT", "FUNCTION", "GENERAL", "GEOMETRY", - "GEOMETRYCOLLECTION", - "GET", "GET_FORMAT", "GLOBAL", "GRANT", "GRANTS", "GROUP", "HANDLER", "HASH", "HAVING", "HELP", "HIGH_PRIORITY", - "HOST", "HOSTS", "HOUR", - "HOUR_MICROSECOND", "HOUR_MINUTE", "HOUR_SECOND", "IDENTIFIED", "IF", "IGNORE", "IGNORE_SERVER_IDS", "IMPORT", "IN", - "INDEX", "INDEXES", - "INFILE", "INITIAL_SIZE", "INNER", "INOUT", "INSENSITIVE", "INSERT", "INSERT_METHOD", "INSTALL", "INT", "INT1", - "INT2", "INT3", "INT4", - "INT8", "INTEGER", "INTERVAL", "INTO", "INVOKER", "IO", "IO_AFTER_GTIDS", "IO_BEFORE_GTIDS", "IO_THREAD", "IPC", - "IS", "ISOLATION", "ISSUER", - "ITERATE", "JOIN", "KEY", "KEYS", "KEY_BLOCK_SIZE", "KILL", "LANGUAGE", "LAST", "LEADING", "LEAVE", "LEAVES", "LEFT", - "LESS", "LEVEL", - "LIKE", "LIMIT", "LINEAR", "LINES", "LINESTRING", "LIST", "LOAD", "LOCAL", "LOCALTIME", "LOCALTIMESTAMP", "LOCK", - "LOCKS", "LOGFILE", "LOGS", - "LONG", "LONGBLOB", "LONGTEXT", "LOOP", "LOW_PRIORITY", "MASTER", "MASTER_AUTO_POSITION", "MASTER_BIND", - "MASTER_CONNECT_RETRY", "MASTER_DELAY", - "MASTER_HEARTBEAT_PERIOD", "MASTER_HOST", "MASTER_LOG_FILE", "MASTER_LOG_POS", "MASTER_PASSWORD", "MASTER_PORT", - "MASTER_RETRY_COUNT", "MASTER_SERVER_ID", - "MASTER_SSL", "MASTER_SSL_CA", "MASTER_SSL_CAPATH", "MASTER_SSL_CERT", "MASTER_SSL_CIPHER", "MASTER_SSL_CRL", - "MASTER_SSL_CRLPATH", "MASTER_SSL_KEY", - "MASTER_SSL_VERIFY_SERVER_CERT", "MASTER_USER", "MATCH", "MAXVALUE", "MAX_CONNECTIONS_PER_HOUR", - "MAX_QUERIES_PER_HOUR", "MAX_ROWS", "MAX_SIZE", "MAX_UPDATES_PER_HOUR", - "MAX_USER_CONNECTIONS", "MEDIUM", "MEDIUMBLOB", - "MEDIUMINT", "MEDIUMTEXT", "MEMORY", "MERGE", "MESSAGE_TEXT", "MICROSECOND", "MIDDLEINT", "MIGRATE", "MINUTE", - "MINUTE_MICROSECOND", "MINUTE_SECOND", "MIN_ROWS", - "MOD", "MODE", "MODIFIES", "MODIFY", "MONTH", "MULTILINESTRING", "MULTIPOINT", "MULTIPOLYGON", "MUTEX", - "MYSQL_ERRNO", "NAME", "NAMES", "NATIONAL", "NATURAL", "NCHAR", - "NDB", "NDBCLUSTER", "NEW", "NEXT", "NO", "NODEGROUP", "NONE", "NOT", "NO_WAIT", "NO_WRITE_TO_BINLOG", "NULL", - "NUMBER", "NUMERIC", "NVARCHAR", "OFFSET", "OLD_PASSWORD", - "ON", "ONE", "ONE_SHOT", "ONLY", "OPEN", "OPTIMIZE", "OPTION", "OPTIONALLY", "OPTIONS", "OR", "ORDER", "OUT", - "OUTER", "OUTFILE", "OWNER", "PACK_KEYS", "PAGE", "PARSER", - "PARTIAL", "PARTITION", "PARTITIONING", "PARTITIONS", "PASSWORD", "PHASE", "PLUGIN", "PLUGINS", "PLUGIN_DIR", - "POINT", "POLYGON", "PORT", "PRECISION", "PREPARE", - "PRESERVE", "PREV", "PRIMARY", "PRIVILEGES", "PROCEDURE", "PROCESSLIST", "PROFILE", "PROFILES", "PROXY", "PURGE", - "QUARTER", "QUERY", "QUICK", "RANGE", "READ", - "READS", "READ_ONLY", "READ_WRITE", "REAL", "REBUILD", "RECOVER", "REDOFILE", "REDO_BUFFER_SIZE", "REDUNDANT", - "REFERENCES", "REGEXP", "RELAY", "RELAYLOG", - "RELAY_LOG_FILE", "RELAY_LOG_POS", "RELAY_THREAD", "RELEASE", "RELOAD", "REMOVE", "RENAME", "REORGANIZE", "REPAIR", - "REPEAT", "REPEATABLE", "REPLACE", "REPLICATION", - "REQUIRE", "RESET", "RESIGNAL", "RESTORE", "RESTRICT", "RESUME", "RETURN", "RETURNED_SQLSTATE", "RETURNS", "REVERSE", - "REVOKE", "RIGHT", "RLIKE", "ROLLBACK", - "ROLLUP", "ROUTINE", "ROW", "ROWS", "ROW_COUNT", "ROW_FORMAT", "RTREE", "SAVEPOINT", "SCHEDULE", "SCHEMA", "SCHEMAS", - "SCHEMA_NAME", "SECOND", "SECOND_MICROSECOND", - "SECURITY", "SELECT", "SENSITIVE", "SEPARATOR", "SERIAL", "SERIALIZABLE", "SERVER", "SESSION", "SET", "SHARE", - "SHOW", "SHUTDOWN", "SIGNAL", "SIGNED", "SIMPLE", - "SLAVE", "SLOW", "SMALLINT", "SNAPSHOT", "SOCKET", "SOME", "SONAME", "SOUNDS", "SOURCE", "SPATIAL", "SPECIFIC", - "SQL", "SQLEXCEPTION", "SQLSTATE", "SQLWARNING", - "SQL_AFTER_GTIDS", "SQL_AFTER_MTS_GAPS", "SQL_BEFORE_GTIDS", "SQL_BIG_RESULT", "SQL_BUFFER_RESULT", "SQL_CACHE", - "SQL_CALC_FOUND_ROWS", "SQL_NO_CACHE", "SQL_SMALL_RESULT", - "SQL_THREAD", "SQL_TSI_DAY", "SQL_TSI_HOUR", "SQL_TSI_MINUTE", "SQL_TSI_MONTH", "SQL_TSI_QUARTER", "SQL_TSI_SECOND", - "SQL_TSI_WEEK", "SQL_TSI_YEAR", "SSL", "START", "STARTING", - "STARTS", "STATS_AUTO_RECALC", "STATS_PERSISTENT", "STATS_SAMPLE_PAGES", "STATUS", "STOP", "STORAGE", - "STRAIGHT_JOIN", "STRING", "SUBCLASS_ORIGIN", "SUBJECT", "SUBPARTITION", - "SUBPARTITIONS", "SUPER", "SUSPEND", "SWAPS", "SWITCHES", "TABLE", "TABLES", "TABLESPACE", "TABLE_CHECKSUM", - "TABLE_NAME", "TEMPORARY", "TEMPTABLE", "TERMINATED", - "TEXT", "THAN", "THEN", "TIME", "TIMESTAMP", "TIMESTAMPADD", "TIMESTAMPDIFF", "TINYBLOB", "TINYINT", "TINYTEXT", - "TO", "TRAILING", "TRANSACTION", "TRIGGER", "TRIGGERS", - "TRUE", "TRUNCATE", "TYPE", "TYPES", "UNCOMMITTED", "UNDEFINED", "UNDO", "UNDOFILE", "UNDO_BUFFER_SIZE", "UNICODE", - "UNINSTALL", "UNION", "UNIQUE", "UNKNOWN", "UNLOCK", - "UNSIGNED", "UNTIL", "UPDATE", "UPGRADE", "USAGE", "USE", "USER", "USER_RESOURCES", "USE_FRM", "USING", "UTC_DATE", - "UTC_TIME", "UTC_TIMESTAMP", "VALUE", "VALUES", - "VARBINARY", "VARCHAR", "VARCHARACTER", "VARIABLES", "VARYING", "VIEW", "WAIT", "WARNINGS", "WEEK", "WEIGHT_STRING", - "WHEN", "WHERE", "WHILE", "WITH", "WORK", "WRAPPER", - "WRITE", "X509", "XA", "XML", "XOR", "YEAR", "YEAR_MONTH", "ZEROFILL"} - -// MySQL57_KEYWORD TODO -var MySQL57_KEYWORD = []string{"ACCESSIBLE", "ACCOUNT", "ACTION", "ADD", "AFTER", "AGAINST", "AGGREGATE", "ALGORITHM", - "ALL", "ALTER", - "ALWAYS", "ANALYSE", "ANALYZE", "AND", "ANY", "AS", "ASC", "ASCII", "ASENSITIVE", "AT", "AUTOEXTEND_SIZE", - "AUTO_INCREMENT", - "AVG", "AVG_ROW_LENGTH", "BACKUP", "BEFORE", "BEGIN", "BETWEEN", "BIGINT", "BINARY", "BINLOG", "BIT", "BLOB", - "BLOCK", - "BOOL", "BOOLEAN", "BOTH", "BTREE", "BY", "BYTE", "CACHE", "CALL", "CASCADE", "CASCADED", "CASE", "CATALOG_NAME", - "CHAIN", - "CHANGE", "CHANGED", "CHANNEL", "CHAR", "CHARACTER", "CHARSET", "CHECK", "CHECKSUM", "CIPHER", "CLASS_ORIGIN", - "CLIENT", - "CLOSE", "COALESCE", "CODE", "COLLATE", "COLLATION", "COLUMN", "COLUMNS", "COLUMN_FORMAT", "COLUMN_NAME", "COMMENT", - "COMMIT", "COMMITTED", "COMPACT", "COMPLETION", "COMPRESSED", "COMPRESSION", "CONCURRENT", "CONDITION", "CONNECTION", - "CONSISTENT", "CONSTRAINT", "CONSTRAINT_CATALOG", "CONSTRAINT_NAME", "CONSTRAINT_SCHEMA", "CONTAINS", "CONTEXT", - "CONTINUE", - "CONVERT", "CPU", "CREATE", "CROSS", "CUBE", "CURRENT", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP", - "CURRENT_USER", - "CURSOR", "CURSOR_NAME", "DATA", "DATABASE", "DATABASES", "DATAFILE", "DATE", "DATETIME", "DAY", "DAY_HOUR", - "DAY_MICROSECOND", - "DAY_MINUTE", "DAY_SECOND", "DEALLOCATE", "DEC", "DECIMAL", "DECLARE", "DEFAULT", "DEFAULT_AUTH", "DEFINER", - "DELAYED", "DELAY_KEY_WRITE", - "DELETE", "DESC", "DESCRIBE", "DES_KEY_FILE", "DETERMINISTIC", "DIAGNOSTICS", "DIRECTORY", "DISABLE", "DISCARD", - "DISK", "DISTINCT", - "DISTINCTROW", "DIV", "DO", "DOUBLE", "DROP", "DUAL", "DUMPFILE", "DUPLICATE", "DYNAMIC", "EACH", "ELSE", "ELSEIF", - "ENABLE", "ENCLOSED", - "ENCRYPTION", "END", "ENDS", "ENGINE", "ENGINES", "ENUM", "ERROR", "ERRORS", "ESCAPE", "ESCAPED", "EVENT", "EVENTS", - "EVERY", "EXCHANGE", - "EXECUTE", "EXISTS", "EXIT", "EXPANSION", "EXPIRE", "EXPLAIN", "EXPORT", "EXTENDED", "EXTENT_SIZE", "FALSE", "FAST", - "FAULTS", "FETCH", - "FIELDS", "FILE", "FILE_BLOCK_SIZE", "FILTER", "FIRST", "FIXED", "FLOAT", "FLOAT4", "FLOAT8", "FLUSH", "FOLLOWS", - "FOR", "FORCE", "FOREIGN", - "FORMAT", "FOUND", "FROM", "FULL", "FULLTEXT", "FUNCTION", "GENERAL", "GENERATED", "GEOMETRY", "GEOMETRYCOLLECTION", - "GET", "GET_FORMAT", - "GLOBAL", "GRANT", "GRANTS", "GROUP", "GROUP_REPLICATION", "HANDLER", "HASH", "HAVING", "HELP", "HIGH_PRIORITY", - "HOST", "HOSTS", "HOUR", - "HOUR_MICROSECOND", "HOUR_MINUTE", "HOUR_SECOND", "IDENTIFIED", "IF", "IGNORE", "IGNORE_SERVER_IDS", "IMPORT", "IN", - "INDEX", "INDEXES", - "INFILE", "INITIAL_SIZE", "INNER", "INOUT", "INSENSITIVE", "INSERT", "INSERT_METHOD", "INSTALL", "INSTANCE", "INT", - "INT1", "INT2", "INT3", - "INT4", "INT8", "INTEGER", "INTERVAL", "INTO", "INVOKER", "IO", "IO_AFTER_GTIDS", "IO_BEFORE_GTIDS", "IO_THREAD", - "IPC", "IS", "ISOLATION", - "ISSUER", "ITERATE", "JOIN", "JSON", "KEY", "KEYS", "KEY_BLOCK_SIZE", "KILL", "LANGUAGE", "LAST", "LEADING", "LEAVE", - "LEAVES", "LEFT", "LESS", - "LEVEL", "LIKE", "LIMIT", "LINEAR", "LINES", "LINESTRING", "LIST", "LOAD", "LOCAL", "LOCALTIME", "LOCALTIMESTAMP", - "LOCK", "LOCKS", "LOGFILE", - "LOGS", "LONG", "LONGBLOB", "LONGTEXT", "LOOP", "LOW_PRIORITY", "MASTER", "MASTER_AUTO_POSITION", "MASTER_BIND", - "MASTER_CONNECT_RETRY", "MASTER_DELAY", - "MASTER_HEARTBEAT_PERIOD", "MASTER_HOST", "MASTER_LOG_FILE", "MASTER_LOG_POS", "MASTER_PASSWORD", "MASTER_PORT", - "MASTER_RETRY_COUNT", "MASTER_SERVER_ID", - "MASTER_SSL", "MASTER_SSL_CA", "MASTER_SSL_CAPATH", "MASTER_SSL_CERT", "MASTER_SSL_CIPHER", "MASTER_SSL_CRL", - "MASTER_SSL_CRLPATH", "MASTER_SSL_KEY", - "MASTER_SSL_VERIFY_SERVER_CERT", "MASTER_TLS_VERSION", "MASTER_USER", "MATCH", "MAXVALUE", - "MAX_CONNECTIONS_PER_HOUR", "MAX_QUERIES_PER_HOUR", "MAX_ROWS", - "MAX_SIZE", "MAX_STATEMENT_TIME", "MAX_UPDATES_PER_HOUR", "MAX_USER_CONNECTIONS", "MEDIUM", "MEDIUMBLOB", - "MEDIUMINT", "MEDIUMTEXT", "MEMORY", "MERGE", - "MESSAGE_TEXT", "MICROSECOND", "MIDDLEINT", "MIGRATE", "MINUTE", "MINUTE_MICROSECOND", "MINUTE_SECOND", "MIN_ROWS", - "MOD", "MODE", "MODIFIES", "MODIFY", - "MONTH", "MULTILINESTRING", "MULTIPOINT", "MULTIPOLYGON", "MUTEX", "MYSQL_ERRNO", "NAME", "NAMES", "NATIONAL", - "NATURAL", "NCHAR", "NDB", "NDBCLUSTER", - "NEVER", "NEW", "NEXT", "NO", "NODEGROUP", "NONBLOCKING", "NONE", "NOT", "NO_WAIT", "NO_WRITE_TO_BINLOG", "NULL", - "NUMBER", "NUMERIC", "NVARCHAR", "OFFSET", - "OLD_PASSWORD", "ON", "ONE", "ONLY", "OPEN", "OPTIMIZE", "OPTIMIZER_COSTS", "OPTION", "OPTIONALLY", "OPTIONS", "OR", - "ORDER", "OUT", "OUTER", "OUTFILE", - "OWNER", "PACK_KEYS", "PAGE", "PARSER", "PARSE_GCOL_EXPR", "PARTIAL", "PARTITION", "PARTITIONING", "PARTITIONS", - "PASSWORD", "PHASE", "PLUGIN", "PLUGINS", - "PLUGIN_DIR", "POINT", "POLYGON", "PORT", "PRECEDES", "PRECISION", "PREPARE", "PRESERVE", "PREV", "PRIMARY", - "PRIVILEGES", "PROCEDURE", "PROCESSLIST", - "PROFILE", "PROFILES", "PROXY", "PURGE", "QUARTER", "QUERY", "QUICK", "RANGE", "READ", "READS", "READ_ONLY", - "READ_WRITE", "REAL", "REBUILD", "RECOVER", - "REDOFILE", "REDO_BUFFER_SIZE", "REDUNDANT", "REFERENCES", "REGEXP", "RELAY", "RELAYLOG", "RELAY_LOG_FILE", - "RELAY_LOG_POS", "RELAY_THREAD", "RELEASE", - "RELOAD", "REMOVE", "RENAME", "REORGANIZE", "REPAIR", "REPEAT", "REPEATABLE", "REPLACE", "REPLICATE_DO_DB", - "REPLICATE_DO_TABLE", "REPLICATE_IGNORE_DB", - "REPLICATE_IGNORE_TABLE", "REPLICATE_REWRITE_DB", "REPLICATE_WILD_DO_TABLE", "REPLICATE_WILD_IGNORE_TABLE", - "REPLICATION", "REQUIRE", "RESET", "RESIGNAL", - "RESTORE", "RESTRICT", "RESUME", "RETURN", "RETURNED_SQLSTATE", "RETURNS", "REVERSE", "REVOKE", "RIGHT", "RLIKE", - "ROLLBACK", "ROLLUP", "ROTATE", "ROUTINE", - "ROW", "ROWS", "ROW_COUNT", "ROW_FORMAT", "RTREE", "SAVEPOINT", "SCHEDULE", "SCHEMA", "SCHEMAS", "SCHEMA_NAME", - "SECOND", "SECOND_MICROSECOND", "SECURITY", - "SELECT", "SENSITIVE", "SEPARATOR", "SERIAL", "SERIALIZABLE", "SERVER", "SESSION", "SET", "SHARE", "SHOW", - "SHUTDOWN", "SIGNAL", "SIGNED", "SIMPLE", "SLAVE", - "SLOW", "SMALLINT", "SNAPSHOT", "SOCKET", "SOME", "SONAME", "SOUNDS", "SOURCE", "SPATIAL", "SPECIFIC", "SQL", - "SQLEXCEPTION", "SQLSTATE", "SQLWARNING", - "SQL_AFTER_GTIDS", "SQL_AFTER_MTS_GAPS", "SQL_BEFORE_GTIDS", "SQL_BIG_RESULT", "SQL_BUFFER_RESULT", "SQL_CACHE", - "SQL_CALC_FOUND_ROWS", "SQL_NO_CACHE", - "SQL_SMALL_RESULT", "SQL_THREAD", "SQL_TSI_DAY", "SQL_TSI_HOUR", "SQL_TSI_MINUTE", "SQL_TSI_MONTH", - "SQL_TSI_QUARTER", "SQL_TSI_SECOND", "SQL_TSI_WEEK", - "SQL_TSI_YEAR", "SSL", "STACKED", "START", "STARTING", "STARTS", "STATS_AUTO_RECALC", "STATS_PERSISTENT", - "STATS_SAMPLE_PAGES", "STATUS", "STOP", "STORAGE", - "STORED", "STRAIGHT_JOIN", "STRING", "SUBCLASS_ORIGIN", "SUBJECT", "SUBPARTITION", "SUBPARTITIONS", "SUPER", - "SUSPEND", "SWAPS", "SWITCHES", "TABLE", "TABLES", - "TABLESPACE", "TABLE_CHECKSUM", "TABLE_NAME", "TEMPORARY", "TEMPTABLE", "TERMINATED", "TEXT", "THAN", "THEN", "TIME", - "TIMESTAMP", "TIMESTAMPADD", "TIMESTAMPDIFF", - "TINYBLOB", "TINYINT", "TINYTEXT", "TO", "TRAILING", "TRANSACTION", "TRIGGER", "TRIGGERS", "TRUE", "TRUNCATE", - "TYPE", "TYPES", "UNCOMMITTED", "UNDEFINED", "UNDO", - "UNDOFILE", "UNDO_BUFFER_SIZE", "UNICODE", "UNINSTALL", "UNION", "UNIQUE", "UNKNOWN", "UNLOCK", "UNSIGNED", "UNTIL", - "UPDATE", "UPGRADE", "USAGE", "USE", "USER", - "USER_RESOURCES", "USE_FRM", "USING", "UTC_DATE", "UTC_TIME", "UTC_TIMESTAMP", "VALIDATION", "VALUE", "VALUES", - "VARBINARY", "VARCHAR", "VARCHARACTER", "VARIABLES", - "VARYING", "VIEW", "VIRTUAL", "WAIT", "WARNINGS", "WEEK", "WEIGHT_STRING", "WHEN", "WHERE", "WHILE", "WITH", - "WITHOUT", "WORK", "WRAPPER", "WRITE", "X509", "XA", "XID", - "XML", "XOR", "YEAR", "YEAR_MONTH", "ZEROFILL"} - -// MySQL80_KEYWORD TODO -var MySQL80_KEYWORD = []string{"ACCESSIBLE", "ACCOUNT", "ACTION", "ACTIVE", "ADD", "ADMIN", "AFTER", "AGAINST", - "AGGREGATE", "ALGORITHM", "ALL", - "ALTER", "ALWAYS", "ANALYSE", "ANALYZE", "AND", "ANY", "ARRAY", "AS", "ASC", "ASCII", "ASENSITIVE", "AT", - "ATTRIBUTE", "AUTHENTICATION", - "AUTOEXTEND_SIZE", "AUTO_INCREMENT", "AVG", "AVG_ROW_LENGTH", "BACKUP", "BEFORE", "BEGIN", "BETWEEN", "BIGINT", - "BINARY", "BINLOG", - "BIT", "BLOB", "BLOCK", "BOOL", "BOOLEAN", "BOTH", "BTREE", "BUCKETS", "BULK", "BY", "BYTE", "CACHE", "CALL", - "CASCADE", "CASCADED", - "CASE", "CATALOG_NAME", "CHAIN", "CHALLENGE_RESPONSE", "CHANGE", "CHANGED", "CHANNEL", "CHAR", "CHARACTER", - "CHARSET", "CHECK", "CHECKSUM", - "CIPHER", "CLASS_ORIGIN", "CLIENT", "CLONE", "CLOSE", "COALESCE", "CODE", "COLLATE", "COLLATION", "COLUMN", - "COLUMNS", "COLUMN_FORMAT", "COLUMN_NAME", - "COMMENT", "COMMIT", "COMMITTED", "COMPACT", "COMPLETION", "COMPONENT", "COMPRESSED", "COMPRESSION", "CONCURRENT", - "CONDITION", "CONNECTION", "CONSISTENT", - "CONSTRAINT", "CONSTRAINT_CATALOG", "CONSTRAINT_NAME", "CONSTRAINT_SCHEMA", "CONTAINS", "CONTEXT", "CONTINUE", - "CONVERT", "CPU", "CREATE", "CROSS", "CUBE", - "CUME_DIST", "CURRENT", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_USER", "CURSOR", "CURSOR_NAME", - "DATA", "DATABASE", "DATABASES", - "DATAFILE", "DATE", "DATETIME", "DAY", "DAY_HOUR", "DAY_MICROSECOND", "DAY_MINUTE", "DAY_SECOND", "DEALLOCATE", - "DEC", "DECIMAL", "DECLARE", "DEFAULT", - "DEFAULT_AUTH", "DEFINER", "DEFINITION", "DELAYED", "DELAY_KEY_WRITE", "DELETE", "DENSE_RANK", "DESC", "DESCRIBE", - "DESCRIPTION", "DES_KEY_FILE", - "DETERMINISTIC", "DIAGNOSTICS", "DIRECTORY", "DISABLE", "DISCARD", "DISK", "DISTINCT", "DISTINCTROW", "DIV", "DO", - "DOUBLE", "DROP", "DUAL", "DUMPFILE", - "DUPLICATE", "DYNAMIC", "EACH", "ELSE", "ELSEIF", "EMPTY", "ENABLE", "ENCLOSED", "ENCRYPTION", "END", "ENDS", - "ENFORCED", "ENGINE", "ENGINES", - "ENGINE_ATTRIBUTE", "ENUM", "ERROR", "ERRORS", "ESCAPE", "ESCAPED", "EVENT", "EVENTS", "EVERY", "EXCEPT", "EXCHANGE", - "EXCLUDE", "EXECUTE", "EXISTS", - "EXIT", "EXPANSION", "EXPIRE", "EXPLAIN", "EXPORT", "EXTENDED", "EXTENT_SIZE", "FACTOR", "FAILED_LOGIN_ATTEMPTS", - "FALSE", "FAST", "FAULTS", "FETCH", - "FIELDS", "FILE", "FILE_BLOCK_SIZE", "FILTER", "FINISH", "FIRST", "FIRST_VALUE", "FIXED", "FLOAT", "FLOAT4", - "FLOAT8", "FLUSH", "FOLLOWING", "FOLLOWS", - "FOR", "FORCE", "FOREIGN", "FORMAT", "FOUND", "FROM", "FULL", "FULLTEXT", "FUNCTION", "GENERAL", "GENERATE", - "GENERATED", "GEOMCOLLECTION", "GEOMETRY", - "GEOMETRYCOLLECTION", "GET", "GET_FORMAT", "GET_MASTER_PUBLIC_KEY", "GET_SOURCE_PUBLIC_KEY", "GLOBAL", "GRANT", - "GRANTS", "GROUP", "GROUPING", "GROUPS", - "GROUP_REPLICATION", "GTID_ONLY", "HANDLER", "HASH", "HAVING", "HELP", "HIGH_PRIORITY", "HISTOGRAM", "HISTORY", - "HOST", "HOSTS", "HOUR", "HOUR_MICROSECOND", - "HOUR_MINUTE", "HOUR_SECOND", "IDENTIFIED", "IF", "IGNORE", "IGNORE_SERVER_IDS", "IMPORT", "IN", "INACTIVE", "INDEX", - "INDEXES", "INFILE", "INITIAL", - "INITIAL_SIZE", "INITIATE", "INNER", "INOUT", "INSENSITIVE", "INSERT", "INSERT_METHOD", "INSTALL", "INSTANCE", "INT", - "INT1", "INT2", "INT3", "INT4", "INT8", - "INTEGER", "INTERSECT", "INTERVAL", "INTO", "INVISIBLE", "INVOKER", "IO", "IO_AFTER_GTIDS", "IO_BEFORE_GTIDS", - "IO_THREAD", "IPC", "IS", "ISOLATION", "ISSUER", - "ITERATE", "JOIN", "JSON", "JSON_TABLE", "JSON_VALUE", "KEY", "KEYRING", "KEYS", "KEY_BLOCK_SIZE", "KILL", "LAG", - "LANGUAGE", "LAST", "LAST_VALUE", - "LATERAL", "LEAD", "LEADING", "LEAVE", "LEAVES", "LEFT", "LESS", "LEVEL", "LIKE", "LIMIT", "LINEAR", "LINES", - "LINESTRING", "LIST", "LOAD", "LOCAL", - "LOCALTIME", "LOCALTIMESTAMP", "LOCK", "LOCKED", "LOCKS", "LOGFILE", "LOGS", "LONG", "LONGBLOB", "LONGTEXT", "LOOP", - "LOW_PRIORITY", "MASTER", "MASTER_AUTO_POSITION", - "MASTER_BIND", "MASTER_COMPRESSION_ALGORITHMS", "MASTER_CONNECT_RETRY", "MASTER_DELAY", "MASTER_HEARTBEAT_PERIOD", - "MASTER_HOST", "MASTER_LOG_FILE", - "MASTER_LOG_POS", "MASTER_PASSWORD", "MASTER_PORT", "MASTER_PUBLIC_KEY_PATH", "MASTER_RETRY_COUNT", - "MASTER_SERVER_ID", "MASTER_SSL", "MASTER_SSL_CA", - "MASTER_SSL_CAPATH", "MASTER_SSL_CERT", "MASTER_SSL_CIPHER", "MASTER_SSL_CRL", "MASTER_SSL_CRLPATH", - "MASTER_SSL_KEY", "MASTER_SSL_VERIFY_SERVER_CERT", - "MASTER_TLS_CIPHERSUITES", "MASTER_TLS_VERSION", "MASTER_USER", "MASTER_ZSTD_COMPRESSION_LEVEL", "MATCH", "MAXVALUE", - "MAX_CONNECTIONS_PER_HOUR", - "MAX_QUERIES_PER_HOUR", "MAX_ROWS", "MAX_SIZE", "MAX_UPDATES_PER_HOUR", "MAX_USER_CONNECTIONS", "MEDIUM", - "MEDIUMBLOB", "MEDIUMINT", "MEDIUMTEXT", - "MEMBER", "MEMORY", "MERGE", "MESSAGE_TEXT", "MICROSECOND", "MIDDLEINT", "MIGRATE", "MINUTE", "MINUTE_MICROSECOND", - "MINUTE_SECOND", "MIN_ROWS", - "MOD", "MODE", "MODIFIES", "MODIFY", "MONTH", "MULTILINESTRING", "MULTIPOINT", "MULTIPOLYGON", "MUTEX", - "MYSQL_ERRNO", "NAME", "NAMES", "NATIONAL", - "NATURAL", "NCHAR", "NDB", "NDBCLUSTER", "NESTED", "NETWORK_NAMESPACE", "NEVER", "NEW", "NEXT", "NO", "NODEGROUP", - "NONE", "NOT", "NOWAIT", "NO_WAIT", - "NO_WRITE_TO_BINLOG", "NTH_VALUE", "NTILE", "NULL", "NULLS", "NUMBER", "NUMERIC", "NVARCHAR", "OF", "OFF", "OFFSET", - "OJ", "OLD", "ON", "ONE", "ONLY", - "OPEN", "OPTIMIZE", "OPTIMIZER_COSTS", "OPTION", "OPTIONAL", "OPTIONALLY", "OPTIONS", "OR", "ORDER", "ORDINALITY", - "ORGANIZATION", "OTHERS", "OUT", - "OUTER", "OUTFILE", "OVER", "OWNER", "PACK_KEYS", "PAGE", "PARSER", "PARTIAL", "PARTITION", "PARTITIONING", - "PARTITIONS", "PASSWORD", "PASSWORD_LOCK_TIME", - "PATH", "PERCENT_RANK", "PERSIST", "PERSIST_ONLY", "PHASE", "PLUGIN", "PLUGINS", "PLUGIN_DIR", "POINT", "POLYGON", - "PORT", "PRECEDES", "PRECEDING", "PRECISION", - "PREPARE", "PRESERVE", "PREV", "PRIMARY", "PRIVILEGES", "PRIVILEGE_CHECKS_USER", "PROCEDURE", "PROCESS", - "PROCESSLIST", "PROFILE", "PROFILES", "PROXY", - "PURGE", "QUARTER", "QUERY", "QUICK", "RANDOM", "RANGE", "RANK", "READ", "READS", "READ_ONLY", "READ_WRITE", "REAL", - "REBUILD", "RECOVER", "RECURSIVE", - "REDOFILE", "REDO_BUFFER_SIZE", "REDUNDANT", "REFERENCE", "REFERENCES", "REGEXP", "REGISTRATION", "RELAY", - "RELAYLOG", "RELAY_LOG_FILE", "RELAY_LOG_POS", - "RELAY_THREAD", "RELEASE", "RELOAD", "REMOTE", "REMOVE", "RENAME", "REORGANIZE", "REPAIR", "REPEAT", "REPEATABLE", - "REPLACE", "REPLICA", "REPLICAS", - "REPLICATE_DO_DB", "REPLICATE_DO_TABLE", "REPLICATE_IGNORE_DB", "REPLICATE_IGNORE_TABLE", "REPLICATE_REWRITE_DB", - "REPLICATE_WILD_DO_TABLE", "REPLICATE_WILD_IGNORE_TABLE", - "REPLICATION", "REQUIRE", "REQUIRE_ROW_FORMAT", "RESET", "RESIGNAL", "RESOURCE", "RESPECT", "RESTART", "RESTORE", - "RESTRICT", "RESUME", "RETAIN", "RETURN", - "RETURNED_SQLSTATE", "RETURNING", "RETURNS", "REUSE", "REVERSE", "REVOKE", "RIGHT", "RLIKE", "ROLE", "ROLLBACK", - "ROLLUP", "ROTATE", "ROUTINE", "ROW", "ROWS", - "ROW_COUNT", "ROW_FORMAT", "ROW_NUMBER", "RTREE", "SAVEPOINT", "SCHEDULE", "SCHEMA", "SCHEMAS", "SCHEMA_NAME", - "SECOND", "SECONDARY", "SECONDARY_ENGINE", - "SECONDARY_ENGINE_ATTRIBUTE", "SECONDARY_LOAD", "SECONDARY_UNLOAD", "SECOND_MICROSECOND", "SECURITY", "SELECT", - "SENSITIVE", "SEPARATOR", "SERIAL", "SERIALIZABLE", - "SERVER", "SESSION", "SET", "SHARE", "SHOW", "SHUTDOWN", "SIGNAL", "SIGNED", "SIMPLE", "SKIP", "SLAVE", "SLOW", - "SMALLINT", "SNAPSHOT", "SOCKET", "SOME", "SONAME", - "SOUNDS", "SOURCE", "SOURCE_AUTO_POSITION", "SOURCE_BIND", "SOURCE_COMPRESSION_ALGORITHMS", "SOURCE_CONNECT_RETRY", - "SOURCE_DELAY", "SOURCE_HEARTBEAT_PERIOD", - "SOURCE_HOST", "SOURCE_LOG_FILE", "SOURCE_LOG_POS", "SOURCE_PASSWORD", "SOURCE_PORT", "SOURCE_PUBLIC_KEY_PATH", - "SOURCE_RETRY_COUNT", "SOURCE_SSL", "SOURCE_SSL_CA", - "SOURCE_SSL_CAPATH", "SOURCE_SSL_CERT", "SOURCE_SSL_CIPHER", "SOURCE_SSL_CRL", "SOURCE_SSL_CRLPATH", - "SOURCE_SSL_KEY", "SOURCE_SSL_VERIFY_SERVER_CERT", - "SOURCE_TLS_CIPHERSUITES", "SOURCE_TLS_VERSION", "SOURCE_USER", "SOURCE_ZSTD_COMPRESSION_LEVEL", "SPATIAL", - "SPECIFIC", "SQL", "SQLEXCEPTION", "SQLSTATE", - "SQLWARNING", "SQL_AFTER_GTIDS", "SQL_AFTER_MTS_GAPS", "SQL_BEFORE_GTIDS", "SQL_BIG_RESULT", "SQL_BUFFER_RESULT", - "SQL_CACHE", "SQL_CALC_FOUND_ROWS", "SQL_NO_CACHE", - "SQL_SMALL_RESULT", "SQL_THREAD", "SQL_TSI_DAY", "SQL_TSI_HOUR", "SQL_TSI_MINUTE", "SQL_TSI_MONTH", - "SQL_TSI_QUARTER", "SQL_TSI_SECOND", "SQL_TSI_WEEK", "SQL_TSI_YEAR", - "SRID", "SSL", "STACKED", "START", "STARTING", "STARTS", "STATS_AUTO_RECALC", "STATS_PERSISTENT", - "STATS_SAMPLE_PAGES", "STATUS", "STOP", "STORAGE", "STORED", "STRAIGHT_JOIN", - "STREAM", "STRING", "SUBCLASS_ORIGIN", "SUBJECT", "SUBPARTITION", "SUBPARTITIONS", "SUPER", "SUSPEND", "SWAPS", - "SWITCHES", "SYSTEM", "TABLE", "TABLES", "TABLESPACE", - "TABLE_CHECKSUM", "TABLE_NAME", "TEMPORARY", "TEMPTABLE", "TERMINATED", "TEXT", "THAN", "THEN", "THREAD_PRIORITY", - "TIES", "TIME", "TIMESTAMP", "TIMESTAMPADD", - "TIMESTAMPDIFF", "TINYBLOB", "TINYINT", "TINYTEXT", "TLS", "TO", "TRAILING", "TRANSACTION", "TRIGGER", "TRIGGERS", - "TRUE", "TRUNCATE", "TYPE", "TYPES", "UNBOUNDED", - "UNCOMMITTED", "UNDEFINED", "UNDO", "UNDOFILE", "UNDO_BUFFER_SIZE", "UNICODE", "UNINSTALL", "UNION", "UNIQUE", - "UNKNOWN", "UNLOCK", "UNREGISTER", "UNSIGNED", "UNTIL", - "UPDATE", "UPGRADE", "URL", "USAGE", "USE", "USER", "USER_RESOURCES", "USE_FRM", "USING", "UTC_DATE", "UTC_TIME", - "UTC_TIMESTAMP", "VALIDATION", "VALUE", "VALUES", - "VARBINARY", "VARCHAR", "VARCHARACTER", "VARIABLES", "VARYING", "VCPU", "VIEW", "VIRTUAL", "VISIBLE", "WAIT", - "WARNINGS", "WEEK", "WEIGHT_STRING", "WHEN", "WHERE", - "WHILE", "WINDOW", "WITH", "WITHOUT", "WORK", "WRAPPER", "WRITE", "X509", "XA", "XID", "XML", "XOR", "YEAR", - "YEAR_MONTH", "ZEROFILL", "ZONE"} - -// ALL_KEYWORD TODO -var ALL_KEYWORD = []string{"CONSTRAINT_NAME", "JSON_VALUE", "SOURCE_HOST", "CUBE", "COLLATE", "EXCEPT", - "MASTER_CONNECT_RETRY", "INITIAL", "REDO_BUFFER_SIZE", - "HOUR", "DEFINER", "DISABLE", "PERSIST_ONLY", "DESC", "OTHERS", "SKIP", "CHANGED", "KEYS", "HOUR_SECOND", "REDOFILE", - "DEC", "CHARSET", "ASCII", - "MASTER_PORT", "DEFINITION", "PLUGIN_DIR", "ON", "LINESTRING", "LONGTEXT", "PRESERVE", "MASTER_PUBLIC_KEY_PATH", - "UNSIGNED", "COMMITTED", "CHANNEL", - "AS", "CUME_DIST", "REUSE", "SWAPS", "SECONDARY_LOAD", "LIKE", "OPTIMIZE", "ENUM", "ELSE", "VISIBLE", "WINDOW", - "FOUND", "PARTIAL", "DESCRIPTION", - "READ_WRITE", "CPU", "MAX_USER_CONNECTIONS", "TINYBLOB", "FOLLOWS", "CURRENT_USER", "FLOAT8", "MASTER_LOG_POS", - "TIMESTAMP", "BETWEEN", "INSTALL", - "SOURCE_SSL_CERT", "SHUTDOWN", "EXPANSION", "FLOAT4", "RETURNED_SQLSTATE", "MASTER_TLS_VERSION", "REPLICATE_DO_DB", - "SOURCE_SSL_CAPATH", "STATS_SAMPLE_PAGES", - "JSON", "DELETE", "INITIAL_SIZE", "OPTIONALLY", "REQUIRE", "TRUNCATE", "SIMPLE", "OPTIMIZER_COSTS", - "REPLICATE_WILD_DO_TABLE", "FORCE", "FIRST_VALUE", "AND", - "BINARY", "TRUE", "OFF", "INFILE", "STREAM", "NDBCLUSTER", "GENERATED", "ESCAPE", "DATABASES", "VALUES", "WAIT", - "TINYTEXT", "FLUSH", "IDENTIFIED", "CURRENT", - "AGGREGATE", "FILE", "GRANT", "SOUNDS", "ALTER", "EXPLAIN", "FLOAT", "DUMPFILE", "DUAL", "USER_RESOURCES", - "MIN_ROWS", "GROUP", "DELAY_KEY_WRITE", "LEAVES", - "PARTITIONS", "MAX_UPDATES_PER_HOUR", "SCHEMAS", "BIT", "RETAIN", "ISOLATION", "SRID", "DUPLICATE", "AFTER", - "REPLICA", "CALL", "OJ", "TLS", "ADMIN", "TEMPORARY", - "DIV", "CONSTRAINT", "SQL_TSI_WEEK", "CROSS", "MASTER_SSL_CERT", "CHECKSUM", "PREPARE", "COMPRESSED", "SERIAL", - "LOCALTIME", "SECONDARY_ENGINE", "BOOL", "ROLLBACK", - "XOR", "LOCKS", "NO_WRITE_TO_BINLOG", "RELOAD", "SHOW", "PRECEDING", "REPLICATE_IGNORE_TABLE", - "SOURCE_AUTO_POSITION", "MAX_ROWS", "INDEX", "SLAVE", "CURRENT_DATE", - "PROXY", "RELAY", "TERMINATED", "DISTINCT", "X509", "ENGINE_ATTRIBUTE", "CURSOR_NAME", "RENAME", "SUBPARTITIONS", - "NO", "QUARTER", "REBUILD", "WRAPPER", "MASTER", - "SQL_BEFORE_GTIDS", "THAN", "OR", "MEMORY", "OFFSET", "UNDOFILE", "ORDINALITY", "VARYING", "ERROR", "IPC", - "SMALLINT", "SIGNED", "OUTER", "STATS_AUTO_RECALC", "PERCENT_RANK", - "DES_KEY_FILE", "INOUT", "NODEGROUP", "URL", "DISTINCTROW", "REPLICATION", "TIME", "MASTER_AUTO_POSITION", - "MINUTE_SECOND", "HAVING", "NCHAR", "STOP", "MASTER_SSL_CA", - "RETURN", "NATIONAL", "CURSOR", "IGNORE", "NEW", "STORAGE", "CHAIN", "REPLICATE_WILD_IGNORE_TABLE", "DAY_MINUTE", - "LIMIT", "PRIMARY", "COMPRESSION", "INACTIVE", "REPEAT", - "TEMPTABLE", "EXISTS", "ATTRIBUTE", "ROLE", "OPEN", "INDEXES", "TIMESTAMPDIFF", "REAL", "COLUMNS", "PURGE", "ROTATE", - "HIGH_PRIORITY", "MULTIPOINT", "VARCHAR", "ROWS", - "NESTED", "GROUP_REPLICATION", "PLUGIN", "DELAYED", "EVENT", "SCHEMA_NAME", "INVISIBLE", "AGAINST", "ANY", - "SOURCE_LOG_FILE", "EXTENT_SIZE", "LAST", "ZONE", "PARTITION", - "INTERVAL", "REPLICATE_DO_TABLE", "NATURAL", "MESSAGE_TEXT", "MEMBER", "BLOCK", "EXPORT", "ROLLUP", "START", "LEAVE", - "UNIQUE", "SOURCE_RETRY_COUNT", "NTILE", "MODIFIES", - "BEGIN", "BIGINT", "OWNER", "STRING", "PRECISION", "LAST_VALUE", "JOIN", "RANDOM", "HELP", "REPLACE", "SLOW", - "DEFAULT_AUTH", "SQL_BUFFER_RESULT", "TO", "SCHEDULE", - "MEDIUM", "LOW_PRIORITY", "FIRST", "SOURCE_COMPRESSION_ALGORITHMS", "USER", "REPLICAS", "OUT", "SQL_TSI_SECOND", - "AUTO_INCREMENT", "MAX_STATEMENT_TIME", "GEOMCOLLECTION", - "TABLE_NAME", "COMPACT", "CHALLENGE_RESPONSE", "MIDDLEINT", "TABLESPACE", "REPLICATE_REWRITE_DB", "INT1", "UNICODE", - "CLOSE", "EXTENDED", "DATETIME", "RESET", "MATCH", - "DECIMAL", "EACH", "FETCH", "LESS", "SOURCE_TLS_VERSION", "OPTION", "FILTER", "NUMBER", "MODE", "MASTER_SSL", "LOCK", - "COMPLETION", "FALSE", "REGISTRATION", "NAME", - "CONDITION", "STRAIGHT_JOIN", "LAG", "WEIGHT_STRING", "BTREE", "TABLE_CHECKSUM", "VALIDATION", "IO", "MONTH", - "CIPHER", "RETURNS", "SPECIFIC", "HANDLER", "ENFORCED", - "MASTER_HOST", "TIES", "UNKNOWN", "CONSISTENT", "DROP", "MOD", "PROFILES", "NOWAIT", "SOURCE_SSL_CRL", - "AUTOEXTEND_SIZE", "PASSWORD", "MASTER_SERVER_ID", "SERVER", - "TRIGGERS", "FROM", "COMPONENT", "MINUTE_MICROSECOND", "OF", "CLASS_ORIGIN", "NONBLOCKING", "CONCURRENT", - "UTC_TIMESTAMP", "NVARCHAR", "MASTER_SSL_CRLPATH", - "SQL_BIG_RESULT", "LOGS", "ALGORITHM", "DESCRIBE", "MEDIUMINT", "BOTH", "MASTER_BIND", "NTH_VALUE", "RANGE", - "REMOTE", "THEN", "QUERY", "UNION", "PORT", "FAST", - "CASCADE", "LOAD", "INT4", "FAULTS", "CASCADED", "PRECEDES", "BOOLEAN", "INVOKER", "ASENSITIVE", "SET", - "SOURCE_PUBLIC_KEY_PATH", "DENSE_RANK", "ROUTINE", - "REORGANIZE", "MAX_SIZE", "CLIENT", "DIAGNOSTICS", "ROW_COUNT", "RIGHT", "LOCKED", "GENERATE", "NULLS", "CONNECTION", - "SWITCHES", "PREV", "TABLE", "SOURCE_SSL_CRLPATH", - "SQL_TSI_HOUR", "CATALOG_NAME", "RETURNING", "NDB", "SOURCE_ZSTD_COMPRESSION_LEVEL", "MASTER_SSL_CIPHER", - "AVG_ROW_LENGTH", "SUPER", "DAY_SECOND", "PAGE", - "OUTFILE", "LOCAL", "HISTORY", "DEFAULT", "WITH", "KEYRING", "AUTHENTICATION", "EXCLUDE", "ISSUER", "TYPES", - "REGEXP", "FOR", "UPDATE", "SAVEPOINT", "CONVERT", - "BULK", "ENGINE", "ROW", "DAY_HOUR", "DAY_MICROSECOND", "OLD_PASSWORD", "SOURCE_PASSWORD", "ALL", "MASTER_PASSWORD", - "FULLTEXT", "RLIKE", "PASSWORD_LOCK_TIME", - "WHEN", "DEALLOCATE", "SOURCE", "LANGUAGE", "RELAYLOG", "EMPTY", "FULL", "NUMERIC", "GROUPS", "DETERMINISTIC", - "PROFILE", "UNDEFINED", "FOREIGN", "RESUME", - "MASTER_DELAY", "HASH", "SEPARATOR", "BY", "RESTRICT", "MEDIUMBLOB", "GET_MASTER_PUBLIC_KEY", "VALUE", "LIST", - "VARBINARY", "UTC_TIME", "PROCESSLIST", - "SQLWARNING", "EXCHANGE", "VIRTUAL", "UNDO", "READ_ONLY", "SOURCE_SSL_KEY", "CONTEXT", "YEAR", "GLOBAL", "ALWAYS", - "SIGNAL", "ACTION", "GET", "RESTORE", - "SQLSTATE", "AVG", "MEDIUMTEXT", "SOURCE_TLS_CIPHERSUITES", "MASTER_SSL_KEY", "CHANGE", "GET_FORMAT", - "MULTILINESTRING", "RELAY_LOG_POS", "UTC_DATE", "EVERY", - "SQL_TSI_MONTH", "XA", "INTERSECT", "MASTER_TLS_CIPHERSUITES", "RELAY_LOG_FILE", "IO_AFTER_GTIDS", "INTEGER", "EXIT", - "HOUR_MINUTE", "ACCOUNT", - "THREAD_PRIORITY", "FINISH", "GEOMETRY", "READS", "TINYINT", "TYPE", "BEFORE", "FOLLOWING", "SUBCLASS_ORIGIN", - "SHARE", "SCHEMA", "SESSION", - "ADD", "INSENSITIVE", "GEOMETRYCOLLECTION", "WITHOUT", "WEEK", "GTID_ONLY", "SUSPEND", "LEVEL", "PARTITIONING", - "SECOND", "SELECT", "INSERT_METHOD", - "SUBPARTITION", "INTO", "FUNCTION", "ENGINES", "REMOVE", "STATUS", "WORK", "REPLICATE_IGNORE_DB", "OPTIONS", - "LOCALTIMESTAMP", "REPAIR", "EVENTS", - "MYSQL_ERRNO", "SQL_TSI_MINUTE", "PROCESS", "NAMES", "RELAY_THREAD", "SNAPSHOT", "ENCLOSED", "TEXT", "UPGRADE", - "ASC", "EXECUTE", "RESTART", "COLUMN_NAME", - "ESCAPED", "HOUR_MICROSECOND", "MASTER_RETRY_COUNT", "COLUMN", "DATE", "DATA", "SQL_NO_CACHE", "STORED", "LATERAL", - "READ", "RESPECT", - "SOURCE_USER", "RECURSIVE", "END", "CONTINUE", "BLOB", "DOUBLE", "USE_FRM", "TIMESTAMPADD", "RECOVER", "ENDS", - "LONGBLOB", "INSERT", "USING", - "ROW_FORMAT", "DISK", "AUTHORS", "LINEAR", "MAXVALUE", "XML", "CHARACTER", "INNER", "OLD", "REFERENCES", - "SOURCE_SSL_CIPHER", "MAX_CONNECTIONS_PER_HOUR", - "SQL_SMALL_RESULT", "LEAD", "DATAFILE", "PATH", "SENSITIVE", "REDUNDANT", "POLYGON", "SQL_CACHE", "FACTOR", - "REFERENCE", "ANALYZE", "BACKUP", "SOURCE_HEARTBEAT_PERIOD", - "IS", "ENCRYPTION", "XID", "NO_WAIT", "UNREGISTER", "CODE", "SQL_CALC_FOUND_ROWS", "USE", "SQL_AFTER_GTIDS", - "MASTER_SSL_CAPATH", "BINLOG", "ORDER", - "ZEROFILL", "STARTING", "CURRENT_TIME", "INT8", "TABLES", "NETWORK_NAMESPACE", "IN", "SOME", "CHAR", "CASE", "LONG", - "AT", "REVERSE", "FILE_BLOCK_SIZE", - "FAILED_LOGIN_ATTEMPTS", "KILL", "TRANSACTION", "SQL", "SQL_THREAD", "PHASE", "WRITE", - "MASTER_COMPRESSION_ALGORITHMS", "JSON_TABLE", "TRIGGER", "STACKED", - "CONTAINS", "LINES", "SSL", "WARNINGS", "LOGFILE", "SECOND_MICROSECOND", "GET_SOURCE_PUBLIC_KEY", - "MAX_QUERIES_PER_HOUR", "UNCOMMITTED", "PRIVILEGE_CHECKS_USER", - "MASTER_SSL_CRL", "ARRAY", "ENABLE", "CLONE", "NONE", "DATABASE", "EXPIRE", "WHERE", "PRIVILEGES", "ONE", "CACHE", - "STATS_PERSISTENT", "UNINSTALL", "MODIFY", - "COLUMN_FORMAT", "COMMENT", "RELEASE", "IF", "UNTIL", "VIEW", "PERSIST", "SPATIAL", "KEY_BLOCK_SIZE", "QUICK", "NOT", - "GRANTS", "IMPORT", "MICROSECOND", "RESIGNAL", - "INSTANCE", "ROW_NUMBER", "SOURCE_CONNECT_RETRY", "CURRENT_TIMESTAMP", "ERRORS", "USAGE", "PLUGINS", - "CONSTRAINT_CATALOG", "SQL_AFTER_MTS_GAPS", "LEADING", - "ACCESSIBLE", "SECONDARY_UNLOAD", "LEFT", "SOURCE_LOG_POS", "UNBOUNDED", "MULTIPOLYGON", "MASTER_LOG_FILE", - "GENERAL", "ITERATE", "IGNORE_SERVER_IDS", - "INT3", "POINT", "HOST", "TRAILING", "DIRECTORY", "NEVER", "SQL_TSI_DAY", "OPTIONAL", "KEY", "REPEATABLE", "NEXT", - "SQLEXCEPTION", "BUCKETS", "RESOURCE", - "YEAR_MONTH", "SECURITY", "SQL_TSI_YEAR", "MASTER_ZSTD_COMPRESSION_LEVEL", "VARCHARACTER", - "SECONDARY_ENGINE_ATTRIBUTE", "SOURCE_SSL_CA", "CONSTRAINT_SCHEMA", - "REQUIRE_ROW_FORMAT", "COMMIT", "CHECK", "VCPU", "UNDO_BUFFER_SIZE", "COLLATION", "COALESCE", "DISCARD", "MERGE", - "MASTER_HEARTBEAT_PERIOD", "PARSER", "RANK", - "MINUTE", "MASTER_SSL_VERIFY_SERVER_CERT", "MUTEX", "UNLOCK", "FIXED", "DAY", "SOCKET", "SONAME", "RTREE", "DYNAMIC", - "SQL_TSI_QUARTER", "IO_BEFORE_GTIDS", - "SOURCE_BIND", "INT", "PARSE_GCOL_EXPR", "HISTOGRAM", "CONTRIBUTORS", "SOURCE_PORT", "OVER", - "SOURCE_SSL_VERIFY_SERVER_CERT", "DO", "ELSEIF", "SERIALIZABLE", - "WHILE", "ONE_SHOT", "PACK_KEYS", "ACTIVE", "INITIATE", "SYSTEM", "STARTS", "FORMAT", "SECONDARY", "CREATE", "HOSTS", - "SOURCE_DELAY", "NULL", "VARIABLES", - "IO_THREAD", "MASTER_USER", "REVOKE", "DECLARE", "GROUPING", "LOOP", "SUBJECT", "BYTE", "PROCEDURE", "MIGRATE", - "ORGANIZATION", "SOURCE_SSL", "FIELDS", "ONLY", "INT2", "ANALYSE"} diff --git a/dbm-services/mysql/db-simulation/app/syntax/rule.go b/dbm-services/mysql/db-simulation/app/syntax/rule.go index 15b5279bd2..bb06707c06 100644 --- a/dbm-services/mysql/db-simulation/app/syntax/rule.go +++ b/dbm-services/mysql/db-simulation/app/syntax/rule.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package syntax import ( @@ -48,6 +58,14 @@ func (c *CheckerResult) Parse(rule *RuleItem, val interface{}, s string) { } } +// ParseBultinBan TODO +func (c *CheckerResult) ParseBultinBan(f func() (bool, string)) { + matched, msg := f() + if matched { + c.BanWarns = append(c.BanWarns, msg) + } +} + const ( // DEFAUTL_RULE_FILE TODO DEFAUTL_RULE_FILE = "rule.yaml" @@ -91,7 +109,7 @@ type RuleItem struct { Expr string `yaml:"expr"` Desc string `yaml:"desc"` Item interface{} `yaml:"item"` - // 是都是禁用的行为 + // ban: true 是都是禁用的行为 Ban bool `yaml:"ban"` Val interface{} ruleProgram *vm.Program @@ -103,6 +121,26 @@ type Rules struct { CreateTableRule CreateTableRule `yaml:"CreateTableRule"` AlterTableRule AlterTableRule `yaml:"AlterTableRule"` DmlRule DmlRule `yaml:"DmlRule"` + BuiltInRule BuiltInRule `yaml:"BuiltInRule"` +} + +// BuiltInRule TODO +type BuiltInRule struct { + TableNameSpecification TableNameSpecification `yaml:"TableNameSpecification"` + ShemaNamespecification ShemaNamespecification `yaml:"ShemaNamespecification"` +} + +// TableNameSpecification TODO +type TableNameSpecification struct { + KeyWord bool `yaml:"keyword"` + SpeicalChar bool `yaml:"speicalChar"` +} + +// ShemaNamespecification TODO +type ShemaNamespecification struct { + KeyWord bool `yaml:"keyword"` + SpeicalChar bool `yaml:"speicalChar"` + sysDbName bool `yaml:"sysDbName"` } // CommandRule TODO diff --git a/dbm-services/mysql/db-simulation/app/syntax/syntax.go b/dbm-services/mysql/db-simulation/app/syntax/syntax.go index 2f8f9a33fd..42c82aa6ce 100644 --- a/dbm-services/mysql/db-simulation/app/syntax/syntax.go +++ b/dbm-services/mysql/db-simulation/app/syntax/syntax.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + // Package syntax TODO package syntax @@ -18,10 +28,10 @@ import ( "sync" "time" + "dbm-services/common/go-pubpkg/bkrepo" "dbm-services/common/go-pubpkg/logger" "dbm-services/mysql/db-simulation/app" "dbm-services/mysql/db-simulation/app/config" - "dbm-services/mysql/db-simulation/pkg/bkrepo" ) // CheckSyntax TODO @@ -88,7 +98,8 @@ type RiskInfo struct { WarnInfo string `json:"warn_info"` } -var lock sync.Mutex +// DDLMAP_FILE_SUFFIX TODO +const DDLMAP_FILE_SUFFIX = ".tbl.map" // DoSQL TODO func (tf *TmysqlParseFile) DoSQL(dbtype string) (result map[string]*CheckInfo, err error) { @@ -134,17 +145,54 @@ func (tf *TmysqlParseFile) Do(dbtype string) (result map[string]*CheckInfo, err logger.Error("failed to execute tmysqlparse: %s", err.Error()) return nil, err } - logger.Info("err is %v", err) // 对tmysqlparse的处理结果进行分析,为json文件,后面用到了rule mysqlVersion := tf.Param.MysqlVersion + logger.Info("start to analyze the parsing result") if err = tf.AnalyzeParseResult(mysqlVersion, dbtype); err != nil { logger.Error("failed to analyze the parsing result:%s", err.Error()) return tf.result, err } - + // // 在一定程度上会增加语法检查的耗时、后续先观察一下 + // if dbtype == app.Spider { + // tf.UploadDdlTblMapFile() + // } return tf.result, nil } +// CreateAndUploadDDLTblFile TODO +func (tf *TmysqlParseFile) CreateAndUploadDDLTblFile() (err error) { + logger.Info("start to create and upload ddl table file") + logger.Info("doing....") + if err = tf.Init(); err != nil { + logger.Error("Do init failed %s", err.Error()) + return err + } + // 最后删除临时目录,不会返回错误 + // 暂时屏蔽 观察过程文件 + defer tf.delTempDir() + + if err = tf.Downloadfile(); err != nil { + logger.Error("failed to download sql file from the product library %s", err.Error()) + return err + } + + if err = tf.Execute(); err != nil { + logger.Error("failed to execute tmysqlparse: %s", err.Error()) + return err + } + for inputFileName := range tf.fileMap { + if err = tf.analyzeDDLTbls(inputFileName); err != nil { + logger.Error("failed to analyzeDDLTbls %s,err:%s", inputFileName, err.Error()) + return + } + } + if err = tf.UploadDdlTblMapFile(); err != nil { + logger.Error("failed to upload ddl table file %s", err.Error()) + return err + } + return nil +} + // Init TODO func (t *TmysqlParse) Init() (err error) { tmpDir := fmt.Sprintf("tmysqlparse_%s_%s", time.Now().Format("20060102150405"), strconv.Itoa(rand.Intn(10000))) @@ -197,6 +245,23 @@ func (tf *TmysqlParseFile) Downloadfile() (err error) { return } +// UploadDdlTblMapFile TODO +func (tf *TmysqlParseFile) UploadDdlTblMapFile() (err error) { + for _, fileName := range tf.Param.FileNames { + ddlTblFile := fileName + DDLMAP_FILE_SUFFIX + resp, err := tf.bkRepoClient.Upload(path.Join(tf.tmpWorkdir, ddlTblFile), ddlTblFile, + tf.Param.BkRepoBasePath) + if err != nil { + logger.Error("download %s from bkrepo failed :%s", fileName, err.Error()) + return err + } + if resp.Code != 0 { + logger.Warn("upload ddl table map file for %s failed,msg:%s,cod:%d", fileName, resp.Message, resp.Code) + } + } + return +} + // Execute 运行tmysqlpase // // @receiver tf @@ -244,6 +309,7 @@ func (t *TmysqlParse) AnalyzeParseResult(mysqlVersion string, dbtype string) (er for inputFileName := range t.fileMap { wg.Add(1) c <- struct{}{} + logger.Info("start to analyze %s", inputFileName) go func(fileName string) { err = t.AnalyzeOne(fileName, mysqlVersion, dbtype) if err != nil { @@ -254,6 +320,7 @@ func (t *TmysqlParse) AnalyzeParseResult(mysqlVersion string, dbtype string) (er }(inputFileName) } wg.Wait() + logger.Info("end to analyze %d files", len(t.fileMap)) if len(errs) > 0 { return fmt.Errorf("errors: %s", strings.Join(errs, "\n")) } @@ -282,9 +349,80 @@ func (c *CheckInfo) ParseResult(rule *RuleItem, res ParseLineQueryBase) { } } +// analyzeDDLTbls TODO +func (tf *TmysqlParse) analyzeDDLTbls(inputfileName string) (err error) { + ddlTbls := make(map[string][]string) + defer func() { + if r := recover(); r != nil { + logger.Error("panic error:%v,stack:%s", r, string(debug.Stack())) + logger.Error("Recovered. Error: %v", r) + } + }() + tf.result[inputfileName] = &CheckInfo{} + f, err := os.Open(tf.getAbsoutputfilePath(inputfileName)) + if err != nil { + logger.Error("open file failed %s", err.Error()) + return + } + defer f.Close() + reader := bufio.NewReader(f) + for { + line, err_r := reader.ReadBytes(byte('\n')) + if err_r != nil { + if err_r == io.EOF { + break + } + logger.Error("read Line Error %s", err_r.Error()) + return err_r + } + if len(line) == 1 && line[0] == byte('\n') { + continue + } + var res ParseLineQueryBase + if err = json.Unmarshal(line, &res); err != nil { + logger.Error("json unmasrshal line:%s failed %s", string(line), err.Error()) + return + } + // 判断是否有语法错误 + if res.ErrorCode != 0 { + return + } + switch res.Command { + case "create_table", "alter_table": + var o CommDDLResult + if err = json.Unmarshal(line, &o); err != nil { + logger.Error("json unmasrshal line failed %s", err.Error()) + return + } + // 如果dbname为空,则实际库名由参数指定,无特殊情况 + ddlTbls[o.DbName] = append(ddlTbls[o.DbName], o.TableName) + } + } + fd, err := os.Create(path.Join(tf.tmpWorkdir, inputfileName+DDLMAP_FILE_SUFFIX)) + if err != nil { + logger.Error("create file failed %s", err.Error()) + return err + } + defer fd.Close() + b, err := json.Marshal(ddlTbls) + if err != nil { + logger.Error("json marshal failed %s", err.Error()) + return err + } + _, err = fd.Write(b) + if err != nil { + logger.Error("write file failed %s", err.Error()) + return err + } + return nil +} + // AnalyzeOne TODO func (tf *TmysqlParse) AnalyzeOne(inputfileName string, mysqlVersion string, dbtype string) (err error) { var idx int + var syntaxFailInfos []FailedInfo + var buf []byte + ddlTbls := make(map[string][]string) defer func() { if r := recover(); r != nil { logger.Error("panic error:%v,stack:%s", r, string(debug.Stack())) @@ -292,7 +430,6 @@ func (tf *TmysqlParse) AnalyzeOne(inputfileName string, mysqlVersion string, dbt err = fmt.Errorf("line:%d,err:%v", idx, r) } }() - lock.Lock() tf.result[inputfileName] = &CheckInfo{} f, err := os.Open(tf.getAbsoutputfilePath(inputfileName)) if err != nil { @@ -301,8 +438,6 @@ func (tf *TmysqlParse) AnalyzeOne(inputfileName string, mysqlVersion string, dbt } defer f.Close() reader := bufio.NewReader(f) - var syntaxFailInfos []FailedInfo - var buf []byte for { idx++ line, isPrefix, err_r := reader.ReadLine() @@ -346,21 +481,38 @@ func (tf *TmysqlParse) AnalyzeOne(inputfileName string, mysqlVersion string, dbt // tmysqlparse检查结果全部正确,开始判断语句是否符合定义的规则(即虽然语法正确,但语句可能是高危语句或禁用的命令) tf.result[inputfileName].ParseResult(R.CommandRule.HighRiskCommandRule, res) tf.result[inputfileName].ParseResult(R.CommandRule.BanCommandRule, res) - // tf.result[inputfileName].runcheck(res, bs, mysqlVersion) case app.Spider: // tmysqlparse检查结果全部正确,开始判断语句是否符合定义的规则(即虽然语法正确,但语句可能是高危语句或禁用的命令) tf.result[inputfileName].ParseResult(SR.CommandRule.HighRiskCommandRule, res) tf.result[inputfileName].ParseResult(SR.CommandRule.BanCommandRule, res) - tf.result[inputfileName].runSpidercheck(res, bs, mysqlVersion) + tf.result[inputfileName].runSpidercheck(ddlTbls, res, bs, mysqlVersion) } } + // if dbtype == app.Spider { + // fd, err := os.Create(path.Join(tf.tmpWorkdir, inputfileName+DDLMAP_FILE_SUFFIX)) + // if err != nil { + // logger.Error("create file failed %s", err.Error()) + // return err + // } + // defer fd.Close() + // b, err := json.Marshal(ddlTbls) + // if err != nil { + // logger.Error("json marshal failed %s", err.Error()) + // return err + // } + // _, err = fd.Write(b) + // if err != nil { + // logger.Error("write file failed %s", err.Error()) + // return err + // } + // } tf.result[inputfileName].SyntaxFailInfos = syntaxFailInfos - lock.Unlock() return nil } -func (ch *CheckInfo) runSpidercheck(res ParseLineQueryBase, bs []byte, mysqlVersion string) (err error) { +func (ch *CheckInfo) runSpidercheck(ddlTbls map[string][]string, res ParseLineQueryBase, bs []byte, + mysqlVersion string) (err error) { var c SpiderChecker // 其他规则分析 switch res.Command { @@ -372,6 +524,8 @@ func (ch *CheckInfo) runSpidercheck(res ParseLineQueryBase, bs []byte, mysqlVers } o.TableOptionMap = ConverTableOptionToMap(o.TableOptions) c = o + // 如果dbname为空,则实际库名由参数指定,无特殊情况 + ddlTbls[o.DbName] = append(ddlTbls[o.DbName], o.TableName) case "create_db": var o CreateDBResult if err = json.Unmarshal(bs, &o); err != nil { @@ -379,6 +533,13 @@ func (ch *CheckInfo) runSpidercheck(res ParseLineQueryBase, bs []byte, mysqlVers return } c = o + case "alter_table": + var o AlterTableResult + if err = json.Unmarshal(bs, &o); err != nil { + logger.Error("json unmasrshal line failed %s", err.Error()) + return + } + ddlTbls[o.DbName] = append(ddlTbls[o.DbName], o.TableName) } if c == nil { return diff --git a/dbm-services/mysql/db-simulation/app/syntax/tmysqlpase.go b/dbm-services/mysql/db-simulation/app/syntax/tmysqlpase.go index 4cca444460..1bf6780657 100644 --- a/dbm-services/mysql/db-simulation/app/syntax/tmysqlpase.go +++ b/dbm-services/mysql/db-simulation/app/syntax/tmysqlpase.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package syntax import util "dbm-services/common/go-pubpkg/cmutil" @@ -55,6 +65,14 @@ func ConverTableOptionToMap(options []TableOption) map[string]interface{} { return r } +// CommDDLResult TODO +type CommDDLResult struct { + QueryID int `json:"query_id"` + Command string `json:"command"` + DbName string `json:"db_name"` + TableName string `json:"table_name"` +} + // CreateTableResult TODO type CreateTableResult struct { QueryID int `json:"query_id"` diff --git a/dbm-services/mysql/db-simulation/go.mod b/dbm-services/mysql/db-simulation/go.mod index 37b69bcac7..7f08178528 100644 --- a/dbm-services/mysql/db-simulation/go.mod +++ b/dbm-services/mysql/db-simulation/go.mod @@ -6,7 +6,7 @@ require ( github.com/antonmedv/expr v1.12.5 github.com/gin-contrib/pprof v1.4.0 github.com/gin-contrib/requestid v0.0.6 - github.com/gin-gonic/gin v1.9.0 + github.com/gin-gonic/gin v1.9.1 github.com/pkg/errors v0.9.1 github.com/spf13/viper v1.15.0 gopkg.in/yaml.v2 v2.4.0 @@ -18,15 +18,17 @@ require ( ) require ( - github.com/bytedance/sonic v1.8.8 // indirect - github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/bytedance/sonic v1.10.0-rc3 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect + github.com/chenzhuoyu/iasm v0.9.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-logr/logr v1.2.2 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.12.0 // indirect + github.com/go-playground/validator/v10 v10.14.1 // indirect github.com/go-sql-driver/mysql v1.7.1 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect @@ -39,15 +41,15 @@ require ( github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/cpuid/v2 v2.2.4 // indirect - github.com/leodido/go-urn v1.2.3 // indirect + github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/leodido/go-urn v1.2.4 // indirect github.com/magiconair/properties v1.8.7 // indirect - github.com/mattn/go-isatty v0.0.18 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/moby/spdystream v0.2.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.0.7 // indirect + github.com/pelletier/go-toml/v2 v2.0.9 // indirect github.com/spf13/afero v1.9.5 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect @@ -55,16 +57,16 @@ require ( github.com/subosito/gotenv v1.4.2 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect - golang.org/x/arch v0.3.0 // indirect - golang.org/x/crypto v0.8.0 // indirect - golang.org/x/net v0.9.0 // indirect + golang.org/x/arch v0.4.0 // indirect + golang.org/x/crypto v0.11.0 // indirect + golang.org/x/net v0.13.0 // indirect golang.org/x/oauth2 v0.7.0 // indirect - golang.org/x/sys v0.7.0 // indirect - golang.org/x/term v0.7.0 // indirect - golang.org/x/text v0.9.0 // indirect + golang.org/x/sys v0.10.0 // indirect + golang.org/x/term v0.10.0 // indirect + golang.org/x/text v0.11.0 // indirect golang.org/x/time v0.3.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.30.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/dbm-services/mysql/db-simulation/go.sum b/dbm-services/mysql/db-simulation/go.sum index 4ddbbdea91..3874d33706 100644 --- a/dbm-services/mysql/db-simulation/go.sum +++ b/dbm-services/mysql/db-simulation/go.sum @@ -52,12 +52,16 @@ github.com/antonmedv/expr v1.12.5 h1:Fq4okale9swwL3OeLLs9WD9H6GbgBLJyN/NUHRv+n0E github.com/antonmedv/expr v1.12.5/go.mod h1:FPC8iWArxls7axbVLsW+kpg1mz29A1b2M6jt+hZfDkU= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.8.8 h1:Kj4AYbZSeENfyXicsYppYKO0K2YWab+i2UTSY7Ukz9Q= -github.com/bytedance/sonic v1.8.8/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= +github.com/bytedance/sonic v1.10.0-rc3 h1:uNSnscRapXTwUgTyOF0GVljYD08p9X/Lbr9MweSV3V0= +github.com/bytedance/sonic v1.10.0-rc3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= +github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo= +github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -87,6 +91,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/gin-contrib/pprof v1.4.0 h1:XxiBSf5jWZ5i16lNOPbMTVdgHBdhfGRD5PZ1LWazzvg= github.com/gin-contrib/pprof v1.4.0/go.mod h1:RrehPJasUVBPK6yTUwOl8/NP6i0vbUgmxtis+Z5KE90= github.com/gin-contrib/requestid v0.0.6 h1:mGcxTnHQ45F6QU5HQRgQUDsAfHprD3P7g2uZ4cSZo9o= @@ -94,8 +100,8 @@ github.com/gin-contrib/requestid v0.0.6/go.mod h1:9i4vKATX/CdggbkY252dPVasgVucy/ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= -github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8= -github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k= +github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= +github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -116,8 +122,8 @@ github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= -github.com/go-playground/validator/v10 v10.12.0 h1:E4gtWgxWxp8YSxExrQFv5BpCahla0PVF2oTTEYaWQGI= -github.com/go-playground/validator/v10 v10.12.0/go.mod h1:hCAPuzYvKdP33pxWa+2+6AIKXEKqjIUyqsNCtbsSJrA= +github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k= +github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= @@ -222,8 +228,9 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= -github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= +github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -235,15 +242,15 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= -github.com/leodido/go-urn v1.2.3 h1:6BE2vPT0lqoz3fmOesHZiaiFh7889ssCo2GMvLCfiuA= -github.com/leodido/go-urn v1.2.3/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= -github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= @@ -268,8 +275,8 @@ github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGV github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= -github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us= -github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= +github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0= +github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -307,8 +314,9 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= @@ -328,8 +336,8 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= -golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc= +golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -340,8 +348,8 @@ golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWP golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= -golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -411,8 +419,8 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/net v0.13.0 h1:Nvo8UFsZ8X3BhAC9699Z1j7XQ3rsZnUUm7jfBEk1ueY= +golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -478,16 +486,16 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= -golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= +golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -496,8 +504,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -650,8 +658,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -704,6 +712,7 @@ k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2R k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk= k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= diff --git a/dbm-services/mysql/db-simulation/handler/handler.go b/dbm-services/mysql/db-simulation/handler/handler.go index ec32b47e50..58ddfb88c2 100644 --- a/dbm-services/mysql/db-simulation/handler/handler.go +++ b/dbm-services/mysql/db-simulation/handler/handler.go @@ -39,6 +39,7 @@ func CreateTmpSpiderPodCluster(r *gin.Context) { } ps := service.NewDbPodSets() ps.BaseInfo = &service.MySQLPodBaseInfo{ + PodName: param.PodName, RootPwd: param.Pwd, Charset: "utf8mb4", diff --git a/dbm-services/mysql/db-simulation/handler/syntax_check.go b/dbm-services/mysql/db-simulation/handler/syntax_check.go index b6c86ab0ec..eb2d00647a 100644 --- a/dbm-services/mysql/db-simulation/handler/syntax_check.go +++ b/dbm-services/mysql/db-simulation/handler/syntax_check.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package handler import ( @@ -138,3 +148,31 @@ func SyntaxCheckFile(r *gin.Context) { } SendResponse(r, nil, data, requestId) } + +// CreateAndUploadDDLTblListFile TODO +func CreateAndUploadDDLTblListFile(r *gin.Context) { + requestId := r.GetString("request_id") + var param CheckFileParam + // 将request中的数据按照json格式直接解析到结构体中 + if err := r.ShouldBindJSON(¶m); err != nil { + logger.Error("ShouldBind failed %s", err) + SendResponse(r, err, nil, requestId) + return + } + check := &syntax.TmysqlParseFile{ + TmysqlParse: syntax.TmysqlParse{ + TmysqlParseBinPath: tmysqlParserBin, + BaseWorkdir: workdir, + }, + Param: syntax.CheckSqlFileParam{ + BkRepoBasePath: param.Path, + FileNames: param.Files, + MysqlVersion: param.Version, + }, + } + if err := check.CreateAndUploadDDLTblFile(); err != nil { + SendResponse(r, err, nil, requestId) + return + } + SendResponse(r, nil, "ok", requestId) +} diff --git a/dbm-services/mysql/db-simulation/main.go b/dbm-services/mysql/db-simulation/main.go index bc52da0763..46dca75251 100644 --- a/dbm-services/mysql/db-simulation/main.go +++ b/dbm-services/mysql/db-simulation/main.go @@ -1,11 +1,21 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package main import ( "bytes" "io" - "io/ioutil" "net/http" "os" + "time" "dbm-services/common/go-pubpkg/logger" "dbm-services/mysql/db-simulation/app/config" @@ -46,9 +56,20 @@ func apiLogger(c *gin.Context) { var buf bytes.Buffer if c.Request.Method == http.MethodPost { tee := io.TeeReader(c.Request.Body, &buf) - body, _ := ioutil.ReadAll(tee) - c.Request.Body = ioutil.NopCloser(&buf) - model.CreateRequestRecord(rid, string(body)) + body, _ := io.ReadAll(tee) + c.Request.Body = io.NopCloser(&buf) + if len(body) <= 0 { + body = []byte("{}") + } + model.DB.Create(&model.TbRequestRecord{ + RequestID: rid, + Method: c.Request.Method, + Path: c.Request.URL.Path, + SourceIP: c.Request.RemoteAddr, + RequestBody: string(body), + CreateTime: time.Now(), + UpdateTime: time.Now(), + }) } c.Next() } diff --git a/dbm-services/mysql/db-simulation/model/tb_request_record.go b/dbm-services/mysql/db-simulation/model/tb_request_record.go index f623506643..4a4ee45850 100644 --- a/dbm-services/mysql/db-simulation/model/tb_request_record.go +++ b/dbm-services/mysql/db-simulation/model/tb_request_record.go @@ -1,33 +1,36 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package model import ( - "strings" "time" ) // TbRequestRecord TODO type TbRequestRecord struct { - ID int `gorm:"primaryKey;column:id;type:int(11);not null" json:"-"` - RequestID string `gorm:"unique;column:request_id;type:varchar(64);not null" json:"request_id"` // request_id - RequestBody string `gorm:"column:request_body;type:json" json:"request_body"` - UpdateTime time.Time `gorm:"column:update_time;type:timestamp;default:CURRENT_TIMESTAMP()" json:"update_time"` // 最后修改时间 - CreateTime time.Time `gorm:"column:create_time;type:timestamp;default:CURRENT_TIMESTAMP()" json:"create_time"` // 创建时间 + ID int `gorm:"primaryKey;column:id;type:int(11);not null" json:"-"` + RequestID string `gorm:"unique;column:request_id;type:varchar(64);not null" json:"request_id"` // request_id + RequestBody string `gorm:"column:request_body;type:json" json:"request_body"` + Method string `gorm:"column:method;type:varchar(16);not null" json:"method"` + // 请求的用户 + User string `gorm:"column:user;type:varchar(32);not null" json:"user"` + // 请求路径 + Path string `gorm:"column:path;type:varchar(32);not null" json:"path"` + // 请求来源Ip + SourceIP string `gorm:"column:source_ip;type:varchar(32);not null" json:"source_ip"` + UpdateTime time.Time `gorm:"column:update_time;type:timestamp;default:CURRENT_TIMESTAMP()" json:"update_time"` + CreateTime time.Time `gorm:"column:create_time;type:timestamp;default:CURRENT_TIMESTAMP()" json:"create_time"` } // GetTableName get sql table name.获取数据库名字 func (obj *TbRequestRecord) GetTableName() string { return "tb_request_record" } - -// CreateRequestRecord TODO -func CreateRequestRecord(requestid, body string) (err error) { - if strings.TrimSpace(body) == "" { - body = "{}" - } - return DB.Create(&TbRequestRecord{ - RequestID: requestid, - RequestBody: body, - UpdateTime: time.Now(), - CreateTime: time.Now(), - }).Error -} diff --git a/dbm-services/mysql/db-simulation/model/tb_syntax_rule.go b/dbm-services/mysql/db-simulation/model/tb_syntax_rule.go index e13c07af53..0f88322526 100644 --- a/dbm-services/mysql/db-simulation/model/tb_syntax_rule.go +++ b/dbm-services/mysql/db-simulation/model/tb_syntax_rule.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package model import ( @@ -22,15 +32,21 @@ const ( // TbSyntaxRule [...] type TbSyntaxRule struct { - ID int `gorm:"primaryKey;column:id;type:int(11);not null" json:"-"` - GroupName string `gorm:"uniqueIndex:group;column:group_name;type:varchar(64);not null" json:"group_name"` // 规则组名称 - RuleName string `gorm:"uniqueIndex:group;column:rule_name;type:varchar(64);not null" json:"rule_name"` // 子规则项,一个规则可能包括过个子规则 - Item json.RawMessage `gorm:"column:item;type:varchar(1024);not null" json:"item"` - ItemType string `gorm:"column:item_type;type:varchar(128);not null" json:"item_type"` - Expr string `gorm:"column:expr;type:varchar(128);not null" json:"expr"` // 规则表达式 - Desc string `gorm:"column:desc;type:varchar(512);not null" json:"desc"` // 规则提示信息 - WarnLevel int16 `gorm:"column:warn_level;type:smallint(2);not null" json:"warn_level"` // 0:作为普通检查项,1:禁用命中该规则的行为 - Status bool `gorm:"column:status;type:tinyint(1);not null" json:"status"` // 1:启用,0:禁用 + ID int `gorm:"primaryKey;column:id;type:int(11);not null" json:"-"` + // 规则组名称 + GroupName string `gorm:"uniqueIndex:group;column:group_name;type:varchar(64);not null" json:"group_name"` + // 子规则项,一个规则可能包括过个子规则 + RuleName string `gorm:"uniqueIndex:group;column:rule_name;type:varchar(64);not null" json:"rule_name"` + Item json.RawMessage `gorm:"column:item;type:varchar(1024);not null" json:"item"` + ItemType string `gorm:"column:item_type;type:varchar(128);not null" json:"item_type"` + // 规则表达式 + Expr string `gorm:"column:expr;type:varchar(128);not null" json:"expr"` + // 规则提示信息 + Desc string `gorm:"column:desc;type:varchar(512);not null" json:"desc"` + // 0:作为普通检查项,1:禁用命中该规则的行为 + WarnLevel int16 `gorm:"column:warn_level;type:smallint(2);not null" json:"warn_level"` + // 1:启用,0:禁用 + Status bool `gorm:"column:status;type:tinyint(1);not null" json:"status"` } // GetTableName get sql table name.获取数据库名字 @@ -54,7 +70,9 @@ func InitRule() (err error) { Expr: "Val in Item", ItemType: ArryItem, Item: []byte( - `["drop_table", "drop_index", "lock_tables", "drop_db", "analyze","rename_table", "drop_procedure", "drop_view", "drop_trigger","drop_function", "drop_server", "drop_event", "drop_compression_dictionary","optimize", "alter_tablespace"]`), + `["drop_table", "drop_index", "lock_tables", "drop_db", "analyze","rename_table", + "drop_procedure", "drop_view","drop_trigger","drop_function", "drop_server", + "drop_event", "drop_compression_dictionary","optimize", "alter_tablespace"]`), Desc: "高危命令", WarnLevel: 0, Status: true, @@ -64,8 +82,10 @@ func InitRule() (err error) { RuleName: "BanCommandRule", Expr: "Val in Item", ItemType: ArryItem, - Item: []byte( - ` ["truncate", "revoke", "kill", "reset", "drop_user", "grant","create_user", "revoke_all", "shutdown", "lock_tables_for_backup","reset", "purge", "lock_binlog_for_backup","lock_tables_for_backup","install_plugin", "uninstall_plugin","alter_user"]`), + Item: []byte(`["truncate", "revoke", "kill", "reset", "drop_user", "grant", + "create_user", "revoke_all", "shutdown", "lock_tables_for_backup", + "reset", "purge", "lock_binlog_for_backup","lock_tables_for_backup", + "install_plugin", "uninstall_plugin","alter_user"]`), Desc: "高危变更类型", WarnLevel: 1, Status: true, diff --git a/dbm-services/mysql/db-simulation/pkg/bkrepo/bkrepo.go b/dbm-services/mysql/db-simulation/pkg/bkrepo/bkrepo.go deleted file mode 100644 index b79b9d579d..0000000000 --- a/dbm-services/mysql/db-simulation/pkg/bkrepo/bkrepo.go +++ /dev/null @@ -1,174 +0,0 @@ -// Package bkrepo TODO -package bkrepo - -import ( - "encoding/json" - "fmt" - "io" - "log" - "net/http" - "net/url" - "os" - "path" - "path/filepath" - "strings" - - util "dbm-services/common/go-pubpkg/cmutil" - "dbm-services/common/go-pubpkg/logger" -) - -/* - API: GET /generic/{project}/{repo}/{path}?download=true - API 名称: download - 功能说明: - - 中文:下载通用制品文件 - English:download generic file - 请求体 此接口请求体为空 -*/ - -// BkRepoClient TODO -type BkRepoClient struct { - Client *http.Client - BkRepoProject string - BkRepoPubBucket string - BkRepoEndpoint string - BkRepoUser string - BkRepoPwd string -} - -// BkRepoRespone TODO -type BkRepoRespone struct { - Code int `json:"code"` - Message string `json:"message"` - Data json.RawMessage `json:"data"` - TraceId string `json:"traceId"` -} - -// getBaseUrl TODO -// -// @receiver b -func (b *BkRepoClient) getBaseUrl() string { - u, err := url.Parse(b.BkRepoEndpoint) - if err != nil { - log.Fatal(err) - } - r, err := url.Parse(path.Join(u.Path, "generic", b.BkRepoProject, b.BkRepoPubBucket)) - if err != nil { - log.Fatal(err) - } - uri := u.ResolveReference(r).String() - logger.Info("uri:%s", uri) - return uri -} - -// Download 从制品库下载文件 -// -// @receiver b -func (b *BkRepoClient) Download(sqlpath, filename, downloaddir string) (err error) { - uri := b.getBaseUrl() + path.Join("/", sqlpath, filename) + "?download=true" - logger.Info("The download url is %s", uri) - req, err := http.NewRequest(http.MethodGet, uri, nil) - if err != nil { - return err - } - if strings.Contains(filename, "..") { - return fmt.Errorf("%s there is a risk of path crossing", filename) - } - fileAbPath, err := filepath.Abs(path.Join(downloaddir, filename)) - if err != nil { - return err - } - f, err := os.Create(fileAbPath) - if err != nil { - return err - } - req.SetBasicAuth(b.BkRepoUser, b.BkRepoPwd) - resp, err := b.Client.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - logger.Info("respone code %d", resp.StatusCode) - if resp.StatusCode != http.StatusOK { - bs, err := io.ReadAll(resp.Body) - if err != nil { - return err - } - return fmt.Errorf("respone code is %d,respone body is :%s", resp.StatusCode, string(bs)) - } - size, err := io.Copy(f, resp.Body) - if err != nil { - return err - } - logger.GetLogger().Info(fmt.Sprintf("Downloaded a file %s with size %d", filename, size)) - fileNodeInfo, err := b.QueryFileNodeInfo(sqlpath, filename) - if err != nil { - return err - } - logger.Info("node detail %v", fileNodeInfo) - if size != int64(fileNodeInfo.Size) { - bs, _ := os.ReadFile(fileAbPath) - return fmt.Errorf("body:%s,current file:%s source file size is inconsistent,current file is:%d,bkrepo file is:%d", - string(bs), filename, size, - fileNodeInfo.Size) - } - - currentFileMd5, err := util.GetFileMd5(fileAbPath) - if err != nil { - return err - } - if currentFileMd5 != fileNodeInfo.Md5 { - return fmt.Errorf("current file:%s source file md5 is inconsistent,current file is:%s,bkrepo file is:%s", filename, - currentFileMd5, - fileNodeInfo.Md5) - } - return nil -} - -// FileNodeInfo TODO -type FileNodeInfo struct { - Name string `json:"name"` - Sha256 string `json:"sha256"` - Md5 string `json:"md5"` - Size int `json:"size"` - Metadata map[string]string `json:"metadata"` -} - -// QueryFileNodeInfo TODO -// QueryMetaData 查询文件元数据信息 -// -// @receiver b -func (b *BkRepoClient) QueryFileNodeInfo(filepath, filename string) (realData FileNodeInfo, err error) { - var baseResp BkRepoRespone - u, err := url.Parse(b.BkRepoEndpoint) - if err != nil { - return - } - r, err := url.Parse(path.Join("repository/api/node/detail/", b.BkRepoProject, b.BkRepoPubBucket, filepath, filename)) - if err != nil { - logger.Error(err.Error()) - return - } - uri := u.ResolveReference(r).String() - logger.Info("query node detail url %s", uri) - req, err := http.NewRequest(http.MethodGet, uri, nil) - if err != nil { - return FileNodeInfo{}, err - } - resp, err := b.Client.Do(req) - if err != nil { - return FileNodeInfo{}, err - } - defer resp.Body.Close() - if err = json.NewDecoder(resp.Body).Decode(&baseResp); err != nil { - return FileNodeInfo{}, err - } - if baseResp.Code != 0 { - return FileNodeInfo{}, fmt.Errorf("bkrepo Return Code: %d,Messgae:%s", baseResp.Code, baseResp.Message) - } - if err = json.Unmarshal([]byte(baseResp.Data), &realData); err != nil { - return FileNodeInfo{}, err - } - return -} diff --git a/dbm-services/mysql/db-simulation/pkg/bkrepo/bkrepo_test.go b/dbm-services/mysql/db-simulation/pkg/bkrepo/bkrepo_test.go deleted file mode 100644 index 7e29fa36fd..0000000000 --- a/dbm-services/mysql/db-simulation/pkg/bkrepo/bkrepo_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package bkrepo_test - -import ( - "net/http" - "testing" - - "dbm-services/mysql/db-simulation/pkg/bkrepo" -) - -func TestDownload(t *testing.T) { - t.Log("start ...") - b := &bkrepo.BkRepoClient{ - Client: &http.Client{ - Transport: &http.Transport{}, - }, - BkRepoProject: "", - BkRepoPubBucket: "", - BkRepoUser: "", - BkRepoPwd: "", - BkRepoEndpoint: "", - } - err := b.Download("/dbbackup/latest", "dbbackup_2.2.48.tar.gz", "/data/") - if err != nil { - t.Fatalf(err.Error()) - } - t.Log("ending ...") -} - -func TestQueryMeta(t *testing.T) { - t.Log("start ...") - b := &bkrepo.BkRepoClient{ - Client: &http.Client{ - Transport: &http.Transport{}, - }, - BkRepoProject: "", - BkRepoPubBucket: "", - BkRepoUser: "", - BkRepoPwd: "", - BkRepoEndpoint: "", - } - d, err := b.QueryFileNodeInfo("/dbbackup/latest", "dbbackup_2.2.48.tar.gz") - if err != nil { - t.Fatalf(err.Error()) - } - t.Log("ending ...", d) -} diff --git a/dbm-services/mysql/db-simulation/pkg/util/spider.go b/dbm-services/mysql/db-simulation/pkg/util/spider.go index 14eb82104a..4def7af407 100644 --- a/dbm-services/mysql/db-simulation/pkg/util/spider.go +++ b/dbm-services/mysql/db-simulation/pkg/util/spider.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package util import ( @@ -97,7 +107,6 @@ func ParseGetSpiderUserComment(tableComment string) (ret int) { if bs[pos] == 0x20 || pos >= len(bs)-1 { break } - } kw := string(keywordBuf) if !validateCommentKeyWord(kw) { @@ -139,7 +148,6 @@ func ParseGetSpiderUserComment(tableComment string) (ret int) { default: continue } - } parseAllDone: return ret diff --git a/dbm-services/mysql/db-simulation/router/router.go b/dbm-services/mysql/db-simulation/router/router.go index 9a26a355d9..b8fa0e9063 100644 --- a/dbm-services/mysql/db-simulation/router/router.go +++ b/dbm-services/mysql/db-simulation/router/router.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + // Package router TODO package router @@ -20,6 +30,7 @@ func RegisterRouter(engine *gin.Engine) { s := engine.Group("/syntax") s.POST("/check/file", handler.SyntaxCheckFile) s.POST("/check/sql", handler.SyntaxCheckSQL) + s.POST("/upload/ddl/tbls", handler.CreateAndUploadDDLTblListFile) // rule r := engine.Group("/rule") r.POST("/manage", handler.ManageRule) @@ -29,7 +40,6 @@ func RegisterRouter(engine *gin.Engine) { sp := engine.Group("/spider") sp.POST("/simulation", handler.SpiderClusterSimulation) sp.POST("/create", handler.CreateTmpSpiderPodCluster) - } // TurnOnDebug TODO diff --git a/dbm-services/mysql/db-simulation/rule.yaml b/dbm-services/mysql/db-simulation/rule.yaml index 3e16a2f6ee..27a04b36a6 100644 --- a/dbm-services/mysql/db-simulation/rule.yaml +++ b/dbm-services/mysql/db-simulation/rule.yaml @@ -1,3 +1,17 @@ +BuiltInRule: + # 表名规范检查 + TableNameSpecification: + keyword: true + speicalChar: true + # 库名规范检查 + ShemaNamespecification: + # 关键字检查 + keyword: true + # 特殊字符检查 + speicalChar: true + # 不允许包含系统库检查 + sysDbName: true + CommandRule: HighRiskCommandRule: expr: ' Val in Item ' diff --git a/dbm-services/mysql/db-tools/dbactuator/.gitignore b/dbm-services/mysql/db-tools/dbactuator/.gitignore index 1a34a6ea74..3810e2c293 100644 --- a/dbm-services/mysql/db-tools/dbactuator/.gitignore +++ b/dbm-services/mysql/db-tools/dbactuator/.gitignore @@ -24,4 +24,8 @@ conf/ sync_test.sh .vscode/ scripts/upload_media.sh -scripts/upload.sh \ No newline at end of file +scripts/upload.sh +localtest/ +.codecc +.idea +.vscode \ No newline at end of file diff --git a/dbm-services/mysql/db-tools/dbactuator/.golangci.yml b/dbm-services/mysql/db-tools/dbactuator/.golangci.yml index 74b121ed6f..b86858cf6b 100644 --- a/dbm-services/mysql/db-tools/dbactuator/.golangci.yml +++ b/dbm-services/mysql/db-tools/dbactuator/.golangci.yml @@ -38,7 +38,6 @@ linters: - gofmt - gocritic - gocyclo - - whitespace - sqlclosecheck - deadcode - govet diff --git a/dbm-services/mysql/db-tools/dbactuator/Makefile b/dbm-services/mysql/db-tools/dbactuator/Makefile index dcca994de4..6410406eb9 100644 --- a/dbm-services/mysql/db-tools/dbactuator/Makefile +++ b/dbm-services/mysql/db-tools/dbactuator/Makefile @@ -10,7 +10,7 @@ BUILD_MINI_FLAG = " -s -w -X main.version=${VERSION} -X main.buildstamp=`date -u .PHONY: all build clean build: - cd ${BASE_DIR}/cmd && CGO_ENABLED=0 GOOS=${GOOS} GOARCH=amd64 go build -gcflags=-trimpath=${PWD} -asmflags=-trimpath=${PWD} -ldflags ${BUILD_FLAG} -o $(BASE_DIR)/build/$(APPNAME) -v . + cd ${BASE_DIR}/cmd && CGO_ENABLED=0 GOOS=${GOOS} GOARCH=amd64 go build -gcflags="all=-trimpath=${PWD}" -asmflags="all=-trimpath=${PWD}" -ldflags ${BUILD_FLAG} -o $(BASE_DIR)/build/$(APPNAME) -v . external : cd ${BASE_DIR}/cmd && CGO_ENABLED=0 GOOS=${GOOS} GOARCH=amd64 go build -gcflags=-trimpath=${PWD} -asmflags=-trimpath=${PWD} -ldflags ${BUILD_EXTERNAL_FLAG} -o $(BASE_DIR)/build/$(APPNAME) -v . diff --git a/dbm-services/mysql/db-tools/dbactuator/cmd/cmd.go b/dbm-services/mysql/db-tools/dbactuator/cmd/cmd.go index d5706e17ca..7c65cddf12 100644 --- a/dbm-services/mysql/db-tools/dbactuator/cmd/cmd.go +++ b/dbm-services/mysql/db-tools/dbactuator/cmd/cmd.go @@ -21,6 +21,7 @@ import ( "dbm-services/mysql/db-tools/dbactuator/internal/subcmd/spidercmd" "dbm-services/mysql/db-tools/dbactuator/internal/subcmd/spiderctlcmd" "dbm-services/mysql/db-tools/dbactuator/internal/subcmd/sysinitcmd" + "dbm-services/mysql/db-tools/dbactuator/internal/subcmd/tbinlogdumpercmd" "dbm-services/mysql/db-tools/dbactuator/pkg/core/cst" "dbm-services/mysql/db-tools/dbactuator/pkg/util/templates" @@ -144,6 +145,12 @@ Buildstamp:%s`, version, githash, strings.ToUpper(external), buildstamp, spiderctlcmd.NewSpiderCtlCommand(), }, }, + { + Message: "tbinlogdumper operation sets", + Commands: []*cobra.Command{ + tbinlogdumpercmd.NewTbinlogDumperCommand(), + }, + }, } groups.Add(cmds) // 标志可以是 "persistent" 的,这意味着该标志将可用于分配给它的命令以及该命令下的每个命令。对于全局标志,将标志分配为根上的持久标志。 diff --git a/dbm-services/mysql/db-tools/dbactuator/go.mod b/dbm-services/mysql/db-tools/dbactuator/go.mod index 6dec465d9e..e609e63e41 100644 --- a/dbm-services/mysql/db-tools/dbactuator/go.mod +++ b/dbm-services/mysql/db-tools/dbactuator/go.mod @@ -3,6 +3,8 @@ module dbm-services/mysql/db-tools/dbactuator go 1.19 require ( + ariga.io/atlas v0.13.1 + github.com/BurntSushi/toml v1.3.2 github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 github.com/dustin/go-humanize v1.0.1 github.com/go-sql-driver/mysql v1.7.1 @@ -11,13 +13,19 @@ require ( github.com/pkg/errors v0.9.1 github.com/shirou/gopsutil/v3 v3.23.2 github.com/spf13/cobra v1.7.0 + golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb gopkg.in/ini.v1 v1.67.0 ) require ( + github.com/agext/levenshtein v1.2.1 // indirect + github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect github.com/fatih/color v1.13.0 // indirect github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-openapi/inflect v0.19.0 // indirect + github.com/google/go-cmp v0.5.9 // indirect github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 // indirect + github.com/hashicorp/hcl/v2 v2.10.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jtolds/gls v4.20.0+incompatible // indirect github.com/kr/fs v0.1.0 // indirect @@ -27,13 +35,17 @@ require ( github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-isatty v0.0.18 // indirect github.com/mattn/go-sqlite3 v1.14.16 // indirect + github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect github.com/smartystreets/assertions v1.2.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/tklauser/go-sysconf v0.3.11 // indirect github.com/tklauser/numcpus v0.6.0 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect + github.com/zclconf/go-cty v1.8.0 // indirect + golang.org/x/mod v0.11.0 // indirect golang.org/x/sys v0.8.0 // indirect + golang.org/x/text v0.9.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect ) diff --git a/dbm-services/mysql/db-tools/dbactuator/go.sum b/dbm-services/mysql/db-tools/dbactuator/go.sum index 37021fa587..c3c9377a71 100644 --- a/dbm-services/mysql/db-tools/dbactuator/go.sum +++ b/dbm-services/mysql/db-tools/dbactuator/go.sum @@ -1,7 +1,18 @@ +ariga.io/atlas v0.13.1 h1:oSkEYgI3qUnQZ6b6+teAEiIuizjBvkZ4YDbz0XWfCdQ= +ariga.io/atlas v0.13.1/go.mod h1:+TR129FJZ5Lvzms6dvCeGWh1yR6hMvmXBhug4hrNIGk= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 h1:ZBbLwSJqkHBuFDA6DUhhse0IGJ7T5bemHyNILUjvOq4= github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2/go.mod h1:VSw57q4QFiWDbRnjdX8Cb3Ow0SFncRw+bA/ofY6Q83w= +github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8= +github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= +github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM= +github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= +github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= +github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/caarlos0/env/v6 v6.10.1 h1:t1mPSxNpei6M5yAeu1qtRdPAK29Nbcf/n3G7x+b3/II= github.com/caarlos0/env/v6 v6.10.1/go.mod h1:hvp/ryKXKipEkcuYjs9mI4bBCg+UI0Yhgm5Zu0ddvwc= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= @@ -20,13 +31,21 @@ github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4= +github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= +github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs= github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -34,6 +53,8 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGa github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/hcl/v2 v2.10.0 h1:1S1UnuhDGlv3gRFV4+0EdwB+znNP5HmcGbIqwnSCByg= +github.com/hashicorp/hcl/v2 v2.10.0/go.mod h1:FwWsfWEjyV/CMj8s/gqAuiviY72rJ1/oayI9WftqcKg= github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f h1:7LYC+Yfkj3CTRcShK0KOL/w6iTiKyqqBA9a41Wnggw8= github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f/go.mod h1:pFlLw2CfqZiIBOx6BuCeRLCrfxBJipTY0nIOF/VbGcI= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -46,6 +67,7 @@ github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -53,6 +75,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4= +github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.0 h1:Zx5DJFEYQXio93kgXnQ09fXNiUKsqv4OUEu2UtGcB1E= github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= @@ -71,6 +95,8 @@ github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwp github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= +github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM= +github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/panjf2000/ants/v2 v2.7.2 h1:2NUt9BaZFO5kQzrieOmK/wdb/tQ/K+QHaxN8sOgD63U= @@ -88,6 +114,8 @@ github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b/go.mod h1:Om github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shirou/gopsutil/v3 v3.23.2 h1:PAWSuiAszn7IhPMBtXsbSCafej7PqUOvY6YywlQUExU= github.com/shirou/gopsutil/v3 v3.23.2/go.mod h1:gv0aQw33GLo3pG8SiWKiQrbDzbRY1K80RyZJ7V4Th1M= github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= @@ -98,11 +126,13 @@ github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -113,19 +143,38 @@ github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+Kd github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= +github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= +github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= +github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= +github.com/zclconf/go-cty v1.8.0 h1:s4AvqaeQzJIu3ndv4gVIhplVD0krU+bgrcLSVUnaWuA= +github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= +github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb h1:xIApU0ow1zwMa2uL1VDNeQlNVFTWMQxZUZCMDy0Q4Us= +golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= +golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -144,11 +193,18 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= diff --git a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/backup_demand.go b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/backup_demand.go index 38ebea33e2..8ad5f7c87a 100644 --- a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/backup_demand.go +++ b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/backup_demand.go @@ -11,19 +11,21 @@ package mysqlcmd import ( "fmt" - "github.com/spf13/cobra" - "dbm-services/bigdata/db-tools/dbactuator/pkg/util" "dbm-services/common/go-pubpkg/logger" "dbm-services/mysql/db-tools/dbactuator/internal/subcmd" "dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/backupdemand" + + "github.com/spf13/cobra" ) +// BackupDemandAct TODO type BackupDemandAct struct { *subcmd.BaseOptions Payload backupdemand.Component } +// NewBackupDemandCommand create new subcommand func NewBackupDemandCommand() *cobra.Command { act := BackupDemandAct{BaseOptions: subcmd.GBaseOptions} cmd := &cobra.Command{ @@ -41,6 +43,7 @@ func NewBackupDemandCommand() *cobra.Command { return cmd } +// Init prepare run env func (d *BackupDemandAct) Init() (err error) { if err = d.BaseOptions.Validate(); err != nil { // @todo 应该在一开始就validate return err @@ -54,10 +57,12 @@ func (d *BackupDemandAct) Init() (err error) { return } +// Validate run selfdefine validate function func (d *BackupDemandAct) Validate() error { return nil } +// Run start run command func (d *BackupDemandAct) Run() (err error) { defer util.LoggerErrorStack(logger.Error, err) steps := subcmd.Steps{ diff --git a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/cmd.go b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/cmd.go deleted file mode 100644 index cefa08d6a3..0000000000 --- a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/cmd.go +++ /dev/null @@ -1,68 +0,0 @@ -package mysqlcmd - -import ( - "dbm-services/mysql/db-tools/dbactuator/internal/subcmd" - "dbm-services/mysql/db-tools/dbactuator/pkg/util/templates" - - "github.com/spf13/cobra" -) - -// NewMysqlCommand mysql子命令 -func NewMysqlCommand() *cobra.Command { - cmds := &cobra.Command{ - Use: "mysql [mysql operation]", - Short: "MySQL Operation Command Line Interface", - RunE: subcmd.ValidateSubCommand(), - } - groups := templates.CommandGroups{ - { - Message: "mysql operation sets", - Commands: []*cobra.Command{ - NewDeployMySQLInstanceCommand(), - NewStartMysqlCommand(), - NewUnInstallMysqlCommand(), - NewGrantReplCommand(), - NewExecSQLFileCommand(), - CloneClientGrantCommand(), - NewBackupTruncateDatabaseCommand(), - //NewBackupDatabaseTableCommand(), - MycnfChangeCommand(), - FindLocalBackupCommand(), - MycnfCloneCommand(), - NewCutOverToSlaveCommnad(), - CleanMysqlCommand(), - PtTableSyncCommand(), - ParseBinlogTimeCommand(), - FlashbackBinlogCommand(), - NewPtTableChecksumCommand(), - NewInstallMySQLChecksumCommand(), - NewInstallNewDbBackupCommand(), - //NewFullBackupCommand(), - NewInstallRotateBinlogCommand(), - NewInstallDBAToolkitCommand(), - NewDeployMySQLCrondCommand(), - ClearInstanceConfigCommand(), - NewInstallMySQLMonitorCommand(), - NewExecPartitionSQLCommand(), - NewBackupDemandCommand(), - }, - }, - { - Message: "mysql semantic check operation sets", - Commands: []*cobra.Command{ - NewSenmanticCheckCommand(), - NewSenmanticDumpSchemaCommand(), - }, - }, - { - Message: "mysql slave operation sets", - Commands: []*cobra.Command{ - NewBuildMsRelatioCommand(), - RestoreDRCommand(), - RecoverBinlogCommand(), - }, - }, - } - groups.Add(cmds) - return cmds -} diff --git a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/drop_table.go b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/drop_table.go new file mode 100644 index 0000000000..b3212f42e4 --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/drop_table.go @@ -0,0 +1,100 @@ +// TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +// Copyright (C) 2017-2023 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 https://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. + +package mysqlcmd + +import ( + "dbm-services/common/go-pubpkg/logger" + "dbm-services/mysql/db-tools/dbactuator/internal/subcmd" + "dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql" + "dbm-services/mysql/db-tools/dbactuator/pkg/util" + "fmt" + + "github.com/spf13/cobra" +) + +type DropTableAct struct { + *subcmd.BaseOptions + Service mysql.DropTableComp +} + +const ( + DropTable = "drop-table" +) + +func NewDropTableCommand() *cobra.Command { + act := DropTableAct{ + BaseOptions: subcmd.GBaseOptions, + } + + cmd := &cobra.Command{ + Use: DropTable, + Short: "删除表", + Example: fmt.Sprintf( + `dbactuator mysql %s %s %s`, + DropTable, subcmd.CmdBaseExampleStr, subcmd.ToPrettyJson(act.Service.Example())), + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(act.Validate()) + util.CheckErr(act.Init()) + util.CheckErr(act.Run()) + }, + } + return cmd +} + +func (c *DropTableAct) Validate() (err error) { + return c.BaseOptions.Validate() +} + +func (c *DropTableAct) Init() (err error) { + if err = c.Deserialize(&c.Service.Params); err != nil { + logger.Error("DeserializeAndValidate err %s", err.Error()) + return err + } + c.Service.GeneralParam = subcmd.GeneralRuntimeParam + logger.Info("extend params: %s", c.Service.Params) + return nil +} + +func (c *DropTableAct) Run() (err error) { + steps := subcmd.Steps{ + { + FunName: "初始化", + Func: c.Service.Init, + }, + { + FunName: "执行前检查", + Func: c.Service.PreCheck, + }, + { + FunName: "查找数据文件", + Func: c.Service.FindDatafiles, + }, + { + FunName: "建立硬连接", + Func: c.Service.MakeHardlink, + }, + { + FunName: "删除表", + Func: c.Service.DropTable, + }, + { + FunName: "查找遗留硬连接", + Func: c.Service.FindLegacyHardlink, + }, + { + FunName: "删除硬连接", + Func: c.Service.DeleteHardlink, + }, + } + if err := steps.Run(); err != nil { + return err + } + logger.Info("删表完成") + return nil +} diff --git a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/enable_tokudb.go b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/enable_tokudb.go new file mode 100644 index 0000000000..1b7dbe441f --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/enable_tokudb.go @@ -0,0 +1,82 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package mysqlcmd + +import ( + "fmt" + + "github.com/spf13/cobra" + + "dbm-services/bigdata/db-tools/dbactuator/pkg/util" + "dbm-services/common/go-pubpkg/logger" + "dbm-services/mysql/db-tools/dbactuator/internal/subcmd" + "dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql" +) + +// EnableTokudbPluginAct TODO +type EnableTokudbPluginAct struct { + *subcmd.BaseOptions + Service mysql.EnableTokudbEngineComp +} + +// NewEnableTokudbPluginCommand TODO +func NewEnableTokudbPluginCommand() *cobra.Command { + act := &EnableTokudbPluginAct{} + cmd := &cobra.Command{ + Use: "enable-tokudb-engine", + Short: "安装tokudb插件", + Example: fmt.Sprintf(`dbactuator mysql enable-tokudb-engine %s %s`, + subcmd.CmdBaseExampleStr, subcmd.ToPrettyJson(act.Service.Example()), + ), + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(act.Init()) + util.CheckErr(act.Run()) + }, + } + return cmd +} + +// Init TODO +func (e *EnableTokudbPluginAct) Init() (err error) { + if e.BaseOptions, err = subcmd.Deserialize(&e.Service.Params); err != nil { + logger.Error("DeserializeAndValidate failed, %v", err) + return err + } + e.Service.GeneralParam = subcmd.GeneralRuntimeParam + return nil +} + +// Run TODO +func (e *EnableTokudbPluginAct) Run() (err error) { + steps := subcmd.Steps{ + { + FunName: "前置初始化", + Func: e.Service.Init, + }, + { + FunName: "写入tokudb配置到my.cnf", + Func: e.Service.ReWriteMyCnf, + }, + { + FunName: "instal tokudb plugin", + Func: e.Service.Install, + }, + { + FunName: "close db conn", + Func: e.Service.CloseConn, + }, + } + if err = steps.Run(); err != nil { + return err + } + logger.Info("enable-tokudb-engine success") + return nil +} diff --git a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/install_backup_client.go b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/install_backup_client.go new file mode 100644 index 0000000000..16908fc337 --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/install_backup_client.go @@ -0,0 +1,98 @@ +package mysqlcmd + +import ( + "fmt" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/mysql/db-tools/dbactuator/internal/subcmd" + "dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql" + "dbm-services/mysql/db-tools/dbactuator/pkg/util" + + "github.com/spf13/cobra" +) + +// InstallBackupClientAct TODO +type InstallBackupClientAct struct { + *subcmd.BaseOptions + Service mysql.InstallBackupClientComp +} + +// CommandInstallBackupClient TODO +const CommandInstallBackupClient = "install-backup-client" + +// InstallBackupClientCommand TODO +func InstallBackupClientCommand() *cobra.Command { + act := InstallBackupClientAct{ + BaseOptions: subcmd.GBaseOptions, + } + cmd := &cobra.Command{ + Use: CommandInstallBackupClient, + Short: "部署 backup_client", + Example: fmt.Sprintf( + `dbactuator mysql %s %s %s`, CommandInstallBackupClient, + subcmd.CmdBaseExampleStr, + subcmd.ToPrettyJson(act.Service.Example()), + ), + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(act.Validate()) + if act.RollBack { + util.CheckErr(act.Rollback()) + return + } + util.CheckErr(act.Init()) + util.CheckErr(act.Run()) + }, + } + return cmd +} + +// Init TODO +func (d *InstallBackupClientAct) Init() (err error) { + if err = d.Deserialize(&d.Service.Params); err != nil { + logger.Error("DeserializeAndValidate err %s", err.Error()) + return err + } + d.Service.GeneralParam = subcmd.GeneralRuntimeParam + return nil +} + +// Run TODO +func (d *InstallBackupClientAct) Run() (err error) { + steps := subcmd.Steps{ + { + FunName: "init", + Func: d.Service.Init, + }, + { + FunName: "预检查", + Func: d.Service.PreCheck, + }, + { + FunName: "部署二进制", + Func: d.Service.DeployBinary, + }, + { + FunName: "渲染 config.toml", + Func: d.Service.GenerateBinaryConfig, + }, + { + FunName: "生成 cosinfo.toml", + Func: d.Service.GenerateBucketConfig, + }, + { + FunName: "添加 upload crontab", + Func: d.Service.InstallCrontab, + }, + } + + if err := steps.Run(); err != nil { + return err + } + logger.Info("install backup_client successfully~") + return nil +} + +// Rollback TODO +func (d *InstallBackupClientAct) Rollback() (err error) { + return +} diff --git a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/mysqlcmd.go b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/mysqlcmd.go index fbc1bb8119..b490d46791 100644 --- a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/mysqlcmd.go +++ b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/mysqlcmd.go @@ -1,2 +1,86 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + // Package mysqlcmd TODO package mysqlcmd + +import ( + "dbm-services/mysql/db-tools/dbactuator/internal/subcmd" + "dbm-services/mysql/db-tools/dbactuator/pkg/util/templates" + + "github.com/spf13/cobra" +) + +// NewMysqlCommand mysql子命令 +func NewMysqlCommand() *cobra.Command { + cmds := &cobra.Command{ + Use: "mysql [mysql operation]", + Short: "MySQL Operation Command Line Interface", + RunE: subcmd.ValidateSubCommand(), + } + groups := templates.CommandGroups{ + { + Message: "mysql operation sets", + Commands: []*cobra.Command{ + NewDeployMySQLInstanceCommand(), + NewUnInstallMysqlCommand(), + NewGrantReplCommand(), + NewExecSQLFileCommand(), + CloneClientGrantCommand(), + NewBackupTruncateDatabaseCommand(), + // NewBackupDatabaseTableCommand(), + MycnfChangeCommand(), + FindLocalBackupCommand(), + MycnfCloneCommand(), + NewCutOverToSlaveCommnad(), + CleanMysqlCommand(), + PtTableSyncCommand(), + ParseBinlogTimeCommand(), + FlashbackBinlogCommand(), + NewPtTableChecksumCommand(), + NewInstallMySQLChecksumCommand(), + NewInstallNewDbBackupCommand(), + // NewFullBackupCommand(), + NewInstallRotateBinlogCommand(), + NewInstallDBAToolkitCommand(), + NewDeployMySQLCrondCommand(), + ClearInstanceConfigCommand(), + NewInstallMySQLMonitorCommand(), + NewExecPartitionSQLCommand(), + NewBackupDemandCommand(), + NewDropTableCommand(), + InstallBackupClientCommand(), + NewEnableTokudbPluginCommand(), + NewOpenAreaDumpSchemaCommand(), + NewOpenAreaImportSchemaCommand(), + NewOpenAreaDumpData(), + NewOpenAreaImportData(), + OSCmdRunCommand(), + OSInfoGetCommand(), + }, + }, + { + Message: "mysql semantic check operation sets", + Commands: []*cobra.Command{ + NewSenmanticDumpSchemaCommand(), + }, + }, + { + Message: "mysql slave operation sets", + Commands: []*cobra.Command{ + NewBuildMsRelatioCommand(), + RestoreDRCommand(), + RecoverBinlogCommand(), + }, + }, + } + groups.Add(cmds) + return cmds +} diff --git a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/open_area_dump_data.go b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/open_area_dump_data.go new file mode 100644 index 0000000000..ae6688b208 --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/open_area_dump_data.go @@ -0,0 +1,96 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package mysqlcmd + +import ( + "fmt" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/mysql/db-tools/dbactuator/internal/subcmd" + "dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql" + "dbm-services/mysql/db-tools/dbactuator/pkg/util" + + "github.com/spf13/cobra" +) + +// OpenAreaDumpDataAct TODO +type OpenAreaDumpDataAct struct { + *subcmd.BaseOptions + Service mysql.OpenAreaDumpSchemaComp +} + +// NewOpenAreaDumpData TODO +func NewOpenAreaDumpData() *cobra.Command { + act := OpenAreaDumpDataAct{ + BaseOptions: subcmd.GBaseOptions, + } + cmd := &cobra.Command{ + Use: "open_area_dumpdata", + Short: "开区导出数据", + Example: fmt.Sprintf( + `dbactuator mysql open_area_dumpdata %s %s`, + subcmd.CmdBaseExampleStr, subcmd.ToPrettyJson(act.Service.Example()), + ), + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(act.Validate()) + util.CheckErr(act.Init()) + util.CheckErr(act.Run()) + }, + } + return cmd +} + +// Validate TODO +func (d *OpenAreaDumpDataAct) Validate() (err error) { + return d.BaseOptions.Validate() +} + +// Init TODO +func (d *OpenAreaDumpDataAct) Init() (err error) { + if err = d.Deserialize(&d.Service.Params); err != nil { + logger.Error("DeserializeAndValidate failed, %v", err) + return err + } + // d.Deserialize方法执行后,并直接返回值, + d.Service.GeneralParam = subcmd.GeneralRuntimeParam + return nil +} + +// Run TODO +func (d *OpenAreaDumpDataAct) Run() (err error) { + steps := subcmd.Steps{ + { + FunName: "init", + Func: d.Service.Init, + }, + { + FunName: "precheck", + Func: d.Service.Precheck, + }, + { + FunName: "运行导出指定表数据", + Func: d.Service.OpenAreaDumpData, + }, + { + FunName: "压缩开区文件", + Func: d.Service.CompressDumpDir, + }, + { + FunName: "上传指定表数据文件", + Func: d.Service.Upload, + }, + } + if err := steps.Run(); err != nil { + return err + } + logger.Info("开区导出表数据成功") + return nil +} diff --git a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/open_area_dump_schema.go b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/open_area_dump_schema.go new file mode 100644 index 0000000000..327cf53c89 --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/open_area_dump_schema.go @@ -0,0 +1,98 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package mysqlcmd + +import ( + "fmt" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/mysql/db-tools/dbactuator/internal/subcmd" + "dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql" + "dbm-services/mysql/db-tools/dbactuator/pkg/util" + + "github.com/spf13/cobra" +) + +// OpenAreaDumpSchemaAct TODO +type OpenAreaDumpSchemaAct struct { + *subcmd.BaseOptions + Service mysql.OpenAreaDumpSchemaComp +} + +// NewOpenAreaDumpSchemaCommand TODO +func NewOpenAreaDumpSchemaCommand() *cobra.Command { + // *subcmd.BaseOptions是指针变量,需要初始化, subcmd.GBaseOptions在subcmd的init中已被初始化 + act := OpenAreaDumpSchemaAct{ + BaseOptions: subcmd.GBaseOptions, + } + + cmd := &cobra.Command{ + Use: "open_area_dumpschema", + Short: "开区导出表结构", + Example: fmt.Sprintf( + `dbactuator mysql open_area_dumpschema %s %s`, + subcmd.CmdBaseExampleStr, subcmd.ToPrettyJson(act.Service.Example()), + ), + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(act.Validate()) + util.CheckErr(act.Init()) + util.CheckErr(act.Run()) + }, + } + return cmd +} + +// Validate TODO +func (d *OpenAreaDumpSchemaAct) Validate() (err error) { + return d.BaseOptions.Validate() +} + +// Init TODO +func (d *OpenAreaDumpSchemaAct) Init() (err error) { + if err = d.Deserialize(&d.Service.Params); err != nil { + logger.Error("DeserializeAndValidate failed, %v", err) + return err + } + // d.Deserialize方法执行后,并直接返回值, + d.Service.GeneralParam = subcmd.GeneralRuntimeParam + return nil +} + +// Run TODO +func (d *OpenAreaDumpSchemaAct) Run() (err error) { + steps := subcmd.Steps{ + { + FunName: "init", + Func: d.Service.Init, + }, + { + FunName: "precheck", + Func: d.Service.Precheck, + }, + { + FunName: "运行导出表结构", + Func: d.Service.OpenAreaDumpSchema, + }, + { + FunName: "压缩开区文件", + Func: d.Service.CompressDumpDir, + }, + { + FunName: "上传表结构", + Func: d.Service.Upload, + }, + } + if err := steps.Run(); err != nil { + return err + } + logger.Info("开区导出表结构成功") + return nil +} diff --git a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/open_area_import_data.go b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/open_area_import_data.go new file mode 100644 index 0000000000..9bc4d81c5b --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/open_area_import_data.go @@ -0,0 +1,96 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package mysqlcmd + +import ( + "fmt" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/mysql/db-tools/dbactuator/internal/subcmd" + "dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql" + "dbm-services/mysql/db-tools/dbactuator/pkg/util" + + "github.com/spf13/cobra" +) + +// OpenAreaImportDataAct TODO +type OpenAreaImportDataAct struct { + *subcmd.BaseOptions + Service mysql.OpenAreaImportSchemaComp +} + +// NewOpenAreaImportData TODO +func NewOpenAreaImportData() *cobra.Command { + act := OpenAreaImportDataAct{ + BaseOptions: subcmd.GBaseOptions, + } + cmd := &cobra.Command{ + Use: "open_area_importdata", + Short: "开区导入数据", + Example: fmt.Sprintf( + `dbactuator mysql open_area_importschema %s %s`, + subcmd.CmdBaseExampleStr, subcmd.ToPrettyJson(act.Service.Example()), + ), + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(act.Validate()) + util.CheckErr(act.Init()) + util.CheckErr(act.Run()) + }, + } + return cmd +} + +// Validate TODO +func (d *OpenAreaImportDataAct) Validate() (err error) { + return d.BaseOptions.Validate() +} + +// Init TODO +func (d *OpenAreaImportDataAct) Init() (err error) { + if err = d.Deserialize(&d.Service.Params); err != nil { + logger.Info("DeserializeAndValidate failed, %v", err) + return err + } + d.Service.GeneralParam = subcmd.GeneralRuntimeParam + return +} + +// Run TODO +func (d *OpenAreaImportDataAct) Run() (err error) { + steps := subcmd.Steps{ + { + FunName: "init", + Func: d.Service.Init, + }, + { + FunName: "precheck", + Func: d.Service.Precheck, + }, + { + FunName: "解压data文件", + Func: d.Service.DecompressDumpDir, + }, + { + FunName: "导入数据文件", + Func: d.Service.OpenAreaImportData, + }, + { + FunName: "清除dump目录", + Func: d.Service.CleanDumpDir, + }, + } + + if err := steps.Run(); err != nil { + return err + } + logger.Info("开区导入数据成功") + return +} diff --git a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/open_area_import_schema.go b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/open_area_import_schema.go new file mode 100644 index 0000000000..2c110a23b7 --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/open_area_import_schema.go @@ -0,0 +1,103 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package mysqlcmd + +import ( + "fmt" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/mysql/db-tools/dbactuator/internal/subcmd" + "dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql" + "dbm-services/mysql/db-tools/dbactuator/pkg/util" + + "github.com/spf13/cobra" +) + +// OpenAreaImportSchemaAct TODO +type OpenAreaImportSchemaAct struct { + *subcmd.BaseOptions + Service mysql.OpenAreaImportSchemaComp +} + +// NewOpenAreaImportSchemaCommand TODO +func NewOpenAreaImportSchemaCommand() *cobra.Command { + act := OpenAreaImportSchemaAct{ + BaseOptions: subcmd.GBaseOptions, + } + cmd := &cobra.Command{ + Use: "open_area_importschema", + Short: "开区导入表结构", + Example: fmt.Sprintf( + `dbactuator mysql open_area_importschema %s %s`, + subcmd.CmdBaseExampleStr, subcmd.ToPrettyJson(act.Service.Example()), + ), + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(act.Validate()) + util.CheckErr(act.Init()) + util.CheckErr(act.Run()) + }, + } + return cmd +} + +// Validate TODO +func (d *OpenAreaImportSchemaAct) Validate() (err error) { + return d.BaseOptions.Validate() +} + +// Init TODO +func (d *OpenAreaImportSchemaAct) Init() (err error) { + if err = d.Deserialize(&d.Service.Params); err != nil { + logger.Error("DeserializeAndValidate failed, %v", err) + return err + } + d.Service.GeneralParam = subcmd.GeneralRuntimeParam + return +} + +// Run TODO +func (d *OpenAreaImportSchemaAct) Run() (err error) { + steps := subcmd.Steps{ + { + FunName: "init", + Func: d.Service.Init, + }, + { + FunName: "precheck", + Func: d.Service.Precheck, + }, + { + FunName: "解压schema文件", + Func: d.Service.DecompressDumpDir, + }, + { + FunName: "抹除AutoIncrement", + Func: d.Service.EraseAutoIncrement, + }, + { + FunName: "创建新库", + Func: d.Service.CreateNewDatabase, + }, + { + FunName: "导入表结构文件", + Func: d.Service.OpenAreaImportSchema, + }, + { + FunName: "清除dump目录", + Func: d.Service.CleanDumpDir, + }, + } + if err := steps.Run(); err != nil { + return err + } + logger.Info("开区导入表结构成功") + return +} diff --git a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/oscmd_run.go b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/oscmd_run.go new file mode 100644 index 0000000000..a9fbe8929f --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/oscmd_run.go @@ -0,0 +1,80 @@ +package mysqlcmd + +import ( + "fmt" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/mysql/db-tools/dbactuator/internal/subcmd" + "dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql" + "dbm-services/mysql/db-tools/dbactuator/pkg/util" + + "github.com/spf13/cobra" +) + +// OSCmdRunAct TODO +type OSCmdRunAct struct { + *subcmd.BaseOptions + Payload mysql.OSCmdRunComp +} + +// OSCmdRunCommand godoc +// +// @Summary 执行os简单命令 +// @Description 执行os简单命令 +// @Tags mysql +// @Accept json +// @Param body body mysql.OSCmds true "short description" +// @Success 200 {object} mysql.OSCmdRunResp +// @Router /mysql/oscmd-run [post] +func OSCmdRunCommand() *cobra.Command { + act := OSCmdRunAct{ + BaseOptions: subcmd.GBaseOptions, + } + cmd := &cobra.Command{ + Use: "oscmd-run", + Short: "执行os简单命令", + Example: fmt.Sprintf( + `dbactuator mysql oscmd-run %s %s`, + subcmd.CmdBaseExampleStr, subcmd.ToPrettyJson(act.Payload.Example()), + ), + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(act.Validate()) + util.CheckErr(act.Init()) + util.CheckErr(act.Run()) + }, + } + return cmd +} + +// Init TODO +func (d *OSCmdRunAct) Init() (err error) { + if err = d.BaseOptions.Validate(); err != nil { // @todo 应该在一开始就validate + return err + } + if err = d.Deserialize(&d.Payload.Params); err != nil { + logger.Error("DeserializeAndValidate err %s", err.Error()) + return err + } + return +} + +// Validate TODO +func (d *OSCmdRunAct) Validate() error { + return nil +} + +// Run TODO +func (d *OSCmdRunAct) Run() (err error) { + defer util.LoggerErrorStack(logger.Error, err) + steps := subcmd.Steps{ + { + FunName: "执行命令集", + Func: d.Payload.Start, + }, + } + if err = steps.Run(); err != nil { + return err + } + logger.Info("oscmd-run done") + return nil +} diff --git a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/osinfo_get.go b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/osinfo_get.go new file mode 100644 index 0000000000..873a2c4abf --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/osinfo_get.go @@ -0,0 +1,84 @@ +package mysqlcmd + +import ( + "fmt" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/mysql/db-tools/dbactuator/internal/subcmd" + "dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql" + "dbm-services/mysql/db-tools/dbactuator/pkg/util" + + "github.com/spf13/cobra" +) + +// OSInfoGetAct TODO +type OSInfoGetAct struct { + *subcmd.BaseOptions + Payload mysql.OSInfoGetComp +} + +// OSInfoGetCommand godoc +// +// @Summary 获取 os 内存、cpu、目录/磁盘 信息 +// @Description 获取 os 内存、cpu、目录/磁盘 信息 +// @Tags mysql +// @Accept json +// @Param body body mysql.OSInfoGetComp true "short description" +// @Success 200 {object} mysql.OSInfoResult +// @Router /mysql/osinfo-get [post] +func OSInfoGetCommand() *cobra.Command { + act := OSInfoGetAct{ + BaseOptions: subcmd.GBaseOptions, + } + cmd := &cobra.Command{ + Use: "osinfo-get", + Short: "获取 os 内存、cpu、目录/磁盘 信息", + Example: fmt.Sprintf( + `dbactuator mysql osinfo-get %s %s`, + subcmd.CmdBaseExampleStr, subcmd.ToPrettyJson(act.Payload.Example()), + ), + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(act.Validate()) + util.CheckErr(act.Init()) + util.CheckErr(act.Run()) + }, + } + return cmd +} + +// Init TODO +func (d *OSInfoGetAct) Init() (err error) { + if len(d.BaseOptions.Payload) == 0 { + return nil + } + if err = d.Deserialize(&d.Payload.Params); err != nil { + logger.Error("DeserializeAndValidate err %s", err.Error()) + return err + } + return +} + +// Validate TODO +func (d *OSInfoGetAct) Validate() error { + return nil +} + +// Run TODO +func (d *OSInfoGetAct) Run() (err error) { + defer util.LoggerErrorStack(logger.Error, err) + steps := subcmd.Steps{ + { + FunName: "采集信息", + Func: d.Payload.Start, + }, + { + FunName: "输出信息", + Func: d.Payload.OutputCtx, + }, + } + if err = steps.Run(); err != nil { + return err + } + logger.Info("osinfo-get done") + return nil +} diff --git a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/semantic_check.go b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/semantic_check.go deleted file mode 100644 index 15f7084fa2..0000000000 --- a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/semantic_check.go +++ /dev/null @@ -1,103 +0,0 @@ -package mysqlcmd - -import ( - "fmt" - - "dbm-services/common/go-pubpkg/logger" - "dbm-services/mysql/db-tools/dbactuator/internal/subcmd" - "dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql" - "dbm-services/mysql/db-tools/dbactuator/pkg/util" - - "github.com/spf13/cobra" -) - -// SenmanticCheckAct TODO -type SenmanticCheckAct struct { - *subcmd.BaseOptions - Payload mysql.SemanticCheckComp - Clean bool -} - -// NewSenmanticCheckCommand godoc -// -// @Summary 运行语义检查 -// @Description 运行语义检查 -// @Tags mysql -// @Accept json -// @Produce json -// @Param body body mysql.SemanticCheckComp true "short description" -// @Router /mysql/semantic-check [post] -func NewSenmanticCheckCommand() *cobra.Command { - act := SenmanticCheckAct{ - BaseOptions: subcmd.GBaseOptions, - Payload: mysql.SemanticCheckComp{}, - } - cmd := &cobra.Command{ - Use: "semantic-check", - Short: "运行语义检查", - Example: fmt.Sprintf( - `dbactuator mysql senmantic-check %s %s`, - subcmd.CmdBaseExampleStr, subcmd.ToPrettyJson(act.Payload.Example()), - ), - Run: func(cmd *cobra.Command, args []string) { - util.CheckErr(act.Validate()) - if act.RollBack { - util.CheckErr(act.Rollback()) - return - } - util.CheckErr(act.Init()) - if act.Clean { - util.CheckErr(act.Payload.Clean()) - return - } - util.CheckErr(act.Run()) - }, - } - cmd.Flags().BoolVarP(&act.Clean, "clean", "c", act.Clean, "清理语义检查实例") - return cmd -} - -// Validate TODO -func (d *SenmanticCheckAct) Validate() (err error) { - return d.BaseOptions.Validate() -} - -// Init TODO -func (d *SenmanticCheckAct) Init() (err error) { - if err = d.Deserialize(&d.Payload.Params); err != nil { - logger.Error("DeserializeAndValidate failed, %v", err) - return err - } - d.Payload.GeneralParam = subcmd.GeneralRuntimeParam - return nil -} - -// Rollback TODO -func (d *SenmanticCheckAct) Rollback() (err error) { - return -} - -// Run TODO -func (d *SenmanticCheckAct) Run() (err error) { - steps := subcmd.Steps{ - { - FunName: "precheck", - Func: d.Payload.Precheck, - }, { - FunName: "init", - Func: func() error { - return d.Payload.Init(d.Uid) - }, - }, - { - FunName: "运行语义分析", - Func: d.Payload.Run, - }, - } - if err := steps.Run(); err != nil { - return err - } - - logger.Info("运行语义检查成功") - return nil -} diff --git a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/semantic_dump_schema.go b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/semantic_dump_schema.go index 4229fd1e74..cc7e6d8143 100644 --- a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/semantic_dump_schema.go +++ b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/semantic_dump_schema.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package mysqlcmd import ( @@ -65,12 +75,13 @@ func (d *SenmanticDumpSchemaAct) Init() (err error) { func (d *SenmanticDumpSchemaAct) Run() (err error) { steps := subcmd.Steps{ { - FunName: "precheck", - Func: d.Service.Precheck, - }, { FunName: "init", Func: d.Service.Init, }, + { + FunName: "precheck", + Func: d.Service.Precheck, + }, { FunName: "运行导出表结构", Func: d.Service.DumpSchema, diff --git a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/start_mysql.go b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/start_mysql.go deleted file mode 100644 index 81caf04793..0000000000 --- a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/mysqlcmd/start_mysql.go +++ /dev/null @@ -1,52 +0,0 @@ -package mysqlcmd - -import ( - "dbm-services/common/go-pubpkg/logger" - "dbm-services/mysql/db-tools/dbactuator/internal/subcmd" - "dbm-services/mysql/db-tools/dbactuator/pkg/components/computil" - "dbm-services/mysql/db-tools/dbactuator/pkg/util" - - "github.com/spf13/cobra" -) - -// StartMysqlAct TODO -type StartMysqlAct struct { - *subcmd.BaseOptions - Payload computil.StartMySQLParam -} - -// NewStartMysqlCommand TODO -func NewStartMysqlCommand() *cobra.Command { - act := StartMysqlAct{ - BaseOptions: subcmd.GBaseOptions, - } - cmd := &cobra.Command{ - Use: "start", - Short: "启动MySQL实例", - Run: func(cmd *cobra.Command, args []string) { - util.CheckErr(act.Validate()) - util.CheckErr(act.Init()) - util.CheckErr(act.Run()) - }, - } - return cmd -} - -// Init TODO -func (s *StartMysqlAct) Init() (err error) { - if err = s.DeserializeAndValidate(&s.Payload); err != nil { - logger.Error("DeserializeAndValidate err %s", err.Error()) - return err - } - return -} - -// Run TODO -func (s *StartMysqlAct) Run() (err error) { - defer util.LoggerErrorStack(logger.Error, err) - if _, err = s.Payload.StartMysqlInstance(); err != nil { - logger.Error("start %s:%d failed,err:%s", s.Payload.Host, s.Payload.Port, err.Error()) - return err - } - return -} diff --git a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/spiderctlcmd/add_spider_slave_relationship.go b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/spiderctlcmd/add_spider_slave_relationship.go index db34b1b85c..04409b43cd 100644 --- a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/spiderctlcmd/add_spider_slave_relationship.go +++ b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/spiderctlcmd/add_spider_slave_relationship.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package spiderctlcmd import ( @@ -15,22 +25,18 @@ import ( // // AddSlaveClusterRoutingAct 添加spider slave集群时,添加相关路由信息 type AddSlaveClusterRoutingAct struct { - *subcmd.BaseOptions Service spiderctl.AddSlaveClusterRoutingComp } // AddSlaveClusterRoutingCommand TODO func AddSlaveClusterRoutingCommand() *cobra.Command { - act := AddSlaveClusterRoutingAct{ - BaseOptions: subcmd.GBaseOptions, - } + act := AddSlaveClusterRoutingAct{} cmd := &cobra.Command{ Use: "add-slave-cluster-routing", Short: "添加spider-slave集群的相关路由信息", Example: fmt.Sprintf(`dbactuator spiderctl add-slave-cluster-routing %s %s`, subcmd.CmdBaseExampleStr, subcmd.ToPrettyJson(act.Service.Example())), Run: func(cmd *cobra.Command, args []string) { - util.CheckErr(act.Validate()) util.CheckErr(act.Init()) util.CheckErr(act.Run()) }, @@ -41,7 +47,7 @@ func AddSlaveClusterRoutingCommand() *cobra.Command { // Init 初始化 func (d *AddSlaveClusterRoutingAct) Init() (err error) { logger.Info("InitCLusterRoutingAct Init") - if err = d.Deserialize(&d.Service.Params); err != nil { + if _, err = subcmd.Deserialize(&d.Service.Params); err != nil { logger.Error("DeserializeAndValidate failed, %v", err) return err } diff --git a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/spiderctlcmd/add_temporary_spider.go b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/spiderctlcmd/add_temporary_spider.go deleted file mode 100644 index c616759764..0000000000 --- a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/spiderctlcmd/add_temporary_spider.go +++ /dev/null @@ -1,74 +0,0 @@ -// Package spiderctlcmd TODO -package spiderctlcmd - -import ( - "fmt" - - "dbm-services/common/go-pubpkg/logger" - "dbm-services/mysql/db-tools/dbactuator/internal/subcmd" - "dbm-services/mysql/db-tools/dbactuator/pkg/components/spiderctl" - "dbm-services/mysql/db-tools/dbactuator/pkg/util" - - "github.com/spf13/cobra" -) - -// AddTmpSpiderAct act comp param -// 用于承接act命令的参数 分为基本act参数信息和之后操作所需要的参数 内设comp承接 -type AddTmpSpiderAct struct { - *subcmd.BaseOptions - Service spiderctl.AddTmpSpiderComp -} - -// NewAddTmpSpiderCommand TODO -func NewAddTmpSpiderCommand() *cobra.Command { - act := AddTmpSpiderAct{ - BaseOptions: subcmd.GBaseOptions, - } - cmd := &cobra.Command{ - Use: "add-tmp-spider", - Short: "添加临时spider节点", - Example: fmt.Sprintf(`dbactuator spiderctl add-tmp-spider %s %s`, - subcmd.CmdBaseExampleStr, subcmd.ToPrettyJson(act.Service.Example()), - ), - Run: func(cmd *cobra.Command, args []string) { - util.CheckErr(act.Validate()) - util.CheckErr(act.Init()) - util.CheckErr(act.Run()) - - }, - } - return cmd -} - -// Init TODO -func (d *AddTmpSpiderAct) Init() (err error) { - logger.Info("AddTmpSpiderAct Init") - // 反序列化 - if err = d.Deserialize(&d.Service.Params); err != nil { - logger.Error("DeserializeAndValidate failed, %v", err) - return err - } - // 初始化变量 - d.Service.GeneralParam = subcmd.GeneralRuntimeParam - return nil -} - -// Run TODO -func (d *AddTmpSpiderAct) Run() (err error) { - // 是一个切片 - steps := subcmd.Steps{ - { - FunName: "初始化", - Func: d.Service.Init, - }, - { - FunName: "中控节点配置新增spider节点信息", - Func: d.Service.AddTmpSpider, - }, - } - if err = steps.Run(); err != nil { - return err - } - logger.Info("add temporary spider node successfully") - return nil -} diff --git a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/spiderctlcmd/cluster_backend_migrate_cutover.go b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/spiderctlcmd/cluster_backend_migrate_cutover.go new file mode 100644 index 0000000000..dd44f5de04 --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/spiderctlcmd/cluster_backend_migrate_cutover.go @@ -0,0 +1,90 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package spiderctlcmd + +import ( + "fmt" + + "github.com/spf13/cobra" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/mysql/db-tools/dbactuator/internal/subcmd" + "dbm-services/mysql/db-tools/dbactuator/pkg/components/spiderctl" + "dbm-services/mysql/db-tools/dbactuator/pkg/util" +) + +// ClusterBackendMigrateCutOverAct TODO +type ClusterBackendMigrateCutOverAct struct { + *subcmd.BaseOptions + Service spiderctl.SpiderClusterBackendMigrateCutoverComp +} + +// NewClusterMigrateCutOverCommand TODO +func NewClusterMigrateCutOverCommand() *cobra.Command { + act := ClusterBackendMigrateCutOverAct{ + BaseOptions: subcmd.GBaseOptions, + } + cmd := &cobra.Command{ + Use: "cluster-backend-migrate-cutover", + Short: "spider集群后端迁移切换", + Example: fmt.Sprintf(`dbactuator spiderctl cluster-backend-migrate-cutover %s %s`, + subcmd.CmdBaseExampleStr, subcmd.ToPrettyJson(act.Service.Example()), + ), + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(act.Validate()) + util.CheckErr(act.Init()) + util.CheckErr(act.Run()) + }, + } + return cmd +} + +// Init TODO +func (d *ClusterBackendMigrateCutOverAct) Init() (err error) { + if err = d.Deserialize(&d.Service.Params); err != nil { + logger.Error("DeserializeAndValidate failed, %v", err) + return err + } + d.Service.GeneralParam = subcmd.GeneralRuntimeParam + return nil +} + +// Run TODO +func (d *ClusterBackendMigrateCutOverAct) Run() (err error) { + // 是一个切片 + steps := subcmd.Steps{ + { + FunName: "[未切换]: 初始化", + Func: d.Service.Init, + }, + { + FunName: "[未切换]: 切换前置检查", + Func: d.Service.PreCheck, + }, + { + FunName: "[未切换]: 持久化回滚SQL", + Func: d.Service.PersistenceRollbackFile, + }, + { + FunName: "[主分片切换中]: 开始切换", + Func: d.Service.CutOver, + }, + { + FunName: "[已完成切换]: 断开数据同步", + Func: d.Service.StopRepl, + }, + } + if err = steps.Run(); err != nil { + return err + } + logger.Info("cluster backend migrate cutover successfully") + return nil +} diff --git a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/spiderctlcmd/cluster_backend_switch.go b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/spiderctlcmd/cluster_backend_switch.go new file mode 100644 index 0000000000..69de826ff8 --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/spiderctlcmd/cluster_backend_switch.go @@ -0,0 +1,107 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package spiderctlcmd + +import ( + "fmt" + + "github.com/spf13/cobra" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/mysql/db-tools/dbactuator/internal/subcmd" + "dbm-services/mysql/db-tools/dbactuator/pkg/components/spiderctl" + "dbm-services/mysql/db-tools/dbactuator/pkg/util" +) + +// ClusterBackendSwitchAct TODO +type ClusterBackendSwitchAct struct { + Service spiderctl.SpiderClusterBackendSwitchComp +} + +// NewClusterBackendSwitchCommand TODO +func NewClusterBackendSwitchCommand() *cobra.Command { + act := ClusterBackendSwitchAct{} + cmd := &cobra.Command{ + Use: "cluster-backend-switch", + Short: "spider集群后端切换", + Example: fmt.Sprintf(`dbactuator spiderctl cluster-backend-switch %s %s`, + subcmd.CmdBaseExampleStr, subcmd.ToPrettyJson(act.Service.Example()), + ), + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(act.Init()) + util.CheckErr(act.Run()) + }, + } + return cmd +} + +// Init TODO +func (d *ClusterBackendSwitchAct) Init() (err error) { + if _, err = subcmd.Deserialize(&d.Service.Params); err != nil { + logger.Error("DeserializeAndValidate failed, %v", err) + return err + } + d.Service.GeneralParam = subcmd.GeneralRuntimeParam + return nil +} + +// Run TODO +func (d *ClusterBackendSwitchAct) Run() (err error) { + // 是一个切片 + steps := subcmd.Steps{ + { + FunName: "[未切换]: 初始化", + Func: d.Service.Init, + }, + { + FunName: "[未切换]: 切换前置检查", + Func: d.Service.PreCheck, + }, + { + FunName: "[未切换]: 持久化回滚SQL", + Func: d.Service.PersistenceRollbackFile, + }, + { + FunName: "[主分片切换中]: 开始切换主分片", + Func: d.Service.CutOver, + }, + { + FunName: "[主分片切换成功]: 断开NewMaster的同步", + Func: d.Service.StopRepl, + }, + } + if err = steps.Run(); err != nil { + return err + } + logger.Info("master spt switching has been completed") + // 如果非强制切换,则需要执行互切的后续操作 + if !d.Service.Params.Force { + flowSteps := subcmd.Steps{ + { + FunName: "[主分片切换成功]: 授权repl给OldMaster", + Func: d.Service.GrantReplForNewSlave, + }, + { + FunName: "[主分片切换成功]: 建立复制关系", + Func: d.Service.ChangeMasterToNewMaster, + }, + { + FunName: "[开始切换从分片]: 切换从分片", + Func: d.Service.CutOverSlave, + }, + } + if err = flowSteps.Run(); err != nil { + return err + } + } + logger.Info("cluster backend switch successfully") + return nil +} diff --git a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/spiderctlcmd/cluster_schema_check.go b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/spiderctlcmd/cluster_schema_check.go new file mode 100644 index 0000000000..82678d6979 --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/spiderctlcmd/cluster_schema_check.go @@ -0,0 +1,73 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package spiderctlcmd + +import ( + "fmt" + + "github.com/spf13/cobra" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/mysql/db-tools/dbactuator/internal/subcmd" + "dbm-services/mysql/db-tools/dbactuator/pkg/components/spiderctl" + "dbm-services/mysql/db-tools/dbactuator/pkg/util" +) + +// ClusterSchemaCheckAct TODO +type ClusterSchemaCheckAct struct { + *subcmd.BaseOptions + Service spiderctl.TableSchemaCheckComp +} + +// NewClusterSchemaCheckCommand TODO +func NewClusterSchemaCheckCommand() *cobra.Command { + act := &ClusterSchemaCheckAct{} + cmd := &cobra.Command{ + Use: "schema-check", + Short: "spider 集群表结构校验", + Example: fmt.Sprintf(`dbactuator spiderctl schema-check %s %s`, + subcmd.CmdBaseExampleStr, subcmd.ToPrettyJson(act.Service.Example()), + ), + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(act.Init()) + util.CheckErr(act.Run()) + }, + } + return cmd +} + +// Init TODO +func (d *ClusterSchemaCheckAct) Init() (err error) { + if d.BaseOptions, err = subcmd.Deserialize(&d.Service.Params); err != nil { + logger.Error("DeserializeAndValidate failed, %v", err) + return err + } + d.Service.GeneralParam = subcmd.GeneralRuntimeParam + return nil +} + +// Run TODO +func (d *ClusterSchemaCheckAct) Run() (err error) { + steps := subcmd.Steps{ + { + FunName: "初始化", + Func: d.Service.Init, + }, + { + FunName: "集群表结构校验", + Func: d.Service.Run, + }, + } + if err = steps.Run(); err != nil { + return err + } + return +} diff --git a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/spiderctlcmd/cluster_schema_repair.go b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/spiderctlcmd/cluster_schema_repair.go new file mode 100644 index 0000000000..c0a08d1d6a --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/spiderctlcmd/cluster_schema_repair.go @@ -0,0 +1,78 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package spiderctlcmd + +import ( + "fmt" + + "github.com/spf13/cobra" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/mysql/db-tools/dbactuator/internal/subcmd" + "dbm-services/mysql/db-tools/dbactuator/pkg/components/spiderctl" + "dbm-services/mysql/db-tools/dbactuator/pkg/util" +) + +// ClusterSchemaRepairAct TODO +type ClusterSchemaRepairAct struct { + Service spiderctl.TableSchemaRepairComp +} + +// NewClusterSchemaRepairCommand TODO +func NewClusterSchemaRepairCommand() *cobra.Command { + act := &ClusterSchemaRepairAct{} + cmd := &cobra.Command{ + Use: "schema-repair", + Short: "spider 集群表结构修复", + Example: fmt.Sprintf(`dbactuator spiderctl schema-repair %s %s`, + subcmd.CmdBaseExampleStr, subcmd.ToPrettyJson(act.Service.Example()), + ), + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(act.Init()) + util.CheckErr(act.Run()) + }, + } + return cmd +} + +// Init TODO +func (d *ClusterSchemaRepairAct) Init() (err error) { + if _, err = subcmd.Deserialize(&d.Service.Params); err != nil { + logger.Error("DeserializeAndValidate failed, %v", err) + return err + } + d.Service.GeneralParam = subcmd.GeneralRuntimeParam + return nil +} + +// Run TODO +func (d *ClusterSchemaRepairAct) Run() (err error) { + steps := subcmd.Steps{ + { + FunName: "初始化", + Func: d.Service.Init, + }, + { + FunName: "集群表结构修复", + Func: func() error { + if d.Service.Params.AutoFix { + logger.Info("根据校验异常的信息去修改集群表结构") + return d.Service.RunAutoFix() + } + return d.Service.Run() + }, + }, + } + if err = steps.Run(); err != nil { + return err + } + return +} diff --git a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/spiderctlcmd/cmd.go b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/spiderctlcmd/cmd.go index bc6448eab0..e764e29724 100644 --- a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/spiderctlcmd/cmd.go +++ b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/spiderctlcmd/cmd.go @@ -24,9 +24,12 @@ func NewSpiderCtlCommand() *cobra.Command { Commands: []*cobra.Command{ NewDeploySpiderCtlCommand(), NewInitCLusterRoutingCommand(), - NewAddTmpSpiderCommand(), AddSlaveClusterRoutingCommand(), NewUnInstallSpiderCtlCommand(), + NewClusterMigrateCutOverCommand(), + NewClusterBackendSwitchCommand(), + NewClusterSchemaCheckCommand(), + NewClusterSchemaRepairCommand(), }, }, } diff --git a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/subcmd.go b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/subcmd.go index 194cb9fa1e..0fbff05b46 100644 --- a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/subcmd.go +++ b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/subcmd.go @@ -141,14 +141,52 @@ func (b *BaseOptions) DeserializeAndValidate(s interface{}) (err error) { } // Deserialize TODO -/* - { - "general":{} // - "extend":{} // 实际参数 - } - 反序列化payload,并校验参数 - ps: 参数校验 from golang validate v10 -*/ +func Deserialize(s interface{}) (p *BaseOptions, err error) { + var bp []byte + if err = GBaseOptions.Validate(); err != nil { + return nil, err + } + if GBaseOptions.PayloadFormat == PayloadFormatRaw { + bp = []byte(GBaseOptions.Payload) + } else { + logger.Info("Deserialize payload body: %s", GBaseOptions.Payload) + bp, err = base64.StdEncoding.DecodeString(GBaseOptions.Payload) + if err != nil { + return nil, err + } + } + if err := env.Parse(s); err != nil { + logger.Warn("env parse error, ignore environment variables for payload:%s", err.Error()) + } + logger.Info("params from env %+v", s) + g := components.RuntimeAccountParam{} + if err := env.Parse(&g); err != nil { + logger.Warn("env parse error, ignore environment variables for payload:%s", err.Error()) + } + // logger.Info("Account from env: %+v", g) + bip := components.BaseInputParam{ + ExtendParam: s, + GeneralParam: &components.GeneralParam{RuntimeAccountParam: g}, + } + defer logger.Info("payload parsed: %+v", bip) + if err = json.Unmarshal(bp, &bip); err != nil { + logger.Error("json.Unmarshal failed, %v", s, err) + err = errors.WithMessage(err, "参数解析错误") + return nil, err + } + // logger.Info("params after unmarshal %+v", bip) + if err = validate.GoValidateStruct(bip, false, true); err != nil { + logger.Error("validate struct failed, %v", s, err) + err = errors.WithMessage(err, "参数输入错误") + return nil, err + } + GeneralRuntimeParam = bip.GeneralParam + return GBaseOptions, nil +} + +// Deserialize 反序列化payload,并校验参数 +// +// ps: 参数校验 from golang validate v10 func (b *BaseOptions) Deserialize(s interface{}) (err error) { var bp []byte if b.PayloadFormat == PayloadFormatRaw { @@ -177,13 +215,13 @@ func (b *BaseOptions) Deserialize(s interface{}) (err error) { if err = json.Unmarshal(bp, &bip); err != nil { logger.Error("json.Unmarshal failed, %v", s, err) err = errors.WithMessage(err, "参数解析错误") - return + return err } // logger.Info("params after unmarshal %+v", bip) if err = validate.GoValidateStruct(bip, false, true); err != nil { logger.Error("validate struct failed, %v", s, err) err = errors.WithMessage(err, "参数输入错误") - return + return err } GeneralRuntimeParam = bip.GeneralParam return nil diff --git a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/subcmd_helper.go b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/subcmd_helper.go index f100f7d040..96ede3df71 100644 --- a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/subcmd_helper.go +++ b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/subcmd_helper.go @@ -222,7 +222,7 @@ func (d *Definition) ExpandProperties(defs *Definitions) { for pname, prop := range d.Properties { prop.depth = d.depth prop.name = pname - if util.StringsHas(d.Required, pname) { + if util.ContainElem(d.Required, pname) { prop.required = true } diff --git a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/tbinlogdumpercmd/backup_demand_for_tbinlogdumper.go b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/tbinlogdumpercmd/backup_demand_for_tbinlogdumper.go new file mode 100644 index 0000000000..0c7ce5a343 --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/tbinlogdumpercmd/backup_demand_for_tbinlogdumper.go @@ -0,0 +1,88 @@ +// TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +// Copyright (C) 2017-2023 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 https://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. + +package tbinlogdumpercmd + +import ( + "fmt" + + "github.com/spf13/cobra" + + "dbm-services/bigdata/db-tools/dbactuator/pkg/util" + "dbm-services/common/go-pubpkg/logger" + "dbm-services/mysql/db-tools/dbactuator/internal/subcmd" + "dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/backupdemand" +) + +type BackupDemandAct struct { + *subcmd.BaseOptions + Payload backupdemand.Component +} + +func NewDumperBackupDemandCommand() *cobra.Command { + act := BackupDemandAct{BaseOptions: subcmd.GBaseOptions} + cmd := &cobra.Command{ + Use: "backup-demand", + Short: "备份请求", + Example: fmt.Sprintf( + `dbactuator tbinlogdumper backup-demand %s %s`, + subcmd.CmdBaseExampleStr, subcmd.ToPrettyJson(act.Payload.Example())), + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(act.Validate()) + util.CheckErr(act.Init()) + util.CheckErr(act.Run()) + }, + } + return cmd +} + +func (d *BackupDemandAct) Init() (err error) { + if err = d.BaseOptions.Validate(); err != nil { // @todo 应该在一开始就validate + return err + } + if err = d.Deserialize(&d.Payload.Params); err != nil { + logger.Error("DeserializeAndValidate err %s", err.Error()) + return err + } + logger.Warn("params %+v", d.Payload.Params) + + return +} + +func (d *BackupDemandAct) Validate() error { + return nil +} + +func (d *BackupDemandAct) Run() (err error) { + defer util.LoggerErrorStack(logger.Error, err) + steps := subcmd.Steps{ + { + FunName: "初始化", + Func: d.Payload.Init, + }, + { + FunName: "生成备份配置", + Func: d.Payload.GenerateBackupConfig, + }, + { + FunName: "执行备份", + Func: d.Payload.DoBackup, + }, + { + FunName: "返回报告", + Func: d.Payload.OutPutForTBinlogDumper, + }, + } + + if err = steps.Run(); err != nil { + return err + } + + logger.Info("backup demand success") + return nil +} diff --git a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/tbinlogdumpercmd/cmd.go b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/tbinlogdumpercmd/cmd.go new file mode 100644 index 0000000000..7e068afff0 --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/tbinlogdumpercmd/cmd.go @@ -0,0 +1,34 @@ +// Package 命令 +/* + * @Description: spider 相关操作的子命令集合 + */ +package tbinlogdumpercmd + +import ( + "dbm-services/mysql/db-tools/dbactuator/internal/subcmd" + "dbm-services/mysql/db-tools/dbactuator/pkg/util/templates" + + "github.com/spf13/cobra" +) + +// NewSpiderCommand tendbcluster 命令 +func NewTbinlogDumperCommand() *cobra.Command { + cmds := &cobra.Command{ + Use: "tbinlogdumper [spider operation]", + Short: "Tbinlogdumper Operation Command Line Interface", + RunE: subcmd.ValidateSubCommand(), + } + groups := templates.CommandGroups{ + { + Message: "tbinlogdumper operation sets", + Commands: []*cobra.Command{ + NewDeployTbinlogDumperCommand(), + NewUnInstallTbinlogDumperCommand(), + NewDumperBackupDemandCommand(), + NewDumpSchemaCommand(), + }, + }, + } + groups.Add(cmds) + return cmds +} diff --git a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/tbinlogdumpercmd/dump_schema_for_tbinlogdumper.go b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/tbinlogdumpercmd/dump_schema_for_tbinlogdumper.go new file mode 100644 index 0000000000..3fbb91b43f --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/tbinlogdumpercmd/dump_schema_for_tbinlogdumper.go @@ -0,0 +1,104 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package tbinlogdumpercmd + +import ( + "fmt" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/mysql/db-tools/dbactuator/internal/subcmd" + "dbm-services/mysql/db-tools/dbactuator/pkg/components/tbinlogdumper" + "dbm-services/mysql/db-tools/dbactuator/pkg/util" + + "github.com/spf13/cobra" +) + +// DumpSchemaAct TODO +type DumpSchemaAct struct { + *subcmd.BaseOptions + Service tbinlogdumper.DumpSchemaComp +} + +// NewDumpSchemaCommand godoc +// +// @Summary 备份表结构并导入 +// @Description 备份表结构并导入 +// @Tags tbinlogdumper +// @Accept json +// @Produce json +// @Param body body tbinlogdumper.DumpSchemaComp true "short description" +// @Router /tbinlogdumper/semantic-dumpschema [post] +func NewDumpSchemaCommand() *cobra.Command { + act := DumpSchemaAct{ + BaseOptions: subcmd.GBaseOptions, + } + cmd := &cobra.Command{ + Use: "dumpschema", + Short: "运行导出并导入表结构", + Example: fmt.Sprintf( + `dbactuator tbinlogdumper dumpschema %s %s`, + subcmd.CmdBaseExampleStr, subcmd.ToPrettyJson(act.Service.Example()), + ), + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(act.Validate()) + util.CheckErr(act.Init()) + util.CheckErr(act.Run()) + }, + } + return cmd +} + +// Validate TODO +func (d *DumpSchemaAct) Validate() (err error) { + return d.BaseOptions.Validate() +} + +// Init TODO +func (d *DumpSchemaAct) Init() (err error) { + if err = d.Deserialize(&d.Service.Params); err != nil { + logger.Error("DeserializeAndValidate failed, %v", err) + return err + } + d.Service.GeneralParam = subcmd.GeneralRuntimeParam + return nil +} + +// Run TODO +func (d *DumpSchemaAct) Run() (err error) { + steps := subcmd.Steps{ + { + FunName: "init", + Func: d.Service.Init, + }, + { + FunName: "precheck", + Func: d.Service.Precheck, + }, + { + FunName: "运行导出表结构", + Func: d.Service.DumpSchema, + }, + { + FunName: "修改表结构的存储引擎", + Func: d.Service.ModifyEngine, + }, + { + FunName: "导入表结构到TBinlogdumper", + Func: d.Service.LoadSchema, + }, + } + if err := steps.Run(); err != nil { + return err + } + + logger.Info("同步表结构到TBinlogdumper实例成功") + return nil +} diff --git a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/tbinlogdumpercmd/install_tbinlogdumper.go b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/tbinlogdumpercmd/install_tbinlogdumper.go new file mode 100644 index 0000000000..bf3c769624 --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/tbinlogdumpercmd/install_tbinlogdumper.go @@ -0,0 +1,132 @@ +package tbinlogdumpercmd + +import ( + "encoding/json" + "fmt" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/mysql/db-tools/dbactuator/internal/subcmd" + "dbm-services/mysql/db-tools/dbactuator/pkg/components/tbinlogdumper" + "dbm-services/mysql/db-tools/dbactuator/pkg/rollback" + "dbm-services/mysql/db-tools/dbactuator/pkg/util" + + "github.com/spf13/cobra" +) + +// DeployTbinlogDumperAct TODO +type DeployTbinlogDumperAct struct { + *subcmd.BaseOptions + Service tbinlogdumper.InstallTbinlogDumperComp +} + +// NewDeployTbinlogDumperCommand godoc +// +// @Summary 部署 tbinlogdumper 实例 +// @Description 部署 tbinlogdumper 实例说明 +// @Tags tbinlogdumper +// @Accept json +// @Param body body tbinlogdumper.InstallTbinlogDumperComp true "short description" +// @Router /tbinlogdumper/deploy [post] +func NewDeployTbinlogDumperCommand() *cobra.Command { + act := DeployTbinlogDumperAct{ + BaseOptions: subcmd.GBaseOptions, + } + cmd := &cobra.Command{ + Use: "deploy", + Short: "部署TbinlogDumper实例", + Example: fmt.Sprintf( + `dbactuator tbinlogdumper deploy %s %s`, + subcmd.CmdBaseExampleStr, subcmd.ToPrettyJson(act.Service.Example()), + ), + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(act.Validate()) + if act.RollBack { + util.CheckErr(act.Rollback()) + return + } + util.CheckErr(act.Init()) + util.CheckErr(act.Run()) + }, + } + return cmd +} + +// Init TODO +func (d *DeployTbinlogDumperAct) Init() (err error) { + logger.Info("DeployMySQLAct Init") + if err = d.Deserialize(&d.Service.Params); err != nil { + logger.Error("DeserializeAndValidate failed, %v", err) + return err + } + // 解析额外参数 + if err = d.Deserialize(&d.Service.Configs); err != nil { + logger.Error("DeserializeAndValidate failed, %v", err) + return err + } + d.Service.GeneralParam = subcmd.GeneralRuntimeParam + return d.Service.InitDumperDefaultParam() +} + +// Rollback TODO +// +// @receiver d +// @return err +func (d *DeployTbinlogDumperAct) Rollback() (err error) { + var r rollback.RollBackObjects + if err = d.DeserializeAndValidate(&r); err != nil { + logger.Error("DeserializeAndValidate failed, %v", err) + return err + } + err = r.RollBack() + if err != nil { + logger.Error("roll back failed %s", err.Error()) + } + return +} + +// Run TODO +func (d *DeployTbinlogDumperAct) Run() (err error) { + steps := subcmd.Steps{ + { + FunName: "预检查", + Func: d.Service.PreCheck, + }, + { + FunName: "渲染tbinlogdumper配置", + Func: d.Service.GenerateDumperMycnf, + }, + { + FunName: "初始化tbinlogdumper相关目录", + Func: d.Service.InitInstanceDirs, + }, + { + FunName: "下载并且解压安装包", + Func: d.Service.DecompressDumperPkg, + }, + { + FunName: "初始化系统库表", + Func: d.Service.DumperInstall, + }, + { + FunName: "启动tbinlogdumper", + Func: d.Service.Startup, + }, + { + FunName: "执行初始化系统基础权限、库表SQL", + Func: d.Service.InitDefaultPrivAndSchema, + }, + } + + if err := steps.Run(); err != nil { + rollbackCtxb, rerr := json.Marshal(d.Service.RollBackContext) + if rerr != nil { + logger.Error("json Marshal %s", err.Error()) + fmt.Printf("Can't RollBack\n") + } + fmt.Printf("%s\n", string(rollbackCtxb)) + return err + } + + logger.Info("install_tbinlogdumper successfully") + return nil +} diff --git a/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/tbinlogdumpercmd/uninstall_tbinlogdumper.go b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/tbinlogdumpercmd/uninstall_tbinlogdumper.go new file mode 100644 index 0000000000..2ee182b725 --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/internal/subcmd/tbinlogdumpercmd/uninstall_tbinlogdumper.go @@ -0,0 +1,80 @@ +package tbinlogdumpercmd + +import ( + "fmt" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/mysql/db-tools/dbactuator/internal/subcmd" + "dbm-services/mysql/db-tools/dbactuator/pkg/components/tbinlogdumper" + "dbm-services/mysql/db-tools/dbactuator/pkg/util" + + "github.com/spf13/cobra" +) + +// UnInstallTbinlogDumperAct TODO +type UnInstallTbinlogDumperAct struct { + *subcmd.BaseOptions + Service tbinlogdumper.UnInstallTbinlogDumperComp +} + +// NewUnInstallTbinlogDumperCommand TODO +// +// @Summary 卸载 tbinlogdumper 实例 +// @Description 卸载 tbinlogdumper 实例说明 +// @Tags tbinlogdumper +// @Accept json +// @Param body body tbinlogdumper.UnInstallTbinlogDumperComp true "short description" +// @Router /tbinlogdumper/uninstall [post] +func NewUnInstallTbinlogDumperCommand() *cobra.Command { + act := UnInstallTbinlogDumperAct{ + BaseOptions: subcmd.GBaseOptions, + } + cmd := &cobra.Command{ + Use: "uninstall", + Short: "下架tbinlogdumper", + Example: fmt.Sprintf(`dbactuator tbinlogdumper uninstall %s`, subcmd.CmdBaseExampleStr), + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(act.Validate()) + if act.RollBack { + return + } + util.CheckErr(act.Init()) + util.CheckErr(act.Run()) + }, + } + return cmd +} + +// Init TODO +func (d *UnInstallTbinlogDumperAct) Init() (err error) { + logger.Info("UnInstallMysqlAct Init") + if err = d.Deserialize(&d.Service.Params); err != nil { + logger.Error("DeserializeAndValidate failed, %v", err) + return err + } + d.Service.GeneralParam = subcmd.GeneralRuntimeParam + return d.Service.Init() +} + +// Run TODO +func (d *UnInstallTbinlogDumperAct) Run() (err error) { + steps := subcmd.Steps{ + { + FunName: "预检查", + Func: d.Service.PreCheck, + }, + { + FunName: "停止数据库实例", + Func: d.Service.ShutDownMySQLD, + }, + { + FunName: "清理机器数据&日志目录", + Func: d.Service.TbinlogDumperClearDir, + }, + } + if err := steps.Run(); err != nil { + return err + } + logger.Info("uninstall tbinlogdumper successfully") + return nil +} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/base.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/base.go index 8bae6b4dcf..da2ca53ef8 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/base.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/base.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package components // BaseInputParam TODO @@ -24,16 +34,30 @@ type RuntimeAccountParam struct { TdbctlAccoutParam } +// GetAllSysAccount TODO +func (g *GeneralParam) GetAllSysAccount() (accounts []string) { + accounts = g.RuntimeExtend.MySQLSysUsers + accounts = append(accounts, g.RuntimeAccountParam.AdminUser) + accounts = append(accounts, g.RuntimeAccountParam.DbBackupUser) + accounts = append(accounts, g.RuntimeAccountParam.MonitorAccessAllUser) + accounts = append(accounts, g.RuntimeAccountParam.MonitorUser) + accounts = append(accounts, g.RuntimeAccountParam.ReplUser) + accounts = append(accounts, g.RuntimeAccountParam.YwUser) + accounts = append(accounts, g.RuntimeAccountParam.TdbctlUser) + return +} + // GetAccountRepl TODO func GetAccountRepl(g *GeneralParam) MySQLReplAccount { Repl := MySQLReplAccount{} - if g == nil { + switch { + case g == nil: return Repl - } else if &g.RuntimeAccountParam == nil { + case g.RuntimeAccountParam == RuntimeAccountParam{}: return Repl - } else if &g.RuntimeAccountParam.MySQLAccountParam == nil { + case g.RuntimeAccountParam.MySQLAccountParam == MySQLAccountParam{}: return Repl - } else { - return g.RuntimeAccountParam.MySQLAccountParam.MySQLReplAccount + default: + return g.RuntimeAccountParam.MySQLReplAccount } } diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/computil/mysql_operate.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/computil/mysql_operate.go index b767dd8edc..86d02731f9 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/computil/mysql_operate.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/computil/mysql_operate.go @@ -159,7 +159,7 @@ type ShutdownMySQLParam struct { // 2. 可能需要通过 expect 方式,避免暴露密码。 func (param ShutdownMySQLParam) ShutdownMySQLBySocket() (err error) { shellCMD := fmt.Sprintf("mysqladmin -u%s -p%s -S %s shutdown", param.MySQLUser, param.MySQLPwd, param.Socket) - output, err := osutil.ExecShellCommand(false, shellCMD) + output, err := mysqlutil.ExecCommandMySQLShell(shellCMD) if err != nil { if !strings.Contains(err.Error(), "Can't connect to local MySQL server") { logger.Info("shutdown mysql error %s,output:%s. cmd:%s", err.Error(), output, shellCMD) @@ -185,7 +185,7 @@ func ShutdownMySQLBySocket2(user, password, socket string) (err error) { // @return err func (param ShutdownMySQLParam) ForceShutDownMySQL() (err error) { shellCMD := fmt.Sprintf("mysqladmin -u%s -p%s -S%s shutdown", param.MySQLUser, param.MySQLPwd, param.Socket) - output, err := osutil.ExecShellCommand(false, shellCMD) + output, err := mysqlutil.ExecCommandMySQLShell(shellCMD) if err != nil { logger.Warn("使用mysqladmin shutdown 失败:%s output:%s", err.Error(), string(output)) // 如果用 shutdown 执行失败 diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/crontab/clear_crontab.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/crontab/clear_crontab.go index c0ae039401..189781a0b9 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/crontab/clear_crontab.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/crontab/clear_crontab.go @@ -1,6 +1,8 @@ package crontab import ( + "errors" + "fmt" "os" "dbm-services/common/go-pubpkg/logger" @@ -47,13 +49,31 @@ func (u *ClearCrontabParam) CleanCrontab() (err error) { func (u *ClearCrontabParam) CleanDBToolsFolder() (err error) { logger.Info("开始删除相关周边组件目录") - os.RemoveAll(cst.ChecksumInstallPath) - os.RemoveAll(cst.DbbackupGoInstallPath) - os.RemoveAll(cst.DBAToolkitPath) - os.RemoveAll(cst.MySQLCrondInstallPath) - os.RemoveAll(cst.MysqlRotateBinlogInstallPath) - os.RemoveAll(cst.MySQLMonitorInstallPath) - os.RemoveAll(cst.DBAReportBase) + var errList []error + var isErr bool + rmList := []string{ + cst.ChecksumInstallPath, + cst.DbbackupGoInstallPath, + cst.DBAToolkitPath, + cst.MySQLCrondInstallPath, + cst.MysqlRotateBinlogInstallPath, + cst.MySQLMonitorInstallPath, + cst.DBAReportBase, + } + for _, f := range rmList { + errList = append(errList, os.RemoveAll(f)) + } + + // 打印所有的err信息 + for _, err := range errList { + if err != nil && !errors.Is(err, os.ErrNotExist) { + logger.Error(err.Error()) + isErr = true + } + } + if isErr { + return fmt.Errorf("clean db-tool-folder failed") + } return nil } diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/db_base_account.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/db_base_account.go index ea1e99dcb5..379cc921f8 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/db_base_account.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/db_base_account.go @@ -206,7 +206,8 @@ type MySQLDbBackupAccount struct { DbBackupPwd string `json:"backup_pwd,omitempty"` // dbbackup pwd } -// GetAccountPrivs TODO +// GetAccountPrivs 获取备份语句 +// 如果是 mysql 8.0,grant 需要 BACKUP_ADMIN 权限 func (m MySQLDbBackupAccount) GetAccountPrivs(is80 bool, grantHosts ...string) MySQLAccountPrivs { privPairs := []PrivPari{ {Object: "*.*", Privs: backupUserPriv}, diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/medium.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/medium.go index cf24fbb9da..86f7785df1 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/medium.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/medium.go @@ -46,7 +46,7 @@ func (m *Medium) GetAbsolutePath() string { // 用于做软连接使用 func (m *Medium) GePkgBaseName() string { pkgFullName := filepath.Base(m.GetAbsolutePath()) - return regexp.MustCompile("(.tar.gz|.tgz)$").ReplaceAllString(pkgFullName, "") + return regexp.MustCompile("(.tar.gz|.tgz|.tar.xz)$").ReplaceAllString(pkgFullName, "") } // GetPkgTypeName 通过介质包文件名称获取对应的组件类型 diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/backupdemand/backup_demand.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/backupdemand/backup_demand.go index 038cf46eaa..a61fe849ec 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/backupdemand/backup_demand.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/backupdemand/backup_demand.go @@ -19,26 +19,29 @@ import ( "strings" "time" - "bk-dbconfig/pkg/core/logger" - - "gopkg.in/ini.v1" - + "dbm-services/common/go-pubpkg/mysqlcomm" "dbm-services/mysql/db-tools/dbactuator/pkg/components" "dbm-services/mysql/db-tools/dbactuator/pkg/core/cst" "dbm-services/mysql/db-tools/dbactuator/pkg/tools" "dbm-services/mysql/db-tools/mysql-dbbackup/pkg/config" "dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/dbareport" + + "gopkg.in/ini.v1" + + "dbm-services/common/go-pubpkg/logger" ) type Component struct { - Params *Parma `json:"extend"` + Params *Param `json:"extend"` tools *tools.ToolSet context `json:"-"` } -type Parma struct { +type Param struct { Host string `json:"host" validate:"required,ip"` Port int `json:"port" validate:"required,gte=3306,lt=65535"` + Role string `json:"role" validate:"required"` + ShardID int `json:"shard_id"` BackupType string `json:"backup_type" validate:"required"` BackupGSD []string `json:"backup_gsd" validate:"required"` // [grant, schema, data] Regex string `json:"regex"` @@ -48,11 +51,13 @@ type Parma struct { } type context struct { - backupConfigPath string - now time.Time - randString string - resultReportPath string - statusReportPath string + backupConfigPaths map[int]string + now time.Time + randString string + resultReportPath string + statusReportPath string + backupPort []int // 当在 spider master备份时, 会有 [25000, 26000] 两个端口 + backupDir string //只是兼容tbinlogdumper的备份日志输出,存储备份目录信息,没有任何处理逻辑 } type Report struct { @@ -61,6 +66,8 @@ type Report struct { } func (c *Component) Init() (err error) { + c.Params.Role = strings.ToUpper(c.Params.Role) + c.tools, err = tools.NewToolSetWithPick(tools.ToolDbbackupGo) if err != nil { logger.Error("init toolset failed: %s", err.Error()) @@ -71,104 +78,129 @@ func (c *Component) Init() (err error) { rand.Seed(c.now.UnixNano()) c.randString = fmt.Sprintf("%d%d", c.now.UnixNano(), rand.Intn(100)) - c.backupConfigPath = filepath.Join( + c.backupConfigPaths = make(map[int]string) + + c.backupPort = append(c.backupPort, c.Params.Port) + c.backupConfigPaths[c.Params.Port] = filepath.Join( cst.BK_PKG_INSTALL_PATH, fmt.Sprintf("dbactuator-%s", c.Params.BillId), fmt.Sprintf("dbbackup.%d.%s.ini", c.Params.Port, c.randString), ) + if c.Params.Role == cst.BackupRoleSpiderMaster { + tdbctlPort := mysqlcomm.GetTdbctlPortBySpider(c.Params.Port) + c.backupPort = append(c.backupPort, tdbctlPort) + + c.backupConfigPaths[tdbctlPort] = filepath.Join( + cst.BK_PKG_INSTALL_PATH, + fmt.Sprintf("dbactuator-%s", c.Params.BillId), + fmt.Sprintf("dbbackup.%d.%s.ini", tdbctlPort, c.randString), + ) + } + return nil } func (c *Component) GenerateBackupConfig() error { - dailyBackupConfigPath := filepath.Join( - cst.DbbackupGoInstallPath, - fmt.Sprintf("dbbackup.%d.ini", c.Params.Port), - ) - - dailyBackupConfigFile, err := ini.LoadSources(ini.LoadOptions{ - PreserveSurroundedQuote: true, - IgnoreInlineComment: true, - AllowBooleanKeys: true, - AllowShadows: true, - }, dailyBackupConfigPath) - if err != nil { - logger.Error("load %s failed: %s", dailyBackupConfigPath, err.Error()) - return err - } + for _, port := range c.backupPort { + dailyBackupConfigPath := filepath.Join( + cst.DbbackupGoInstallPath, + fmt.Sprintf("dbbackup.%d.ini", port), + ) + + dailyBackupConfigFile, err := ini.LoadSources(ini.LoadOptions{ + PreserveSurroundedQuote: true, + IgnoreInlineComment: true, + AllowBooleanKeys: true, + AllowShadows: true, + }, dailyBackupConfigPath) + if err != nil { + logger.Error("load %s failed: %s", dailyBackupConfigPath, err.Error()) + return err + } - var backupConfig config.BackupConfig - err = dailyBackupConfigFile.MapTo(&backupConfig) - if err != nil { - logger.Error("map %s to struct failed: %s", dailyBackupConfigPath, err.Error()) - return err - } + var backupConfig config.BackupConfig + err = dailyBackupConfigFile.MapTo(&backupConfig) + if err != nil { + logger.Error("map %s to struct failed: %s", dailyBackupConfigPath, err.Error()) + return err + } - backupConfig.Public.BackupType = c.Params.BackupType - backupConfig.Public.BackupTimeOut = "" - backupConfig.Public.BillId = c.Params.BillId - backupConfig.Public.BackupId = c.Params.BackupId - backupConfig.Public.DataSchemaGrant = strings.Join(c.Params.BackupGSD, ",") + backupConfig.Public.BackupType = c.Params.BackupType + backupConfig.Public.BackupTimeOut = "" + backupConfig.Public.BillId = c.Params.BillId + backupConfig.Public.BackupId = c.Params.BackupId + backupConfig.Public.DataSchemaGrant = strings.Join(c.Params.BackupGSD, ",") + backupConfig.Public.ShardValue = c.Params.ShardID - backupConfig.LogicalBackup.Regex = "" - if c.Params.BackupType == "logical" { - backupConfig.LogicalBackup.Regex = c.Params.Regex - } + backupConfig.LogicalBackup.Regex = "" + if c.Params.BackupType == "logical" { + backupConfig.LogicalBackup.Regex = c.Params.Regex + } - if c.Params.CustomBackupDir != "" { - backupConfig.Public.BackupDir = filepath.Join( - backupConfig.Public.BackupDir, - fmt.Sprintf("%s_%s_%s", - c.Params.CustomBackupDir, - c.now.Format("20060102_150405"), - c.randString)) + if c.Params.CustomBackupDir != "" { + backupConfig.Public.BackupDir = filepath.Join( + backupConfig.Public.BackupDir, + fmt.Sprintf("%s_%s_%d_%s", + c.Params.CustomBackupDir, + c.now.Format("20060102_150405"), + port, + c.randString)) + + err := os.Mkdir(backupConfig.Public.BackupDir, 0755) + if err != nil { + logger.Error("mkdir %s failed: %s", backupConfig.Public.BackupDir, err.Error()) + return err + } + // 增加为tbinlogdumper做库表备份的日志输出,保存流程上下文 + c.backupDir = backupConfig.Public.BackupDir + } - err := os.Mkdir(backupConfig.Public.BackupDir, 0755) + backupConfigFile := ini.Empty() + err = backupConfigFile.ReflectFrom(&backupConfig) if err != nil { - logger.Error("mkdir %s failed: %s", backupConfig.Public.BackupDir, err.Error()) + logger.Error("reflect backup config failed: %s", err.Error()) return err } - } - backupConfigFile := ini.Empty() - err = backupConfigFile.ReflectFrom(&backupConfig) - if err != nil { - logger.Error("reflect backup config failed: %s", err.Error()) - return err - } + backupConfigPath := c.backupConfigPaths[port] + err = backupConfigFile.SaveTo(backupConfigPath) + if err != nil { + logger.Error("write backup config to %s failed: %s", + backupConfigPath, err.Error()) + return err + } - err = backupConfigFile.SaveTo(c.backupConfigPath) - if err != nil { - logger.Error("write backup config to %s failed: %s", - c.backupConfigPath, err.Error()) - return err + c.resultReportPath = filepath.Join( + backupConfig.Public.ResultReportPath, + fmt.Sprintf("dbareport_result_%d.log", c.Params.Port), + ) + c.statusReportPath = filepath.Join( + backupConfig.Public.StatusReportPath, + fmt.Sprintf("dbareport_status_%d.log", c.Params.Port), + ) } - c.resultReportPath = filepath.Join( - backupConfig.Public.ResultReportPath, - fmt.Sprintf("dbareport_result_%d.log", c.Params.Port), - ) - c.statusReportPath = filepath.Join( - backupConfig.Public.StatusReportPath, - fmt.Sprintf("dbareport_status_%d.log", c.Params.Port), - ) - return nil } func (c *Component) DoBackup() error { - cmd := exec.Command(c.tools.MustGet(tools.ToolDbbackupGo), []string{ - "dumpbackup", - "--config", c.backupConfigPath, - }...) + for _, port := range c.backupPort { + backupConfigPath := c.backupConfigPaths[port] - logger.Info("backup command: %s", cmd) - err := cmd.Run() - if err != nil { - logger.Error("execute %s failed: %s", cmd, err.Error()) - return err + cmd := exec.Command(c.tools.MustGet(tools.ToolDbbackupGo), []string{ + "dumpbackup", + "--config", backupConfigPath, + }...) + + logger.Info("backup command: %s", cmd) + err := cmd.Run() + if err != nil { + logger.Error("execute %s failed: %s", cmd, err.Error()) + return err + } + logger.Info("backup success") } - logger.Info("backup success") return nil } @@ -264,7 +296,7 @@ func (c *Component) OutPut() error { func (c *Component) Example() interface{} { return Component{ - Params: &Parma{ + Params: &Param{ Host: "x.x.x.x", Port: 20000, BackupType: "logical", @@ -276,3 +308,22 @@ func (c *Component) Example() interface{} { }, } } + +// OutPutForTBinlogDumper 增加为tbinlogdumper做库表备份的日志输出,保存流程上下文 +func (c *Component) OutPutForTBinlogDumper() error { + ret := make(map[string]interface{}) + report, err := c.generateReport() + if err != nil { + return err + } + ret["report_result"] = report.Result + ret["report_status"] = report.Status + ret["backup_dir"] = c.backupDir + + err = components.PrintOutputCtx(ret) + if err != nil { + logger.Error("output backup report failed: %s", err.Error()) + return err + } + return nil +} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/change_master.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/change_master.go index f9ac3ccf5f..4b7146be3d 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/change_master.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/change_master.go @@ -147,7 +147,8 @@ func (b *BuildMSRelationComp) CheckCurrentSlaveStatus() (err error) { } // 如果没有加强制参数,只要存在关系,就抛出错误 if !b.Params.Force { - return fmt.Errorf("当前实例实际存在主从关系") + return fmt.Errorf("当前实例实际存在主从关系, master_host=%s, master_port=%d", + slaveStatus.MasterHost, slaveStatus.MasterPort) } logger.Info("show slave status Info is %v", slaveStatus) // 强制参数force=true,直接执行stop slave && reset slave @@ -171,7 +172,7 @@ func (b *BuildMSRelationComp) CheckCurrentSlaveStatus() (err error) { func (b *BuildMSRelationComp) BuildMSRelation() (err error) { logger.Info("begin change Master to %s:%d", b.Params.MasterHost, b.Params.Port) changeMasterSql := b.getChangeMasterSql() - logger.Debug("change master sql: %s", changeMasterSql) + logger.Info("change master sql: %s", changeMasterSql) if _, err = b.db.Exec(changeMasterSql); err != nil { logger.Error("change master to %s:%d failed,err:%s", b.Params.MasterHost, b.Params.MasterPort, err.Error()) return err diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/clean_mysql.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/clean_mysql.go index 94d0491d2f..2a5395f556 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/clean_mysql.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/clean_mysql.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package mysql import ( @@ -19,6 +29,7 @@ import ( type CleanMysqlComp struct { GeneralParam *components.GeneralParam `json:"general"` Params CleanMysqlParam `json:"extend"` + ctx } // Example TODO @@ -55,50 +66,51 @@ type CleanMysqlParam struct { CheckIntervalSec int `json:"check_interval_sec"` // 清空目标实例 TgtInstance *native.Instance `json:"tgt_instance" validate:"required"` +} +type ctx struct { + sysUsers []string checkDuration time.Duration - - myCnf *util.CnfFile - dbworker *native.DbWorker - instObj *native.InsObject - // account *components.RuntimeAccountParam + myCnf *util.CnfFile + dbworker *native.DbWorker + instObj *native.InsObject } // Init TODO func (c *CleanMysqlComp) Init() error { f := util.GetMyCnfFileName(c.Params.TgtInstance.Port) - c.Params.myCnf = &util.CnfFile{FileName: f} - if err := c.Params.myCnf.Load(); err != nil { + c.myCnf = &util.CnfFile{FileName: f} + if err := c.myCnf.Load(); err != nil { return err } - dbSocket, err := c.Params.myCnf.GetMySQLSocket() + dbSocket, err := c.myCnf.GetMySQLSocket() if err != nil { return err } - c.Params.instObj = &native.InsObject{ + c.instObj = &native.InsObject{ Host: c.Params.TgtInstance.Host, Port: c.Params.TgtInstance.Port, User: c.GeneralParam.RuntimeAccountParam.AdminUser, Pwd: c.GeneralParam.RuntimeAccountParam.AdminPwd, Socket: dbSocket, } - if dbw, err := c.Params.instObj.ConnBySocket(); err != nil { + if dbw, err := c.instObj.ConnBySocket(); err != nil { return err } else { - c.Params.dbworker = dbw + c.dbworker = dbw } if c.Params.CheckIntervalSec == 0 { c.Params.CheckIntervalSec = 31 } - c.Params.checkDuration = time.Duration(c.Params.CheckIntervalSec) * time.Second + c.checkDuration = time.Duration(c.Params.CheckIntervalSec) * time.Second return nil } // PreCheck 前置检查 // 会初始化 needRestart func (c *CleanMysqlComp) PreCheck() error { - if err := c.Params.instObj.CheckInstanceConnIdle(c.GeneralParam.RuntimeExtend.MySQLSysUsers, - c.Params.checkDuration); err != nil { + if err := c.instObj.CheckInstanceConnIdle(c.GeneralParam.GetAllSysAccount(), + c.checkDuration); err != nil { logger.Warn("clean_mysql precheck error %w", err) if c.Params.Force { return nil @@ -111,12 +123,12 @@ func (c *CleanMysqlComp) PreCheck() error { // Start TODO func (c *CleanMysqlComp) Start() error { if c.Params.StopSlave { - if err := c.Params.dbworker.StopSlave(); err != nil { + if err := c.dbworker.StopSlave(); err != nil { return errors.WithMessage(err, "stop slave") } } if c.Params.ResetSlave { - if err := c.Params.dbworker.ResetSlave(); err != nil { + if err := c.dbworker.ResetSlave(); err != nil { return errors.WithMessage(err, "reset slave") } } @@ -125,8 +137,8 @@ func (c *CleanMysqlComp) Start() error { inStr, _ := mysqlutil.UnsafeBuilderStringIn(native.DBSys, "'") dbsSql := fmt.Sprintf("select SCHEMA_NAME from information_schema.SCHEMATA where SCHEMA_NAME not in (%s)", inStr) - if databases, err := c.Params.dbworker.Query(dbsSql); err != nil { - if c.Params.dbworker.IsNotRowFound(err) { + if databases, err := c.dbworker.Query(dbsSql); err != nil { + if c.dbworker.IsNotRowFound(err) { return nil } else { return err @@ -136,7 +148,7 @@ func (c *CleanMysqlComp) Start() error { dropSQL := fmt.Sprintf("DROP DATABASE `%s`;", dbName["SCHEMA_NAME"]) logger.Warn("run sql %s", dropSQL) if c.Params.DropDatabase { - if _, err := c.Params.dbworker.Exec(dropSQL); err != nil { + if _, err := c.dbworker.Exec(dropSQL); err != nil { return errors.WithMessage(err, dropSQL) } } else { @@ -144,7 +156,7 @@ func (c *CleanMysqlComp) Start() error { } } if c.Params.DropDatabase && c.Params.Restart { - if err := computil.RestartMysqlInstanceNormal(*c.Params.instObj); err != nil { + if err := computil.RestartMysqlInstanceNormal(*c.instObj); err != nil { return err } } diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/cutover/base.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/cutover/base.go index 2c4a525a6c..63e512cca8 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/cutover/base.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/cutover/base.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package cutover import ( @@ -388,7 +398,8 @@ func (m *MySQLClusterDetail) MSVarsCheck(checkVars []string) (err error) { // MSCheck 切换前同步检查 type MSCheck struct { SlavedbConn *native.DbWorker - NeedCheckSumRd bool // 需要存在校验记录 + NeedCheckSumRecord bool // 需要存在校验记录 + NotVerifyChecksum bool // 是否检查checksum AllowDiffCount int // 允许存在差异的校验记录的行数 AllowDelaySec int // 允许存在的延迟差异 AllowDelayBinlogByte int // 允许binlog的最大延迟 @@ -398,7 +409,7 @@ type MSCheck struct { func NewMsCheck(dbConn *native.DbWorker) *MSCheck { return &MSCheck{ SlavedbConn: dbConn, - NeedCheckSumRd: true, + NeedCheckSumRecord: true, AllowDiffCount: AllowedChecksumMaxOffset, AllowDelaySec: AllowedSlaveDelayMax, AllowDelayBinlogByte: ExecSlowKbytes, @@ -406,73 +417,31 @@ func NewMsCheck(dbConn *native.DbWorker) *MSCheck { } // Check TODO -// ValidateCheckSum 校验checksum 表 func (s *MSCheck) Check() (err error) { - // 检查主从同步delay binlog size - total, err := s.SlavedbConn.TotalDelayBinlogSize() + slaveStatus, err := s.SlavedbConn.ShowSlaveStatus() if err != nil { - logger.Error("get total delay binlog size failed %s", err.Error()) return err } - if total > s.AllowDelayBinlogByte { - return fmt.Errorf("the total delay binlog size %d 超过了最大允许值 %d", total, s.AllowDelayBinlogByte) + if !slaveStatus.ReplSyncIsOk() { + return fmt.Errorf("IOThread:%s,SQLThread:%s", slaveStatus.SlaveIORunning, slaveStatus.SlaveSQLRunning) } - - // 以为内部版本需要校验的参数 - if s.SlavedbConn.IsEmptyInstance() { - logger.Info("主从关系正常,从库是空实例,跳过检查checksum表") - return nil - } - var cnt int - c := fmt.Sprintf( - "select count(distinct db, tbl) as cnt from %s.checksum where ts > date_sub(now(), interval 14 day)", - native.INFODBA_SCHEMA, - ) - if err = s.SlavedbConn.Queryxs(&cnt, c); err != nil { - logger.Error("查询最近14天checkTable总数失败%s", err.Error()) + if err = s.SlavedbConn.ReplicateDelayCheck(s.AllowDelaySec, s.AllowDelayBinlogByte); err != nil { + logger.Error("主从延迟检查异常:%s", err.Error()) return err } - - if !s.NeedCheckSumRd { - logger.Info("不需要检查校验记录. 获取到的CheckSum Record 总数为%d", cnt) - } - - // 如果查询不到 校验记录需要 return error - if cnt == 0 && s.NeedCheckSumRd { - logger.Warn("没有查询到最近14天的校验记录") - return fmt.Errorf("主从校验记录为空") - } - - c = fmt.Sprintf( - `select count(distinct db, tbl,chunk) as cnt from %s.checksum - where (this_crc <> master_crc or this_cnt <> master_cnt) - and ts > date_sub(now(), interval 14 day);`, native.INFODBA_SCHEMA, - ) - if err = s.SlavedbConn.Queryxs(&cnt, c); err != nil { - logger.Error("查询数据校验差异表失败: %s", err.Error()) - return err + // 如果不需要检查checksum table 则直接返回 + if s.NotVerifyChecksum { + return } - c = fmt.Sprintf( - `select check_result as slave_delay from %s.master_slave_check - WHERE check_item='slave_delay_sec';`, native.INFODBA_SCHEMA, - ) - if cnt > s.AllowDiffCount { - return fmt.Errorf("checksum 不同值的 chunk 个数是 %d, 超过了上限 %d", cnt, s.AllowDiffCount) - } - var delaysec int - if err = s.SlavedbConn.Queryxs(&delaysec, c); err != nil { - logger.Error("查询slave delay sec: %s", err.Error()) - return err - } - if delaysec > s.AllowDelaySec { - return fmt.Errorf("slave 延迟时间 %d, 超过了上限 %d", delaysec, s.AllowDelaySec) + // 以为内部版本需要校验的参数 + if s.SlavedbConn.IsEmptyInstance() { + logger.Info("主从关系正常,从库是空实例,跳过检查checksum表") + return nil } - return nil + return s.SlavedbConn.ValidateChecksum(s.AllowDiffCount, s.NeedCheckSumRecord) } -// CheckCheckSum TODO -// CheckMSReplStatus -// 只在待切换的从库检查CheckSum 和主从同步状态 +// CheckCheckSum 只在待切换的从库检查CheckSum 和主从同步状态 func (s AltSlaveInfo) CheckCheckSum() (err error) { return NewMsCheck(s.dbConn).Check() } @@ -495,24 +464,5 @@ func CompareMSBinPos(master MasterInfo, slave AltSlaveInfo) (err error) { logger.Error("show slave status on %s failed:%s", slave.Addr(), err.Error()) return err } - // 比较从库回放到了对应主库的哪个BinLog File - msg := fmt.Sprintf( - "Master Current BinlogFile:%s Slave SQL Thread Exec BinlogFile:%s", - masterStatus.File, slaveStatus.RelayMasterLogFile, - ) - logger.Info(msg) - if strings.Compare(masterStatus.File, slaveStatus.RelayMasterLogFile) != 0 { - return fmt.Errorf("主从同步可能有差异," + msg) - } - // 比较主库的位点和从库已经回放的位点信息 - // 比较从库回放到了对应主库的哪个BinLog File - msg = fmt.Sprintf( - "Master Current Pos:%d Slave SQL Thread Exec Pos:%d", - masterStatus.Position, slaveStatus.ExecMasterLogPos, - ) - logger.Info(msg) - if masterStatus.Position != slaveStatus.ExecMasterLogPos { - return fmt.Errorf("主从执行的位点信息有差异%s", msg) - } - return err + return native.CompareBinlogPos(masterStatus, slaveStatus) } diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/cutover/cutover.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/cutover/cutover.go index bdf73c9886..7fad97f841 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/cutover/cutover.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/cutover/cutover.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + // Package cutover 主故障切换 // 下发到Slave节点的机器 执行 package cutover @@ -42,6 +52,7 @@ type runCtx struct { replPwd string backupUser string cluster *MySQLClusterDetail + sysUsers []string // 是否是成对切换 isCutOverPair bool } @@ -57,6 +68,7 @@ func (m *CutOverToSlaveComp) Init() (err error) { m.replPwd = m.GeneralParam.RuntimeAccountParam.ReplPwd m.backupUser = m.GeneralParam.RuntimeAccountParam.DbBackupUser m.checkVars = []string{"character_set_server", "lower_case_table_names"} + m.sysUsers = m.GeneralParam.GetAllSysAccount() if err = m.cluster.InitProxyConn(m.proxyAdminUser, m.proxyAdminPwd); err != nil { logger.Error("connect alt proxies failed,err:%s ", err.Error()) @@ -176,8 +188,7 @@ func (m *CutOverToSlaveComp) PreCheck() (err error) { if err = m.cluster.AltSlaveIns.CheckCheckSum(); err != nil { return err } - prcsls, err := m.cluster.AltSlaveIns.dbConn.ShowApplicationProcesslist( - m.GeneralParam.RuntimeExtend.MySQLSysUsers) + prcsls, err := m.cluster.AltSlaveIns.dbConn.ShowApplicationProcesslist(m.sysUsers) if err != nil { logger.Error("show processlist failed %s", err.Error()) return err @@ -200,13 +211,17 @@ func (m *CutOverToSlaveComp) PreCheck() (err error) { return nil } - if err = m.cluster.AltSlaveIns.dbConn.CheckSlaveReplStatus(); err != nil { + if err = m.cluster.AltSlaveIns.dbConn.CheckSlaveReplStatus(func() (resp native.ShowSlaveStatusResp, err error) { + return m.cluster.AltSlaveIns.dbConn.ShowSlaveStatus() + }); err != nil { logger.Error("检查主从同步状态出错: %s", err.Error()) return err } if m.isCutOverPair { - if err = m.cluster.AltSlaveIns.Slave.dbConn.CheckSlaveReplStatus(); err != nil { + if err = m.cluster.AltSlaveIns.Slave.dbConn.CheckSlaveReplStatus(func() (resp native.ShowSlaveStatusResp, err error) { + return m.cluster.AltSlaveIns.Slave.dbConn.ShowSlaveStatus() + }); err != nil { return err } } @@ -265,7 +280,9 @@ func (m *CutOverToSlaveComp) CutOver() (binPos string, err error) { } if !m.Params.IsDeadMaster { - if err = m.cluster.AltSlaveIns.dbConn.CheckSlaveReplStatus(); err != nil { + if err = m.cluster.AltSlaveIns.dbConn.CheckSlaveReplStatus(func() (resp native.ShowSlaveStatusResp, err error) { + return m.cluster.AltSlaveIns.dbConn.ShowSlaveStatus() + }); err != nil { logger.Error("再次检查下主从状态 %s", err.Error()) return "", err } diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/dbbackup/backup_index.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/dbbackup/backup_index.go index 18c07b2090..28c0734754 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/dbbackup/backup_index.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/dbbackup/backup_index.go @@ -24,7 +24,7 @@ type BackupIndexFile struct { MysqlVersion string `json:"mysql_version"` BackupCharset string `json:"backup_charset"` - BkBizId string `json:"bk_biz_id"` + BkBizId int `json:"bk_biz_id"` // unique uuid BackupId string `json:"backup_id"` BillId string `json:"bill_id"` @@ -79,6 +79,12 @@ type StatusInfo struct { MasterPort int `json:"master_port"` } +// String 用于打印 +func (s *StatusInfo) String() string { + return fmt.Sprintf("Status{BinlogFile:%s, BinlogPos:%s, MasterHost:%s, MasterPort:%d}", + s.BinlogFile, s.BinlogPos, s.MasterHost, s.MasterPort) +} + // String 用于打印 func (s *BinlogStatusInfo) String() string { return fmt.Sprintf("BinlogStatusInfo{MasterStatus:%+v, SlaveStatus:%+v}", s.ShowMasterStatus, s.ShowSlaveStatus) @@ -92,8 +98,8 @@ func ParseBackupIndexFile(indexFilePath string, indexObj *BackupIndexFile) error return err } if err := json.Unmarshal(bodyBytes, indexObj); err != nil { - logger.Error("fail to read index file to struct: %s", fileName) - // return err + logger.Error("fail to read index file to struct: %s, err:%s", fileName, err.Error()) + return err } indexObj.indexFilePath = indexFilePath diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/dbbackup/types.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/dbbackup/types.go deleted file mode 100644 index 5da73c2de9..0000000000 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/dbbackup/types.go +++ /dev/null @@ -1,105 +0,0 @@ -package dbbackup - -//// LogicBackupDataOption TODO -//type LogicBackupDataOption struct { -// // "grant,schema,data" -// DataSchemaGrant string `json:"DataSchemaGrant"` -//} -// -//// cfg the config of dumping backup -//type cfg struct { -// Public CnfShared `json:"Public" ini:"Public" validate:"required"` -// BackupClient CnfBackupClient `json:"BackupClient" ini:"BackupClient" validate:"required"` -// LogicalBackup CnfLogicalBackup `json:"LogicalBackup" ini:"LogicalBackup" validate:"required"` -// // LogicalLoad CnfLogicalLoad `json:"LogicalLoad" ini:"LogicalLoad"` -// PhysicalBackup CnfPhysicalBackup `json:"PhysicalBackup" ini:"PhysicalBackup"` -//} -// -//// CnfShared TODO -//type CnfShared struct { -// BkBizId string `ini:"BkBizId"` -// BkCloudId string `ini:"BkCloudId"` -// BillId string `ini:"BillId"` -// BackupId string `ini:"BackupId"` -// ClusterAddress string `ini:"ClusterAddress"` -// ClusterId string `ini:"ClusterId"` -// ShardValue int `ini:"ShardValue"` -// MysqlHost string `ini:"MysqlHost"` -// MysqlPort string `ini:"MysqlPort"` -// MysqlUser string `ini:"MysqlUser"` -// MysqlPasswd string `ini:"MysqlPasswd"` -// DataSchemaGrant string `ini:"DataSchemaGrant"` -// BackupDir string `ini:"BackupDir" validate:"required"` -// MysqlRole string `ini:"MysqlRole"` -// MysqlCharset string `ini:"MysqlCharset"` -// BackupTimeOut string `ini:"BackupTimeout" validate:"required,time"` -// BackupType string `ini:"BackupType"` -// OldFileLeftDay string `ini:"OldFileLeftDay"` -// // TarSizeThreshold tar file will be split to this package size. MB -// TarSizeThreshold uint64 `ini:"TarSizeThreshold" validate:"required,gte=128"` -// // IOLimitMBPerSec tar or split default io limit, mb/s. 0 means no limit -// IOLimitMBPerSec int `ini:"IOLimitMBPerSec"` -// ResultReportPath string `ini:"ResultReportPath"` -// StatusReportPath string `ini:"StatusReportPath"` -//} -// -//// CnfBackupClient TODO -//type CnfBackupClient struct { -// FileTag string `ini:"FileTag"` -// RemoteFileSystem string `ini:"RemoteFileSystem"` -// DoChecksum string `ini:"DoChecksum"` -//} -// -//// CnfLogicalBackup the config of logical backup -//type CnfLogicalBackup struct { -// // ChunkFileSize Split tables into chunks of this output file size. This value is in MB -// ChunkFileSize uint64 `ini:"ChunkFileSize"` -// Regex string `ini:"Regex"` -// Threads int `ini:"Threads"` -// DisableCompress bool `ini:"DisableCompress"` -// FlushRetryCount int `ini:"FlushRetryCount"` -// DefaultsFile string `ini:"DefaultsFile"` -// // ExtraOpt other mydumper options string to be appended -// ExtraOpt string `ini:"ExtraOpt"` -//} -// -//// CnfPhysicalBackup the config of physical backup -//type CnfPhysicalBackup struct { -// // Threads –parallel to copy files -// Threads int `ini:"Threads"` -// // SplitSpeed tar split limit in MB/s, default 300 -// SplitSpeed int64 `ini:"SplitSpeed"` -// // Throttle limits the number of chunks copied per second. The chunk size is 10 MB, 0 means no limit -// Throttle int `ini:"Throttle"` -// DefaultsFile string `ini:"DefaultsFile" validate:"required,file"` -// // ExtraOpt other xtrabackup options string to be appended -// ExtraOpt string `ini:"ExtraOpt"` -//} -// -//// CnfLogicalLoad the config of logical loading -//type CnfLogicalLoad struct { -// MysqlHost string `ini:"MysqlHost"` -// MysqlPort string `ini:"MysqlPort"` -// MysqlUser string `ini:"MysqlUser"` -// MysqlPasswd string `ini:"MysqlPasswd"` -// MysqlCharset string `ini:"MysqlCharset"` -// MysqlLoadDir string `ini:"MysqlLoadDir"` -// Threads int `ini:"Threads"` -// Regex string `ini:"Regex"` -// EnableBinlog bool `ini:"EnableBinlog"` -// IndexFilePath string `ini:"IndexFilePath" validate:"required"` -// // ExtraOpt other myloader options string to be appended -// ExtraOpt string `json:"ExtraOpt"` -//} -// -//// CnfPhysicalLoad the config of physical loading -//type CnfPhysicalLoad struct { -// MysqlLoadDir string `ini:"MysqlLoadDir" validate:"required"` -// Threads int `ini:"Threads"` -// // CopyBack use copy-back or move-back -// CopyBack bool `ini:"CopyBack"` -// IndexFilePath string `ini:"IndexFilePath" validate:"required,file"` -// DefaultsFile string `ini:"DefaultsFile" validate:"required"` -// // ExtraOpt other xtrabackup recover options string to be appended -// ExtraOpt string `json:"ExtraOpt"` -//} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/deploy_mysql_crond.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/deploy_mysql_crond.go index 797258d45d..694ab2e9c3 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/deploy_mysql_crond.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/deploy_mysql_crond.go @@ -195,6 +195,10 @@ func (c *DeployMySQLCrondComp) Start() (err error) { */ errChan := make(chan error) go func() { + // 重装的时候无脑尝试关闭一次 + _, _ = http.Get("http://127.0.0.1:9999/quit") + time.Sleep(15 * time.Second) + cmd := exec.Command( "su", "-", "mysql", "-c", fmt.Sprintf( @@ -214,7 +218,7 @@ func (c *DeployMySQLCrondComp) Start() (err error) { started := false LabelSelectLoop: - for i := 1; i <= 10; i++ { + for i := 1; i <= 30; i++ { select { case err := <-errChan: if err != nil { @@ -241,22 +245,8 @@ LabelSelectLoop: } // 关闭前台启动的 mysql-crond - resp, err := http.Get("http://127.0.0.1:9999/quit") - if err != nil { - logger.Error("call quit failed: %s", err.Error()) - return err - } - defer func() { - _ = resp.Body.Close() - }() - - if resp.StatusCode != 200 { - err := errors.Errorf("quit api err: %s", err.Error()) - logger.Error(err.Error()) - return err - } + _, _ = http.Get("http://127.0.0.1:9999/quit") - // quit 要等10s time.Sleep(15 * time.Second) // 确认监听端口已经关闭 diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/drop_large_table.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/drop_large_table.go deleted file mode 100644 index 3eb388313c..0000000000 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/drop_large_table.go +++ /dev/null @@ -1,70 +0,0 @@ -package mysql - -import ( - "fmt" - - "dbm-services/mysql/db-tools/dbactuator/pkg/components" -) - -// DropLargeTableComp TODO -type DropLargeTableComp struct { - GeneralParam *components.GeneralParam `json:"general"` - Params DropTableParam `json:"extend"` -} - -// DropTableParam godoc -// 1. show create table -// 2. rename table, create table -// 3. 做硬链接 -// 4. drop table -// 5. 删除硬链和原始文件 -type DropTableParam struct { - Database string `json:"database" validate:"required"` - Tables []string `json:"tables" validate:"required"` - LargeTable bool `json:"large_table"` - // 每秒删除速度 MB/s - BWLimitMB int `json:"bwlimit_mb"` - // 超过多少 MB 算大文件,大文件采用 trunc 限速删除 - LargeTableSizeMB int `json:"large_table_size_mb"` - // 是否保留表结构,相当于 truncate table - KeepSchema bool `json:"keep_schema"` - - // "table1" - fileList map[string][]*linkFiles -} - -type linkFiles struct { - srcFile string - destFile string -} - -// select @@datadir -// select SPACE,NAME,FILE_SIZE from INNODB_SYS_TABLESPACES where NAME like 'query_analyzer/%' -// query_analyzer/query_history#P#p202206 .ibd -// - -func (d *DropTableParam) dropInnodbTable() error { - // innodb_file_per_table - - for _, fileList := range d.fileList { - for _, file := range fileList { - file.destFile = fmt.Sprintf("%s.__drop__", file.srcFile) - // osutil.MakeHardLink(file.srcFile, file.destFile) - // osutil.TruncateFile(file.srcFile, d.BWLimit) - } - } - - return nil -} - -func dropTokudbTable() error { - return nil -} - -func dropMyisamTable() error { - return nil -} - -func dropRocksdbTable() error { - return nil -} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/drop_table.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/drop_table.go new file mode 100644 index 0000000000..3e9aacd1d6 --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/drop_table.go @@ -0,0 +1,531 @@ +package mysql + +import ( + "dbm-services/common/go-pubpkg/cmutil" + "dbm-services/common/go-pubpkg/logger" + "dbm-services/mysql/db-tools/dbactuator/pkg/components" + "dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/common" + "dbm-services/mysql/db-tools/dbactuator/pkg/native" + "dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans" + "dbm-services/mysql/db-tools/dbactuator/pkg/util/osutil" + "fmt" + "io/fs" + "os" + "path/filepath" + "regexp" + "strings" + + "github.com/jmoiron/sqlx" + "github.com/pkg/errors" + "golang.org/x/exp/slices" +) + +// DropTableComp 删表 +type DropTableComp struct { + GeneralParam *components.GeneralParam `json:"general"` + Params *DropTableParam `json:"extend"` + DropTableContext +} + +// DropTableParam 删表参数 +/* +Init->PreCheck->FindDatafiles->MakeHardLink->DropTable->FindLegacyHardlink->DeleteHardlink +代替 mysql 的 drop table +当 LimitSpeed == True 时 +DeleteHardlink 会按 BWLimitMB 的速度限制删除文件 +*/ +type DropTableParam struct { + Database string `json:"database" validate:"required"` + Tables []string `json:"tables" validate:"required"` + LimitSpeed bool `json:"limit_speed"` + BWLimitMB int `json:"bw_limit_mb"` + Host string `json:"host"` + Port int `json:"port"` +} + +type tableInfo struct { + tableName string + tableSchema string + engine string + tableIds []int + spaces []int + datafiles []string // 带库表路径的文件名,完整路径还需要加上 $datadir + linkfiles []string // 带库表路径的文件名,完整路径还需要加上 $datadir +} + +type DropTableContext struct { + db *sqlx.DB + dataDir string + tableInfos map[string]*tableInfo + innodbSysTables string + innodbSysDatafiles string +} + +func (d *DropTableComp) Init() (err error) { + d.tableInfos = make(map[string]*tableInfo) + + instObj := &native.InsObject{ + Host: d.Params.Host, + Port: d.Params.Port, + User: d.GeneralParam.RuntimeAccountParam.AdminUser, + Pwd: d.GeneralParam.RuntimeAccountParam.AdminPwd, + } + dbWorker, err := instObj.Conn() + if err != nil { + return err + } + d.db = dbWorker.GetSqlxDb() + + err = d.db.Get(&d.dataDir, `SELECT @@datadir`) + if err != nil { + return err + } + + var versionStr string + err = d.db.Get(&versionStr, `SELECT @@VERSION`) + if err != nil { + return err + } + + mysqlVersion := cmutil.MySQLVersionParse(versionStr) + if mysqlVersion < 5007000 { + return fmt.Errorf("%s not support", versionStr) + } + + if mysqlVersion < 8000000 { + d.innodbSysTables = `INNODB_SYS_TABLES` + d.innodbSysDatafiles = `INNODB_SYS_DATAFILES` + } else { + d.innodbSysTables = `INNODB_TABLES` + d.innodbSysDatafiles = `INNODB_DATAFILES` + } + + q, args, err := sqlx.In( + `SELECT TABLE_SCHEMA, TABLE_NAME, ENGINE + FROM INFORMATION_SCHEMA.TABLES + WHERE TABLE_SCHEMA=? AND TABLE_NAME IN (?)`, + d.Params.Database, + d.Params.Tables) + if err != nil { + return err + } + + /* + 不存在的表不会写入 tableInfos + 所以整个过程是幂等的 + */ + var res []struct { + TableSchema string `db:"TABLE_SCHEMA"` + TableName string `db:"TABLE_NAME"` + Engine string `db:"ENGINE"` + } + err = d.db.Select(&res, d.db.Rebind(q), args...) + if err != nil { + return err + } + + for _, ele := range res { + innerName := fmt.Sprintf("%s/%s", ele.TableSchema, ele.TableName) + engine := strings.ToLower(ele.Engine) + d.tableInfos[innerName] = &tableInfo{ + tableName: ele.TableName, + tableSchema: ele.TableSchema, + engine: engine, + tableIds: []int{}, + spaces: []int{}, + datafiles: []string{}, + linkfiles: []string{}, + } + } + return nil +} + +func (d *DropTableComp) PreCheck() (err error) { + for innerName, info := range d.tableInfos { + switch info.engine { + case "innodb": + err = d.preCheckInnodb(innerName, info) + if err != nil { + return err + } + case "myisam": + err = d.preCheckMyIsam(innerName, info) + if err != nil { + return err + } + default: + return fmt.Errorf("engine %s not support", info.engine) + } + } + + return nil +} + +func (d *DropTableComp) preCheckInnodb(innerName string, info *tableInfo) (err error) { + var res []*struct { + TableId int `db:"TABLE_ID"` + Name string `db:"NAME"` + Space int `db:"SPACE"` + SpaceType string `db:"SPACE_TYPE"` + IdentifierName string + /* + Name 是 filename set 格式,比如库表 `db-a.tb-b` 的 Name 是 db@002da/tb@002db + IdentifierName 是还原成 `db-a/tb-a` + */ + } + err = d.db.Select( + &res, + fmt.Sprintf( + `SELECT TABLE_ID, NAME, SPACE, SPACE_TYPE FROM INFORMATION_SCHEMA.%s WHERE NAME LIKE ?`, + d.innodbSysTables), + fmt.Sprintf("%s/%s%%", + identifiertrans.TablenameToFilename(info.tableSchema), + identifiertrans.TablenameToFilename(info.tableName)), + ) + if err != nil { + return err + } + + for _, row := range res { + s := strings.Split(row.Name, "/") + d0, err := identifiertrans.FilenameToTableName(s[0]) + if err != nil { + return err + } + + var d1 string + // 分区表的 #p.. 部分要去掉才能合法转换 + partitionPattern := regexp.MustCompile(`^(.*)(#[pP]#.*)$`) + match := partitionPattern.FindStringSubmatch(s[1]) + if match != nil { + d1, err = identifiertrans.FilenameToTableName(match[1]) + if err != nil { + return err + } + d1 = fmt.Sprintf("%s%s", d1, match[2]) + } else { + d1, err = identifiertrans.FilenameToTableName(s[1]) + if err != nil { + return err + } + } + + row.IdentifierName = fmt.Sprintf("%s/%s", d0, d1) + } + + if len(res) == 1 && res[0].IdentifierName == innerName { + /* + 普通的 innodb 表 + */ + row := res[0] + if row.Space == 0 || strings.ToLower(row.SpaceType) == "system" { + return fmt.Errorf("%s not in single table space", innerName) + } + + info.tableIds = append(info.tableIds, row.TableId) + info.spaces = append(info.spaces, row.Space) + } else if (len(res) > 1) || (len(res) == 1 && res[0].IdentifierName != innerName) { + /* + innodb 分区表 + */ + partitionPattern := regexp.MustCompile(fmt.Sprintf(`^%s#[pP]#.*$`, innerName)) + for _, row := range res { + if partitionPattern.MatchString(row.IdentifierName) { + info.tableIds = append(info.tableIds, row.TableId) + info.spaces = append(info.spaces, row.Space) + } + } + } else { + return fmt.Errorf("%s table detail not found", innerName) + } + + if len(info.spaces) == 0 || len(info.tableIds) == 0 { + return fmt.Errorf("%s table detail not found", innerName) + } + + return nil +} + +// 不知道需要检查些什么 +func (d *DropTableComp) preCheckMyIsam(innerName string, info *tableInfo) (err error) { + return nil +} + +func (d *DropTableComp) FindDatafiles() (err error) { + for innerName, info := range d.tableInfos { + switch info.engine { + case "innodb": + err = d.innodbFindDatafiles(innerName, info) + if err != nil { + return err + } + case "myisam": + err = d.myisamFindDatafiles(innerName, info) + if err != nil { + return err + } + default: + return fmt.Errorf("engine %s not support", info.engine) + } + } + + return nil +} + +func (d *DropTableComp) innodbFindDatafiles(innerName string, info *tableInfo) (err error) { + q, args, err := sqlx.In( + fmt.Sprintf( + `SELECT SPACE, PATH FROM INFORMATION_SCHEMA.%s WHERE SPACE IN (?)`, + d.innodbSysDatafiles, + ), + info.spaces) + if err != nil { + return err + } + + var res []struct { + Space int `db:"SPACE"` + Path string `db:"PATH"` + } + err = d.db.Select(&res, d.db.Rebind(q), args...) + if err != nil { + return err + } + + if len(res) != len(info.spaces) { + var missingSpaces []int + for _, space := range info.spaces { + i := slices.IndexFunc(res, func(s struct { + Space int `db:"SPACE"` + Path string `db:"PATH"` + }) bool { + return s.Space == space + }) + if i < 0 { + missingSpaces = append(missingSpaces, space) + } + } + + return fmt.Errorf("%v space not found", missingSpaces) + } + + for _, row := range res { + info.datafiles = append(info.datafiles, row.Path) + } + + return nil +} + +func (d *DropTableComp) myisamFindDatafiles(innerName string, info *tableInfo) (err error) { + dbDiskName := identifiertrans.TablenameToFilename(info.tableSchema) + tableDiskName := identifiertrans.TablenameToFilename(info.tableName) + + partitionPattern := regexp.MustCompile(fmt.Sprintf(`^%s#P#.*$`, tableDiskName)) + + err = filepath.WalkDir( + filepath.Join(d.dataDir, dbDiskName), + func(path string, de fs.DirEntry, err error) error { + if err != nil { + return fs.SkipDir + } + + /* + myisam 表有 MYD 和 MYI 两个文件 + 这里只遍历 MYD 找到后再去看 MYI,可以避免重复文件 + */ + if !de.IsDir() && filepath.Ext(de.Name()) == ".MYD" { + diskfilePrefix := strings.TrimSuffix(de.Name(), ".MYD") + + // 普通表或者分区表 + if diskfilePrefix == tableDiskName || partitionPattern.MatchString(diskfilePrefix) { + myiDiskfileName := fmt.Sprintf("%s.MYI", diskfilePrefix) + + // 获取 MYI 信息的任何错误都不能容忍 + if _, err := os.Stat(filepath.Join(d.dataDir, dbDiskName, myiDiskfileName)); err != nil { + return err + } + + info.datafiles = append(info.datafiles, + filepath.Join(dbDiskName, de.Name()), + filepath.Join(dbDiskName, myiDiskfileName)) + } + } + return nil + }) + + if err != nil { + return err + } + + return nil +} + +func (d *DropTableComp) MakeHardlink() (err error) { + for _, info := range d.tableInfos { + + for _, datafile := range info.datafiles { + datafilePath := filepath.Join(d.dataDir, datafile) + linkfile := fmt.Sprintf("%s.__HARDLINK__", datafile) + linkpath := filepath.Join(d.dataDir, linkfile) + + _, err := os.Stat(linkpath) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + err = os.Link(datafilePath, linkpath) + if err != nil { + return err + } + } else { + return err + } + } + info.linkfiles = append(info.linkfiles, linkfile) + } + } + return nil +} + +func (d *DropTableComp) DropTable() (err error) { + for _, info := range d.tableInfos { + _, err := d.db.Exec(fmt.Sprintf("DROP TABLE IF EXISTS `%s`.`%s`", info.tableSchema, info.tableName)) + if err != nil { + return err + } + } + + return nil +} + +func (d *DropTableComp) FindLegacyHardlink() (err error) { + /* + 在某些未知的情况下可能存在 + 表已经没了,但是残留了没删除的硬连接 + 所以需要找出来保证幂等 + 在 tableInfos 中不存在的表需要扫描 + */ + for _, tableName := range d.Params.Tables { + innerName := fmt.Sprintf("%s/%s", d.Params.Database, tableName) + if _, ok := d.tableInfos[innerName]; ok { + continue + } + + // 在 Param.Tables, 但是不在 tableInfos 的表会走到这里 + dbDiskName := identifiertrans.TablenameToFilename(d.Params.Database) + tableDiskName := identifiertrans.TablenameToFilename(tableName) + + partitionPattern := regexp.MustCompile(fmt.Sprintf(`^%s#P#.*$`, tableDiskName)) + + linkFilepaths, err := filepath.Glob( + filepath.Join( + d.dataDir, + dbDiskName, + fmt.Sprintf(`%s*.*.__HARDLINK__`, tableDiskName))) + if err != nil { + return err + } + + /* + 只有 linkfiles 是重要的 + tableName 和 tableSchema 也就拿来打日志 + */ + legacyInfo := tableInfo{ + tableName: tableName, + tableSchema: d.Params.Database, + engine: "", + tableIds: nil, + spaces: nil, + datafiles: []string{}, + linkfiles: []string{}, + } + for _, linkFilepath := range linkFilepaths { + linkFilename := filepath.Base(linkFilepath) + + originalName := strings.TrimSuffix(linkFilename, ".__HARDLINK__") + + ext := filepath.Ext(originalName) + originalPrefix := strings.TrimSuffix(originalName, ext) + + // 确定是 tableName 的硬连接文件了 + if originalPrefix == tableDiskName || partitionPattern.MatchString(originalPrefix) { + + _, err := os.Stat( + filepath.Join( + d.dataDir, + dbDiskName, + originalName)) + + // 如果是获得文件状态的其他错误 + if err != nil && !errors.Is(err, os.ErrNotExist) { + return err + } + if err == nil { + /* + 表不存在 + 有硬连接文件 + 表的数据文件还存在 + 这应该是不正常的 + */ + return fmt.Errorf( + "table %s.%s: %s original file %s still exists", + d.Params.Database, tableName, linkFilename, originalName) + } + + legacyInfo.linkfiles = append(legacyInfo.linkfiles, filepath.Join(dbDiskName, linkFilename)) + } + } + d.tableInfos[innerName] = &legacyInfo + } + return nil +} + +func (d *DropTableComp) DeleteHardlink() (err error) { + /* + 该做的检查前面都做了 + 这里只需要无脑删除文件 + */ + for _, info := range d.tableInfos { + for _, filename := range info.linkfiles { + if d.Params.LimitSpeed { + done := make(chan int, 1) + go func(chan int) { + osutil.PrintFileSizeIncr( + filepath.Join(d.dataDir, filename), + 1, + 10, + logger.Info, + done) + }(done) + + err = cmutil.TruncateFile( + filepath.Join(d.dataDir, filename), + d.Params.BWLimitMB) + + close(done) + } else { + err = os.Remove(filepath.Join(d.dataDir, filename)) + } + if err != nil { + return err + } + } + } + return nil +} + +func (d *DropTableComp) Example() interface{} { + comp := DropTableComp{ + GeneralParam: &components.GeneralParam{ + RuntimeAccountParam: components.RuntimeAccountParam{ + MySQLAccountParam: common.AccountAdminExample, + }}, + Params: &DropTableParam{ + Database: "test", + Tables: []string{"table1", "table2"}, + LimitSpeed: false, + BWLimitMB: 0, + Host: "x.x.x.x", + Port: 12345, + }, + } + return comp +} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/excute_sql_file.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/excute_sql_file.go index 65ccb2fc84..b84366552d 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/excute_sql_file.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/excute_sql_file.go @@ -1,8 +1,17 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + // Package mysql TODO // -// package -// ignore_dbnames: 变更时候需要忽略的dbname,支持正则匹配 [db1,db2,db3%] -// dbnames: 变更时候 需要指定的变更的库 +// ignore_dbnames: 变更时候需要忽略的dbname,支持正则匹配 [db1,db2,db3%] +// dbnames: 变更时候 需要指定的变更的库 package mysql import ( @@ -135,25 +144,6 @@ func (e *ExcuteSQLFileComp) Init() (err error) { return nil } -// MvFile2TaskDir 将gse的文件move 到taskdir -// -// @receiver e -// @receiver err -// func (e *ExcuteSQLFileComp) MvFile2TaskDir(taskdir string) (err error) { -// e.taskdir = path.Join(cst.BK_PKG_INSTALL_PATH, taskdir) -// if err = os.MkdirAll(e.taskdir, os.ModePerm); err != nil { -// logger.Error("初始化任务目录失败%s:%s", e.taskdir, err.Error()) -// return -// } -// for _, o := range e.Params.ExcuteObjects { -// if err = os.Rename(path.Join(cst.BK_PKG_INSTALL_PATH, o.SQLFile), path.Join(e.taskdir, o.SQLFile)); err != nil { -// logger.Error("将SQL文件%s移动到%s 错误:%s", o.SQLFile, e.taskdir, err.Error()) -// return -// } -// } -// return err -// } - // Excute TODO func (e *ExcuteSQLFileComp) Excute() (err error) { for _, port := range e.ports { diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/execute_partition_sql.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/execute_partition_sql.go index 031d17bca8..f67d1e6d05 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/execute_partition_sql.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/execute_partition_sql.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package mysql import ( @@ -135,13 +145,13 @@ func (e *ExcutePartitionSQLComp) Excute() (err error) { port := e.Params.MasterPort user := e.GeneralParam.RuntimeAccountParam.AdminUser pwd := e.GeneralParam.RuntimeAccountParam.AdminPwd - param := "" - if strings.Contains(e.Params.ShardName, "TDBCTL") { - param = "&tc_admin=0" - } - dbw, err := InitDB(ip, port, user, pwd, param) - defer dbw.Close() + dbw, err := initDB(ip, port, user, pwd) + defer func() { + if dbw != nil { + dbw.Close() + } + }() c := make(chan struct{}, 4) wg := &sync.WaitGroup{} errs := []string{} @@ -160,8 +170,14 @@ func (e *ExcutePartitionSQLComp) Excute() (err error) { // 每个任务中会并发执行单条sql if len(eb.InitPartition) > 0 { logger.Info(fmt.Sprintf("初始化分区,config_id=%d\n", eb.ConfigID)) - // 初始化分区使用pt工具,因此通过命令行的形式进行执行 - err = e.excuteInitSql(eb.InitPartition, errfile, 10) + if strings.Contains(e.Params.ShardName, "TDBCTL") { + // TDBCTL不使用pt工具 + initPartition := e.getInitPartitionSQL(eb.InitPartition) + err = e.excuteOne(dbw, initPartition, errfile, 10) + } else { + // 初始化分区使用pt工具,因此通过命令行的形式进行执行 + err = e.excuteInitSql(eb.InitPartition, errfile, 10) + } if err != nil { lock.Lock() errsall = append(errsall, err.Error()) @@ -269,13 +285,13 @@ func (e *ExcutePartitionSQLComp) excuteOne( return nil } -// InitDB TODO -func InitDB(host string, port int, user string, pwd string, param string) (dbw *sql.DB, err error) { +// initDB TODO +func initDB(host string, port int, user string, pwd string) (dbw *sql.DB, err error) { tcpdsn := fmt.Sprintf("%s:%d", host, port) dsn := fmt.Sprintf( - "%s:%s@tcp(%s)/?charset=utf8&parseTime=True&loc=Local&timeout=30s&readTimeout=30s&lock_wait_timeout=5%s", user, + "%s:%s@tcp(%s)/?charset=utf8&parseTime=True&loc=Local&timeout=30s&readTimeout=30s&lock_wait_timeout=5", user, pwd, - tcpdsn, param, + tcpdsn, ) SqlDB, err := sql.Open("mysql", dsn) if err != nil { @@ -290,10 +306,17 @@ func (e *ExcutePartitionSQLComp) excuteInitSql( connum int, ) (err error) { // 在执行初始化分区前,需要预先检查磁盘剩余空间是否满足初始化分区的条件 + // 使用pt工具执行初始化分区,暂时不做并发操作 errs := []string{} for _, partitionSQL := range partitionSQLSets { flag, err := e.precheck(partitionSQL.NeedSize) - command := fmt.Sprintf("%s/%s %s", cst.DBAToolkitPath, "percona-toolkit-3.5.0", partitionSQL.Sql) + pt_tool := "percona-toolkit-3.5.0/bin/pt-online-schema-change" + user := e.GeneralParam.RuntimeAccountParam.AdminUser + pwd := e.GeneralParam.RuntimeAccountParam.AdminPwd + socket := e.socket + command := fmt.Sprintf("%s/%s -u%s -p%s --socket %s %s", cst.DBAToolkitPath, + pt_tool, user, pwd, socket, partitionSQL.Sql) + if err != nil { return err } @@ -362,3 +385,12 @@ func (e *ExcutePartitionSQLComp) getPartitionInfo(filePath string) (epsos []Excu func (e *ExcutePartitionSQLComp) replace(partitionSQL string) string { return strings.Replace(partitionSQL, "`", "\\`", -1) } + +func (e *ExcutePartitionSQLComp) getInitPartitionSQL(initPartitions []InitPartitionContent) []string { + var initPartitionSQL []string + for _, initPartition := range initPartitions { + initsql := fmt.Sprintf("%s;;;%s;", "set tc_admin=0", initPartition.Sql) + initPartitionSQL = append(initPartitionSQL, initsql) + } + return initPartitionSQL +} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/find_backup_local.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/find_backup_local.go index cb8e668408..52f433c752 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/find_backup_local.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/find_backup_local.go @@ -4,6 +4,8 @@ import ( "fmt" "strings" + "github.com/spf13/cast" + "dbm-services/common/go-pubpkg/cmutil" "dbm-services/common/go-pubpkg/logger" "dbm-services/mysql/db-tools/dbactuator/pkg/components" @@ -35,7 +37,7 @@ type FindLocalBackupParam struct { // LocalBackupObj TODO type LocalBackupObj struct { - BKBizID string `json:"bk_biz_id"` + BKBizID int `json:"bk_biz_id"` // 备份所属 host InstHost string `json:"inst_host"` // 备份所属 port @@ -136,7 +138,7 @@ func (f *FindLocalBackupParam) StartOld() error { FileList: fileList, BackupType: file.BackupType, // InfoFile: file, - BKBizID: file.App, + BKBizID: cast.ToInt(file.App), // file.App BackupTime: file.StartTime, InstHost: file.BackupHost, InstPort: file.BackupPort, @@ -176,17 +178,6 @@ func (f *FindLocalBackupParam) Start() error { indexList := util.SplitAnyRune(strings.TrimSpace(out), " \n") for _, info := range indexList { file := dbbackup.BackupIndexFile{} - /* - contentBytes, err := os.ReadFile(info) - if err != nil { - return err - } - if err := json.Unmarshal(contentBytes, &file); err != nil { - logger.Error("fail to read index file to struct: %s", info) - continue - // return err - } - */ if err := dbbackup.ParseBackupIndexFile(info, &file); err != nil { logger.Warn("file %s parse error: %s", info, err.Error()) continue diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/grant/repl.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/grant/repl.go index 87f5de020e..c7e471c6b8 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/grant/repl.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/grant/repl.go @@ -67,13 +67,6 @@ func (g *GrantReplComp) Init() (err error) { g.masterVersion = ver logger.Info("Version is %s", g.masterVersion) - // 增加对tdbctl授权的判断,初始化session设置tc_admin=0 - if strings.Contains(ver, "tdbctl") { - if _, err := g.Db.Exec("set tc_admin = 0 "); err != nil { - logger.Error("set tc_admin is 0 failed %v", err) - return err - } - } return } @@ -82,6 +75,12 @@ func (g *GrantReplComp) GrantRepl() (err error) { repl_user := g.GeneralParam.RuntimeAccountParam.ReplUser repl_pwd := g.GeneralParam.RuntimeAccountParam.ReplPwd var execSQLs []string + + // 增加对tdbctl授权的判断,初始化session设置tc_admin=0 + if strings.Contains(g.masterVersion, "tdbctl") { + execSQLs = append(execSQLs, "set tc_admin = 0;") + } + for _, replHost := range g.Params.ReplHosts { execSQLs = append( execSQLs, diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/install_backup_client.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/install_backup_client.go new file mode 100644 index 0000000000..30f187e172 --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/install_backup_client.go @@ -0,0 +1,172 @@ +package mysql + +import ( + "bytes" + "fmt" + "os" + "os/user" + "path/filepath" + + "dbm-services/common/go-pubpkg/backupclient" + "dbm-services/common/go-pubpkg/cmutil" + "dbm-services/common/go-pubpkg/logger" + "dbm-services/common/go-pubpkg/netutil" + "dbm-services/mysql/db-tools/dbactuator/pkg/components" + "dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/common" + "dbm-services/mysql/db-tools/dbactuator/pkg/core/cst" + "dbm-services/mysql/db-tools/dbactuator/pkg/util/osutil" + + "github.com/BurntSushi/toml" + "github.com/pkg/errors" +) + +// InstallBackupClientComp 基本结构 +type InstallBackupClientComp struct { + GeneralParam *components.GeneralParam `json:"general"` + Params InstallBackupClientParam `json:"extend"` + configFile string + binPath string + installPath string +} + +// InstallBackupClientParam 输入参数 +type InstallBackupClientParam struct { + components.Medium + Config backupclient.CosClientConfig `json:"config" validate:"required"` // 模板配置 + CosInfo backupclient.CosInfo `json:"cosinfo" validate:"required"` + // 发起执行actor的用户,仅用于审计 + ExecUser string `json:"exec_user"` +} + +// Init 初始化 +func (c *InstallBackupClientComp) Init() (err error) { + c.installPath = cst.BackupClientInstallPath + c.binPath = filepath.Join(c.installPath, "bin/backup_client") + return nil +} + +// PreCheck 预检查 +func (c *InstallBackupClientComp) PreCheck() (err error) { + ipList := netutil.GetAllIpAddr() + if !cmutil.StringsHas(ipList, c.Params.Config.Cfg.NetAddr) { + return errors.Errorf("ipaddr %s is not in host net address list", c.Params.Config.Cfg.NetAddr) + } + if err = c.Params.Medium.Check(); err != nil { + logger.Error("check backup_client pkg failed: %s", err.Error()) + return err + } + return nil +} + +// DeployBinary 部署 backup_client 二进制 +func (c *InstallBackupClientComp) DeployBinary() (err error) { + decompressCmd := fmt.Sprintf( + `tar zxf %s -C %s`, + c.Params.Medium.GetAbsolutePath(), "/usr/local", + ) + _, err = osutil.ExecShellCommand(false, decompressCmd) + if err != nil { + logger.Error("decompress backup_client pkg failed: %s", err.Error()) + return err + } + chownCmd := fmt.Sprintf(`chown -R root.root %s && chmod +x %s`, c.installPath, c.binPath) + _, err = osutil.ExecShellCommand(false, chownCmd) + if err != nil { + logger.Error("chown %s to root failed: %s", c.installPath, err.Error()) + return err + } + return nil +} + +// GenerateBinaryConfig 生成 mysql-rotatebinlog 配置文件 +func (c *InstallBackupClientComp) GenerateBinaryConfig() (err error) { + c.configFile = filepath.Join(c.installPath, "conf/config.toml") + buf := bytes.NewBuffer([]byte{}) + if err = toml.NewEncoder(buf).Encode(&c.Params.Config); err != nil { + return errors.Wrapf(err, "write config file") + } + + if err := os.WriteFile(c.configFile, buf.Bytes(), 0644); err != nil { + return err + } + return nil +} + +func (c *InstallBackupClientComp) GenerateBucketConfig() (err error) { + mysqlUser, err := user.Lookup("mysql") + if err != nil { + return err + } + userHome := mysqlUser.HomeDir + if userHome != "" && cmutil.IsDirectory(userHome) { + + } + configFile := filepath.Join(userHome, ".cosinfo.toml") + buf := bytes.NewBuffer([]byte{}) + if err := toml.NewEncoder(buf).Encode(&c.Params.CosInfo); err != nil { + return err + } + if err := os.WriteFile(configFile, buf.Bytes(), 0644); err != nil { + return err + } + return nil +} + +// InstallCrontab 注册crontab +func (c *InstallBackupClientComp) InstallCrontab() (err error) { + err = osutil.RemoveSystemCrontab("backup_client upload") + if err != nil { + logger.Error("remove old 'backup_client upload' crontab failed: %s", err.Error()) + return err + } + uploadCrontabCmd := fmt.Sprintf("%s addcrontab -u root >/dev/null", c.binPath) + str, err := osutil.ExecShellCommand(false, uploadCrontabCmd) + if err != nil { + logger.Error( + "failed add '%s' to crond: %s(%s)", uploadCrontabCmd, str, err.Error(), + ) + } + return err +} + +// Example 样例 +func (c *InstallBackupClientComp) Example() interface{} { + return InstallBackupClientComp{ + GeneralParam: &components.GeneralParam{ + RuntimeAccountParam: components.RuntimeAccountParam{ + MySQLAccountParam: common.AccountMonitorExample, + }, + }, + Params: InstallBackupClientParam{ + Medium: components.Medium{ + Pkg: "backup_client.tar.gz", + PkgMd5: "12345", + }, + Config: backupclient.CosClientConfig{ + Base: backupclient.BaseLimit{ + BlockSize: 100, + LocalFileLimit: 100, + LocalTotalLimit: 100, + }, + Cfg: backupclient.UploadConfig{ + FileTagAllowed: "MYSQL_FULL_BACKUP,REDIS_FULL....", + NetAddr: "x.x.x.x", + }, + }, + CosInfo: backupclient.CosInfo{ + Cos: &backupclient.CosAuth{ + Region: "xxx", + BucketName: "yyy", + SecretId: "sid encrypted", + SecretKey: "skey encrypted", + CosServer: "urlxxx", + }, + AppAttr: &backupclient.AppAttr{ + BkBizId: 3, + BkCloudId: 0, + }, + }, + ExecUser: "sys", + }, + } +} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/install_checksum.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/install_checksum.go index 0371da35bf..1f4d0aaf44 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/install_checksum.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/install_checksum.go @@ -6,6 +6,7 @@ import ( "os" "os/exec" "path" + "path/filepath" "time" "dbm-services/common/go-pubpkg/logger" @@ -88,6 +89,27 @@ func (c *InstallMySQLChecksumComp) DeployBinary() (err error) { return err } + chmodCmd := fmt.Sprintf(`chmod +x %s`, filepath.Join(cst.ChecksumInstallPath, "pt-table-checksum")) + _, err = osutil.ExecShellCommand(false, chmodCmd) + if err != nil { + logger.Error("chmod pt-table-checksum failed: %s", err.Error()) + return err + } + + chmodCmd = fmt.Sprintf(`chmod +x %s`, filepath.Join(cst.ChecksumInstallPath, "pt-table-sync")) + _, err = osutil.ExecShellCommand(false, chmodCmd) + if err != nil { + logger.Error("chmod pt-table-sync failed: %s", err.Error()) + return err + } + + chmodCmd = fmt.Sprintf(`chmod +x %s`, filepath.Join(cst.ChecksumInstallPath, "mysql-table-checksum")) + _, err = osutil.ExecShellCommand(false, chmodCmd) + if err != nil { + logger.Error("chmod mysql-table-checksum failed: %s", err.Error()) + return err + } + return nil } diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/install_monitor.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/install_monitor.go index f72043e230..bb26b9e436 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/install_monitor.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/install_monitor.go @@ -6,6 +6,7 @@ import ( "os" "os/exec" "path" + "path/filepath" "time" "dbm-services/common/go-pubpkg/logger" @@ -39,7 +40,7 @@ type InstallMySQLMonitorParam struct { ItemsConfig map[string]struct { Enable *bool `json:"enable" yaml:"enable"` Schedule *string `json:"schedule" yaml:"schedule"` - MachineType string `json:"machine_type" yaml:"machine_type"` + MachineType []string `json:"machine_type" yaml:"machine_type"` Role []string `json:"role" yaml:"role"` } `json:"items_config"` } @@ -48,7 +49,7 @@ type monitorItem struct { Name string `json:"name" yaml:"name"` Enable *bool `json:"enable" yaml:"enable"` Schedule *string `json:"schedule" yaml:"schedule"` - MachineType string `json:"machine_type" yaml:"machine_type"` + MachineType []string `json:"machine_type" yaml:"machine_type"` Role []string `json:"role" yaml:"role"` } @@ -120,6 +121,27 @@ func (c *InstallMySQLMonitorComp) DeployBinary() (err error) { logger.Error("chown %s to mysql failed: %s", cst.MySQLMonitorInstallPath, err.Error()) return err } + + chmodCmd := fmt.Sprintf(`chmod +x %s`, filepath.Join(cst.MySQLMonitorInstallPath, "pt-config-diff")) + _, err = osutil.ExecShellCommand(false, chmodCmd) + if err != nil { + logger.Error("chmod pt-config-diff failed: %s", err.Error()) + return err + } + + chmodCmd = fmt.Sprintf(`chmod +x %s`, filepath.Join(cst.MySQLMonitorInstallPath, "pt-summary")) + _, err = osutil.ExecShellCommand(false, chmodCmd) + if err != nil { + logger.Error("chmod pt-summary failed: %s", err.Error()) + return err + } + + chmodCmd = fmt.Sprintf(`chmod +x %s`, filepath.Join(cst.MySQLMonitorInstallPath, "mysql-monitor")) + _, err = osutil.ExecShellCommand(false, chmodCmd) + if err != nil { + logger.Error("chmod mysql-monitor failed: %s", err.Error()) + return err + } return nil } diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/install_mysql.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/install_mysql.go index bb3d5aea81..c8b66f146e 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/install_mysql.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/install_mysql.go @@ -22,6 +22,8 @@ import ( "dbm-services/mysql/db-tools/dbactuator/pkg/util" "dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil" "dbm-services/mysql/db-tools/dbactuator/pkg/util/osutil" + "dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/spider" + "dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/masterslaveheartbeat" "github.com/pkg/errors" ) @@ -116,7 +118,7 @@ type Mysqld struct { SpiderAutoIncrementModeValue SpiderAutoIncrModeValue `json:"spider_auto_increment_mode_value"` } -// Example TODO +// Example subcommand example input func (i *InstallMySQLComp) Example() interface{} { comp := InstallMySQLComp{ Params: &InstallMySQLParams{ @@ -296,9 +298,10 @@ func (i *InstallMySQLComp) precheckMysqlProcess() (err error) { return nil } - if output, err = osutil.ExecShellCommand(false, "ps -efwww|grep -w mysqld|grep -v grep|wc -l"); err != nil { + if output, err = osutil.ExecShellCommand(false, "ps -ef|grep 'mysqld ' |grep -v grep |wc -l"); err != nil { return errors.Wrap(err, "执行ps -efwww|grep -w mysqld|grep -v grep|wc -l失败") } + fmt.Println("output", output) if mysqldNum, err = strconv.Atoi(osutil.CleanExecShellOutput(output)); err != nil { logger.Error("strconv.Atoi %s failed:%s", output, err.Error()) return err @@ -311,10 +314,10 @@ func (i *InstallMySQLComp) precheckMysqlProcess() (err error) { func (i *InstallMySQLComp) precheckMysqlPackageBitOS() error { var mysqlBits = cst.Bit64 - if strings.Contains(i.Params.MysqlVersion, cst.Bit32) { + if strings.Contains(i.Params.Medium.Pkg, cst.X32) { mysqlBits = cst.Bit32 } - if strings.Compare(mysqlBits, strconv.Itoa(cst.OSBits)) != 0 { + if mysqlBits != cst.OSBits { return fmt.Errorf("mysql 安装包的和系统不匹配,当前系统是%d", cst.OSBits) } return nil @@ -505,8 +508,8 @@ func (i *InstallMySQLComp) DecompressMysqlPkg() (err error) { } } pkgAbPath := i.Params.Medium.GetAbsolutePath() - if output, err := osutil.ExecShellCommand(false, fmt.Sprintf("tar zxf %s", pkgAbPath)); err != nil { - logger.Error("tar zxf %s error:%s,%s", pkgAbPath, output, err.Error()) + if output, err := osutil.ExecShellCommand(false, fmt.Sprintf("tar -xf %s", pkgAbPath)); err != nil { + logger.Error("tar -xf %s error:%s,%s", pkgAbPath, output, err.Error()) return err } mysqlBinaryFile := i.Params.Medium.GePkgBaseName() @@ -614,19 +617,6 @@ func (i *InstallMySQLComp) Startup() (err error) { } i.RollBackContext.AddKillProcess(pid) } - return i.linkTmpSoket() -} - -// linkTmpSoket TODO -// 软链实例socket到/tmp/mysql.sock -func (i *InstallMySQLComp) linkTmpSoket() (err error) { - if len(i.InsPorts) == 1 { - socket := i.InsSockets[i.InsPorts[0]] - if strings.TrimSpace(socket) == "" { - return nil - } - return osutil.CreateSoftLink(socket, "/tmp/mysql.sock") - } return nil } @@ -716,6 +706,12 @@ func (i *InstallMySQLComp) GetDBHAAccount(realVersion string) (initAccountsql [] func (i *InstallMySQLComp) InitDefaultPrivAndSchema() (err error) { var bsql []byte var initSQLs []string + + // 拼接tdbctl session级命令,初始化session设置tc_admin=0 + if strings.Contains(i.Params.Pkg, "tdbctl") { + initSQLs = append(initSQLs, "set tc_admin = 0;") + } + if bsql, err = staticembed.DefaultSysSchemaSQL.ReadFile(staticembed.DefaultSysSchemaSQLFileName); err != nil { logger.Error("读取嵌入文件%s失败", staticembed.DefaultSysSchemaSQLFileName) return @@ -725,25 +721,27 @@ func (i *InstallMySQLComp) InitDefaultPrivAndSchema() (err error) { initSQLs = append(initSQLs, value) } } - // 剔除最后一个空字符,spilts 会多分割出一个空字符 if len(initSQLs) < 2 { return fmt.Errorf("初始化sql为空%v", initSQLs) } - initSQLs = initSQLs[0 : len(initSQLs)-1] + if strings.Contains(i.Params.Pkg, "tdbctl") { + initSQLs = append(initSQLs, staticembed.SpiderInitSQL) + } + + // 调用 mysql-monitor 里的主从复制延迟检查心跳表, infodba_schema.master_slave_heartbeat + initSQLs = append(initSQLs, masterslaveheartbeat.DropTableSQL, masterslaveheartbeat.CreateTableSQL) + if !strings.Contains(i.Params.Pkg, "tspider") { // 避免迁移实例时,新机器还没有这个表,会同步失败 + initSQLs = append(initSQLs, spider.GetGlobalBackupSchema("InnoDB", nil)) + } + for _, port := range i.InsPorts { var dbWork *native.DbWorker if dbWork, err = native.NewDbWorker(native.DsnBySocket(i.InsSockets[port], "root", "")); err != nil { logger.Error("connenct by %s failed,err:%s", port, err.Error()) return } - // 拼接tdbctl session级命令,初始化session设置tc_admin=0 - if strings.Contains(i.Params.Pkg, "tdbctl") { - if _, err := dbWork.Exec("set tc_admin = 0 "); err != nil { - logger.Error("set tc_admin is 0 failed %v", err) - return err - } - } + // 初始化schema if _, err := dbWork.ExecMore(initSQLs); err != nil { logger.Error("flush privileges failed %v", err) @@ -763,7 +761,12 @@ func (i *InstallMySQLComp) InitDefaultPrivAndSchema() (err error) { return err } initAccountSqls = i.generateDefaultSpiderAccount(version) + } else if strings.Contains(version, "tdbctl") { + // 对tdbctl 初始化权限 + initAccountSqls = append(initAccountSqls, "set tc_admin = 0;") + initAccountSqls = append(initAccountSqls, i.generateDefaultMysqlAccount(version)...) } else { + // 默认按照mysql的初始化权限的方式 initAccountSqls = i.generateDefaultMysqlAccount(version) } // 初始化数据库之后,reset master,标记binlog重头开始,避免同步干扰 @@ -867,6 +870,7 @@ func (i *InstallMySQLComp) InstallRplSemiSyncPlugin() (err error) { } // DecompressTdbctlPkg 针对mysql-tdbctl的场景,解压并生成新的目录作为tdbctl运行目录 +// mysql 安装包可能有 .tar.gz .tar.xz 两种格式 func (i *InstallMySQLComp) DecompressTdbctlPkg() (err error) { if err = os.Chdir(i.InstallDir); err != nil { return fmt.Errorf("cd to dir %s failed, err:%w", i.InstallDir, err) @@ -892,9 +896,9 @@ func (i *InstallMySQLComp) DecompressTdbctlPkg() (err error) { pkgAbPath := i.Params.Medium.GetAbsolutePath() if output, err := osutil.ExecShellCommand( false, - fmt.Sprintf("mkdir %s && tar zxf %s -C %s --strip-components 1 ", tdbctlBinaryFile, pkgAbPath, + fmt.Sprintf("mkdir %s && tar -xf %s -C %s --strip-components 1 ", tdbctlBinaryFile, pkgAbPath, tdbctlBinaryFile)); err != nil { - logger.Error("tar zxf %s error:%s,%s", pkgAbPath, output, err.Error()) + logger.Error("tar -xf %s error:%s,%s", pkgAbPath, output, err.Error()) return err } diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/install_new_dbbackup.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/install_new_dbbackup.go index 269c188891..8d7a045164 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/install_new_dbbackup.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/install_new_dbbackup.go @@ -9,7 +9,10 @@ import ( "text/template" "time" + "gopkg.in/ini.v1" + "dbm-services/common/go-pubpkg/logger" + "dbm-services/common/go-pubpkg/mysqlcomm" "dbm-services/mysql/db-tools/dbactuator/pkg/components" "dbm-services/mysql/db-tools/dbactuator/pkg/core/cst" "dbm-services/mysql/db-tools/dbactuator/pkg/native" @@ -39,6 +42,7 @@ type logicBackupDataOption struct { // InstallNewDbBackupParam TODO type InstallNewDbBackupParam struct { components.Medium + // Configs BackupConfig Configs map[string]map[string]string `json:"configs" validate:"required"` // 模板配置 Options BackupOptions `json:"options" validate:"required"` // 选项参数配置 Host string `json:"host" validate:"required,ip"` // 当前实例的主机地址 @@ -51,13 +55,14 @@ type InstallNewDbBackupParam struct { ClusterId map[Port]int `json:"cluster_id"` // cluster id ShardValue map[Port]int `json:"shard_value"` // shard value for spider ExecUser string `json:"exec_user"` // 执行Job的用户 + UntarOnly bool `json:"untar_only"` // 只解压,不校验不渲染配置,不连接 db } type runtimeContext struct { - installPath string // dbbackupInstallPath - dbConn map[Port]*native.DbWorker // db连接池 - versionMap map[Port]string // 当前机器数据库实例版本 - renderCnf map[Port]config.BackupConfig + installPath string // dbbackupInstallPath + dbConn map[Port]*native.DbWorker // db连接池 + versionMap map[Port]string // 当前机器数据库实例版本 + renderCnf map[Port]config.BackupConfig // 绝对不能改成指针数组 ignoredbs []string ignoretbls []string } @@ -102,11 +107,17 @@ func (i *InstallNewDbBackupComp) Example() interface{} { // Init TODO func (i *InstallNewDbBackupComp) Init() (err error) { + i.Params.Role = strings.ToUpper(i.Params.Role) + i.initBackupOptions() i.installPath = path.Join(cst.MYSQL_TOOL_INSTALL_PATH, cst.BackupDir) i.dbConn = make(map[int]*native.DbWorker) i.versionMap = make(map[int]string) i.renderCnf = make(map[int]config.BackupConfig) + if i.Params.UntarOnly { + logger.Info("untar_only=true do not try to connect") + return nil + } for _, port := range i.Params.Ports { dbwork, err := native.InsObject{ Host: i.Params.Host, @@ -123,8 +134,26 @@ func (i *InstallNewDbBackupComp) Init() (err error) { return err } i.versionMap[port] = version - } + if i.Params.Role == cst.BackupRoleSpiderMaster { + tdbctlPort := mysqlcomm.GetTdbctlPortBySpider(port) + tdbctlWork, err := native.InsObject{ + Host: i.Params.Host, + Port: tdbctlPort, + User: i.GeneralParam.RuntimeAccountParam.AdminUser, + Pwd: i.GeneralParam.RuntimeAccountParam.AdminPwd, + }.Conn() + if err != nil { + return fmt.Errorf("init tdbcl conn %d failed:%w", tdbctlPort, err) + } + i.dbConn[tdbctlPort] = tdbctlWork + version, err := tdbctlWork.SelectVersion() + if err != nil { + return err + } + i.versionMap[tdbctlPort] = version + } + } logger.Info("config %v", i.Params.Configs) return nil } @@ -134,6 +163,8 @@ func (i *InstallNewDbBackupComp) initBackupOptions() { var ignoretbls, ignoredbs []string ignoredbs = strings.Split(i.Params.Options.IgnoreObjs.IgnoreDatabases, ",") ignoredbs = append(ignoredbs, native.DBSys...) + // 默认备份需要 infodba_schema 库 + ignoredbs = util.StringsRemove(ignoredbs, native.INFODBA_SCHEMA) ignoretbls = strings.Split(i.Params.Options.IgnoreObjs.IgnoreTables, ",") i.ignoredbs = util.UniqueStrings(util.RemoveEmpty(ignoredbs)) @@ -182,11 +213,16 @@ func (i *InstallNewDbBackupComp) getInsShardValue(port int) int { return 0 } -// InitRenderData 初始化待渲染的配置变量 +// InitRenderData 初始化待渲染的配置变量 renderCnf[port]: backup_configs func (i *InstallNewDbBackupComp) InitRenderData() (err error) { + if i.Params.UntarOnly { + logger.Info("untar_only=true do not need InitRenderData") + return nil + } + bkuser := i.GeneralParam.RuntimeAccountParam.DbBackupUser bkpwd := i.GeneralParam.RuntimeAccountParam.DbBackupPwd - regexfunc, err := db_table_filter.NewDbTableFilter([]string{"*"}, []string{"*"}, i.ignoredbs, i.ignoretbls) + regexfunc, err := db_table_filter.BuildMydumperRegex([]string{"*"}, []string{"*"}, i.ignoredbs, i.ignoretbls) if err != nil { return err } @@ -194,7 +230,7 @@ func (i *InstallNewDbBackupComp) InitRenderData() (err error) { logger.Info("regexStr %v", regexStr) // 根据role 选择备份参数选项 var dsg string - i.Params.Role = strings.ToUpper(i.Params.Role) + switch i.Params.Role { case cst.BackupRoleMaster, cst.BackupRoleRepeater: dsg = i.Params.Options.Master.DataSchemaGrant @@ -203,14 +239,14 @@ func (i *InstallNewDbBackupComp) InitRenderData() (err error) { case cst.BackupRoleOrphan: // orphan 使用的是 tendbsingle Master.DataSchemaGrant dsg = i.Params.Options.Master.DataSchemaGrant - case cst.RoleSpiderMaster, cst.RoleSpiderSlave: + case cst.BackupRoleSpiderMaster, cst.BackupRoleSpiderSlave: // spider 只在 spider_master and tdbctl_master 上,备份schema,grant dsg = "schema,grant" default: return fmt.Errorf("未知的备份角色%s", i.Params.Role) } for _, port := range i.Params.Ports { - i.renderCnf[port] = config.BackupConfig{ + cfg := config.BackupConfig{ Public: config.Public{ MysqlHost: i.Params.Host, MysqlPort: port, @@ -231,6 +267,15 @@ func (i *InstallNewDbBackupComp) InitRenderData() (err error) { DefaultsFile: util.GetMyCnfFileName(port), }, } + + i.renderCnf[port] = cfg + + if i.Params.Role == cst.BackupRoleSpiderMaster { + tdbctlPort := mysqlcomm.GetTdbctlPortBySpider(port) + cfg.Public.MysqlPort = tdbctlPort + cfg.PhysicalBackup.DefaultsFile = util.GetMyCnfFileName(tdbctlPort) + i.renderCnf[tdbctlPort] = cfg + } } return nil } @@ -261,8 +306,8 @@ func (i *InstallNewDbBackupComp) DecompressPkg() (err error) { return err } cmd := fmt.Sprintf( - "tar zxf %s -C %s && chown -R mysql %s", i.Params.Medium.GetAbsolutePath(), - path.Dir(i.installPath), i.installPath, + "tar zxf %s -C %s && mkdir -p %s && chown -R mysql %s", i.Params.Medium.GetAbsolutePath(), + path.Dir(i.installPath), filepath.Join(i.installPath, "logs"), i.installPath, ) output, err := osutil.ExecShellCommand(false, cmd) if err != nil { @@ -272,28 +317,58 @@ func (i *InstallNewDbBackupComp) DecompressPkg() (err error) { return nil } -// InitBackupUserPriv TODO +// InitBackupUserPriv 创建备份用户 +// TODO 用户初始化考虑在部署 mysqld 的时候进行 func (i *InstallNewDbBackupComp) InitBackupUserPriv() (err error) { + if i.Params.UntarOnly { + logger.Info("untar_only=true do not need InitBackupUserPriv") + return nil + } for _, port := range i.Params.Ports { - ver := i.versionMap[port] - var isMysql80 = mysqlutil.MySQLVersionParse(ver) >= mysqlutil.MySQLVersionParse("8.0") && - !strings.Contains(ver, "tspider") - privs := i.GeneralParam.RuntimeAccountParam.MySQLDbBackupAccount.GetAccountPrivs(isMysql80, i.Params.Host) - sqls := privs.GenerateInitSql(ver) - dc, ok := i.dbConn[port] - if !ok { - return fmt.Errorf("from dbConns 获取%d连接失败", port) + err := i.initPriv(port, false) + if err != nil { + return err } - if _, err = dc.ExecMore(sqls); err != nil { - logger.Error("初始化备份账户失败%s", err.Error()) - return + + if i.Params.Role == cst.BackupRoleSpiderMaster { + err := i.initPriv(mysqlcomm.GetTdbctlPortBySpider(port), true) + if err != nil { + return err + } } } - return + return nil +} + +func (i *InstallNewDbBackupComp) initPriv(port int, isTdbCtl bool) (err error) { + ver := i.versionMap[port] + var isMysql80 = mysqlutil.MySQLVersionParse(ver) >= mysqlutil.MySQLVersionParse("8.0") && + !strings.Contains(ver, "tspider") + logger.Info("mysql version", ver, ", is >=8.0 : ", isMysql80) + privs := i.GeneralParam.RuntimeAccountParam.MySQLDbBackupAccount.GetAccountPrivs(isMysql80, i.Params.Host) + var sqls []string + if isTdbCtl { + logger.Info("tdbctl port %d need tc_admin=0, binlog_format=off", port) + sqls = append(sqls, "set session tc_admin=0;", "set session sql_log_bin=off;") + } + sqls = append(sqls, privs.GenerateInitSql(ver)...) + dc, ok := i.dbConn[port] + if !ok { + return fmt.Errorf("from dbConns 获取%d连接失败", port) + } + if _, err = dc.ExecMore(sqls); err != nil { + logger.Error("初始化备份账户失败%s", err.Error()) + return + } + return err } // GenerateDbbackupConfig TODO func (i *InstallNewDbBackupComp) GenerateDbbackupConfig() (err error) { + if i.Params.UntarOnly { + logger.Info("untar_only=true do not need GenerateDbbackupConfig") + return nil + } // 先渲染模版配置文件 templatePath := path.Join(i.installPath, fmt.Sprintf("%s.tpl", cst.BackupFile)) if err := i.saveTplConfigfile(templatePath); err != nil { @@ -305,33 +380,64 @@ func (i *InstallNewDbBackupComp) GenerateDbbackupConfig() (err error) { return errors.WithMessage(err, "template ParseFiles failed") } - var cnfFiles []*os.File - defer func() { - for _, f := range cnfFiles { - _ = f.Close() - } - }() - for _, port := range i.Params.Ports { - cnfPath := path.Join(i.installPath, cst.GetNewConfigByPort(port)) - cnfFile, err := os.Create(cnfPath) + _, err := i.writeCnf(port, cnfTemp) if err != nil { - return errors.WithMessage(err, fmt.Sprintf("create %s failed", cnfPath)) + return err } - cnfFiles = append(cnfFiles, cnfFile) + if i.Params.Role == cst.BackupRoleSpiderMaster { + cnfPath, err := i.writeCnf(mysqlcomm.GetTdbctlPortBySpider(port), cnfTemp) + if err != nil { + return err + } - if data, ok := i.renderCnf[port]; ok { - if err = cnfTemp.Execute(cnfFile, data); err != nil { - return errors.WithMessage(err, "渲染%d的备份配置文件失败") + tdbCtlCnfIni, err := ini.Load(cnfPath) + if err != nil { + return err + } + + var tdbCtlCnf config.BackupConfig + err = tdbCtlCnfIni.MapTo(&tdbCtlCnf) + if err != nil { + return err + } + + tdbCtlCnf.LogicalBackup.DefaultsFile = filepath.Join(i.installPath, "mydumper_for_tdbctl.cnf") + err = tdbCtlCnfIni.ReflectFrom(&tdbCtlCnf) + if err != nil { + return err + } + err = tdbCtlCnfIni.SaveTo(cnfPath) + if err != nil { + return err } - } else { - return fmt.Errorf("not found %d render data", port) } } return nil } +func (i *InstallNewDbBackupComp) writeCnf(port int, tpl *template.Template) (cnfPath string, err error) { + cnfPath = path.Join(i.installPath, cst.GetNewConfigByPort(port)) + cnfFile, err := os.OpenFile(cnfPath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0755) //os.Create(cnfPath) + if err != nil { + return "", errors.WithMessage(err, fmt.Sprintf("create %s failed", cnfPath)) + } + defer func() { + _ = cnfFile.Close() + }() + + if data, ok := i.renderCnf[port]; ok { + if err = tpl.Execute(cnfFile, data); err != nil { + return "", errors.WithMessage(err, "渲染%d的备份配置文件失败") + } + } else { + return "", fmt.Errorf("not found %d render data", port) + } + + return cnfPath, nil +} + // ChownGroup 更改安装目录的所属组 func (i *InstallNewDbBackupComp) ChownGroup() (err error) { cmd := fmt.Sprintf( @@ -346,27 +452,6 @@ func (i *InstallNewDbBackupComp) ChownGroup() (err error) { return nil } -//func (i *InstallNewDbBackupComp) saveTplConfigfile(tmpl string) (err error) { -// cfg := ini.Empty() -// if err = cfg.ReflectFrom(&i.Params.Configs); err != nil { -// return errors.WithMessage(err, "参数反射ini失败") -// } -// if err := cfg.SaveTo(tmpl); err != nil { -// return errors.WithMessage(err, "保存模版配置失败") -// } -// fileinfo, err := os.Stat(tmpl) -// if err != nil { -// return errors.WithMessage(err, fmt.Sprintf("os stats file %s failed", tmpl)) -// } -// if fileinfo.Size() <= 0 { -// return fmt.Errorf("渲染的配置为空!!!") -// } -// if err := cfg.SaveTo(tmpl); err != nil { -// return errors.WithMessage(err, "保存模版配置失败") -// } -// return -//} - func (i *InstallNewDbBackupComp) saveTplConfigfile(tmpl string) (err error) { f, err := os.OpenFile(tmpl, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0755) if err != nil { @@ -382,17 +467,22 @@ func (i *InstallNewDbBackupComp) saveTplConfigfile(tmpl string) (err error) { return errors.WithMessagef(err, "写配置模版 %s 失败", k) } for k, v := range v { - _, err := fmt.Fprintf(f, "%s=%s\n", k, v) + _, err := fmt.Fprintf(f, "%s = %s\n", k, v) if err != nil { return errors.WithMessagef(err, "写配置模版 %s, %s 失败", k, v) } } + fmt.Fprintf(f, "\n") } return } // AddCrontab TODO func (i *InstallNewDbBackupComp) AddCrontab() error { + if i.Params.UntarOnly { + logger.Info("untar_only=true do not need AddCrontab") + return nil + } if i.Params.ClusterType == cst.TendbCluster { return i.addCrontabSpider() } else { @@ -423,13 +513,13 @@ func (i *InstallNewDbBackupComp) addCrontabLegacy() (err error) { func (i *InstallNewDbBackupComp) addCrontabSpider() (err error) { crondManager := ma.NewManager("http://127.0.0.1:9999") var jobItem ma.JobDefine - if i.Params.Role == cst.RoleSpiderMaster { + if i.Params.Role == cst.BackupRoleSpiderMaster { dbbckupConfFile := fmt.Sprintf("dbbackup.%d.ini", i.Params.Ports[0]) jobItem = ma.JobDefine{ Name: "spiderbackup-schedule", Command: filepath.Join(i.installPath, "dbbackup"), WorkDir: i.installPath, - Args: []string{"spiderbackup", "--schedule", "--config", dbbckupConfFile}, + Args: []string{"spiderbackup", "schedule", "--config", dbbckupConfFile}, Schedule: i.Params.Options.CrontabTime, Creator: i.Params.ExecUser, Enable: true, @@ -439,12 +529,12 @@ func (i *InstallNewDbBackupComp) addCrontabSpider() (err error) { return err } } - if !(i.Params.Role == cst.RoleSpiderMnt || i.Params.Role == cst.RoleSpiderSlave) { // MASTER,SLAVE,REPEATER + if !(i.Params.Role == cst.BackupRoleSpiderMnt || i.Params.Role == cst.BackupRoleSpiderSlave) { // MASTER,SLAVE,REPEATER jobItem = ma.JobDefine{ - Name: "spiderbackup-check-run", + Name: "spiderbackup-check", Command: filepath.Join(i.installPath, "dbbackup"), WorkDir: i.installPath, - Args: []string{"spiderbackup", "--check", "--run"}, + Args: []string{"spiderbackup", "check", "--run"}, Schedule: "*/1 * * * *", Creator: i.Params.ExecUser, Enable: true, diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/install_tokudb_plugin.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/install_tokudb_plugin.go new file mode 100644 index 0000000000..40bbc5c36f --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/install_tokudb_plugin.go @@ -0,0 +1,246 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package mysql + +import ( + "fmt" + "path" + "regexp" + "strconv" + "strings" + "sync" + + "dbm-services/common/go-pubpkg/cmutil" + "dbm-services/common/go-pubpkg/logger" + "dbm-services/mysql/db-tools/dbactuator/pkg/components" + "dbm-services/mysql/db-tools/dbactuator/pkg/components/computil" + "dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/common" + "dbm-services/mysql/db-tools/dbactuator/pkg/native" + "dbm-services/mysql/db-tools/dbactuator/pkg/util" + "dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil" + "dbm-services/mysql/db-tools/dbactuator/pkg/util/osutil" + + "github.com/pkg/errors" +) + +var installTokudbSQL = ` +set sql_log_bin=off;use mysql; +INSERT INTO plugin (name,dl) VALUES ('tokudb','ha_tokudb.so'); +INSERT INTO plugin (name,dl) VALUES ('tokudb_trx','ha_tokudb.so'); +INSERT INTO plugin (name,dl) VALUES ('tokudb_locks','ha_tokudb.so'); +INSERT INTO plugin (name,dl) VALUES ('tokudb_lock_waits','ha_tokudb.so'); +INSERT INTO plugin (name,dl) VALUES ('tokudb_file_map','ha_tokudb.so'); +INSERT INTO plugin (name,dl) VALUES ('tokudb_fractal_tree_info','ha_tokudb.so'); +INSERT INTO plugin (name,dl) VALUES ('tokudb_fractal_tree_block_map','ha_tokudb.so');` + +// EnableTokudbEngineComp TODO +type EnableTokudbEngineComp struct { + GeneralParam *components.GeneralParam `json:"general"` + Params EnableTokudbParams + enabledTokudbCtx +} + +type enabledTokudbCtx struct { + conns map[Port]*native.DbWorker + sockeMap map[Port]string + serializeCnfMap map[Port]*util.CnfFile +} + +// EnableTokudbParams TODO +type EnableTokudbParams struct { + Host string `json:"host" validate:"required,ip" ` + Ports []int `json:"ports" validate:"required,gt=0,dive"` +} + +// CloseConn TODO +func (t *EnableTokudbEngineComp) CloseConn() (err error) { + for _, v := range t.conns { + v.Db.Close() + } + return nil +} + +// Example subcommand example input +func (t *EnableTokudbEngineComp) Example() interface{} { + return &EnableTokudbEngineComp{ + Params: EnableTokudbParams{ + Host: "127.0.0.1", + Ports: []int{3306, 3307}, + }, + GeneralParam: &components.GeneralParam{ + RuntimeAccountParam: components.RuntimeAccountParam{ + MySQLAccountParam: common.MySQLAdminReplExample, + }, + }, + } +} + +// Init prepare run env +func (t *EnableTokudbEngineComp) Init() (err error) { + t.conns = make(map[Port]*native.DbWorker) + t.sockeMap = make(map[Port]string) + t.serializeCnfMap = make(map[Port]*util.CnfFile) + for _, port := range t.Params.Ports { + conn, err := native.InsObject{ + Host: t.Params.Host, + Port: port, + User: t.GeneralParam.RuntimeAccountParam.AdminUser, + Pwd: t.GeneralParam.RuntimeAccountParam.AdminPwd, + }.Conn() + if err != nil { + logger.Error("conn err:%v", err) + return err + } + ver, err := conn.SelectVersion() + if err != nil { + logger.Error("select version err:%v", err) + return err + } + // version check + if mysqlutil.GetMajorVersion(mysqlutil.MySQLVersionParse(ver)) != "5.6" { + return errors.New("tokudb engine only support mysql 5.6 vesion") + } + // my.cnf exist check + mycnfFile := util.GetMyCnfFileName(port) + if !cmutil.FileExists(mycnfFile) { + logger.Error("mycnf file not exist:%v", mycnfFile) + return errors.New("mycnf file not exist") + } + cnf, err := util.LoadMyCnfForFile(mycnfFile) + if err != nil { + logger.Error("load mycnf err:%v", err) + return err + } + // get socket path + socket, err := cnf.GetMySQLSocket() + if err != nil { + logger.Error("get mysql socket err:%v", err) + return err + } + t.sockeMap[port] = socket + t.conns[port] = conn + t.serializeCnfMap[port] = cnf + } + return t.disableHugePage() +} + +func (t *EnableTokudbEngineComp) disableHugePage() (err error) { + logger.Info("doing disable huge page ...") + output, err := osutil.StandardShellCommand(false, "echo never > /sys/kernel/mm/transparent_hugepage/enabled") + if err != nil { + logger.Error("Failed to set transparent hugepage to never: %v,output:%s", err, output) + return err + } + logger.Info("set transparent hugepage to never success") + return nil +} + +// ReWriteMyCnf 重新定义tokudb配置 +func (t *EnableTokudbEngineComp) ReWriteMyCnf() (err error) { + for _, port := range t.Params.Ports { + cfg := t.serializeCnfMap[port] + bpSize, err := cfg.GetMysqldKeyVaule("innodb_buffer_pool_size") + if err != nil { + logger.Error("get mysql innodb_buffer_pool_size err:%v", err) + return err + } + result := regexp.MustCompile(`(^\d+)(m|M|g|G)`).FindStringSubmatch(bpSize) + if len(result) != 3 { + return fmt.Errorf("can not find digit in %s", bpSize) + } + size, err := strconv.Atoi(result[1]) + if err != nil { + logger.Error("convert string to int err:%v", err) + return err + } + tokudb_cache_size := fmt.Sprintf("%d%s", size/2, result[2]) + datadir, err := cfg.GetMySQLDataDir() + if err != nil { + logger.Error("get mysql datadir from my.cnf err:%v", err) + return err + } + tokuBasedir := path.Join(datadir, "tokudb") + tokuDatadir := path.Join(tokuBasedir, "data") + tokuLogdir := path.Join(tokuBasedir, "log") + tokuTmpdir := path.Join(tokuBasedir, "tmp") + // init tokudb dir + logger.Info("init tokudb dir:%s", tokuBasedir) + stderr, err := osutil.StandardShellCommand(false, fmt.Sprintf("mkdir -p %s && chown -R mysql %s ", + strings.Join([]string{tokuDatadir, + tokuLogdir, tokuTmpdir}, " "), + tokuBasedir)) + if err != nil { + logger.Error("create dir err:%v,std err:%s", err, stderr) + return err + } + cfg.Cfg.Section("mysqld").Key("tokudb_cache_size").SetValue(tokudb_cache_size) + cfg.Cfg.Section("mysqld").Key("tokudb_data_dir").SetValue(tokuDatadir) + cfg.Cfg.Section("mysqld").Key("tokudb_log_dir").SetValue(tokuLogdir) + cfg.Cfg.Section("mysqld").Key("tokudb_tmp_dir").SetValue(tokuTmpdir) + cfg.Cfg.Section("mysqld").Key("tokudb_commit_sync").SetValue("0") + cfg.Cfg.Section("mysqld").Key("tokudb_fsync_log_period").SetValue("1000") + cfg.Cfg.Section("mysqld").Key("tokudb_lock_timeout").SetValue("50000") + cfg.Cfg.Section("mysqld").Key("tokudb_fs_reserve_percent").SetValue("0") + cfg.ReplaceValue("mysqld", "innodb_buffer_pool_size", false, "200M") + cfg.ReplaceValue("mysqld", "default_storage_engine", false, "Tokudb") + // save configuration file + if err = cfg.SafeSaveFile(false); err != nil { + logger.Error("save mysql config err:%v", err) + return err + } + } + return err +} + +// Install :install +func (t *EnableTokudbEngineComp) Install() (err error) { + wg := sync.WaitGroup{} + mu := sync.Mutex{} + errMap := make(map[Port]error) + for _, port := range t.Params.Ports { + conn, ok := t.conns[port] + if !ok { + return fmt.Errorf("port %d: conn is nil", port) + } + conn.Exec(installTokudbSQL) + wg.Add(1) + go func(port int) { + defer wg.Done() + err := computil.RestartMysqlInstanceNormal(native.InsObject{ + Host: t.Params.Host, + Port: port, + User: t.GeneralParam.RuntimeAccountParam.AdminUser, + Pwd: t.GeneralParam.RuntimeAccountParam.AdminPwd, + Socket: t.sockeMap[port], + }) + if err != nil { + logger.Error("restart mysql %d instance err:%v", port, err) + mu.Lock() + errMap[port] = err + mu.Unlock() + } + }(port) + } + wg.Wait() + if len(errMap) <= 0 { + return nil + } + var gerr error + for port, errx := range errMap { + logger.Error("restart mysql %d instance err:%v", port, errx) + if gerr == nil { + gerr = errx + } else { + gerr = fmt.Errorf("%v,\n%v", gerr, errx) + } + } + return gerr +} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/open_area_dump_schema.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/open_area_dump_schema.go new file mode 100644 index 0000000000..56b9041b40 --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/open_area_dump_schema.go @@ -0,0 +1,335 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package mysql + +import ( + "encoding/json" + "fmt" + "net/url" + "os" + "path" + "reflect" + "strings" + + "dbm-services/common/go-pubpkg/bkrepo" + "dbm-services/common/go-pubpkg/logger" + "dbm-services/mysql/db-tools/dbactuator/pkg/components" + "dbm-services/mysql/db-tools/dbactuator/pkg/core/cst" + "dbm-services/mysql/db-tools/dbactuator/pkg/native" + "dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil" + "dbm-services/mysql/db-tools/dbactuator/pkg/util/osutil" +) + +// OpenAreaDumpSchemaComp TODO +type OpenAreaDumpSchemaComp struct { + GeneralParam *components.GeneralParam `json:"general"` + Params OpenAreaDumpSchemaParam `json:"extend"` + OpenAreaDumpSchemaRunTimeCtx `json:"-"` +} + +// OpenAreaDumpSchemaParam TODO +type OpenAreaDumpSchemaParam struct { + Host string `json:"host" validate:"required,ip"` // 当前实例的主机地址 + Port int `json:"port" validate:"required,lt=65536,gte=3306"` // 当前实例的端口 + CharSet string `json:"charset" validate:"required,checkCharset"` // 字符集参数 传default过来,按照源数据库的字符集 + RootId string `json:"root_id"` + BkCloudId int `json:"bk_cloud_id"` + DBCloudToken string `json:"db_cloud_token"` + DumpDirName string `json:"dump_dir_name"` // dump目录名称 {}_schema {}_data + FileServer FileServer `json:"fileserver"` + OpenAreaParam []OneOpenAreaSchema `json:"open_area_param"` +} + +// OneOpenAreaSchema 用于存放一个区库表的信息 +type OneOpenAreaSchema struct { + Schema string `json:"schema"` // 指定dump的库 + Tbales []string `json:"tables"` +} + +// OpenAreaDumpSchemaRunTimeCtx TODO +type OpenAreaDumpSchemaRunTimeCtx struct { + charset string // 当前实例的字符集 + dumpCmd string // 中控位置不一样 + isSpider bool // 是否spider中控 + dumpDirPath string // dump目录绝对路径 + tarName string // 压缩文件名称 {}.tar.gz + workDir string // schema目录所在的位置 即位于/data/install/mysql_open_area + uploadFile []UploadFile +} + +// UploadFile TODO +type UploadFile struct { + FilePath string // 上传文件的绝对路径 + FileName string // 上传文件的名称 +} + +// Example TODO +func (c *OpenAreaDumpSchemaComp) Example() interface{} { + comp := OpenAreaDumpSchemaComp{ + Params: OpenAreaDumpSchemaParam{ + Host: "0.0.0.0", + Port: 3306, + CharSet: "default", + RootId: "xxxxxxx", + OpenAreaParam: []OneOpenAreaSchema{ + { + Schema: "data1", + Tbales: []string{"tb1", "tb2"}, + }, + { + Schema: "data2", + Tbales: []string{"tb1", "tb2"}, + }, + }, + }, + } + return comp +} + +// Init TODO +func (c *OpenAreaDumpSchemaComp) Init() (err error) { + // 连接实例,查询版本和字符集 同时根据版本确认是否为中控 + conn, err := native.InsObject{ + Host: c.Params.Host, + Port: c.Params.Port, + User: c.GeneralParam.RuntimeAccountParam.AdminUser, + Pwd: c.GeneralParam.RuntimeAccountParam.AdminPwd, + }.Conn() + if err != nil { + logger.Error("Connect %d failed:%s", c.Params.Port, err.Error()) + return err + } + // 获取版本,下面通过版本判断是否是中控节点 + version, err := conn.SelectVersion() + if err != nil { + logger.Error("获取version failed %s", err.Error()) + return err + } + c.isSpider = strings.Contains(version, "tdbctl") + c.charset = c.Params.CharSet + if c.Params.CharSet == "default" { + if c.charset, err = conn.ShowServerCharset(); err != nil { + logger.Error("获取实例的字符集失败:%s", err.Error()) + return err + } + } + c.workDir = path.Join(cst.BK_PKG_INSTALL_PATH, "mysql_open_area") + c.dumpDirPath = path.Join(c.workDir, c.Params.DumpDirName) + c.tarName = fmt.Sprintf("%s.tar.gz", c.Params.DumpDirName) + err = os.MkdirAll(c.dumpDirPath, 0755) + if err != nil { + logger.Error("开区目录创建失败!%s", err.Error()) + return err + } + + return nil +} + +// Precheck TODO +func (c *OpenAreaDumpSchemaComp) Precheck() (err error) { + c.dumpCmd = path.Join(cst.MysqldInstallPath, "bin", "mysqldump") + if c.isSpider { + c.dumpCmd = path.Join(cst.TdbctlInstallPath, "bin", "mysqldump") + } + if !osutil.FileExist(c.dumpCmd) { + return fmt.Errorf("dumpCmd: %s文件不存在", c.dumpCmd) + } + return +} + +// OpenAreaDumpSchema TODO +func (c *OpenAreaDumpSchemaComp) OpenAreaDumpSchema() (err error) { + + for _, oneOpenAreaSchema := range c.Params.OpenAreaParam { + var dumper mysqlutil.Dumper + outputfileName := fmt.Sprintf("%s.sql", oneOpenAreaSchema.Schema) + schema := fmt.Sprintf("%s %s", + oneOpenAreaSchema.Schema, strings.Join(oneOpenAreaSchema.Tbales, " "), + ) + + dumper = &mysqlutil.MySQLDumperTogether{ + MySQLDumper: mysqlutil.MySQLDumper{ + DumpDir: c.dumpDirPath, + Ip: c.Params.Host, + Port: c.Params.Port, + DbBackupUser: c.GeneralParam.RuntimeAccountParam.AdminUser, + DbBackupPwd: c.GeneralParam.RuntimeAccountParam.AdminPwd, + DbNames: []string{schema}, + DumpCmdFile: c.dumpCmd, + Charset: c.charset, + MySQLDumpOption: mysqlutil.MySQLDumpOption{ + NoData: true, + AddDropTable: false, + DumpRoutine: true, + DumpTrigger: false, + GtidPurgedOff: true, + }, + }, + OutputfileName: outputfileName, + } + if err := dumper.Dump(); err != nil { + logger.Error("dump failed: ", err.Error()) + return err + } + } + + return nil +} + +// OpenAreaDumpData TODO +func (c *OpenAreaDumpSchemaComp) OpenAreaDumpData() (err error) { + + for _, oneOpenAreaSchema := range c.Params.OpenAreaParam { + var dumper mysqlutil.Dumper + if len(oneOpenAreaSchema.Tbales) == 0 { + + continue + } + outputfileName := fmt.Sprintf("%s.sql", oneOpenAreaSchema.Schema) + schema := fmt.Sprintf("%s %s", + oneOpenAreaSchema.Schema, strings.Join(oneOpenAreaSchema.Tbales, " "), + ) + + dumper = &mysqlutil.MySQLDumperTogether{ + MySQLDumper: mysqlutil.MySQLDumper{ + DumpDir: c.dumpDirPath, + Ip: c.Params.Host, + Port: c.Params.Port, + DbBackupUser: c.GeneralParam.RuntimeAccountParam.AdminUser, + DbBackupPwd: c.GeneralParam.RuntimeAccountParam.AdminPwd, + DbNames: []string{schema}, + DumpCmdFile: c.dumpCmd, + Charset: c.charset, + MySQLDumpOption: mysqlutil.MySQLDumpOption{ + NoData: false, + AddDropTable: false, + NeedUseDb: false, + NoCreateTb: true, + DumpRoutine: false, + }, + }, + OutputfileName: outputfileName, + } + if err := dumper.Dump(); err != nil { + logger.Error("dump failed: ", err.Error()) + return err + } + } + + return nil +} + +// CompressDumpDir TODO +func (c *OpenAreaDumpSchemaComp) CompressDumpDir() (err error) { + // // 如果不上传制品库,则不用压缩 + // if reflect.DeepEqual(c.Params.FileServer, FileServer{}) { + // logger.Info("the fileserver parameter is empty no upload is required ~") + // return nil + // } + // tarPath是开区目录压缩文件的绝对路径 + tarPath := path.Join(c.workDir, c.tarName) + schemaInfo := UploadFile{ + FilePath: tarPath, + FileName: c.tarName, + } + c.uploadFile = append(c.uploadFile, schemaInfo) + tarCmd := fmt.Sprintf("tar -zcf %s -C %s %s", tarPath, c.workDir, c.Params.DumpDirName) + output, err := osutil.ExecShellCommand(false, tarCmd) + if err != nil { + logger.Error("execute(%s) get an error:%s,%s", tarCmd, output, err.Error()) + return err + } + + // 获取压缩文件的MD5 + md5Val, err := osutil.GetFileMd5(tarPath) + if err != nil { + logger.Error("Failed to obtain the MD5 value of the file!Error:%s", err.Error()) + return err + } + md5FileName := fmt.Sprintf("%s.md5sum", c.Params.DumpDirName) + md5FilePath := path.Join(c.workDir, md5FileName) + md5Info := UploadFile{ + FilePath: md5FilePath, + FileName: md5FileName, + } + c.uploadFile = append(c.uploadFile, md5Info) + md5File, err := os.Create(md5FilePath) + if err != nil { + logger.Error("create file(%s) get an error:%s", md5FileName, err.Error()) + return err + } + defer md5File.Close() + _, err = md5File.WriteString(md5Val) + if err != nil { + logger.Error("Write md5 value(%s) to file(%s) error: %s", md5Val, md5FileName, err.Error()) + return err + } + return nil +} + +// Upload TODO +func (c *OpenAreaDumpSchemaComp) Upload() (err error) { + // 这里不传FileServer相关内容,则不会上传到制品库 + if reflect.DeepEqual(c.Params.FileServer, FileServer{}) { + logger.Info("the fileserver parameter is empty no upload is required ~") + return nil + } + + for _, uf := range c.uploadFile { + r := path.Join("generic", c.Params.FileServer.Project, c.Params.FileServer.Bucket, c.Params.FileServer.UploadPath) + uploadUrl, err := url.JoinPath(c.Params.FileServer.URL, r, "/") + if err != nil { + logger.Error("call url joinPath failed %s ", err.Error()) + return err + } + if c.Params.BkCloudId == 0 { + // 此处设置上传的路径,注意最后是待上传文件名,不是文件路径 + uploadUrl, err = url.JoinPath( + c.Params.FileServer.URL, path.Join( + "/generic", c.Params.FileServer.Project, + c.Params.FileServer.Bucket, c.Params.FileServer.UploadPath, uf.FileName, + ), + ) + if err != nil { + logger.Error("call url joinPath failed %s ", err.Error()) + return err + } + } + logger.Info("bk_cloud_id:%d,upload url:%s", c.Params.BkCloudId, uploadUrl) + resp, err := bkrepo.UploadFile( + uf.FilePath, uploadUrl, c.Params.FileServer.Username, c.Params.FileServer.Password, + c.Params.BkCloudId, c.Params.DBCloudToken, + ) + if err != nil { + logger.Error("upload sqlfile error %s", err.Error()) + return err + } + if resp.Code != 0 { + errMsg := fmt.Sprintf( + "upload respone code is %d,respone msg:%s,traceId:%s", + resp.Code, + resp.Message, + resp.RequestId, + ) + logger.Error(errMsg) + return fmt.Errorf(errMsg) + } + logger.Info("Resp: code:%d,msg:%s,traceid:%s", resp.Code, resp.Message, resp.RequestId) + var uploadRespdata bkrepo.UploadRespData + if err := json.Unmarshal(resp.Data, &uploadRespdata); err != nil { + logger.Error("unmarshal upload respone data failed %s", err.Error()) + return err + } + logger.Info("%v", uploadRespdata) + } + + return nil +} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/open_area_import_schema.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/open_area_import_schema.go new file mode 100644 index 0000000000..17a8cb8ed7 --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/open_area_import_schema.go @@ -0,0 +1,278 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package mysql + +import ( + "fmt" + "os" + "path" + "regexp" + + "dbm-services/bigdata/db-tools/dbactuator/pkg/util" + "dbm-services/common/go-pubpkg/logger" + "dbm-services/mysql/db-tools/dbactuator/pkg/components" + "dbm-services/mysql/db-tools/dbactuator/pkg/core/cst" + "dbm-services/mysql/db-tools/dbactuator/pkg/native" + "dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil" + "dbm-services/mysql/db-tools/dbactuator/pkg/util/osutil" + + "github.com/pkg/errors" +) + +// OpenAreaImportSchemaComp TODO +type OpenAreaImportSchemaComp struct { + GeneralParam *components.GeneralParam `json:"general"` + Params OpenAreaImportSchemaParam `json:"extend"` + OpenAreaImportSchemaRunTimeCtx `json:"-"` +} + +// OpenAreaImportSchemaParam TODO +type OpenAreaImportSchemaParam struct { + Host string `json:"host" validate:"required,ip"` + Port int `json:"port" validate:"required,lt=65536,gte=3306"` + CharSet string `json:"charSet" validate:"required,checkCharset"` + RootId string `json:"root_id"` + BkCloudId int `json:"bk_cloud_id"` + DumpDirName string `json:"dump_dir_name"` // dump目录名称 {}_schema {}_data + DBCloudToken string `json:"db_cloud_token"` + OpenAreaParam []OneOpenAreaImportSchema `json:"open_area_param"` +} + +// OneOpenAreaImportSchema TODO +type OneOpenAreaImportSchema struct { + Schema string `json:"schema"` // 指定dump的库 + NewDB string `json:"newdb"` +} + +// OpenAreaImportSchemaRunTimeCtx TODO +type OpenAreaImportSchemaRunTimeCtx struct { + charset string // 当前实例的字符集 + workDir string + tarFilePath string + md5FilePath string + dumpDir string + conn *native.DbWorker + socket string +} + +// Example TODO +func (c *OpenAreaImportSchemaComp) Example() interface{} { + comp := OpenAreaImportSchemaComp{ + Params: OpenAreaImportSchemaParam{ + Host: "0.0.0.0", + Port: 3306, + CharSet: "default", + RootId: "xxxxxxx", + OpenAreaParam: []OneOpenAreaImportSchema{ + { + Schema: "data1", + NewDB: "data1-1001", + }, + { + Schema: "data2", + NewDB: "data2-1001", + }, + }, + }, + } + return comp +} + +// Init TODO +func (c *OpenAreaImportSchemaComp) Init() (err error) { + // 连接实例,确认字符集 + c.conn, err = native.InsObject{ + Host: c.Params.Host, + Port: c.Params.Port, + User: c.GeneralParam.RuntimeAccountParam.AdminUser, + Pwd: c.GeneralParam.RuntimeAccountParam.AdminPwd, + }.Conn() + if err != nil { + logger.Error("Connect %d failed:%s", c.Params.Port, err.Error()) + return err + } + c.charset = c.Params.CharSet + if c.Params.CharSet == "default" { + if c.charset, err = c.conn.ShowServerCharset(); err != nil { + logger.Error("获取实例的字符集失败:%s", err.Error()) + return err + } + } + c.socket, err = c.conn.ShowSocket() + if err != nil { + logger.Error("get socket failed!error:", err.Error()) + return err + } + c.workDir = path.Join(cst.BK_PKG_INSTALL_PATH, "mysql_open_area") + // 绝对路径 + tarFileName := fmt.Sprintf("%s.tar.gz", c.Params.DumpDirName) + c.tarFilePath = path.Join(c.workDir, tarFileName) + md5FileName := fmt.Sprintf("%s.md5sum", c.Params.DumpDirName) + c.md5FilePath = path.Join(c.workDir, md5FileName) + c.dumpDir = path.Join(c.workDir, c.Params.DumpDirName) + return +} + +// Precheck TODO +func (c *OpenAreaImportSchemaComp) Precheck() (err error) { + if !util.FileExists(c.tarFilePath) { + logger.Error("tar file(*s) does not exist.", c.tarFilePath) + return errors.New("压缩文件不存在") + } + if !util.FileExists(c.md5FilePath) { + logger.Error("tar file(*s) does not exist.", c.tarFilePath) + return errors.New("md5sum文件不存在") + } + return +} + +// DecompressDumpDir TODO +func (c *OpenAreaImportSchemaComp) DecompressDumpDir() (err error) { + md5Byte, err := os.ReadFile(c.md5FilePath) + if err != nil { + logger.Error("read md5sum(%s) file got an error:%s", c.md5FilePath, err.Error()) + return err + } + realMd5sumVal, err := osutil.GetFileMd5(c.tarFilePath) + if err != nil { + logger.Error("get real md5sum value failed!") + return err + } + sourceMd5sumVal := string(md5Byte) + if sourceMd5sumVal != realMd5sumVal { + msg := fmt.Sprintf("realMD5Sum(%s) is not equal to md5sum(%s) recored in the file(%s)", + realMd5sumVal, sourceMd5sumVal, c.md5FilePath) + logger.Error(msg) + return errors.New(msg) + } + logger.Info("get tar file sucess!") + decopressCmd := fmt.Sprintf("tar -zxf %s -C %s", c.tarFilePath, c.workDir) + output, err := osutil.ExecShellCommand(false, decopressCmd) + if err != nil { + logger.Error("execute(%s) get an error:%s,%s", decopressCmd, output, err.Error()) + return err + } + + return +} + +// EraseAutoIncrement TODO +func (c *OpenAreaImportSchemaComp) EraseAutoIncrement() (err error) { + for _, oneSchemaInfo := range c.Params.OpenAreaParam { + schemaFilePath := fmt.Sprintf("%s/%s.sql", c.dumpDir, oneSchemaInfo.Schema) + schemaContent, err := os.ReadFile(schemaFilePath) + if err != nil { + logger.Error("read file(%s) got an error:%s", schemaFilePath, err.Error()) + return err + } + reg, err := regexp.Compile(`(?i)AUTO_INCREMENT=(\d+)`) + if err != nil { + logger.Error("regexp.Compile failed:%s", err.Error()) + return err + } + reg2, err := regexp.Compile(`(?i)SET tc_admin=0`) + if err != nil { + logger.Error("regexp.Compile failed:%s", err.Error()) + return err + } + reg3, err := regexp.Compile(`\/\*!(.*?)\*\/;`) + if err != nil { + logger.Error("regexp.Compile failed:%s", err.Error()) + return err + } + newSchemaContent := reg.ReplaceAllString(string(schemaContent), "") + newSchemaContent = reg2.ReplaceAllString(newSchemaContent, "SET tc_admin=1") + newSchemaContent = reg3.ReplaceAllString(newSchemaContent, "") + newSchemaFilePath := fmt.Sprintf("%s.new", schemaFilePath) + + f, err := os.Create(newSchemaFilePath) + if err != nil { + logger.Error("create file(%s) error:%s", newSchemaFilePath, err.Error()) + return err + } + _, err = f.WriteString(newSchemaContent) + if err != nil { + logger.Error("write file(%s) error:%s", newSchemaFilePath, err.Error()) + return err + } + } + return nil +} + +// CreateNewDatabase TODO +func (c *OpenAreaImportSchemaComp) CreateNewDatabase() (err error) { + for _, oneShemaInfo := range c.Params.OpenAreaParam { + createDBSql := fmt.Sprintf("create database if not exists `%s` charset %s;", + oneShemaInfo.NewDB, c.charset) + _, err := c.conn.Exec(createDBSql) + if err != nil { + logger.Error("create db %s got an error:%s", oneShemaInfo.NewDB, err.Error()) + return err + } + } + return +} + +// OpenAreaImportSchema TODO +func (c *OpenAreaImportSchemaComp) OpenAreaImportSchema() (err error) { + for _, oneShemaInfo := range c.Params.OpenAreaParam { + schemaName := fmt.Sprintf("%s.sql.new", oneShemaInfo.Schema) + err = mysqlutil.ExecuteSqlAtLocal{ + IsForce: false, + Charset: c.charset, + NeedShowWarnings: false, + Host: c.Params.Host, + Port: c.Params.Port, + Socket: c.socket, + WorkDir: c.dumpDir, + User: c.GeneralParam.RuntimeAccountParam.AdminUser, + Password: c.GeneralParam.RuntimeAccountParam.AdminPwd, + }.ExcuteSqlByMySQLClientOne(schemaName, oneShemaInfo.NewDB) + if err != nil { + logger.Error("执行%s文件失败!", schemaName) + return err + } + } + return nil +} + +// OpenAreaImportData TODO +func (c *OpenAreaImportSchemaComp) OpenAreaImportData() (err error) { + for _, oneShemaInfo := range c.Params.OpenAreaParam { + dataFileName := fmt.Sprintf("%s.sql", oneShemaInfo.Schema) + + fmt.Printf("%+v\n", c) + fmt.Println(dataFileName) + fmt.Println(oneShemaInfo.NewDB) + + err = mysqlutil.ExecuteSqlAtLocal{ + IsForce: false, + Charset: c.charset, + NeedShowWarnings: false, + Host: c.Params.Host, + Port: c.Params.Port, + Socket: c.socket, + WorkDir: c.dumpDir, + User: c.GeneralParam.RuntimeAccountParam.AdminUser, + Password: c.GeneralParam.RuntimeAccountParam.AdminPwd, + }.ExcuteSqlByMySQLClientOne(dataFileName, oneShemaInfo.NewDB) + if err != nil { + logger.Error("执行%s文件失败!", dataFileName) + return err + } + } + return nil +} + +// CleanDumpDir TODO +func (c *OpenAreaImportSchemaComp) CleanDumpDir() (err error) { + return +} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/oscmd_run.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/oscmd_run.go new file mode 100644 index 0000000000..9db910ce38 --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/oscmd_run.go @@ -0,0 +1,134 @@ +package mysql + +import ( + "encoding/json" + "fmt" + "strings" + + "dbm-services/common/go-pubpkg/logger" + + "github.com/pkg/errors" + + "dbm-services/common/go-pubpkg/cmutil" + "dbm-services/mysql/db-tools/dbactuator/pkg/components" +) + +// OSCmdRunComp TODO +type OSCmdRunComp struct { + Params OSCmds `json:"extend"` + results []*SimpleCmdResult + errIndex int +} + +// OSCmds TODO +type OSCmds struct { + Cmds []SimpleCmd `json:"cmds" validate:"required"` + WorkDir string `json:"work_dir"` + RunUser string `json:"run_user"` +} +type SimpleCmd struct { + CmdName string `json:"cmd_name" validate:"required"` + CmdArgs []string `json:"cmd_args"` +} + +type SimpleCmdResult struct { + CmdLine string `json:"cmd_line"` + CmdStdout string `json:"cmd_stdout"` + CmdStderr string `json:"cmd_stderr"` + ErrMsg string `json:"err_msg"` + err error +} + +type OSCmdRunResp struct { + Code int `json:"code"` + Message string `json:"message"` + Data []*SimpleCmdResult `json:"data"` +} + +func (s *SimpleCmd) Run(workDir string) *SimpleCmdResult { + cmdLine := fmt.Sprintf(`%s %s`, s.CmdName, strings.Join(s.CmdArgs, " ")) + cmdResult := &SimpleCmdResult{CmdLine: cmdLine} + if strings.Contains(cmdLine, ";") { + cmdResult.err = errors.New("danger cmd") + cmdResult.ErrMsg = cmdResult.err.Error() + return cmdResult + } + fmt.Println(cmdLine) + switch s.CmdName { + case "mkdir", "ls", "cd", "chown", "chmod", "du", "df", "head", "tail", "grep": + logger.Info("oscmd_run command:", cmdLine) + stdout, stderr, err := cmutil.ExecCommand(false, workDir, s.CmdName, s.CmdArgs...) + cmdResult.CmdStdout = stdout + cmdResult.CmdStderr = stderr + cmdResult.err = err + default: + cmdResult.err = errors.New("cmd_name is not allowed") + cmdResult.ErrMsg = cmdResult.err.Error() + return cmdResult + } + if cmdResult.err != nil { + cmdResult.ErrMsg = cmdResult.err.Error() + } + return cmdResult +} + +// Example TODO +func (s *OSCmdRunComp) Example() interface{} { + comp := OSCmdRunComp{ + Params: OSCmds{ + Cmds: []SimpleCmd{ + {CmdName: "mkdir", CmdArgs: []string{"/data/dbbak/123"}}, + {CmdName: "ls", CmdArgs: []string{"/data/dbbak"}}, + }}, + } + return comp +} + +// String 用于打印 +func (s *OSCmdRunComp) String() string { + str, _ := json.Marshal(s) + return string(str) +} + +// Start TODO +func (s *OSCmdRunComp) Start() error { + s.errIndex = -1 + for i, c := range s.Params.Cmds { + res := c.Run(s.Params.WorkDir) + s.results = append(s.results, res) + if res.err != nil { + s.errIndex = i + _ = s.OutputCtx() + return res.err + } + } + return s.OutputCtx() +} + +// WaitDone TODO +func (s *OSCmdRunComp) WaitDone() error { + return nil +} + +// OutputCtx TODO +func (s *OSCmdRunComp) OutputCtx() error { + var resp OSCmdRunResp + if s.errIndex >= 0 { + resp = OSCmdRunResp{ + Message: s.results[s.errIndex].CmdStderr, + Code: 1, + Data: s.results, + } + } else { + resp = OSCmdRunResp{ + Code: 0, + Data: s.results, + } + } + ss, err := components.WrapperOutput(resp) + if err != nil { + return err + } + fmt.Println(ss) + return nil +} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/osinfo_get.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/osinfo_get.go new file mode 100644 index 0000000000..c4b64da869 --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/osinfo_get.go @@ -0,0 +1,90 @@ +package mysql + +import ( + "encoding/json" + "fmt" + + "dbm-services/common/go-pubpkg/cmutil" + "dbm-services/mysql/db-tools/dbactuator/pkg/components" +) + +// OSInfoGetComp TODO +type OSInfoGetComp struct { + Params OSInfoParam `json:"extend"` + result OSInfoResult + defaultParams bool +} + +// OSInfoParam TODO +type OSInfoParam struct { + // Directory default [/ /data /data1 /data2] + Directory []string `json:"directory"` + NoCheckDevice bool `json:"no_check_device"` +} + +type OSInfoResult struct { + Mem *cmutil.MemoryInfo `json:"mem"` + Cpu *cmutil.CPUInfo `json:"cpu"` + Disk []*cmutil.DiskPartInfo `json:"disk"` +} + +// Example TODO +func (s *OSInfoGetComp) Example() interface{} { + comp := OSInfoGetComp{ + Params: OSInfoParam{ + Directory: []string{"/data/dbbak", "/data1/dbbak"}, + NoCheckDevice: false, + }, + } + return comp +} + +// String 用于打印 +func (s *OSInfoGetComp) String() string { + str, _ := json.Marshal(s) + return string(str) +} + +// Start TODO +func (s *OSInfoGetComp) Start() (err error) { + res := OSInfoResult{} + res.Mem, err = cmutil.GetMemoryInfo() + if err != nil { + return err + } + res.Cpu, err = cmutil.GetCPUInfo() + if err != nil { + return err + } + if len(s.Params.Directory) == 0 { + s.defaultParams = true + s.Params.Directory = []string{"/", "/data", "/data1", "/data2"} + } + for _, dir := range s.Params.Directory { + disk, err := cmutil.GetDiskPartInfo(dir, s.Params.NoCheckDevice) + if err != nil { + if s.defaultParams { + continue + } + return err + } + res.Disk = append(res.Disk, disk) + } + s.result = res + return nil +} + +// WaitDone TODO +func (s *OSInfoGetComp) WaitDone() error { + return nil +} + +// OutputCtx TODO +func (s *OSInfoGetComp) OutputCtx() error { + ss, err := components.WrapperOutput(s.result) + if err != nil { + return err + } + fmt.Println(ss) + return nil +} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/pt_table_checksum.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/pt_table_checksum.go index c5b36bf048..dbe1697759 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/pt_table_checksum.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/pt_table_checksum.go @@ -331,12 +331,11 @@ func (c *PtTableChecksumComp) transformFilter() (*_ptFilters, error) { c.Params.DbPatterns, c.Params.TablePatterns, c.Params.IgnoreDbs, - c.Params.IgnoreTables, - ) + c.Params.IgnoreTables) if err != nil { return nil, err } - + filter.BuildFilter() logger.Info("filter: %v", filter) var res _ptFilters err = c.transformInclude(&res, filter) diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/restore/dbloader/dbloader_util.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/restore/dbloader/dbloader_util.go index 878c6e39f6..1ea45fc664 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/restore/dbloader/dbloader_util.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/restore/dbloader/dbloader_util.go @@ -12,8 +12,8 @@ type LoaderUtil struct { TgtInstance native.InsObject `json:"tgt_instance"` IndexObj *dbbackup.BackupIndexFile - // 不写 binlog -without-binlog: set sql_log_bin=0 - WithOutBinlog bool `json:"withoutBinlog"` + // EnableBinlog 导入数据时是否写binlog,默认不启用 (set sql_log_bin=0) + EnableBinlog bool `json:"enable_binlog"` IndexFilePath string `json:"index_file_path" validate:"required"` LoaderDir string `json:"loader_dir"` TaskDir string `json:"taskDir"` diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/restore/dbloader/logical_loader.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/restore/dbloader/logical_loader.go index 346a41fd96..570a897daa 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/restore/dbloader/logical_loader.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/restore/dbloader/logical_loader.go @@ -3,12 +3,14 @@ package dbloader import ( "fmt" "path/filepath" + "strings" "github.com/pkg/errors" "gopkg.in/ini.v1" "dbm-services/common/go-pubpkg/cmutil" "dbm-services/common/go-pubpkg/logger" + "dbm-services/mysql/db-tools/dbactuator/pkg/native" "dbm-services/mysql/db-tools/dbactuator/pkg/util/db_table_filter" "dbm-services/mysql/db-tools/mysql-dbbackup/pkg/config" ) @@ -35,13 +37,16 @@ func (l *LogicalLoader) CreateConfigFile() error { MysqlLoadDir: p.LoaderDir, IndexFilePath: p.IndexFilePath, Threads: 4, - EnableBinlog: true, + EnableBinlog: p.EnableBinlog, Regex: l.myloaderRegex, } if loaderConfig.MysqlCharset == "" { loaderConfig.MysqlCharset = "binary" } - logger.Info("dbloader config file, %+v", loaderConfig) + if l.doDr { + loaderConfig.DBListDropIfExists = native.INFODBA_SCHEMA + } + //logger.Info("dbloader config file, %+v", loaderConfig) // 有密码打印 f := ini.Empty() section, err := f.NewSection("LogicalLoad") @@ -56,7 +61,7 @@ func (l *LogicalLoader) CreateConfigFile() error { return errors.Wrap(err, "create config") } p.cfgFilePath = cfgFilePath - logger.Info("tmp dbloader config file %s", p.cfgFilePath) + //logger.Info("tmp dbloader config file %s", p.cfgFilePath) // 有密码打印 return nil } @@ -89,11 +94,14 @@ func (l *LogicalLoader) Load() error { } func (l *LogicalLoader) loadBackup() error { - cmd := fmt.Sprintf(`cd %s && %s loadbackup --config %s |grep -v WARNING`, l.TaskDir, l.Client, l.cfgFilePath) - logger.Info("dbLoader cmd: %s", cmd) - stdStr, err := cmutil.ExecShellCommand(false, cmd) + cmdArgs := []string{"loadbackup", "--config", l.cfgFilePath} + cmd := []string{l.Client} + cmd = append(cmd, cmdArgs...) + logger.Info("dbLoader cmd: %s", strings.Join(cmd, " ")) + outStr, errStr, err := cmutil.ExecCommand(false, l.TaskDir, cmd[0], cmd[1:]...) if err != nil { - return errors.Wrap(err, stdStr) + logger.Info("dbbackup loadbackup stdout: %s", outStr) + return errors.Wrap(err, errStr) } return nil } @@ -142,6 +150,7 @@ func (l *LogicalLoader) buildFilter() error { ); err != nil { return err } else { + filter.BuildFilter() l.myloaderRegex = filter.MyloaderRegex(l.doDr) } return nil diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/restore/dbloader/physical_loader.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/restore/dbloader/physical_loader.go index 9157db6084..d8fb927db5 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/restore/dbloader/physical_loader.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/restore/dbloader/physical_loader.go @@ -44,7 +44,7 @@ func (l *PhysicalLoader) CreateConfigFile() error { CopyBack: false, Threads: 4, } - logger.Info("dbloader config file, %+v", loaderConfig) + // logger.Info("dbloader config file, %+v", loaderConfig) // 有密码打印 f := ini.Empty() section, err := f.NewSection("PhysicalLoad") @@ -59,7 +59,7 @@ func (l *PhysicalLoader) CreateConfigFile() error { return errors.Wrap(err, "create config") } p.cfgFilePath = cfgFilePath - logger.Info("tmp dbloader config file %s", p.cfgFilePath) + // logger.Info("tmp dbloader config file %s", p.cfgFilePath) // 有密码打印 return nil } @@ -90,7 +90,7 @@ func (l *PhysicalLoader) Load() error { } func (l *PhysicalLoader) loadBackup() error { - cmd := fmt.Sprintf(`cd %s && %s loadbackup --config %s |grep -v WARNING`, l.TaskDir, l.Client, l.cfgFilePath) + cmd := fmt.Sprintf(`cd %s && %s loadbackup --config %s`, l.TaskDir, l.Client, l.cfgFilePath) logger.Info("dbLoader cmd: %s", cmd) stdStr, err := osutil.ExecShellCommand(false, cmd) if err != nil { diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/restore/dbloader/xtrabackup.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/restore/dbloader/xtrabackup.go index cc6965f8cf..17065f2507 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/restore/dbloader/xtrabackup.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/restore/dbloader/xtrabackup.go @@ -24,7 +24,7 @@ type Xtrabackup struct { LoaderDir string // 备份解压后的目录,${taskDir}/backupBaseName/ // 在 PostRun 中会择机初始化 dbWorker *native.DbWorker // TgtInstance - // 在 PreRun 时初始化 + // 在 PreRun 时初始化,本地实例的配置文件 myCnf *util.CnfFile } @@ -134,12 +134,24 @@ func (x *Xtrabackup) cleanXtraEnv() error { // doReplaceCnf godoc // todo 考虑使用 mycnf-change 模块来修改 +// mysql 8.0.30 之后 redo_log 变成 innodb_redo_log_capacity 来控制 func (x *Xtrabackup) doReplaceCnf() error { items := []string{ "innodb_data_file_path", "innodb_log_files_in_group", "innodb_log_file_size", + "innodb_page_size", "tokudb_cache_size", + "lower_case_table_names", + + // mysql 8.0 xtrabackup + "innodb_checksum_algorithm", + "innodb_log_checksums", + "innodb_undo_directory", + "innodb_undo_tablespaces", + "innodb_redo_log_encrypt", + "innodb_undo_log_encrypt", + //"master_key_id", } return x.ReplaceMycnf(items) } diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/restore/dbloader/xtrabackup_repaire.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/restore/dbloader/xtrabackup_repaire.go index 68ca340660..262ef04e59 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/restore/dbloader/xtrabackup_repaire.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/restore/dbloader/xtrabackup_repaire.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package dbloader import ( @@ -247,13 +257,17 @@ func (x *Xtrabackup) ReplaceMycnf(items []string) error { if util.StringsHas(itemsExclude, key) { continue } - itemMap[key] = bakCnfMap.Section[util.MysqldSec].KvMap[key] + // 需要忽略没在 backup-my.cnf 里面的配置项 + if val, ok := bakCnfMap.Section[util.MysqldSec].KvMap[key]; ok { + itemMap[key] = val + } else { + continue + } // sed 's///g' f > /tmp/f && cat /tmp/f > f } if len(itemMap) > 0 { logger.Info("ReplaceMycnf new: %v", itemMap) if err = x.myCnf.ReplaceValuesToFile(itemMap); err != nil { - // x.myCnf.Load() // reload it? return err } } @@ -280,6 +294,7 @@ func (x *Xtrabackup) ChangeDirOwner(dirs []string) error { return nil } +// getBackupCnfName 获取 xtrabackup 目录下的 backup-my.cnf func (x *Xtrabackup) getBackupCnfName() string { return fmt.Sprintf("%s/%s", x.LoaderDir, "backup-my.cnf") } diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/restore/dbloader_restore.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/restore/dbloader_restore.go index 44fbd58fb0..df33f3407b 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/restore/dbloader_restore.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/restore/dbloader_restore.go @@ -88,7 +88,12 @@ func (m *DBLoader) chooseDBBackupLoader() error { IndexObj: m.BackupInfo.indexObj, LoaderDir: m.targetDir, TaskDir: m.taskDir, - WithOutBinlog: true, + //EnableBinlog: m.RestoreOpt.EnableBinlog, + } + if m.RestoreOpt == nil { + m.RestoreOpt = &RestoreOpt{ + EnableBinlog: false, + } } // logger.Warn("validate dbLoaderUtil: %+v", m.dbLoaderUtil) if err := validate.GoValidateStruct(m.dbLoaderUtil, false, false); err != nil { @@ -190,7 +195,7 @@ func (m *DBLoader) getChangeMasterPos(masterInst native.Instance) (*mysqlutil.Ch return nil, errors.New("no master info found in metadata") } if masterInst.Host == "" || masterInst.Port == 0 { // 说明不关注备份位点信息 - return nil, nil + return &mysqlutil.ChangeMaster{}, nil } // 如果备份文件的源实例,就是当前恢复要change master to 的实例,直接用 MasterStatus info if masterInfo.MasterHost == masterInst.Host && masterInfo.MasterPort == masterInst.Port { diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/restore/recover_binlog.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/restore/recover_binlog.go index 21b577a5a4..16872890b6 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/restore/recover_binlog.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/restore/recover_binlog.go @@ -21,7 +21,6 @@ import ( "dbm-services/mysql/db-tools/dbactuator/pkg/util/osutil" binlogParser "dbm-services/mysql/db-tools/mysql-rotatebinlog/pkg/binlog-parser" - "github.com/panjf2000/ants/v2" "github.com/pkg/errors" ) @@ -99,7 +98,7 @@ type RecoverBinlog struct { parseScript string binlogParsedDir string logDir string - tools tools.ToolSet + //tools tools.ToolSet } const ( @@ -174,34 +173,40 @@ func (r *RecoverBinlog) parse(f string) error { // ParseBinlogFiles TODO func (r *RecoverBinlog) ParseBinlogFiles() error { logger.Info("start to parse binlog files with concurrency %d", r.ParseConcurrency) - defer ants.Release() - var errs []error - var wg = &sync.WaitGroup{} - pp, _ := ants.NewPoolWithFunc( - r.ParseConcurrency, func(i interface{}) { - f := i.(string) - if err := r.parse(f); err != nil { - errs = append(errs, err) - return - } - }, - ) - defer pp.Release() - for _, f := range r.BinlogFiles { - if len(errs) > 0 { - break + errChan := make(chan error) + tokenBulkChan := make(chan struct{}, r.ParseConcurrency) + + go func() { + var wg = &sync.WaitGroup{} + wg.Add(len(r.BinlogFiles)) + logger.Info("need parse %d binlog files: %s", len(r.BinlogFiles), r.BinlogFiles) + + for _, f := range r.BinlogFiles { + tokenBulkChan <- struct{}{} + go func(binlogFilePath string) { + err := r.parse(binlogFilePath) + logger.Info("parse %s returned", binlogFilePath) + + <-tokenBulkChan + + if err != nil { + logger.Error("parse %s failed: %s", binlogFilePath, err.Error()) + } + errChan <- err + wg.Done() + logger.Info("parse %s done", binlogFilePath) + }(f) } - if f != "" { - wg.Add(1) - pp.Invoke(f) - wg.Done() + wg.Wait() + logger.Info("all binlog finish") + close(errChan) + }() + for err := range errChan { + if err != nil { + return err } } - wg.Wait() - if len(errs) > 0 { - return util.SliceErrorsToError(errs) - } return nil } @@ -291,7 +296,7 @@ exit $retcode func (r *RecoverBinlog) Init() error { var err error // 工具路径初始化,检查工具路径, 工具可执行权限 - toolset, err := tools.NewToolSetWithPick(tools.ToolMysqlbinlog, tools.ToolMysqlclient) + toolset, err := tools.NewToolSetWithPick(tools.ToolMysqlbinlog, tools.ToolMysqlclient, tools.ToolMysqlbinlogRollback) if err != nil { return err } @@ -308,7 +313,7 @@ func (r *RecoverBinlog) Init() error { if r.QuickMode && !r.RecoverOpt.Flashback { mysqlbinlogCli := r.ToolSet.MustGet(tools.ToolMysqlbinlog) checkMysqlbinlog := fmt.Sprintf(`%s --help |grep "\-\-tables="`, mysqlbinlogCli) - if _, err := osutil.ExecShellCommand(false, checkMysqlbinlog); err != nil { + if _, err := mysqlutil.ExecCommandMySQLShell(checkMysqlbinlog); err != nil { r.QuickMode = false logger.Warn("%s has not --tables option, set recover_binlog quick_mode=false", mysqlbinlogCli) } @@ -402,7 +407,12 @@ func (r *RecoverBinlog) buildBinlogOptions() error { b.options += " --disable-log-bin" } - r.binlogCli += fmt.Sprintf("%s %s", r.ToolSet.MustGet(tools.ToolMysqlbinlog), r.RecoverOpt.options) + if r.RecoverOpt.Flashback { + r.binlogCli += fmt.Sprintf("%s %s", r.ToolSet.MustGet(tools.ToolMysqlbinlogRollback), r.RecoverOpt.options) + } else { + r.binlogCli += fmt.Sprintf("%s %s", r.ToolSet.MustGet(tools.ToolMysqlbinlog), r.RecoverOpt.options) + } + return nil } @@ -638,7 +648,7 @@ func (r *RecoverBinlog) Start() error { r.BinlogDir, r.binlogCli, binlogFiles, r.mysqlCli, outFile, errFile, ) logger.Info(mysqlutil.ClearSensitiveInformation(mysqlutil.RemovePassword(cmd))) - stdoutStr, err := osutil.ExecShellCommand(false, cmd) + stdoutStr, err := mysqlutil.ExecCommandMySQLShell(cmd) if err != nil { if strings.TrimSpace(stdoutStr) == "" { if errContent, err := osutil.ExecShellCommand( @@ -694,7 +704,7 @@ func (r *RecoverBinlog) WaitDone() error { // PostCheck TODO func (r *RecoverBinlog) PostCheck() error { - // 检查 infodba_schema.master_slave_check 里面的时间与 target_time 差异不超过 65s + // 检查 infodba_schema.master_slave_heartbeat 里面的时间与 target_time 差异不超过 65s return nil } diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/restore/restore.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/restore/restore.go index 6b8c4b6774..334fab5c48 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/restore/restore.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/restore/restore.go @@ -34,7 +34,7 @@ type RestoreParam struct { Tools tools.ToolSet `json:"tools"` // 恢复本地的目标实例 TgtInstance native.InsObject `json:"tgt_instance"` - // 备份实例的 ip port,用于生产 change master 语句 + // 备份实例的 ip port,用于生产 change master 语句。如果 host 为空,表示不检查、不生成change master,恢复spider节点时使用 SrcInstance native.Instance `json:"src_instance"` // 恢复完成后是否执行 change master,会 change master 到 src_instance ChangeMaster bool `json:"change_master"` @@ -56,6 +56,8 @@ type RestoreOpt struct { // 在指定时间点回档场景才需要,是否恢复 binlog。在 doSlave 场景,是不需要 recover_binlog。这个选项是控制下一步恢复binlog的行为 // 当 recover_binlog 时,要确保实例的所有库表结构都恢复。在逻辑回档场景,只回档部分库表数据时,依然要恢复所有表结构 WillRecoverBinlog bool `json:"recover_binlog"` + // EnableBinlog 导入数据时是否写binlog,默认不启用 + EnableBinlog bool `json:"enable_binlog"` // 在库表级定点回档时有用,如果是 statement/mixed 格式,导入数据时需要全部导入; // 如果是 row,可只导入指定库表数据, 在 recover-binlog 时可指定 quick_mode=true 也恢复指定库表 binlog SourceBinlogFormat string `json:"source_binlog_format" enums:",ROW,STATEMENT,MIXED"` diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/rollback/flashback.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/rollback/flashback.go index ca22bf0bd9..097eb5ab77 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/rollback/flashback.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/rollback/flashback.go @@ -56,6 +56,7 @@ func (f *Flashback) Init() error { } // recover_binlog 用的是 mysqlbinlog, flashback用的是 mysqlbinlog_rollback f.ToolSet.Set(tools.ToolMysqlbinlog, f.ToolSet.MustGet(tools.ToolMysqlbinlogRollback)) + f.recover = restore.RecoverBinlog{ TgtInstance: f.TgtInstance, WorkDir: f.WorkDir, @@ -85,6 +86,7 @@ func (f *Flashback) Init() error { if err := f.recover.Init(); err != nil { return err } + f.dbWorker = f.recover.GetDBWorker() // 检查起止时间 dbNowTime, err := f.dbWorker.SelectNow() @@ -129,9 +131,9 @@ func (f *Flashback) PreCheck() error { if err = f.checkDBRole(); err != nil { return err } - if err = f.checkDBTableInUse(); err != nil { - return err - } + //if err = f.checkDBTableInUse(); err != nil { + // return err + //} if f.BinlogDir == "" { // 没有指定 binlog 目录,目前是自动从 实例 binlog 目录本地找,并做软链 if totalSize, err := f.getBinlogFiles(""); err != nil { return err diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/semantic_check_run.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/semantic_check_run.go deleted file mode 100644 index f120c4b123..0000000000 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/semantic_check_run.go +++ /dev/null @@ -1,454 +0,0 @@ -package mysql - -import ( - "fmt" - "os" - "os/exec" - "path" - "regexp" - "sync" - - "dbm-services/common/go-pubpkg/logger" - "dbm-services/mysql/db-tools/dbactuator/pkg/components" - "dbm-services/mysql/db-tools/dbactuator/pkg/components/computil" - "dbm-services/mysql/db-tools/dbactuator/pkg/core/cst" - "dbm-services/mysql/db-tools/dbactuator/pkg/native" - "dbm-services/mysql/db-tools/dbactuator/pkg/util" - "dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil" - "dbm-services/mysql/db-tools/dbactuator/pkg/util/osutil" - - ants "github.com/panjf2000/ants/v2" -) - -/* - Prepare: - 1、下发备份表结构命名到目标实例,完成备份表结构 - 2、将备份的表结构文件&待语义检查的SQL文件下发到语义检查的机器上 - 3、选择一个于目标实例版本一致的语义检查的实例,锁定 - ------------------------------------------------------------- - Doing: - 4、将处理后的表结构导入到临时实例上 - 5、导入待检查的SQL文件 - 6、分析语义检查的结果 - ------------------------------------------------------------- - Ending: - 7、Clean 语义检查临时实例,并释放 -*/ - -// SemanticCheckComp TODO -type SemanticCheckComp struct { - GeneralParam *components.GeneralParam `json:"general"` - Params SenmanticCheckParam `json:"extend"` - SenmanticCheckRunTimeCtx `json:"-"` -} - -// SenmanticCheckParam TODO -type SenmanticCheckParam struct { - Host string `json:"host" validate:"required,ip"` // 语义检查实例的主机地址 - Port int `json:"port" validate:"required,lt=65536,gte=3306"` // 语义检查实例的端口 - SchemaFile string `json:"schemafile" validate:"required"` // 表结构文件 - ExcuteObjects []ExcuteSQLFileObj `json:"execute_objects"` - // 用于获取目标实例的字符集,默认存储引擎 - RemoteHost string `json:"remote_host" validate:"required,ip"` // 获取表结构的源实例IP - RemotePort int `json:"remote_port" validate:"required,lt=65536,gte=3306"` // 获取表结构的源实例Port -} - -// SenmanticCheckRunTimeCtx TODO -type SenmanticCheckRunTimeCtx struct { - dbConn *native.DbWorker - adminUser string - adminPwd string - socket string - version string - remoteDefaultEngineIsTokudb bool - remoteVersion string - remoteCharset string - afterdealSchemafile string // schema sqlfile 处理之后的文件 - taskdir string - schemafilename string -} - -// Precheck 前置检查 -// -// @receiver c -// @return err -func (c *SemanticCheckComp) Precheck() (err error) { - if !osutil.FileExist(c.Params.SchemaFile) { - return fmt.Errorf("%s文件不存在", c.Params.SchemaFile) - } - for _, o := range c.Params.ExcuteObjects { - if !osutil.FileExist(path.Join(cst.BK_PKG_INSTALL_PATH, o.SQLFile)) { - return fmt.Errorf("%s文件不存在", o.SQLFile) - } - } - return nil -} - -// Example TODO -func (c *SemanticCheckComp) Example() interface{} { - comp := SemanticCheckComp{ - Params: SenmanticCheckParam{ - Host: "1.1.1.1", - Port: 3306, - SchemaFile: "db_schema.sql", - ExcuteObjects: []ExcuteSQLFileObj{ - { - SQLFile: "test1.sql", - IgnoreDbNames: []string{"db9"}, - DbNames: []string{"db_100*", "db8"}, - }, - { - SQLFile: "test2.sql", - IgnoreDbNames: []string{"db90"}, - DbNames: []string{"db_200*", "db7"}, - }, - }, - RemoteHost: "2.2.2.2", - RemotePort: 3306, - }, - } - return comp -} - -// Init TODO -// -// @receiver c -// @receiver uid -// @return err -func (c *SemanticCheckComp) Init(uid string) (err error) { - c.taskdir = path.Join(cst.BK_PKG_INSTALL_PATH, fmt.Sprintf("semantic_check_%s", uid)) - if err = os.MkdirAll(c.taskdir, os.ModePerm); err != nil { - logger.Error("初始化任务目录失败%s:%s", c.taskdir, err.Error()) - return - } - - c.schemafilename = path.Base(c.Params.SchemaFile) - // 将表结构文件移动到target dir - if err = os.Rename(c.Params.SchemaFile, path.Join(c.taskdir, c.schemafilename)); err != nil { - logger.Error("将表结构文件移动到%s 错误:%s", c.taskdir, err.Error()) - return - } - if err = c.initLocalRuntimeCtxParam(); err != nil { - return - } - return c.initRemoteRuntimeCtxParam() -} - -// initRemoteRuntimeCtxParam TODO -// -// initRuntimeCtxParam 初始化运行时参数 -// @receiver c -// @return err -func (c *SemanticCheckComp) initRemoteRuntimeCtxParam() (err error) { - remotedbConn, err := native.InsObject{ - Host: c.Params.RemoteHost, - Port: c.Params.RemotePort, - User: c.GeneralParam.RuntimeAccountParam.MonitorAccessAllUser, - Pwd: c.GeneralParam.RuntimeAccountParam.MonitorAccessAllPwd, - }.Conn() - if err != nil { - logger.Error("Connect %s:%d failed:%s", c.Params.RemoteHost, c.Params.Port, err.Error()) - return err - } - defer func() { - if remotedbConn.Db != nil { - remotedbConn.Db.Close() - } - }() - if c.remoteCharset, err = remotedbConn.ShowServerCharset(); err != nil { - logger.Error("获取源实例的字符集失败:%s", err.Error()) - return err - } - if c.remoteVersion, err = remotedbConn.SelectVersion(); err != nil { - logger.Error("获取源实例的Version:%s", err.Error()) - return err - } - if c.remoteDefaultEngineIsTokudb, err = remotedbConn.IsSupportTokudb(); err != nil { - logger.Error("判断源实例是否支持:%s", err.Error()) - return err - } - return err -} - -func (c *SemanticCheckComp) initLocalRuntimeCtxParam() (err error) { - c.adminUser = c.GeneralParam.RuntimeAccountParam.AdminUser - c.adminPwd = c.GeneralParam.RuntimeAccountParam.AdminPwd - c.dbConn, err = native.InsObject{ - Host: c.Params.Host, - Port: c.Params.Port, - User: c.adminUser, - Pwd: c.adminPwd, - }.Conn() - if err != nil { - logger.Error("Connect %d failed:%s", c.Params.Port, err.Error()) - return err - } - c.socket, err = c.dbConn.ShowSocket() - if err != nil { - logger.Warn("获取本实例socket val 失败") - } - c.version, err = c.dbConn.SelectVersion() - if err != nil { - logger.Error("获取本实例Version失败:%s", err.Error()) - return err - } - return nil -} - -// dealWithSchemaFile 导入前处理导入文件 -// -// @receiver c -// @return err -func (c *SemanticCheckComp) dealWithSchemaFile() (err error) { - script := fmt.Sprintf("cat %s ", path.Join(c.taskdir, c.schemafilename)) - if c.remoteDefaultEngineIsTokudb { - script += ` | sed -e 's/ROW_FORMAT=TOKUDB_ZLIB/ROW_FORMAT=default/'` - } - // 将没有包含"CREATE TABLE IF NOT EXISTS"的行做替换(直接替换会导致替换结果出现两次IF NOT EXISTS) - script += ` | sed '/CREATE TABLE IF NOT EXISTS /! s/^CREATE TABLE /CREATE TABLE IF NOT EXISTS /'` - engine := "INNODB" - if c.remoteDefaultEngineIsTokudb { - engine = "MYISAM" - } - switch { - case regexp.MustCompile(`tspider-3`).MatchString(c.remoteVersion): - script += ` | sed -e 's/99106 ROW_FORMAT=GCS_DYNAMIC/99104 ROW_FORMAT=DYNAMIC/i'` - script += ` | sed -e 's/99104 COMPRESSED/99999 COMPRESSED/i'` - script += ` | sed -e 's/ROW_FORMAT=FIXED//i'` - script += fmt.Sprintf(" | sed -e 's/ENGINE=SPIDER /ENGINE=%s ROW_FORMAT=DYNAMIC /i'", engine) - script += " | sed '/^ PARTITION `pt/d' " - script += fmt.Sprintf(" | sed 's/ENGINE = SPIDER,$/ENGINE = %s) ;/g'", engine) - script += ` | sed 's/MOD [0-9]*)$/MOD 1)/g'` - case regexp.MustCompile(`spider`).MatchString(c.remoteVersion): - script += ` | sed -e 's/99106 ROW_FORMAT=GCS_DYNAMIC/99104 ROW_FORMAT=DYNAMIC/i'` - script += ` | sed -e 's/99104 COMPRESSED/99999 COMPRESSED/i'` - script += ` | sed -e 's/ROW_FORMAT=FIXED//i'` - script += fmt.Sprintf(" | sed -e 's/ENGINE=SPIDER /ENGINE=%s ROW_FORMAT=DYNAMIC /i'", engine) - script += ` | sed '/^ PARTITION pt/d'` - script += "| sed '/^ PARTITION `pt/d'" - script += fmt.Sprintf(" | sed 's/ENGINE = SPIDER,$/ENGINE = %s) \\\\*\\\\/;/g'", engine) - script += `| sed 's/%[0-9]*)$/\%1)/g'` - default: - script += " | sed -e 's/99106 ROW_FORMAT=GCS_DYNAMIC/99104 ROW_FORMAT=DYNAMIC/i'" - script += " | sed -e 's/99104 COMPRESSED/99999 COMPRESSED/i'" - script += " | sed -e 's/ROW_FORMAT=FIXED//i'" - script += " | sed -e 's/ENGINE=SPIDER DEFAULT CHARSET=/ENGINE=INNODB ROW_FORMAT=DYNAMIC DEFAULT CHARSET=/i'" - } - logger.Info("导入前预处理命令:%s", script) - stdOutFileName := path.Join(c.taskdir, c.schemafilename+".new") - stdOutFile, err := os.OpenFile(stdOutFileName, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm) - if err != nil { - return fmt.Errorf("open file %s failed, err:%w", stdOutFileName, err) - } - defer func() { - if err := stdOutFile.Close(); err != nil { - logger.Warn("close file %s failed, err:%s", stdOutFileName, err.Error()) - } - }() - - stdErrFileName := path.Join(c.taskdir, c.schemafilename+".new.err") - stdErrFile, err := os.OpenFile(stdErrFileName, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm) - if err != nil { - return fmt.Errorf("open %s failed, err:%+v", stdErrFileName, err) - } - defer func() { - if err := stdErrFile.Close(); err != nil { - logger.Warn("close file %s failed, err:%s", stdErrFileName, err.Error()) - } - }() - logger.Info( - "预处理导出的表结构[doing]: 源文件(%s) ==> 预处理后文件(%s), 错误输出(%s)", - c.Params.SchemaFile, - stdOutFileName, - stdErrFileName, - ) - cmd := exec.Command("/bin/bash", "-c", script) - cmd.Stdout = stdOutFile - cmd.Stderr = stdErrFile - if err := cmd.Run(); err != nil { - logger.Error("运行预处理失败%s", err.Error()) - return err - } - logger.Info( - "预处理导出的表结构[doing]: 源文件(%s) ==> 预处理后文件(%s), 错误输出(%s)", - c.Params.SchemaFile, - stdOutFileName, - stdErrFileName, - ) - c.afterdealSchemafile = stdOutFileName - return nil -} - -// LoadSchema 导入远程表结构 -// -// @receiver c -// @return err -func (c *SemanticCheckComp) LoadSchema() (err error) { - if err = c.dealWithSchemaFile(); err != nil { - logger.Error("预处理导入文件失败:%s", err.Error()) - return err - } - return mysqlutil.ExecuteSqlAtLocal{ - Host: c.Params.Host, - Port: c.Params.Port, - Charset: c.remoteCharset, - Socket: c.socket, - User: c.adminUser, - Password: c.adminPwd, - }.ExcuteSqlByMySQLClient(c.afterdealSchemafile, []string{native.TEST_DB}) -} - -// Run 运行语义检查 -// -// @receiver c -// @return err -func (c *SemanticCheckComp) Run() (err error) { - if err = c.LoadSchema(); err != nil { - logger.Error("导入目标实例%s:%d表结构失败", c.Params.RemoteHost, c.Params.RemotePort) - return err - } - e := ExcuteSQLFileComp{ - GeneralParam: c.GeneralParam, - Params: &ExcuteSQLFileParam{ - Host: c.Params.Host, - Ports: []int{c.Params.Port}, - CharSet: c.remoteCharset, - ExcuteObjects: c.Params.ExcuteObjects, - Force: false, - }, - ExcuteSQLFileRunTimeCtx: ExcuteSQLFileRunTimeCtx{}, - } - if err = e.Init(); err != nil { - return err - } - if err = e.Excute(); err != nil { - return err - } - return nil -} - -// Clean 清理并重启语义实例 -// -// @receiver c -// @return err -func (c *SemanticCheckComp) Clean() (err error) { - logger.Info("开始清除语义检查的实例...") - if err = c.initLocalRuntimeCtxParam(); err != nil { - return err - } - if err = c.cleandata(); err != nil { - logger.Warn("清理语义实例失败:%s", err.Error()) - return - } - if err = c.restart(); err != nil { - logger.Error("重启语义实例失败:%s", err.Error()) - return - } - logger.Info("清理语义检查实例成功~") - return -} - -// restart TODO -// shutdown 重启语义检查实例 -// -// @receiver c -// @return err -func (c *SemanticCheckComp) restart() (err error) { - err = computil.ShutdownMySQLParam{ - MySQLUser: c.adminUser, - MySQLPwd: c.adminPwd, - Socket: c.socket, - }.ShutdownMySQLBySocket() - if err != nil { - logger.Error("关闭实例失败:%s", err.Error()) - return err - } - p := &computil.StartMySQLParam{ - MyCnfName: util.GetMyCnfFileName(c.Params.Port), - MySQLUser: c.adminUser, - MySQLPwd: c.adminPwd, - Socket: c.socket, - } - _, err = p.StartMysqlInstance() - if err != nil { - logger.Error("启动实例失败:%s", err.Error()) - return err - } - return -} - -// cleandata TODO -// DropSenmanticInstanceDatabases 清楚语义实例的数据库 -// -// @receiver c -// @return err -func (c *SemanticCheckComp) cleandata() (err error) { - alldbs, err := c.dbConn.ShowDatabases() - if err != nil { - logger.Error("清理实例:获取语义检查实例本地实例失败%s", err.Error()) - return err - } - testTbls, err := c.dbConn.ShowTables(native.TEST_DB) - if err != nil { - logger.Error("清理实例:获取test库内的表失败:%s", err.Error()) - return err - } - dbInfobaseTbls, err := c.dbConn.ShowTables(native.INFODBA_SCHEMA) - if err != nil { - logger.Error("清理实例:获取 infodba_schema 库内的表失败:%s", err.Error()) - return err - } - - var wg sync.WaitGroup - errChan := make(chan error, 1) - pool, _ := ants.NewPool(100) - defer pool.Release() - - dropdbs := util.FilterOutStringSlice(alldbs, computil.GetGcsSystemDatabases(c.version)) - logger.Info("will drop databases is:%v", dropdbs) - f := func(db string) func() { - return func() { - _, err := c.dbConn.Exec(fmt.Sprintf("drop database `%s`;", db)) - if err != nil { - errChan <- fmt.Errorf("drop database %s,err:%w", db, err) - } - wg.Done() - } - } - for _, db := range dropdbs { - wg.Add(1) - pool.Submit(f(db)) - } - type db = string - type tables = []string - var specialDbTbls map[db]tables - specialDbTbls = make(map[string][]string) - specialDbTbls[native.INFODBA_SCHEMA] = util.FilterOutStringSlice(testTbls, []string{"conn_log", "free_space"}) - specialDbTbls[native.INFODBA_SCHEMA] = util.FilterOutStringSlice( - dbInfobaseTbls, - []string{"QUERY_RESPONSE_TIME", "check_heartbeat", "checksum", "master_slave_check", "spes_status"}, - ) - ff := func(db, tbl string) func() { - return func() { - _, err := c.dbConn.Exec(fmt.Sprintf("drop table `%s`.`%s`;", db, tbl)) - if err != nil { - errChan <- fmt.Errorf("drop table `%s`.`%s`,err:%w", db, tbl, err) - } - wg.Done() - } - } - for db, tbls := range specialDbTbls { - for _, tbl := range tbls { - wg.Add(1) - pool.Submit(ff(db, tbl)) - } - } - wg.Wait() - select { - case err := <-errChan: - logger.Error("drop db failed: %s", err.Error()) - return err - default: - } - return nil -} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/semantic_dump_schema.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/semantic_dump_schema.go index 48bf300377..8c5dc3a670 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/semantic_dump_schema.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/semantic_dump_schema.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package mysql import ( @@ -9,13 +19,13 @@ import ( "regexp" "strings" + "dbm-services/common/go-pubpkg/bkrepo" "dbm-services/common/go-pubpkg/logger" "dbm-services/mysql/db-tools/dbactuator/pkg/components" "dbm-services/mysql/db-tools/dbactuator/pkg/components/computil" "dbm-services/mysql/db-tools/dbactuator/pkg/core/cst" "dbm-services/mysql/db-tools/dbactuator/pkg/native" "dbm-services/mysql/db-tools/dbactuator/pkg/util" - "dbm-services/mysql/db-tools/dbactuator/pkg/util/bkrepo" "dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil" "dbm-services/mysql/db-tools/dbactuator/pkg/util/osutil" ) @@ -146,23 +156,27 @@ func (c *SemanticDumpSchemaComp) Precheck() (err error) { // @return err func (c *SemanticDumpSchemaComp) DumpSchema() (err error) { var dumper mysqlutil.Dumper + dumpOption := mysqlutil.MySQLDumpOption{ + NoData: true, + AddDropTable: true, + NeedUseDb: true, + DumpRoutine: true, + DumpTrigger: false, + } + if c.isSpider { + dumpOption.GtidPurgedOff = true + } dumper = &mysqlutil.MySQLDumperTogether{ MySQLDumper: mysqlutil.MySQLDumper{ - DumpDir: c.Params.BackupDir, - Ip: c.Params.Host, - Port: c.Params.Port, - DbBackupUser: c.GeneralParam.RuntimeAccountParam.AdminUser, - DbBackupPwd: c.GeneralParam.RuntimeAccountParam.AdminPwd, - DbNames: c.dbs, - DumpCmdFile: c.dumpCmd, - Charset: c.charset, - MySQLDumpOption: mysqlutil.MySQLDumpOption{ - NoData: true, - AddDropTable: true, - NeedUseDb: true, - DumpRoutine: true, - DumpTrigger: false, - }, + DumpDir: c.Params.BackupDir, + Ip: c.Params.Host, + Port: c.Params.Port, + DbBackupUser: c.GeneralParam.RuntimeAccountParam.AdminUser, + DbBackupPwd: c.GeneralParam.RuntimeAccountParam.AdminPwd, + DbNames: c.dbs, + DumpCmdFile: c.dumpCmd, + Charset: c.charset, + MySQLDumpOption: dumpOption, }, OutputfileName: c.Params.BackupFileName, } diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/uninstall_mysql.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/uninstall_mysql.go index 68ac4cf8c7..84e2e48485 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/uninstall_mysql.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/uninstall_mysql.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + // 下架MySQL实例 // 因为下架需要操作数据目录和日志目录 // 这个两个参数是从my.cnf里面读取的 @@ -63,7 +73,7 @@ type MyCnfObj struct { func (u *UnInstallMySQLComp) Init() (err error) { u.insMyObj = make(map[int]*MyCnfObj) for _, port := range u.Params.Ports { - var datadir, logdir, socket string + var socket string myfile := util.GetMyCnfFileName(port) if !cmutil.FileExists(myfile) { return fmt.Errorf("%s不存在", myfile) @@ -73,19 +83,13 @@ func (u *UnInstallMySQLComp) Init() (err error) { logger.Error("加载%s配置失败%s", myfile, err) return err } - if datadir, err = f.GetMySQLDataDir(); err != nil { - return err - } - if logdir, err = f.GetMySQLLogDir(); err != nil { - return err - } if socket, err = f.GetMySQLSocket(); err != nil { return err } u.insMyObj[port] = &MyCnfObj{ MyCnfPath: myfile, - Datadir: datadir, - LogDir: logdir, + Datadir: "", + LogDir: "", Socket: socket, IsShutdown: false, // 初始化给个默认值,后续判断实例是否正常才变更 } @@ -114,7 +118,7 @@ func (u *UnInstallMySQLComp) PreCheck() (err error) { if !u.Params.Force && !u.insMyObj[port].IsShutdown { // 非强制下架,且实例正常的情况下,需要判断实例是否有业务连接, // todo 这里重新去创建连接,如果检测实例状态和连接业务访问之间出现实例异常,则会触发bug,后续考虑怎么优化这点 - if err := inst.CheckInstanceConnIdle(u.GeneralParam.RuntimeExtend.MySQLSysUsers, time.Second*1); err != nil { + if err := inst.CheckInstanceConnIdle(u.GeneralParam.GetAllSysAccount(), time.Second*1); err != nil { logger.Warn("try connent this mysql instance [%p] failed:%s", port, err.Error()) u.insMyObj[port].IsShutdown = true } diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/spiderctl/add_temporary_spider.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/spiderctl/add_temporary_spider.go deleted file mode 100644 index 2fe4f11c49..0000000000 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/spiderctl/add_temporary_spider.go +++ /dev/null @@ -1,66 +0,0 @@ -// Package spiderctl TODO -package spiderctl - -import ( - "fmt" - - "dbm-services/common/go-pubpkg/logger" - "dbm-services/mysql/db-tools/dbactuator/pkg/components" - "dbm-services/mysql/db-tools/dbactuator/pkg/native" -) - -// AddTmpSpiderComp 分为通用参数和行为专用参数 -type AddTmpSpiderComp struct { - GeneralParam *components.GeneralParam `json:"general"` - Params *AddTmpSpiderParam `json:"extend"` - InitCtx -} - -// AddTmpSpiderParam 具体执行时所需要的参数 -type AddTmpSpiderParam struct { - Host string `json:"host" validate:"required,ip"` - Port int `json:"port" validate:"required,lt=65536,gte=3306"` - SpiderInstances []Instance `json:"spider_instance" validate:"required"` -} - -// Example TODO -func (a *AddTmpSpiderComp) Example() interface{} { - comp := AddTmpSpiderComp{} - return comp -} - -// Init 初始化数据库连接,之后用于客户端登录执行添加新的路由信息 -func (a *AddTmpSpiderComp) Init() (err error) { - a.dbConn, err = native.InsObject{ - Host: a.Params.Host, - Port: a.Params.Port, - User: a.GeneralParam.RuntimeAccountParam.AdminUser, - Pwd: a.GeneralParam.RuntimeAccountParam.AdminPwd, - }.Conn() - if err != nil { - logger.Error("Connect %d failed:%s", a.Params.Port, err.Error()) - return err - } - return nil -} - -// AddTmpSpider TODO -func (a *AddTmpSpiderComp) AddTmpSpider() (err error) { - var execSQLs []string - tdbctlUser := a.GeneralParam.RuntimeAccountParam.TdbctlUser - tdbctlPwd := a.GeneralParam.RuntimeAccountParam.TdbctlPwd - for _, inst := range a.Params.SpiderInstances { - execSQLs = append(execSQLs, fmt.Sprintf( - "tdbctl create node wrapper 'SPIDER' options(user '%s', password '%s', host '%s', port %d) with database;", - tdbctlUser, tdbctlPwd, inst.Host, inst.Port, - ), - ) - } - execSQLs = append(execSQLs, "tdbctl flush routing;") - _, err = a.dbConn.ExecMore(execSQLs) - if err != nil { - logger.Error("tdbctl create node failed:[%s]", err.Error()) - return err - } - return nil -} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/spiderctl/backend_migrate_cutover.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/spiderctl/backend_migrate_cutover.go new file mode 100644 index 0000000000..069a642a8b --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/spiderctl/backend_migrate_cutover.go @@ -0,0 +1,487 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package spiderctl + +import ( + "errors" + "fmt" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/mysql/db-tools/dbactuator/pkg/components" + "dbm-services/mysql/db-tools/dbactuator/pkg/native" + "dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil" +) + +// scene 1 +// +// spider --> master <-- slave +// slave_spider ----------^ +// +// after cutover +// +// spider --> new_master <-- new_slave +// slave_spider ----------------^ + +// scene 2 +// the spider cluster not have slave +// +// spider --> master +// +// after cutover +// +// spider --> new_master + +// SpiderClusterBackendMigrateCutoverComp TODO +type SpiderClusterBackendMigrateCutoverComp struct { + GeneralParam *components.GeneralParam `json:"general"` + Params *RemotedbdrMigrateCutoverParam `json:"extend"` + ctx +} + +// 迁移切换前置条件: +// 带切换的实例, dest master,dest slave 需要提前给tdbctl中控授权 + +// RemotedbdrMigrateCutoverParam TODO +type RemotedbdrMigrateCutoverParam struct { + // tdbctl host port + Host string `json:"host" validate:"required,ip"` + Port int `json:"port" validate:"required,lt=65536,gte=3306"` + // 客户端连接检查 + ClientConnCheck bool `json:"client_conn_check"` + // 主从延迟检查 + SlaveDelayCheck bool `json:"slave_delay_check"` + // 数据校验结果检查 + VerifyChecksum bool `json:"verify_checksum"` + MigrateCutoverPairs []MigrateCutoverPair `json:"migrate_cutover_pairs"` +} + +type ctx struct { + CutOverCtx + destMasterConn map[IPPORT]*native.DbWorker + destSlaveConn map[IPPORT]*native.DbWorker + cutOverPairs []CutOverParis + existRemoteSlave bool + checkVars []string +} + +// CutOverParis TODO +type CutOverParis struct { + MasterSvr native.Server + SlaveSvr native.Server + DestMaster CutoverUnit + DestSlave CutoverUnit +} + +// MigrateCutoverPair TODO +type MigrateCutoverPair struct { + Master Instance `json:"origin_master" validate:"required"` + DestMaster CutoverUnit `json:"dest_master" validate:"required"` + DestSlave CutoverUnit `json:"dest_slave"` +} + +// destSlaveIsEmpty TODO +// 如果参数 +func (m *MigrateCutoverPair) destSlaveIsEmpty() bool { + return m.DestSlave == CutoverUnit{} +} + +// CutoverUnit TODO +type CutoverUnit struct { + Host string `json:"host" validate:"required,ip"` + Port int `json:"port" validate:"required,lt=65536,gte=3306"` + // account password used by the tdbctl system + User string `json:"user"` + Password string `json:"password"` +} + +// GetAlterNodeSql TODO +func (c *CutoverUnit) GetAlterNodeSql(svrName string) string { + return fmt.Sprintf("TDBCTL ALTER NODE %s options(user '%s', password '%s', host '%s', port %d);", + svrName, + c.User, + c.Password, c.Host, c.Port) +} + +// GetHostPort TODO +func (c *CutoverUnit) GetHostPort() string { + return fmt.Sprintf("%s:%d", c.Host, c.Port) +} + +// Conn TODO +func (c *CutoverUnit) Conn() (conn *native.DbWorker, err error) { + conn, err = native.InsObject{ + Host: c.Host, + Port: c.Port, + User: c.User, + Pwd: c.Password, + }.Conn() + return +} + +// Example TODO +func (s *SpiderClusterBackendMigrateCutoverComp) Example() interface{} { + return SpiderClusterBackendMigrateCutoverComp{ + Params: &RemotedbdrMigrateCutoverParam{ + Host: "1.1.1.1", + Port: 26000, + ClientConnCheck: true, + SlaveDelayCheck: true, + VerifyChecksum: true, + MigrateCutoverPairs: []MigrateCutoverPair{ + { + Master: Instance{ + Host: "2.2.2.2", + Port: 3006, + }, + DestMaster: CutoverUnit{ + Host: "3.3.3.3", + Port: 3306, + User: "xx", + Password: "xx", + }, + DestSlave: CutoverUnit{ + Host: "4.4.4.4", + Port: 3306, + User: "xx", + Password: "xx", + }, + }, + { + Master: Instance{ + Host: "2.2.2.2", + Port: 3007, + }, + DestMaster: CutoverUnit{ + Host: "3.3.3.3", + Port: 3307, + User: "xx", + Password: "xx", + }, + DestSlave: CutoverUnit{ + Host: "4.4.4.4", + Port: 3307, + User: "xx", + Password: "xx", + }, + }, + }, + }, + } +} + +// Init TODO +func (s *SpiderClusterBackendMigrateCutoverComp) Init() (err error) { + logger.Info("cutover param is %v", s.Params.MigrateCutoverPairs) + var conn *native.DbWorker + conn, err = native.InsObject{ + Host: s.Params.Host, + Port: s.Params.Port, + User: s.GeneralParam.RuntimeAccountParam.AdminUser, + Pwd: s.GeneralParam.RuntimeAccountParam.AdminPwd, + }.Conn() + if err != nil { + return err + } + s.tdbCtlConn = &native.TdbctlDbWork{DbWorker: *conn} + // 查询mysql.servers tables + servers, err := s.tdbCtlConn.SelectServers() + if err != nil { + return err + } + // connect spider + s.spidersConn, err = connSpiders(servers) + if err != nil { + return err + } + // conn dest master slave + if err = s.connDest(); err != nil { + return err + } + s.getSysUsers(servers, s.GeneralParam.GetAllSysAccount()) + ipPortServersMap, svrNameServersMap := transServersToMap(servers) + s.ipPortServersMap = ipPortServersMap + s.svrNameServersMap = svrNameServersMap + s.checkVars = []string{"character_set_server", "lower_case_table_names", "time_zone", "binlog_format", + "log_bin_compress"} + return nil +} + +// PreCheck TODO +func (s *SpiderClusterBackendMigrateCutoverComp) PreCheck() (err error) { + if err := s.cutOverParisParamCheck(); err != nil { + return err + } + // check dest master with dest slave replicate status + if s.existRemoteSlave { + logger.Info("check whether the master slave synchronization status to be switched is normal") + for addr, conn := range s.destSlaveConn { + slaveStatus, err := conn.ShowSlaveStatus() + if err != nil { + return err + } + if !slaveStatus.ReplSyncIsOk() { + return fmt.Errorf("%s replication status is abnormal ,IO Thread: %s,SQL Thread:%s", addr, + slaveStatus.SlaveIORunning, + slaveStatus.SlaveSQLRunning) + } + } + } + // compare origin master with dest master variables + if err = s.validateServers(); err != nil { + return err + } + logger.Info("check whether the key variables of the source master and the target master are consistent") + if err = s.checkPairsVariables(); err != nil { + return err + } + if s.Params.ClientConnCheck { + if err := s.CheckSpiderAppProcesslist(); err != nil { + return err + } + } + // check origin master with dest master replicate status + if s.Params.VerifyChecksum { + if err = validateChecksum(s.destMasterConn); err != nil { + return err + } + } + return err +} + +func validateChecksum(conns map[IPPORT]*native.DbWorker) (err error) { + for addr, conn := range conns { + if err = conn.ValidateChecksum(3, true); err != nil { + logger.Error("%s validate checksum abnormal %s", addr, err.Error()) + return err + } + } + return +} + +func (s *SpiderClusterBackendMigrateCutoverComp) checkPairsVariables() (err error) { + for _, pair := range s.cutOverPairs { + svrName := pair.MasterSvr.ServerName + destMasterConn, ok := s.destMasterConn[pair.DestMaster.GetHostPort()] + if !ok { + return fmt.Errorf("get %s conn from destMasterConn map failed", pair.DestMaster.GetHostPort()) + } + if err = s.tdbCtlConn.MySQLVarsCompare(svrName, destMasterConn, s.checkVars); err != nil { + return err + } + } + return +} + +// cutOverParisParamCheck 参数中、目标的master 和目标slave 检查 +// 目标slave 只能都为空、或者呀完全匹配目标master的个数 +func (s *SpiderClusterBackendMigrateCutoverComp) cutOverParisParamCheck() (err error) { + destSlaveNum := 0 + s.existRemoteSlave = false + for _, ins := range s.Params.MigrateCutoverPairs { + if ins.destSlaveIsEmpty() { + if destSlaveNum > 0 { + return fmt.Errorf("you must send slave param for master: %s", ins.DestMaster.GetHostPort()) + } + continue + } + destSlaveNum++ + } + slaveSptServers := s.getSlaveSptServers() + slaveSpiderServers := s.getSlaveSpiderServers() + // 如果传参中并没有待切换的slave + // 但是在mysql.server 表中查询到了spider slave 相关信息 + // 则需要抛出异常 + if destSlaveNum == 0 && len(slaveSptServers) > 0 && len(slaveSpiderServers) > 0 { + return errors.New(`the origin spider cluster exist spider spt and slave spider, + but this time cutover not have waited cutover slaves`) + } + // 其他情况只切换spider master对应的主分片 + if destSlaveNum == len(s.Params.MigrateCutoverPairs) { + s.existRemoteSlave = true + } + return nil +} + +func (s *SpiderClusterBackendMigrateCutoverComp) connDest() (err error) { + s.destMasterConn = make(map[string]*native.DbWorker) + s.destSlaveConn = make(map[string]*native.DbWorker) + for _, ins := range s.Params.MigrateCutoverPairs { + destMasterAddr := ins.DestMaster.GetHostPort() + logger.Info("connecting %s ...", destMasterAddr) + masterConn, err := ins.DestMaster.Conn() + if err != nil { + return err + } + s.destMasterConn[destMasterAddr] = masterConn + if !ins.destSlaveIsEmpty() { + slaveConn, err := ins.DestMaster.Conn() + if err != nil { + return err + } + s.destSlaveConn[ins.DestSlave.GetHostPort()] = slaveConn + } + } + logger.Info("dest master conn %v", s.destMasterConn) + return +} + +func (s *SpiderClusterBackendMigrateCutoverComp) validateServers() (err error) { + for _, ins := range s.Params.MigrateCutoverPairs { + var mastersvr native.Server + var exist bool + var pair CutOverParis + if mastersvr, exist = s.ipPortServersMap[ins.Master.IpPort()]; !exist { + return fmt.Errorf("master %s: not found in mysql.servers", ins.Master.IpPort()) + } + if !native.SvrNameIsMasterShard(mastersvr.ServerName) { + return fmt.Errorf("%s in tdbctl server name is not master shard", ins.Master.IpPort()) + } + if s.existRemoteSlave { + slaveSptName := native.GetSlaveShardNameByMasterShardName(mastersvr.ServerName) + logger.Info("slave spt name is:%s", slaveSptName) + slavesvr, exists := s.svrNameServersMap[slaveSptName] + if !exists { + return fmt.Errorf("the key %s,not found in svrNameServersMap ", slaveSptName) + } + pair.SlaveSvr = slavesvr + pair.DestSlave = ins.DestSlave + } + pair.MasterSvr = mastersvr + pair.DestMaster = ins.DestMaster + s.cutOverPairs = append(s.cutOverPairs, pair) + } + return +} + +// PersistenceRollbackFile TODO +func (s *SpiderClusterBackendMigrateCutoverComp) PersistenceRollbackFile() (err error) { + logger.Info("the switching operation will be performed") + if err = s.initRollbackRouteFile(); err != nil { + logger.Info("init create rollback file failed %s", err.Error()) + return err + } + for _, pair := range s.cutOverPairs { + masterSvrName := pair.MasterSvr.ServerName + s.primaryShardrollbackSqls = append(s.primaryShardrollbackSqls, pair.MasterSvr.GetAlterNodeSql(masterSvrName)) + if s.existRemoteSlave { + slaveSvrName := pair.SlaveSvr.ServerName + s.slaveShardrollbackSqls = append(s.slaveShardrollbackSqls, pair.SlaveSvr.GetAlterNodeSql(slaveSvrName)) + } + } + rollbackSqls := append(s.primaryShardrollbackSqls, s.slaveShardrollbackSqls...) + if err = s.writeContents(rollbackSqls); err != nil { + return err + } + s.fd.Close() + return +} + +// CutOver TODO +func (s *SpiderClusterBackendMigrateCutoverComp) CutOver() (err error) { + var tdbctlFlushed bool + // change the central control route + // release the lock until after performing the rollback routing + defer func() { + rollbackSqls := append(s.primaryShardrollbackSqls, s.slaveShardrollbackSqls...) + if err != nil && len(rollbackSqls) > 0 { + _, xerr := s.tdbCtlConn.ExecMore(rollbackSqls) + if xerr != nil { + logger.Error("rollbackup tdbctl router failed %s", xerr.Error()) + err = fmt.Errorf("%w,rollbackup err:%w", err, xerr) + return + } + logger.Info("rollback route successfully~") + if tdbctlFlushed { + return + } + if ferr := s.flushrouting(); ferr != nil { + err = fmt.Errorf("%w,flush rollback route err:%w", err, ferr) + return + } + logger.Info("rollback route successfully~") + } + }() + + logger.Info("start refreshing the primary spt route") + if err = s.switchSpt(); err != nil { + tdbctlFlushed = true + return err + } + + logger.Info("update tdbctl mysql.servers successfully") + // lock all spider write + defer s.Unlock() + logger.Info("start locking the spider") + if err = s.LockaAllSpidersWrite(); err != nil { + return err + } + + logger.Info("lock all spider successfully,record the location of each instance binlog") + // check the master slave synchronization status again + if err = checkReplicationStatus(s.destMasterConn); err != nil { + return err + } + logger.Info("check replication status ok") + // record the binlog position information during the handover + if err = s.RecordBinlogPos(); err != nil { + return err + } + logger.Info("doing tdbctl flush routing force ... ") + return s.flushrouting() +} + +// StopRepl TODO +func (s *SpiderClusterBackendMigrateCutoverComp) StopRepl() (err error) { + for hostPort, destMasterConn := range s.destMasterConn { + logger.Info("%s: execute stop slave,reset slave", hostPort) + if _, err = destMasterConn.ExecMore([]string{"stop slave;", "reset slave all;"}); err != nil { + return err + } + } + return nil +} + +// RecordBinlogPos TODO +func (s *SpiderClusterBackendMigrateCutoverComp) RecordBinlogPos() (err error) { + for hostPort, destMasterConn := range s.destMasterConn { + logger.Info("record %s master status", hostPort) + pos, err := destMasterConn.ShowMasterStatus() + if err != nil { + logger.Warn("%s show master status failed %s", hostPort, err.Error()) + return nil + } + logger.Info("%s,current pos is binlog_file:%s,binlog_pos:%d,gitid_sets:%s", hostPort, pos.File, + pos.Position, + pos.ExecutedGtidSet) + } + return nil +} + +func (s *SpiderClusterBackendMigrateCutoverComp) switchSpt() (err error) { + logger.Info("start switch master spt ...") + var alterSqls []string + for _, pair := range s.cutOverPairs { + masterSvrName := pair.MasterSvr.ServerName + alterSql := pair.DestMaster.GetAlterNodeSql(masterSvrName) + logger.Info("will execute master spt switch sql:%s", mysqlutil.CleanSvrPassword(alterSql)) + alterSqls = append(alterSqls, alterSql) + if s.existRemoteSlave { + slaveSvrName := pair.SlaveSvr.ServerName + alterSql := pair.SlaveSvr.GetAlterNodeSql(slaveSvrName) + logger.Info("will execute slave spt switch sql:%s", mysqlutil.CleanSvrPassword(alterSql)) + alterSqls = append(alterSqls, alterSql) + } + } + if _, err = s.tdbCtlConn.ExecMore(alterSqls); err != nil { + return + } + return err +} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/spiderctl/backend_switch.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/spiderctl/backend_switch.go new file mode 100644 index 0000000000..54c1d031bd --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/spiderctl/backend_switch.go @@ -0,0 +1,749 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package spiderctl + +import ( + "bufio" + "fmt" + "os" + "path" + "regexp" + "strings" + "time" + + "dbm-services/common/go-pubpkg/cmutil" + "dbm-services/common/go-pubpkg/logger" + "dbm-services/mysql/db-tools/dbactuator/pkg/components" + "dbm-services/mysql/db-tools/dbactuator/pkg/core/cst" + "dbm-services/mysql/db-tools/dbactuator/pkg/native" + "dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil" +) + +// SpiderClusterBackendSwitchComp TODO +type SpiderClusterBackendSwitchComp struct { + GeneralParam *components.GeneralParam `json:"general"` + Params *SpiderRemotedbSwitchParam `json:"extend"` + runtimeCtx +} +type runtimeCtx struct { + CutOverCtx + realSwitchSvrPairs []SvrPairs + slavesConn map[IPPORT]*native.DbWorker + mastesConn map[IPPORT]*native.DbWorker +} + +// SpiderRemotedbSwitchParam TODO +type SpiderRemotedbSwitchParam struct { + Host string `json:"host" validate:"required,ip"` + Port int `json:"port" validate:"required,lt=65536,gte=3306"` + // 客户端连接检查 + ClientConnCheck bool `json:"client_conn_check"` + // 数据校验结果检查 + VerifyChecksum bool `json:"verify_checksum"` + SwitchParis []SwitchUnit `json:"switch_paris" validate:"required,gt=0,dive"` + // force: 忽略部分切换前检查 + Force bool `json:"force"` +} + +// CutOverCtx TODO +type CutOverCtx struct { + tdbCtlConn *native.TdbctlDbWork + spidersConn map[string]*native.DbWorker + ipPortServersMap map[IPPORT]native.Server + svrNameServersMap map[SVRNAME]native.Server + newMasterPosInfos map[string]native.MasterStatusResp + sysUsers []string + primaryShardrollbackSqls []string + slaveShardrollbackSqls []string + fd *os.File // 持久化回滚sql + primaryShardSwitchSqls []string + slaveShardSwitchSqls []string +} + +// SvrPairs TODO +type SvrPairs struct { + MptName string + SptName string + Master *native.Server + Slave *native.Server +} + +// SwitchUnit TODO +type SwitchUnit struct { + Master Instance `json:"master" validate:"required"` + Slave Instance `json:"slave" validate:"required"` +} + +// IpPort TODO +func (i Instance) IpPort() string { + return fmt.Sprintf("%s:%d", i.Host, i.Port) +} + +// Example TODO +func (r *SpiderClusterBackendSwitchComp) Example() interface{} { + return SpiderClusterBackendSwitchComp{ + Params: &SpiderRemotedbSwitchParam{ + Host: "1.1.1.1", + Port: 26000, + ClientConnCheck: true, + // SlaveDelayCheck: true, + VerifyChecksum: true, + SwitchParis: []SwitchUnit{ + { + Master: Instance{ + Host: "2.2.2.2", + Port: 3306, + }, + Slave: Instance{ + Host: "3.3.3.3", + Port: 3306, + }, + }, + { + Master: Instance{ + Host: "2.2.2.2", + Port: 3307, + }, + Slave: Instance{ + Host: "3.3.3.3", + Port: 3307, + }, + }, + }, + }, + } +} + +// Init TODO +func (r *SpiderClusterBackendSwitchComp) Init() (err error) { + r.ipPortServersMap = make(map[string]native.Server) + logger.Info("tdbctl connecting ...") + if err = r.connTdbctl(); err != nil { + logger.Error("Connect %d failed:%s", r.Params.Port, err.Error()) + return err + } + logger.Info("query tdbctl servers") + servers, err := r.tdbCtlConn.SelectServers() + if err != nil { + return err + } + // connect spider + logger.Info("connecting all spider ...") + r.spidersConn, err = connSpiders(servers) + if err != nil { + return err + } + logger.Info("get all sys users ...") + r.getSysUsers(servers, r.GeneralParam.GetAllSysAccount()) + ipPortServersMap, svrNameServersMap := transServersToMap(servers) + r.ipPortServersMap = ipPortServersMap + r.svrNameServersMap = svrNameServersMap + // initialize rollback sql file + if err = r.initRollbackRouteFile(); err != nil { + return err + } + logger.Info("connect backend instance ...") + r.mastesConn = make(map[string]*native.DbWorker) + r.slavesConn = make(map[string]*native.DbWorker) + for _, swpair := range r.Params.SwitchParis { + masterAddr := swpair.Master.IpPort() + slaveAddr := swpair.Slave.IpPort() + masterSvr, ok := ipPortServersMap[masterAddr] + if !ok { + return fmt.Errorf("%s: servers not exist in ipPortServersMap", masterAddr) + } + slaveSvr, ok := ipPortServersMap[slaveAddr] + if !ok { + return fmt.Errorf("%s: servers not exist in ipPortServersMap", slaveAddr) + } + masterConn, err := masterSvr.GetConn() + if err != nil { + return err + } + r.mastesConn[masterAddr] = masterConn + slaveConn, err := slaveSvr.GetConn() + if err != nil { + return err + } + r.slavesConn[slaveAddr] = slaveConn + } + return nil +} + +// PreCheck TODO +func (r *SpiderClusterBackendSwitchComp) PreCheck() (err error) { + // verify whether the instance relationship in the parameters is consistent with tdbctl servers + logger.Info("verify whether the instance relationship in the parameters is consistent with tdbctl servers") + if err = r.validateServers(); err != nil { + return err + } + if err = r.consistencySwitchCheck(); err != nil { + if r.Params.Force { + logger.Warn(err.Error()) + return nil + } + return err + } + return r.getSwitchSqls() +} + +func (r *SpiderClusterBackendSwitchComp) consistencySwitchCheck() (err error) { + // 检查复制关系 + if err = r.checkReplicationRelation(); err != nil { + return err + } + // 主从延迟检查 + for addr, conn := range r.slavesConn { + if err = conn.ReplicateDelayCheck(1, 1024); err != nil { + logger.Error("%s replicate delay abnormal %s", addr, err.Error()) + return err + } + } + if r.Params.ClientConnCheck { + if err := r.CheckSpiderAppProcesslist(); err != nil { + return err + } + } + if r.Params.VerifyChecksum { + return validateChecksum(r.slavesConn) + } + return nil +} + +// connSpiders TODO +func connSpiders(servers []native.Server) (conns map[string]*native.DbWorker, err error) { + conns = make(map[string]*native.DbWorker) + spider_regexp := regexp.MustCompile(native.SPIDER_PREFIX) + for _, server := range servers { + if spider_regexp.MatchString(server.ServerName) { + conn, err := native.InsObject{ + Host: server.Host, + Port: server.Port, + User: server.Username, + Pwd: server.Password, + }.Conn() + if err != nil { + return nil, err + } + key := fmt.Sprintf("%s:%d", server.Host, server.Port) + conns[key] = conn + } + } + return +} + +// checkReplicationRelation TODO +// checkSlaveStatus 检查remotedb remotedr 同步关系是否正常 +func (r *SpiderClusterBackendSwitchComp) checkReplicationRelation() (err error) { + for _, switch_pair := range r.Params.SwitchParis { + slaveptname, err := r.getSvrName(switch_pair.Slave.IpPort()) + if err != nil { + return err + } + slaveStaus, err := r.tdbCtlConn.ShowSlaveStatus(slaveptname) + if err != nil { + return err + } + // 检查从库实际的复制主库是否正确 + replMaster := fmt.Sprintf("%s:%d", slaveStaus.MasterHost, slaveStaus.MasterPort) + if strings.Compare(replMaster, switch_pair.Master.IpPort()) != 0 { + return fmt.Errorf("the %s real repl from %s,but the send param master is %s", switch_pair.Slave.IpPort(), + replMaster, switch_pair.Master.IpPort()) + } + err = r.tdbCtlConn.CheckSlaveReplStatus(func() (resp native.ShowSlaveStatusResp, err error) { + return r.tdbCtlConn.ShowSlaveStatus(slaveptname) + }) + if err != nil { + return err + } + } + return +} + +// func (r *SpiderClusterBackendSwitchComp) checkReplicationStatus() (err error) { +// for _, switch_pair := range r.Params.SwitchParis { +// slaveptname, err := r.getSvrName(switch_pair.Slave.IpPort()) +// if err != nil { +// return err +// } +// logger.Info("check %s replicate status ...", switch_pair.Slave.IpPort()) +// err = r.tdbCtlConn.CheckSlaveReplStatus(func() (resp native.ShowSlaveStatusResp, err error) { +// return r.tdbCtlConn.ShowSlaveStatus(slaveptname) +// }) +// if err != nil { +// return err +// } +// } +// return +// } + +func checkReplicationStatus(conns map[IPPORT]*native.DbWorker) (err error) { + for addr, conn := range conns { + logger.Info("check replicate status...") + slaveStatus, err := conn.ShowSlaveStatus() + if err != nil { + return err + } + if !slaveStatus.ReplSyncIsOk() { + return fmt.Errorf("%s replication status is abnormal ,IO Thread: %s,SQL Thread:%s", addr, + slaveStatus.SlaveIORunning, + slaveStatus.SlaveSQLRunning) + } + err = cmutil.Retry(cmutil.RetryConfig{ + DelayTime: 1 * time.Second, + Times: 10, + }, func() error { + return conn.ReplicateDelayCheck(1, 1024) + }) + if err != nil { + logger.Error("delay check failed %s", err.Error()) + return err + } + } + return +} + +func (r *SpiderClusterBackendSwitchComp) getSvrName(hostport string) (svrName string, err error) { + if svr, ok := r.ipPortServersMap[hostport]; ok { + return svr.ServerName, nil + } + return "", fmt.Errorf("get servers empty by %s", hostport) +} + +// IPPORT TODO +type IPPORT = string + +// SVRNAME TODO +type SVRNAME = string + +func transServersToMap(servers []native.Server) (map[IPPORT]native.Server, map[SVRNAME]native.Server) { + m := make(map[IPPORT]native.Server) + sm := make(map[string]native.Server) + for _, server := range servers { + key := fmt.Sprintf("%s:%d", server.Host, server.Port) + m[key] = server + sm[server.ServerName] = server + } + return m, sm +} + +func (r *SpiderClusterBackendSwitchComp) validateServers() (err error) { + svrmap := r.ipPortServersMap + for _, ms := range r.Params.SwitchParis { + var mastersvr, slavesvr native.Server + var exist bool + mh := ms.Master.IpPort() + if mastersvr, exist = svrmap[mh]; !exist { + return fmt.Errorf("master %s: not found in mysql.servers", mh) + } + if !native.SvrNameIsMasterShard(mastersvr.ServerName) { + return fmt.Errorf("%s in tdbctl server name:%s is not master shard", mh, mastersvr.ServerName) + } + sh := ms.Slave.IpPort() + if slavesvr, exist = svrmap[sh]; !exist { + return fmt.Errorf("slave %s: not found in mysql.servers", sh) + } + if !native.SvrNameIsSlaveShard(slavesvr.ServerName) { + return fmt.Errorf("%s in tdbctl server name:%s is not slave shard", sh, slavesvr.ServerName) + } + masterShardNum := native.GetShardNumberFromMasterServerName(mastersvr.ServerName) + slaveShardNum := native.GetShardNumberFromSlaveServerName(slavesvr.ServerName) + if cmutil.IsEmpty(masterShardNum) { + return fmt.Errorf("the master %s shard id is empty", mh) + } + if cmutil.IsEmpty(slaveShardNum) { + return fmt.Errorf("the slave %s shard id is empty", sh) + } + if strings.Compare(masterShardNum, slaveShardNum) != 0 { + return fmt.Errorf("master slave shard id is not equal,master shard id is %s,slave shard id is %s", + masterShardNum, + slaveShardNum) + } + r.realSwitchSvrPairs = append(r.realSwitchSvrPairs, SvrPairs{ + MptName: mastersvr.ServerName, + SptName: slavesvr.ServerName, + Master: &mastersvr, + Slave: &slavesvr, + }) + } + return nil +} + +func (r *SpiderClusterBackendSwitchComp) connTdbctl() (err error) { + // connection central control + conn, err := native.InsObject{ + Host: r.Params.Host, + Port: r.Params.Port, + User: r.GeneralParam.RuntimeAccountParam.AdminUser, + Pwd: r.GeneralParam.RuntimeAccountParam.AdminPwd, + }.Conn() + r.tdbCtlConn = &native.TdbctlDbWork{DbWorker: *conn} + return err +} + +// CutOver TODO +func (r *SpiderClusterBackendSwitchComp) CutOver() (err error) { + var tdbctlFlushed bool + logger.Info("the switching operation will be performed") + // 更改中控路由 + logger.Info("refresh the tdbctl route,but spider hasn t taken effect yet") + defer func() { + if err != nil && len(r.primaryShardrollbackSqls) > 0 { + logger.Info("start execute rollback router sql ... ") + if _, rerr := r.tdbCtlConn.ExecMore(r.primaryShardrollbackSqls); rerr != nil { + logger.Error("failed to roll back tdbctl routing") + return + } + logger.Info("rollback route successfully~") + if tdbctlFlushed { + return + } + if ferr := r.flushrouting(); ferr != nil { + return + } + logger.Info("flush rollback route successfully~") + } + }() + if _, err = r.tdbCtlConn.ExecMore(r.primaryShardSwitchSqls); err != nil { + tdbctlFlushed = true + return err + } + // lock all spider write + defer r.Unlock() + logger.Info("start locking the spider node") + if err = r.LockaAllSpidersWrite(); err != nil { + return err + } + // check the replication status again + if !r.Params.Force { + if err = checkReplicationStatus(r.slavesConn); err != nil { + return err + } + } + logger.Info("record the location of each node binlog") + // record the binlog position information during the handover + if err = r.recordBinLogPos(); err != nil { + return err + } + // flush 到中控生效 + logger.Info("execute:tdbctl flush routing force") + return r.flushrouting() +} + +func (r *SpiderClusterBackendSwitchComp) getSwitchSqls() (err error) { + for _, ins_pair := range r.realSwitchSvrPairs { + primarySwitchSql := ins_pair.Slave.GetAlterNodeSql(ins_pair.MptName) + logger.Info("primary spt switch sql:%s", mysqlutil.CleanSvrPassword(primarySwitchSql)) + r.primaryShardSwitchSqls = append(r.primaryShardSwitchSqls, primarySwitchSql) + if !r.Params.Force { + slaveSwitchSql := ins_pair.Master.GetAlterNodeSql(ins_pair.SptName) + logger.Info("slave spt switch sql:%s", mysqlutil.CleanSvrPassword(slaveSwitchSql)) + r.slaveShardSwitchSqls = append(r.slaveShardSwitchSqls, slaveSwitchSql) + } + } + return +} + +// CutOverSlave TODO +func (r *SpiderClusterBackendSwitchComp) CutOverSlave() (err error) { + var tdbctlFlushed bool + // switch spider rout + defer func() { + if err != nil && len(r.slaveShardrollbackSqls) > 0 { + _, xerr := r.tdbCtlConn.ExecMore(r.slaveShardrollbackSqls) + if xerr != nil { + logger.Error("rollbackup tdbctl router failed %s", xerr.Error()) + } + logger.Info("rollback route successfully~") + if tdbctlFlushed { + return + } + if ferr := r.flushrouting(); ferr != nil { + logger.Error("execute flush rollback route failed %s", err.Error()) + return + } + logger.Info("excute flush rollback route successfully~") + } + }() + if _, err = r.tdbCtlConn.ExecMore(r.slaveShardSwitchSqls); err != nil { + tdbctlFlushed = true + return err + } + // flush 到中控生效 + logger.Info("执行:tdbctl flush routing force") + return r.flushrouting() +} + +// PersistenceRollbackFile TODO +func (r *SpiderClusterBackendSwitchComp) PersistenceRollbackFile() (err error) { + var masterRbSqls, slaveRbSqls, w []string + for _, ins_pair := range r.realSwitchSvrPairs { + masterRbSqls = append(masterRbSqls, ins_pair.Master.GetAlterNodeSql(ins_pair.MptName)) + slaveRbSqls = append(slaveRbSqls, ins_pair.Slave.GetAlterNodeSql(ins_pair.SptName)) + } + if err = r.initRollbackRouteFile(); err != nil { + return err + } + w = masterRbSqls + if !r.Params.Force { + w = append(w, "// slave spt rollback sql text ") + w = append(w, slaveRbSqls...) + } + if err = r.writeContents(w); err != nil { + return err + } + if err = r.fd.Close(); err != nil { + logger.Error("close rollback file error:%s", err.Error()) + return err + } + r.primaryShardrollbackSqls = masterRbSqls + r.slaveShardrollbackSqls = slaveRbSqls + logger.Info("rollback sql file persisted successfully") + return +} + +func (r *SpiderClusterBackendSwitchComp) recordBinLogPos() (err error) { + r.newMasterPosInfos = make(map[string]native.MasterStatusResp) + for _, swpair := range r.Params.SwitchParis { + conn, ok := r.slavesConn[swpair.Slave.IpPort()] + if !ok { + return fmt.Errorf("get %s conn failed", swpair.Slave.IpPort()) + } + pos, err := conn.ShowMasterStatus() + if err != nil { + return err + } + logger.Info("%s,current pos is binlog_file:%s,binlog_pos:%d,gitid_sets:%s", swpair.Slave.IpPort(), pos.File, + pos.Position, + pos.ExecutedGtidSet) + r.newMasterPosInfos[swpair.Slave.IpPort()] = pos + } + return +} + +// GrantReplForNewSlave TODO +func (r *SpiderClusterBackendSwitchComp) GrantReplForNewSlave() (err error) { + for _, swpair := range r.Params.SwitchParis { + conn, ok := r.slavesConn[swpair.Slave.IpPort()] + if !ok { + return fmt.Errorf("get %s conn failed", swpair.Slave.IpPort()) + } + if _, err = conn.ExecMore(r.grantReplSql(swpair.Master.Host)); err != nil { + return err + } + } + return nil +} + +// StopRepl TODO +func (r *SpiderClusterBackendSwitchComp) StopRepl() (err error) { + for _, swpair := range r.Params.SwitchParis { + conn, ok := r.slavesConn[swpair.Slave.IpPort()] + if !ok { + return fmt.Errorf("get %s conn failed", swpair.Slave.IpPort()) + } + if _, err = conn.ExecMore([]string{"stop slave;", "reset slave all;"}); err != nil { + return err + } + } + return nil +} + +func (r *SpiderClusterBackendSwitchComp) grantReplSql(host string) []string { + var execSQLs []string + repl_user := r.GeneralParam.RuntimeAccountParam.ReplUser + repl_pwd := r.GeneralParam.RuntimeAccountParam.ReplPwd + logger.Info("repl user:%s,repl_pwd:%s", repl_user, repl_pwd) + execSQLs = append(execSQLs, fmt.Sprintf("CREATE USER /*!50706 IF NOT EXISTS */ `%s`@`%s` IDENTIFIED BY '%s';", + repl_user, host, repl_pwd)) + execSQLs = append(execSQLs, fmt.Sprintf("GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO `%s`@`%s`;", repl_user, + host)) + return execSQLs +} + +// ChangeMasterToNewMaster TODO +func (r *SpiderClusterBackendSwitchComp) ChangeMasterToNewMaster() (err error) { + repl_user := r.GeneralParam.RuntimeAccountParam.ReplUser + repl_pwd := r.GeneralParam.RuntimeAccountParam.ReplPwd + for _, swpair := range r.Params.SwitchParis { + conn := r.mastesConn[swpair.Master.IpPort()] + pos := r.newMasterPosInfos[swpair.Slave.IpPort()] + changeMastersql := fmt.Sprintf( + `CHANGE MASTER TO MASTER_HOST='%s', + MASTER_USER ='%s', + MASTER_PASSWORD='%s', + MASTER_PORT=%d, + MASTER_LOG_FILE='%s', + MASTER_LOG_POS=%d`, + swpair.Slave.Host, + repl_user, + repl_pwd, + swpair.Slave.Port, + pos.File, + pos.Position, + ) + logger.Info("change master to %s", swpair.Slave.IpPort()) + if _, err = conn.Exec(changeMastersql); err != nil { + return err + } + logger.Info("start slave") + if _, err = conn.Exec("start slave;"); err != nil { + return err + } + err = cmutil.Retry(cmutil.RetryConfig{ + Times: 30, + DelayTime: 1 * time.Second, + }, func() error { + ss, serr := conn.ShowSlaveStatus() + if serr != nil { + return serr + } + if ss.ReplSyncIsOk() { + return nil + } + return fmt.Errorf("wating..., IOThread:%s,SQLThread:%s", ss.SlaveIORunning, ss.SlaveSQLRunning) + }) + if err != nil { + return err + } + } + return err +} + +func (c *CutOverCtx) getSysUsers(servers []native.Server, sysUsers []string) { + for _, server := range servers { + sysUsers = append(sysUsers, server.Username) + } + // get sys user + c.sysUsers = cmutil.RemoveDuplicate(sysUsers) + logger.Info("system user: %v", c.sysUsers) +} + +func (c *CutOverCtx) getSlaveSptServers() (slaveSptServers []native.Server) { + for svrName, server := range c.svrNameServersMap { + if native.SvrNameIsSlaveShard(svrName) { + slaveSptServers = append(slaveSptServers, server) + } + } + return +} + +func (c *CutOverCtx) getSlaveSpiderServers() (slaveSpiderServers []native.Server) { + for svrName, server := range c.svrNameServersMap { + if native.SvrNameIsSlaveSpiderShard(svrName) { + slaveSpiderServers = append(slaveSpiderServers, server) + } + } + return +} + +// CheckSpiderAppProcesslist TODO +func (ctx *CutOverCtx) CheckSpiderAppProcesslist() (err error) { + for addr, spider_conn := range ctx.spidersConn { + pls, err := spider_conn.ShowApplicationProcesslist(ctx.sysUsers) + if err != nil { + return err + } + if len(pls) > 0 { + return fmt.Errorf("spider: %s have application processlist %v", addr, pls) + } + } + return +} + +// LockaAllSpidersWrite TODO +func (ctx *CutOverCtx) LockaAllSpidersWrite() (err error) { + for addr, spider_conn := range ctx.spidersConn { + _, err = spider_conn.Exec("flush table with read lock;") + if err != nil { + return fmt.Errorf("lock tables at %s,err:%w", addr, err) + } + } + return +} + +// Unlock TODO +func (ctx *CutOverCtx) Unlock() (err error) { + for addr, spider_conn := range ctx.spidersConn { + err = cmutil.Retry(cmutil.RetryConfig{ + Times: 3, + DelayTime: 1 * time.Second, + }, func() error { + _, ierr := spider_conn.Exec("unlock tables") + if ierr != nil { + return ierr + } + return nil + }) + if err != nil { + return fmt.Errorf("addr:%s,err:%w", addr, err) + } + } + return +} + +func (c *CutOverCtx) flushrouting() (err error) { + if err = cmutil.Retry(cmutil.RetryConfig{Times: 3, DelayTime: 1 * time.Second}, func() error { + _, ferr := c.tdbCtlConn.Exec("tdbctl flush routing force") + return ferr + }); err != nil { + return err + } + return +} + +// me, ok := err.(*mysql.MySQLError) +// if !ok { +// return +// } +// if me.Number == 12028 { +// partFlushed = true +// } +// partFlushed, err + +func (c *CutOverCtx) initRollbackRouteFile() (err error) { + fileName := "rollback.sql" + currentPath, err := os.Getwd() + if err != nil { + return + } + logger.Info("init rollback route sql file in %s", currentPath) + if cmutil.FileExists(fileName) { + fsInfo, err := os.Stat(fileName) + if err != nil { + return err + } + err = os.Rename(fileName, fileName+"."+fsInfo.ModTime().Format(cst.TimeLayoutDir)) + if err != nil { + return err + } + } + c.fd, err = os.OpenFile(fileName, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644) + if err != nil { + return err + } + logger.Info("create rollback sql file : %s", path.Join(currentPath, fileName)) + return +} + +func (c *CutOverCtx) writeContents(contents []string) (err error) { + write := bufio.NewWriter(c.fd) + for _, content := range contents { + _, err = write.WriteString(content + "\n\r") + if err != nil { + return err + } + } + if err = write.Flush(); err != nil { + return err + } + return +} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/spiderctl/init_cluster_routing_relationship.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/spiderctl/init_cluster_routing_relationship.go index 51ebfab063..ff13e1012b 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/spiderctl/init_cluster_routing_relationship.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/spiderctl/init_cluster_routing_relationship.go @@ -30,8 +30,8 @@ type InitClusterRoutingParam struct { // Instance TODO type Instance struct { - Host string `json:"host"` - Port int `json:"port"` + Host string `json:"host" validate:"required,ip" ` + Port int `json:"port" validate:"required,lt=65536,gte=3306"` ShardID int `json:"shard_id"` } @@ -116,7 +116,7 @@ func (i *InitClusterRoutingComp) InitMySQLServers() (err error) { var execSQLs []string // 先清理mysql.servers 表,保证活动节点执行的幂等性。但这么粗暴地删除会不会有安全隐患? - TruncateSQL := []string{"set tc_admin = 0 ", "truncate table mysql.servers;"} + TruncateSQL := []string{"set tc_admin = 0; ", "truncate table mysql.servers;"} if _, err := i.dbConn.ExecMore(TruncateSQL); err != nil { logger.Error("truncate mysql.servers failed:[%s]", err.Error()) return err diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/spiderctl/table_schema_check.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/spiderctl/table_schema_check.go new file mode 100644 index 0000000000..4fb1e2adcf --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/spiderctl/table_schema_check.go @@ -0,0 +1,140 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package spiderctl + +import ( + "encoding/json" + "fmt" + "time" + + "golang.org/x/exp/slog" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/mysql/db-tools/dbactuator/pkg/components" + "dbm-services/mysql/db-tools/dbactuator/pkg/native" +) + +// TableSchemaCheckComp TODO +type TableSchemaCheckComp struct { + GeneralParam *components.GeneralParam `json:"general"` + Params TableSchemaCheckParam + tableSchemaCheckCtx +} + +// TableSchemaCheckParam TODO +type TableSchemaCheckParam struct { + Host string `json:"host" validate:"required,ip"` + Port int `json:"port" validate:"required,lt=65536,gte=3306"` + CheckObjects []CheckObject `json:"check_objects" validate:"required,dive"` +} + +// CheckObject TODO +type CheckObject struct { + DbName string `json:"dbname" validate:"required"` + Tables []string `json:"tables" validate:"required,dive"` +} +type tableSchemaCheckCtx struct { + tdbCtlConn *native.TdbctlDbWork +} + +// TsccSchemaChecksum TODO +var TsccSchemaChecksum = `CREATE TABLE if not exists infodba_schema.tscc_schema_checksum( + db char(64) NOT NULL, + tbl char(64) NOT NULL, + status char(32) NOT NULL DEFAULT "" COMMENT "检查结果,一致:ok,不一致:inconsistent", + checksum_result json COMMENT "差异表结构信息,tdbctl checksum table 的结果", + update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (db,tbl) +);` + +// Example TODO +func (r *TableSchemaCheckComp) Example() interface{} { + return &TableSchemaCheckComp{ + Params: TableSchemaCheckParam{ + Host: "127.0.0.1", + Port: 26000, + CheckObjects: []CheckObject{ + { + DbName: "test", + Tables: []string{"t1", "t2"}, + }, + }, + }, + } +} + +// Init TODO +func (r *TableSchemaCheckComp) Init() (err error) { + var conn *native.DbWorker + // connection central control + conn, err = native.InsObject{ + Host: r.Params.Host, + Port: r.Params.Port, + User: r.GeneralParam.RuntimeAccountParam.AdminUser, + Pwd: r.GeneralParam.RuntimeAccountParam.AdminPwd, + }.Conn() + if err != nil { + logger.Error("connect tdbctl error: %v", err) + return err + } + r.tdbCtlConn = &native.TdbctlDbWork{DbWorker: *conn} + // init checksum table schema + if _, err = r.tdbCtlConn.ExecMore([]string{"set tc_admin = 0;", "use infodba_schema;", + TsccSchemaChecksum}); err != nil { + logger.Error("init tscc_schema_checksum error: %v", err) + return + } + return err +} + +// Run TODO +func (r *TableSchemaCheckComp) Run() (err error) { + for _, checkObject := range r.Params.CheckObjects { + for _, table := range checkObject.Tables { + // check table schema + var result native.SchemaCheckResults + err = r.tdbCtlConn.Queryx(&result, fmt.Sprintf("set tc_admin=1;tdbctl checksum `%s`.`%s`;", checkObject.DbName, + table)) + if err != nil { + logger.Error("check table schema error: %s", err.Error()) + return err + } + if err = r.atomUpdateCheckResult(checkObject.DbName, table, result.CheckResult()); err == nil { + slog.Info("update checkresult ok") + } + } + } + return nil +} + +func (r *TableSchemaCheckComp) atomUpdateCheckResult(db, tbl string, inconsistentItems []native.SchemaCheckResult) ( + err error) { + r.tdbCtlConn.Exec("set tc_admin=0;") + status := native.SchemaCheckOk + checkResult := []byte("{}") + if len(inconsistentItems) > 0 { + logger.Warn("tabel %s.%s has inconsistent items", db, tbl) + status = "" + checkResult, err = json.Marshal(inconsistentItems) + if err != nil { + logger.Error("json marshal failed %s", err.Error()) + return + } + } + if _, err = r.tdbCtlConn.Exec("replace into infodba_schema.tscc_schema_checksum values(?,?,?,?,?)", db, + tbl, status, + checkResult, + time.Now()); err != nil { + logger.Error("replace checksum record failed", err) + return + } + return +} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/spiderctl/table_schema_repair.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/spiderctl/table_schema_repair.go new file mode 100644 index 0000000000..86564cb68c --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/spiderctl/table_schema_repair.go @@ -0,0 +1,272 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package spiderctl + +import ( + "context" + "encoding/json" + "fmt" + "os" + + "ariga.io/atlas/sql/migrate" + "ariga.io/atlas/sql/mysql" + "ariga.io/atlas/sql/schema" + + "dbm-services/common/go-pubpkg/cmutil" + "dbm-services/common/go-pubpkg/logger" + "dbm-services/mysql/db-tools/dbactuator/pkg/components" + "dbm-services/mysql/db-tools/dbactuator/pkg/native" +) + +// TableSchemaRepairComp TODO +type TableSchemaRepairComp struct { + GeneralParam *components.GeneralParam `json:"general"` + Params TableSchemaRepairParam + tableSchemaRepairCtx +} + +// TableSchemaRepairParam TODO +type TableSchemaRepairParam struct { + Host string `json:"host" validate:"required,ip"` + Port int `json:"port" validate:"required,lt=65536,gte=3306"` + AutoFix bool `json:"auto_fix"` + // 必须指定一个待修复的库 + Db string `json:"db"` + Tables []string `json:"tables"` + DryRun bool `json:"dry_run"` +} +type tableSchemaRepairCtx struct { + tdbCtlConn *native.TdbctlDbWork + taskdir string + svrNameServersMap map[SVRNAME]native.Server + primarySpts []native.Server + spiderSpts []native.Server +} + +// Example TODO +func (r *TableSchemaRepairComp) Example() interface{} { + return &TableSchemaRepairParam{ + Host: "127.0.0.1", + Port: 3306, + Db: "test", + Tables: []string{ + "test", + }, + DryRun: true, + } +} + +// Init TODO +func (r *TableSchemaRepairComp) Init() (err error) { + // connection central control + conn, err := native.InsObject{ + Host: r.Params.Host, + Port: r.Params.Port, + User: r.GeneralParam.RuntimeAccountParam.AdminUser, + Pwd: r.GeneralParam.RuntimeAccountParam.AdminPwd, + }.Conn() + if err != nil { + logger.Error("connect to tdbctl failed, err: %v", err) + return err + } + r.tdbCtlConn = &native.TdbctlDbWork{DbWorker: *conn} + servers, err := r.tdbCtlConn.SelectServers() + if err != nil { + logger.Error("select servers failed, err: %v", err) + return err + } + _, r.svrNameServersMap = transServersToMap(servers) + for _, server := range servers { + if native.SvrNameIsMasterShard(server.ServerName) { + r.primarySpts = append(r.primarySpts, server) + } + if native.SvrNameIsMasterSpiderShard(server.ServerName) { + r.spiderSpts = append(r.spiderSpts, server) + } + } + return nil +} + +type repairTableInfo struct { + native.SchemaCheckResult + referDb string +} + +// RunAutoFix TODO +// 根据校验的结果来自动修复 无需指定修复的库表 +func (r *TableSchemaRepairComp) RunAutoFix() (err error) { + abnormalChecksums, err := r.tdbCtlConn.GetAbnormalSchemaChecksum() + if err != nil { + logger.Error("get abnormal schema checksum failed, err: %v", err) + return err + } + if len(abnormalChecksums) <= 0 { + logger.Info("no abnormal table structure check record was found,bye~") + return nil + } + logger.Info("found %d abnormal table structure check record(s)", len(abnormalChecksums)) + // 根据serverName分组需要修复的表信息 + fixMap := make(map[SVRNAME][]repairTableInfo) + for _, abnormalChecksum := range abnormalChecksums { + var abnormalDetails []native.SchemaCheckResult + if err = json.Unmarshal(abnormalChecksum.ChecksumResult, &abnormalDetails); err != nil { + logger.Error("unmarshal abnormal checksum result failed, err: %v", err) + return err + } + for _, abnormalDetail := range abnormalDetails { + fixMap[abnormalDetail.ServerName] = append(fixMap[abnormalDetail.ServerName], repairTableInfo{ + SchemaCheckResult: abnormalDetail, + referDb: abnormalChecksum.Db, + }) + } + } + var referSchema *schema.Schema + var referConn migrate.Driver + if referConn, err = mysql.Open(r.tdbCtlConn.Db); err != nil { + logger.Error("open refer conn failed, err: %v", err) + return err + } + logger.Info("open refer conn success") + // 根据得到的分组结果来进行修复 + for svrName, abnormalDetails := range fixMap { + // 根据dbName 来分组需要修复的表信息 + fixMapByDb := make(map[string][]string) + referDbMap := make(map[string]string) + for _, repairTableInfo := range abnormalDetails { + fixMapByDb[repairTableInfo.Db] = append(fixMapByDb[repairTableInfo.Db], repairTableInfo.Table) + referDbMap[repairTableInfo.Db] = repairTableInfo.referDb + } + svr, ok := r.svrNameServersMap[svrName] + if !ok { + return fmt.Errorf("server %s not found", svrName) + } + logger.Info("will fix on %s:%s", svrName, svr.GetEndPoint()) + for db, tables := range fixMapByDb { + if referSchema, err = referConn.InspectSchema(context.Background(), referDbMap[db], &schema.InspectOptions{ + Tables: tables, + }); err != nil { + logger.Error("get schema failed, err: %v", err) + return err + } + if err = r.do(svr, db, referConn, referSchema); err != nil { + return err + } + } + } + return nil +} + +// Run TODO +func (r *TableSchemaRepairComp) Run() (err error) { + var referSchema *schema.Schema + var referConn migrate.Driver + if referConn, err = mysql.Open(r.tdbCtlConn.Db); err != nil { + return err + } + if referSchema, err = referConn.InspectSchema(context.Background(), r.Params.Db, &schema.InspectOptions{ + Tables: r.Params.Tables, + }); err != nil { + return err + } + for _, spiderSvr := range r.spiderSpts { + logger.Info("start repair table schema on %s", spiderSvr.ServerName) + err = r.do(spiderSvr, r.Params.Db, referConn, referSchema) + if err != nil { + logger.Error("repair table schema on %s failed, err: %v", spiderSvr.ServerName, err) + return err + } + } + for _, primarySvr := range r.primarySpts { + logger.Info("start repair table schema on %s --%s:%d", primarySvr.ServerName, primarySvr.Host, primarySvr.Port) + shardNum := native.GetShardNumberFromMasterServerName(primarySvr.ServerName) + dbName := fmt.Sprintf("%s_%s", r.Params.Db, shardNum) + if err = r.do(primarySvr, dbName, referConn, referSchema); err != nil { + logger.Error("repair table schema on %s failed, err: %v", primarySvr.ServerName, err) + return err + } + } + return nil +} + +func (r *TableSchemaRepairComp) do(node native.Server, dbName string, referConn migrate.Driver, + referSchema *schema.Schema) (err error) { + logger.Info("start repair table schema on %s -- %s:%d", node.ServerName, node.Host, node.Port) + conn, err := node.Opendb(dbName) + if err != nil { + logger.Error("open db failed, err: %v", err) + return err + } + defer conn.Close() + fromConn, err := mysql.Open(conn) + if err != nil { + logger.Error("open migrate driver failed, err: %v", err) + return err + } + sqlfile := fmt.Sprintf("%s_%s_%d.fix.sql", node.ServerName, node.Host, node.Port) + if cmutil.FileExists(sqlfile) { + if err = os.Remove(sqlfile); err != nil { + logger.Error("remove sql file failed, err: %v", err) + return err + } + } + fd, err := os.Create(sqlfile) + if err != nil { + logger.Error("create sql file failed, err: %v", err) + return err + } + defer fd.Close() + if err = do(dbName, fromConn, referConn, referSchema, r.Params.DryRun, fd); err != nil { + return err + } + return nil +} + +func do(dbName string, fromConn, referConn migrate.Driver, referSchema *schema.Schema, dryrun bool, + fd *os.File) (err error) { + mSchema, err := fromConn.InspectSchema(context.Background(), dbName, &schema.InspectOptions{}) + if err != nil { + logger.Error("inspect schema failed, err: %v", err) + return err + } + mSchema.Name, referSchema.Name = "", "" + changes, err := referConn.SchemaDiff(mSchema, referSchema, schema.DiffSkipChanges(&schema.DropTable{}, + &schema.DropSchema{})) + if err != nil { + logger.Error("schema diff failed, err: %v", err) + return err + } + plan, err := referConn.PlanChanges(context.TODO(), dbName, changes) + if err != nil { + logger.Error("plan changes failed, err: %v", err) + return err + } + files, err := migrate.DefaultFormatter.Format(plan) + if err != nil { + logger.Error("format plan failed, err: %v", err) + return err + } + for _, f := range files { + logger.Info(string(f.Bytes())) + if _, err = fd.Write(f.Bytes()); err != nil { + logger.Warn("write file failed, err: %v", err) + } + } + // if dry run , return + if dryrun { + logger.Info("dry run, no changes will be applied") + return nil + } + if err = fromConn.ApplyChanges(context.TODO(), changes); err != nil { + logger.Error("apply changes failed, err: %v", err) + return err + } + return nil +} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/sysinit/sysinit.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/sysinit/sysinit.go index f175785fb6..5dd2012fea 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/sysinit/sysinit.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/sysinit/sysinit.go @@ -4,6 +4,7 @@ package sysinit import ( "fmt" "io/ioutil" + "os" "dbm-services/common/go-pubpkg/logger" "dbm-services/mysql/db-tools/dbactuator/pkg/core/staticembed" @@ -41,7 +42,7 @@ func ExecSysInitScript() (err error) { return err } tmpScriptName := "/tmp/sysinit.sh" - if err = ioutil.WriteFile(tmpScriptName, data, 07555); err != nil { + if err = os.WriteFile(tmpScriptName, data, 07555); err != nil { logger.Error("write tmp script failed %s", err.Error()) return err } diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/tbinlogdumper/dump_schema.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/tbinlogdumper/dump_schema.go new file mode 100644 index 0000000000..755c3b583a --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/tbinlogdumper/dump_schema.go @@ -0,0 +1,199 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package tbinlogdumper + +import ( + "fmt" + "path" + "regexp" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/mysql/db-tools/dbactuator/internal/subcmd" + "dbm-services/mysql/db-tools/dbactuator/pkg/components" + "dbm-services/mysql/db-tools/dbactuator/pkg/components/computil" + "dbm-services/mysql/db-tools/dbactuator/pkg/core/cst" + "dbm-services/mysql/db-tools/dbactuator/pkg/native" + "dbm-services/mysql/db-tools/dbactuator/pkg/util" + "dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil" + "dbm-services/mysql/db-tools/dbactuator/pkg/util/osutil" +) + +// DumpSchemaComp TODO +type DumpSchemaComp struct { + GeneralParam *components.GeneralParam `json:"general"` + Params DumpSchemaParam `json:"extend"` + DumpSchemaRunTimeCtx `json:"-"` +} + +// DumpSchemaParam TODO +type DumpSchemaParam struct { + Host string `json:"host" validate:"required,ip"` // 当前实例的主机地址 + Port int `json:"port" validate:"required,lt=65536,gte=3306"` // 当前实例的端口 + TBinlogdumperPort int `json:"tbinlogdumper_port" validate:"required,lt=65536,gte=3306"` // 当前TBinlogdumperPort实例的端口 + CharSet string `json:"charset" validate:"required,checkCharset"` // 字符集参数 + +} + +// DumpSchemaRunTimeCtx TODO +type DumpSchemaRunTimeCtx struct { + dbs []string // 需要备份的表结构的数据库名称集合 + charset string // 当前实例的字符集 + dumpCmd string + BackupFileName string // 备份文件 + BackupDir string +} + +// Example godoc +func (c *DumpSchemaComp) Example() interface{} { + comp := DumpSchemaComp{ + Params: DumpSchemaParam{ + Host: "1.1.1.1", + Port: 3306, + TBinlogdumperPort: 27000, + CharSet: "default", + }, + } + return comp +} + +// Init init +// +// @receiver c +// @return err +func (c *DumpSchemaComp) Init() (err error) { + conn, err := native.InsObject{ + Host: c.Params.Host, + Port: c.Params.Port, + User: c.GeneralParam.RuntimeAccountParam.AdminUser, + Pwd: c.GeneralParam.RuntimeAccountParam.AdminPwd, + }.Conn() + if err != nil { + logger.Error("Connect %d failed:%s", c.Params.Port, err.Error()) + return err + } + alldbs, err := conn.ShowDatabases() + if err != nil { + logger.Error("show all databases failed:%s", err.Error()) + return err + } + + version, err := conn.SelectVersion() + if err != nil { + logger.Error("获取version failed %s", err.Error()) + return err + } + + finaldbs := []string{} + reg := regexp.MustCompile(`^bak_cbs`) + for _, db := range util.FilterOutStringSlice(alldbs, computil.GetGcsSystemDatabasesIgnoreTest(version)) { + if reg.MatchString(db) { + continue + } + finaldbs = append(finaldbs, db) + } + if len(finaldbs) == 0 { + return fmt.Errorf("变更实例排除系统库后,再也没有可以变更的库") + } + c.dbs = finaldbs + c.charset = c.Params.CharSet + if c.Params.CharSet == "default" { + if c.charset, err = conn.ShowServerCharset(); err != nil { + logger.Error("获取实例的字符集失败:%s", err.Error()) + return err + } + } + c.BackupDir = cst.DumperDefaultBakDir + c.BackupFileName = fmt.Sprintf("tbinlogdump_%d_schema_%s.sql", c.Params.TBinlogdumperPort, subcmd.GBaseOptions.NodeId) + return err +} + +// Precheck 预检查 +// +// @receiver c +// @return err +func (c *DumpSchemaComp) Precheck() (err error) { + c.dumpCmd = path.Join(cst.MysqldInstallPath, "bin", "mysqldump") + // to export the table structure from the central control + // you need to use the mysqldump that comes with the central control + + if !osutil.FileExist(c.dumpCmd) { + return fmt.Errorf("dumpCmd: %s文件不存在", c.dumpCmd) + } + if !osutil.FileExist(c.BackupDir) { + return fmt.Errorf("backupdir: %s不存在", c.BackupDir) + } + return +} + +// DumpSchema 运行备份表结构 +// +// @receiver c +// @return err +func (c *DumpSchemaComp) DumpSchema() (err error) { + dumper := &mysqlutil.MySQLDumperTogether{ + MySQLDumper: mysqlutil.MySQLDumper{ + DumpDir: c.BackupDir, + Ip: c.Params.Host, + Port: c.Params.Port, + DbBackupUser: c.GeneralParam.RuntimeAccountParam.AdminUser, + DbBackupPwd: c.GeneralParam.RuntimeAccountParam.AdminPwd, + DbNames: c.dbs, + DumpCmdFile: c.dumpCmd, + Charset: c.charset, + MySQLDumpOption: mysqlutil.MySQLDumpOption{ + NoData: true, + AddDropTable: true, + NeedUseDb: true, + DumpRoutine: true, + DumpTrigger: false, + }, + }, + OutputfileName: c.BackupFileName, + } + if err := dumper.Dump(); err != nil { + logger.Error("dump failed: ", err.Error()) + return err + } + return nil +} + +// ModifyEngine 修改备份文件中引擎 +func (c *DumpSchemaComp) ModifyEngine() (err error) { + EngineName := "REDIS" + ModifyCmd := fmt.Sprintf( + "sed -i 's/ENGINE=[^ ]*/ENGINE=%s/g' %s", EngineName, path.Join(c.BackupDir, c.BackupFileName), + ) + logger.Info("ModifyCmd cmd:%s", ModifyCmd) + output, err := osutil.ExecShellCommand(false, ModifyCmd) + if err != nil { + return fmt.Errorf("execte get an error:%s,%w", output, err) + } + return nil +} + +// LoadSchema 导入表结构 +func (c *DumpSchemaComp) LoadSchema() (err error) { + err = mysqlutil.ExecuteSqlAtLocal{ + IsForce: false, + Charset: c.charset, + NeedShowWarnings: false, + Host: c.Params.Host, + Port: c.Params.TBinlogdumperPort, + WorkDir: c.BackupDir, + User: c.GeneralParam.RuntimeAccountParam.AdminUser, + Password: c.GeneralParam.RuntimeAccountParam.AdminPwd, + }.ExcuteSqlByMySQLClientOne(c.BackupFileName, "test") + if err != nil { + logger.Error("执行%s文件失败", path.Join(c.BackupDir, c.BackupFileName)) + return err + } + return nil +} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/tbinlogdumper/install_tbinlogdumper.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/tbinlogdumper/install_tbinlogdumper.go new file mode 100644 index 0000000000..789a533ecc --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/tbinlogdumper/install_tbinlogdumper.go @@ -0,0 +1,383 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package tbinlogdumper + +import ( + "encoding/json" + "fmt" + "html/template" + "os" + "path" + "regexp" + "strconv" + "time" + + "dbm-services/common/go-pubpkg/cmutil" + "dbm-services/common/go-pubpkg/logger" + "dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql" + "dbm-services/mysql/db-tools/dbactuator/pkg/core/cst" + "dbm-services/mysql/db-tools/dbactuator/pkg/util" + "dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil" + "dbm-services/mysql/db-tools/dbactuator/pkg/util/osutil" + + "github.com/pkg/errors" +) + +// InstallTbinlogDumperComp TODO +// tbinlogdumper本质上是mysqld进程,可以继承一些方法 +type InstallTbinlogDumperComp struct { + RenderConfigs map[mysql.Port]renderDumperConfigs + DumperConfigs map[mysql.Port]DumperParams + mysql.InstallMySQLComp + Configs `json:"extend"` +} + +// Configs TODO +type Configs struct { + DumperConfigs json.RawMessage `json:"dumper_configs" validate:"required" ` +} + +// DumperParams TODO +type DumperParams struct { + DumperId string `json:"dumper_id" ` + AreaName string `json:"area_name" ` +} + +// RenderDumperConfigs TODO +type renderDumperConfigs struct { + Mysqld Mysqld +} + +// Mysqld TODO +type Mysqld struct { + Port string `json:"port"` + Basedir string `json:"basedir"` + Datadir string `json:"datadir"` + Logdir string `json:"logdir"` + CharacterSetServer string `json:"character_set_server"` + BindAddress string `json:"bind-address"` + ServerId uint64 `json:"server_id"` + AreaName string `json:"area_name"` + Dumperid string `json:"dumper_id"` +} + +// GetDumperDirName TODO +// input "mysql-5.6.24-linux-x86_64-tbinlogdumper-2.14-gcs" +// output tbinlogdumper2.14 +func GetDumperDirName(dumperVersion string) string { + re := regexp.MustCompile(`(tbinlogdumper)-([\d]+)?.?([\d]+)?`) + result := re.FindStringSubmatch(dumperVersion) + if len(result) != 4 { + logger.Error("parse dumper version failed:%s, %v", dumperVersion, result) + return "" + } + return fmt.Sprintf("%s%s.%s", result[1], result[2], result[3]) +} + +// InitDumperDefaultParam TODO +func (i *InstallTbinlogDumperComp) InitDumperDefaultParam() error { + dumperDirName := GetDumperDirName(i.Params.Medium.Pkg) + i.InstallDir = cst.UsrLocal + i.MysqlInstallDir = path.Join(cst.UsrLocal, dumperDirName) + i.DataRootPath = cst.DumperDefaultDir + i.LogRootPath = cst.DumperDefaultDir + i.DefaultMysqlDataDirName = cst.DefaultMysqlDataBasePath + i.DefaultMysqlLogDirName = cst.DefaultMysqlLogBasePath + i.DataBaseDir = path.Join(cst.DumperDefaultDir, cst.DefaultMysqlDataBasePath) + i.LogBaseDir = path.Join(cst.DumperDefaultDir, cst.DefaultMysqlLogBasePath) + + // 计算获取需要安装的ports + i.InsPorts = i.Params.Ports + i.MyCnfTpls = make(map[int]*util.CnfFile) + i.DumperConfigs = make(map[mysql.Port]DumperParams) + + // 反序列化mycnf 配置 + var mycnfs map[mysql.Port]json.RawMessage + if err := json.Unmarshal([]byte(i.Params.MyCnfConfigs), &mycnfs); err != nil { + logger.Error("反序列化配置失败:%s", err.Error()) + return err + } + + var dumpercnfs map[mysql.Port]json.RawMessage + if err := json.Unmarshal([]byte(i.Configs.DumperConfigs), &dumpercnfs); err != nil { + logger.Error("反序列化配置失败:%s", err.Error()) + return err + } + + for _, port := range i.InsPorts { + var cnfraw json.RawMessage + var dumperCnfRaw json.RawMessage + var ok bool + if cnfraw, ok = mycnfs[port]; !ok { + return fmt.Errorf("cnf参数中没有%d的配置", port) + } + + if dumperCnfRaw, ok = dumpercnfs[port]; !ok { + return fmt.Errorf("dumper参数中没有%d的配置", port) + } + + var mycnf mysqlutil.MycnfObject + var dumpercnf DumperParams + if err := json.Unmarshal(cnfraw, &mycnf); err != nil { + logger.Error("反序列%d 化配置失败:%s", port, err.Error()) + return err + } + if err := json.Unmarshal(dumperCnfRaw, &dumpercnf); err != nil { + logger.Error("dumpercnf反序列%d 化配置失败:%s", port, err.Error()) + return err + } + + cnftpl, err := util.NewMyCnfObject(mycnf, "tpl") + if err != nil { + logger.Error("初始化mycnf ini 模版:%s", err.Error()) + return err + } + i.MyCnfTpls[port] = cnftpl + i.DumperConfigs[port] = dumpercnf + } + // 计算需要替换的参数配置 + if err := i.initInsReplaceConfigs(); err != nil { + return err + } + + i.Checkfunc = append(i.Checkfunc, i.Params.Medium.Check) + i.Checkfunc = append(i.Checkfunc, i.precheckDir) + i.Checkfunc = append(i.Checkfunc, i.precheckProcess) + return nil +} + +// precheckDir TODO +/* + 检查根路径下是已经存在tbinlogdumper相关的数据和日志目录 + eg: + /data/idip_cache/mysqldata/{port} + /data/idip_cache/mysqllog/{port} +*/ +func (i *InstallTbinlogDumperComp) precheckDir() error { + for _, port := range i.InsPorts { + d := path.Join(i.DataBaseDir, strconv.Itoa(port)) + if osutil.FileExist(d) { + return fmt.Errorf("%s 已经存在了", d) + } + l := path.Join(i.LogBaseDir, strconv.Itoa(port)) + if osutil.FileExist(l) { + return fmt.Errorf("%s 已经存在了", l) + } + } + return nil +} + +// precheckProcess 判断机器是否已部署对应的进程 +func (i *InstallTbinlogDumperComp) precheckProcess() (err error) { + + for _, port := range i.InsPorts { + var output string + var tbinlogDumperNum int + + checkCmd := fmt.Sprintf( + "netstat -tnlp |grep -v grep|grep %d|wc -l", + port, + ) + if output, err = osutil.ExecShellCommand(false, checkCmd); err != nil { + return errors.Wrap(err, "执行失败") + } + if tbinlogDumperNum, err = strconv.Atoi(osutil.CleanExecShellOutput(output)); err != nil { + logger.Error("strconv.Atoi %s failed:%s", output, err.Error()) + return err + } + if tbinlogDumperNum > 0 { + return errors.New(fmt.Sprintf(" listen [%d] have %d process running", port, tbinlogDumperNum)) + } + } + return nil + +} + +// DumperInstall TODO +func (i *InstallTbinlogDumperComp) DumperInstall() (err error) { + logger.Info("开始安装tbinlogdumper实例 ~ %v", i.InsPorts) + var isSudo = mysqlutil.IsSudo() + for _, port := range i.InsPorts { + var initialMysql string + var output string + myCnf := util.GetMyCnfFileName(port) + initialLogFile := fmt.Sprintf("/tmp/install_tbinlogdumper_%d.log", port) + + // mysql5.7.18以下版本初始化命令 + initialMysql = fmt.Sprintf( + "su - mysql -c \"cd %s && ./scripts/mysql_install_db --defaults-file=%s --user=mysql --force &>%s\"", + i.MysqlInstallDir, myCnf, initialLogFile) + + // mysql5.7.18以上的版本 + if mysqlutil.MySQLVersionParse(i.Params.MysqlVersion) >= mysqlutil.MySQLVersionParse("5.7.18") { + initialMysql = fmt.Sprintf( + "su - mysql -c \"cd %s && ./bin/mysqld --defaults-file=%s --initialize-insecure --user=mysql &>%s\"", + i.MysqlInstallDir, myCnf, initialLogFile) + } + if output, err = osutil.ExecShellCommand(isSudo, initialMysql); err != nil { + logger.Error("%s execute failed, %s", initialMysql, output) + // 如果存在初始化的日志文件,才初始化错误的时间,将日志cat出来 + if osutil.FileExist(initialLogFile) { + ldat, e := os.ReadFile(initialLogFile) + if e != nil { + logger.Warn("读取初始化tbinlogdumper日志失败%s", e.Error()) + } else { + logger.Error("初始化tbinlogdumper失败日志: %s", string(ldat)) + } + } + return err + } + + time.Sleep(5 * time.Second) + } + logger.Info("Init all mysqld successfully") + return nil +} + +func (i *InstallTbinlogDumperComp) initInsReplaceConfigs() error { + i.RenderConfigs = make(map[int]renderDumperConfigs) + i.InsInitDirs = make(map[int]mysql.InitDirs) + i.InsSockets = make(map[int]string) + for _, port := range i.InsPorts { + insBaseDataDir := path.Join(i.DataBaseDir, strconv.Itoa(port)) + insBaseLogDir := path.Join(i.LogBaseDir, strconv.Itoa(port)) + serverId, err := mysqlutil.GenMysqlServerId(i.Params.Host, port) + if err != nil { + logger.Error("%s:%d generation serverId Failed %s", i.Params.Host, port, err.Error()) + return err + } + DumperServerId, _ := strconv.ParseUint(fmt.Sprintf("%d%d", serverId, port), 10, 64) + i.RenderConfigs[port] = renderDumperConfigs{Mysqld{ + Basedir: i.MysqlInstallDir, + Datadir: insBaseDataDir, + Logdir: insBaseLogDir, + ServerId: DumperServerId, + Port: strconv.Itoa(port), + CharacterSetServer: i.Params.CharSet, + BindAddress: i.Params.Host, + AreaName: i.DumperConfigs[port].AreaName, + Dumperid: i.DumperConfigs[port].DumperId, + }} + + i.InsInitDirs[port] = append(i.InsInitDirs[port], []string{insBaseDataDir, insBaseLogDir}...) + } + return nil + // return i.calInsInitDirs() +} + +// GenerateDumperMycnf TODO +func (i *InstallTbinlogDumperComp) GenerateDumperMycnf() (err error) { + // 1. 根据参数反序列化配置 + var tmplFileName = "/tmp/my.cnf.tpl" + + // 2. 替换数据目录、日志目录生产实际配置文件 + for _, port := range i.InsPorts { + i.MyCnfTpls[port].FileName = tmplFileName + if err = i.MyCnfTpls[port].SafeSaveFile(false); err != nil { + logger.Error("保存模版文件失败:%s", err.Error()) + return err + } + // 防止过快读取到的是空文件 + if err = util.Retry(util.RetryConfig{Times: 3, DelayTime: 100 * time.Millisecond}, func() error { + return util.FileIsEmpty(tmplFileName) + }); err != nil { + return err + } + tmpl, err := template.ParseFiles(tmplFileName) + if err != nil { + return errors.WithMessage(err, "template ParseFiles failed") + } + cnf := util.GetMyCnfFileName(port) + f, err := os.Create(cnf) + if err != nil { + return err + } + defer f.Close() + if err := tmpl.Execute(f, i.RenderConfigs[port]); err != nil { + return err + } + if _, err = osutil.ExecShellCommand(false, fmt.Sprintf("chown -R mysql %s", cnf)); err != nil { + logger.Error("chown -R mysql %s %s", cnf, err.Error()) + return err + } + } + return nil +} + +// DecompressDumperPkg TODO +/** + * @description: 校验、解压tbinlogdumper安装包,考虑需要支持追加部署的方式,怎解压需要判断机器是否对应的版本包 + * @return {*} + */ +func (i *InstallTbinlogDumperComp) DecompressDumperPkg() (err error) { + if err = os.Chdir(i.InstallDir); err != nil { + return fmt.Errorf("cd to dir %s failed, err:%w", i.InstallDir, err) + } + // 判断 对应版本的安装目录是否已经存在,如果存在,则判断是否有对应版本的进程部署,如果没有则重建,有则沿用 + if cmutil.FileExists(i.MysqlInstallDir) { + // 检查是否存在这个版本部署dumper进程 + err, result := i.isInstallDumperWithInstallDir() + if err != nil { + return err + } + if result { + // 表示有对应版本的进程部署,如果无需执行下面逻辑,沿用这套二进制 + logger.Warn( + "This version [%s] already has a corresponding deployment process on the machine", + i.MysqlInstallDir, + ) + return nil + } + + // 如果没有对应进程部署,则重建使用传下来的版本 + if _, err = osutil.ExecShellCommand(false, "rm -r "+i.MysqlInstallDir); err != nil { + logger.Error("rm -r %s error: %w", i.MysqlInstallDir, err) + return err + } + } + pkgAbPath := i.Params.Medium.GetAbsolutePath() + if output, err := osutil.ExecShellCommand(false, fmt.Sprintf("tar -xf %s", pkgAbPath)); err != nil { + logger.Error("tar -xf %s error:%s,%s", pkgAbPath, output, err.Error()) + return err + } + mysqlBinaryFile := i.Params.Medium.GePkgBaseName() + extraCmd := fmt.Sprintf("ln -sf %s %s && chown -R mysql mysql*", mysqlBinaryFile, i.MysqlInstallDir) + if _, err = osutil.ExecShellCommand(false, extraCmd); err != nil { + logger.Error("%s execute failed, %v", extraCmd, err) + return err + } + logger.Info("mysql binary directory: %s", mysqlBinaryFile) + if _, err := os.Stat(i.MysqlInstallDir); err != nil { + logger.Error("%s check failed, %v", i.MysqlInstallDir, err) + return err + } + logger.Info("decompress mysql pkg successfully") + return nil +} + +// isInstallDumperWithInstallDir 根据 +func (i *InstallTbinlogDumperComp) isInstallDumperWithInstallDir() (err error, result bool) { + var output string + var dumperNum int + checkCMD := fmt.Sprintf("ps -efwww|grep -w %s |grep -v grep | wc -l", i.MysqlInstallDir) + + if output, err = osutil.ExecShellCommand(false, checkCMD); err != nil { + return errors.Wrap(err, fmt.Sprintf("执行失败[%s]", checkCMD)), false + } + if dumperNum, err = strconv.Atoi(osutil.CleanExecShellOutput(output)); err != nil { + logger.Error("strconv.Atoi %s failed:%s", output, err.Error()) + return err, false + } + if dumperNum > 0 { + return nil, true + } + return nil, false +} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/tbinlogdumper/unintall_tbinlogdumper.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/tbinlogdumper/unintall_tbinlogdumper.go new file mode 100644 index 0000000000..700672624c --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/tbinlogdumper/unintall_tbinlogdumper.go @@ -0,0 +1,90 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package tbinlogdumper + +import ( + "fmt" + "path" + "strconv" + "time" + + "dbm-services/common/go-pubpkg/cmutil" + "dbm-services/common/go-pubpkg/logger" + "dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql" + "dbm-services/mysql/db-tools/dbactuator/pkg/core/cst" + "dbm-services/mysql/db-tools/dbactuator/pkg/util/osutil" +) + +// UnInstallTbinlogDumperComp TODO +// tbinlogdumper本质上是mysqld进程,可以继承一些方法 +type UnInstallTbinlogDumperComp struct { + mysql.UnInstallMySQLComp +} + +// TbinlogDumperClearDir 删除特定的实例目录 +func (u *UnInstallTbinlogDumperComp) TbinlogDumperClearDir() error { + for _, port := range u.Params.Ports { + var ( + dataBak = cst.DumperDefaultBakDir + dataPath = path.Join( + cst.DumperDefaultDir, + cst.DefaultMysqlDataBasePath, + strconv.Itoa(port), + ) // "/data/idip_cache/mysqldata/{port}" + dataLog = path.Join( + cst.DumperDefaultDir, + cst.DefaultMysqlLogBasePath, + strconv.Itoa(port), + ) // "/data/idip_cache/mysqllog/{port}" + // "/data/idip_cache/dbbak/" + suffix = fmt.Sprintf("_bak_%s", time.Now().Format(cst.TIMELAYOUTSEQ)) + dataLogBak = path.Join( + dataBak, + fmt.Sprintf("%s_%d%s", cst.DefaultMysqlLogBasePath, port, suffix), + ) + ) + if !cmutil.FileExists(dataBak) { + cmd := fmt.Sprintf("mkdir %s && chown -R mysql:mysql %s ;", dataBak, dataBak) + output, err := osutil.ExecShellCommand(false, cmd) + if err != nil { + err = fmt.Errorf("execute [%s] get an error:%w,output:%s", cmd, err, output) + return err + } + } + + if cmutil.FileExists(dataLog) { + cmd := fmt.Sprintf("mv %s %s;", dataLog, dataLogBak) + logger.Info("backup command [%s]", cmd) + output, err := osutil.ExecShellCommand(false, cmd) + if err != nil { + err = fmt.Errorf("execute [%s] get an error:%w,output:%s", cmd, err, output) + return err + } + } + + if cmutil.FileExists(dataPath) { + cmd := fmt.Sprintf( + "mv %s %s_%d%s;", + dataPath, + path.Join(dataBak, cst.DefaultMysqlDataBasePath), + port, + suffix, + ) + logger.Info("backup command [%s]", cmd) + output, err := osutil.ExecShellCommand(false, cmd) + if err != nil { + err = fmt.Errorf("execute [%s] get an error:%w,output:%s", cmd, err, output) + return err + } + } + } + return nil +} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/core/codes/codes.go b/dbm-services/mysql/db-tools/dbactuator/pkg/core/codes/codes.go deleted file mode 100644 index 478a7aee7e..0000000000 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/core/codes/codes.go +++ /dev/null @@ -1,62 +0,0 @@ -// Package codes TODO -package codes - -/* -@description: 相关错误码及对应错误类型 -@rules: -1. 初始化类的错误码使用 30000-39999 -2. 操作系统的错误码使用 40000-49999 -3. MySQL、Redis、Mongo实例操作的错误码 50000-59999 -*/ - -const ( - // Unauthorized TODO - Unauthorized = 10001 - // UnmarshalFailed TODO - UnmarshalFailed = 10002 - // NotExistMountPoint TODO - NotExistMountPoint = 20001 - // NotExistUser TODO - NotExistUser = 20002 - // PermissionDeny TODO - PermissionDeny = 20003 - - // RenderConfigFailed TODO - RenderConfigFailed = 30001 - // InitParamFailed TODO - InitParamFailed = 30002 - // InitMySQLDirFailed TODO - InitMySQLDirFailed = 30003 - - // InstallMySQLFailed TODO - InstallMySQLFailed = 40001 - // ExecuteShellFailed TODO - ExecuteShellFailed = 40002 - // DecompressPkgFailed TODO - DecompressPkgFailed = 40003 - // StartMySQLFailed TODO - StartMySQLFailed = 40004 - // NotAvailableMem TODO - NotAvailableMem = 40005 - - // ImportPrivAndSchemaFailed TODO - ImportPrivAndSchemaFailed = 50001 -) - -// ErrorCodes TODO -var ErrorCodes = map[int]string{ - Unauthorized: "没有进行用户认证", - UnmarshalFailed: "反序列化失败", - NotExistMountPoint: "没有可用的挂载点", - NotExistUser: "用户不存在", - PermissionDeny: "权限不足", - RenderConfigFailed: "初始化配置失败", - InitParamFailed: "初始化参数失败", - InitMySQLDirFailed: "初始化MySQL目录失败", - InstallMySQLFailed: "安装实例失败", - ExecuteShellFailed: "执行Shell脚本失败", - DecompressPkgFailed: "解压文件失败", - StartMySQLFailed: "启动MySQL失败", - NotAvailableMem: "内存不可用", - ImportPrivAndSchemaFailed: "导入权限和库失败", -} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/core/cst/const.go b/dbm-services/mysql/db-tools/dbactuator/pkg/core/cst/const.go deleted file mode 100644 index c6e7d32def..0000000000 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/core/cst/const.go +++ /dev/null @@ -1,31 +0,0 @@ -package cst - -import "time" - -const ( - // Environment TODO - Environment = "enviroment" - // Test TODO - Test = "test" -) - -const ( - // TIMELAYOUT TODO - TIMELAYOUT = "2006-01-02 15:04:05" - // TIMELAYOUTSEQ TODO - TIMELAYOUTSEQ = "2006-01-02_15:04:05" - // TimeLayoutDir TODO - TimeLayoutDir = "20060102150405" -) - -const ( - // BK_PKG_INSTALL_PATH 默认文件下发路径 - BK_PKG_INSTALL_PATH = "/data/install" - // MYSQL_TOOL_INSTALL_PATH 默认工具安装路径 - MYSQL_TOOL_INSTALL_PATH = "/home/mysql" -) - -// GetNowTimeLayoutStr 20060102150405 -func GetNowTimeLayoutStr() string { - return time.Now().Format(TimeLayoutDir) -} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/core/cst/cst.go b/dbm-services/mysql/db-tools/dbactuator/pkg/core/cst/cst.go index 1dab3e254a..b5c7682370 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/core/cst/cst.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/core/cst/cst.go @@ -1,2 +1,42 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + // Package cst 常量 package cst + +import "time" + +const ( + // Environment TODO + Environment = "enviroment" + // Test TODO + Test = "test" +) + +const ( + // TIMELAYOUT TODO + TIMELAYOUT = "2006-01-02 15:04:05" + // TIMELAYOUTSEQ TODO + TIMELAYOUTSEQ = "2006-01-02_15:04:05" + // TimeLayoutDir TODO + TimeLayoutDir = "20060102150405" +) + +const ( + // BK_PKG_INSTALL_PATH 默认文件下发路径 + BK_PKG_INSTALL_PATH = "/data/install" + // MYSQL_TOOL_INSTALL_PATH 默认工具安装路径 + MYSQL_TOOL_INSTALL_PATH = "/home/mysql" +) + +// GetNowTimeLayoutStr 20060102150405 +func GetNowTimeLayoutStr() string { + return time.Now().Format(TimeLayoutDir) +} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/core/cst/mysql.go b/dbm-services/mysql/db-tools/dbactuator/pkg/core/cst/mysql.go index 2f1997a1e9..bee4ebed38 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/core/cst/mysql.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/core/cst/mysql.go @@ -37,8 +37,6 @@ const ( BinLogFileMatch = `(.*)/binlog\d*.bin` // ReBinlogFilename binlog 文件名 ReBinlogFilename = `binlog\d*\.\d+$` - // DatadirMatch 实例数据目录模式 - DatadirMatch = `(.*)/mysqldata/\d+$` // MysqlOsUserName 系统帐号 MysqlOsUserName = "mysql" // MysqlOsUserGroup 系统组 @@ -58,7 +56,11 @@ const ( // MysqlRotateBinlogInstallPath rotate binlog MysqlRotateBinlogInstallPath = "/home/mysql/mysql-rotatebinlog" // DBAReportBase 上报根目录 - DBAReportBase = "/home/mysql/dbareport" + DBAReportBase = "/home/mysql/dbareport" + BackupClientInstallPath = "/usr/local/backup_client" + // tbinlogdumper 相关目录 + DumperDefaultDir = "/data/idip_cache" + DumperDefaultBakDir = "/data/idip_cache/dbbak" ) const ( @@ -103,7 +105,10 @@ const ( BackupRoleSlave = "SLAVE" BackupRoleRepeater = "REPEATER" // BackupRoleOrphan 单节点备份行为 - BackupRoleOrphan = "ORPHAN" + BackupRoleOrphan = "ORPHAN" + BackupRoleSpiderMaster = "SPIDER_MASTER" + BackupRoleSpiderSlave = "SPIDER_SLAVE" + BackupRoleSpiderMnt = "SPIDER_MNT" ) // 规范的 备份类型名 diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/core/cst/os.go b/dbm-services/mysql/db-tools/dbactuator/pkg/core/cst/os.go index 9164ef73b8..079d08d70a 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/core/cst/os.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/core/cst/os.go @@ -1,8 +1,17 @@ package cst +import "strconv" + // bits const ( - Bit64 = "64" - Bit32 = "32" - OSBits = 32 << uintptr(^uintptr(0)>>63) + // X64 x86_64 + X64 = "x86_64" + // X32 x86_32 + X32 = "i686" + // Arm64 aarch64 + Arm64 = "aarch64" + Bit64 = 64 + Bit32 = 32 + //OSBits = 32 << uintptr(^uintptr(0)>>63) + OSBits = strconv.IntSize ) diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/core/staticembed/default_sys_schema.go b/dbm-services/mysql/db-tools/dbactuator/pkg/core/staticembed/default_sys_schema.go index 22c7b3e586..7c466c3749 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/core/staticembed/default_sys_schema.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/core/staticembed/default_sys_schema.go @@ -9,3 +9,13 @@ const DefaultSysSchemaSQLFileName = "default_sys_schema.sql" // //go:embed default_sys_schema.sql var DefaultSysSchemaSQL embed.FS + +// SpiderInitSQL TODO +const SpiderInitSQL = `CREATE TABLE if not exists infodba_schema.tscc_schema_checksum( + db char(64) NOT NULL, + tbl char(64) NOT NULL, + status char(32) NOT NULL DEFAULT "" COMMENT "检查结果,一致:ok,不一致:inconsistent", + checksum_result json COMMENT "差异表结构信息,tdbctl checksum table 的结果", + update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (db,tbl) +);` diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/core/staticembed/default_sys_schema.sql b/dbm-services/mysql/db-tools/dbactuator/pkg/core/staticembed/default_sys_schema.sql index 6c672c1bb9..cce6ec8c6d 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/core/staticembed/default_sys_schema.sql +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/core/staticembed/default_sys_schema.sql @@ -35,19 +35,11 @@ CREATE TABLE if not exists infodba_schema.spes_status( report_day int default 0, PRIMARY KEY ip_id_day (ip, spes_id, report_day) ) engine = InnoDB; -CREATE TABLE IF NOT EXISTS infodba_schema.master_slave_check ( - check_item VARCHAR(64) NOT NULL PRIMARY KEY comment 'check_item to check', - master VARCHAR(64) comment 'the check_item status on master', - slave VARCHAR(64) comment 'the check_item status on slave', - check_result VARCHAR(64) comment 'the different value of master and slave' -) ENGINE = InnoDB; -INSERT INTO infodba_schema.master_slave_check -values('slave_delay_sec', now(), now(), 0); CREATE TABLE IF NOT EXISTS infodba_schema.check_heartbeat ( uid INT NOT NULL PRIMARY KEY, ck_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP on UPDATE CURRENT_TIMESTAMP ) ENGINE = InnoDB; -REPLACE INTO infodba_schema.check_heartbeat(uid) value(1); +REPLACE INTO infodba_schema.check_heartbeat(uid) value(@@server_id); CREATE TABLE IF NOT EXISTS infodba_schema.query_response_time( time_min INT(11) NOT NULL DEFAULT '0', time VARCHAR(14) NOT NULL DEFAULT '', diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/native/db.go b/dbm-services/mysql/db-tools/dbactuator/pkg/native/db.go index e8d9d26d16..0b929b9a59 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/native/db.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/native/db.go @@ -1,15 +1,16 @@ package native import ( + "database/sql" "fmt" "time" + _ "github.com/go-sql-driver/mysql" // mysql TODO + "dbm-services/common/go-pubpkg/cmutil" "dbm-services/common/go-pubpkg/logger" "dbm-services/mysql/db-tools/dbactuator/pkg/core/cst" - "dbm-services/mysql/db-tools/dbactuator/pkg/util/osutil" - - _ "github.com/go-sql-driver/mysql" // mysql TODO + "dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil" ) // Instance TODO @@ -54,6 +55,11 @@ func GetProxyAdminPort(port int) int { return port + cst.ProxyAdminPortInc } +// Opendb TODO +func Opendb(host, user, pwd, dbName string) (conn *sql.DB, err error) { + return sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s)/%s", user, pwd, host, dbName)) +} + // Conn Connect Tcp/Ip func (o InsObject) Conn() (*DbWorker, error) { if o.Socket != "" { @@ -100,7 +106,7 @@ func (o InsObject) MySQLClientExec(mysqlClient, sqlStr string) (string, error) { o.mysqlCli = o.MySQLClientCmd(mysqlClient) } cmd := fmt.Sprintf(`%s -A -Nse "%s"`, o.mysqlCli, sqlStr) - return osutil.ExecShellCommand(false, cmd) + return mysqlutil.ExecCommandMySQLShell(cmd) } // CheckInstanceConnIdle TODO diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/native/dbworker.go b/dbm-services/mysql/db-tools/dbactuator/pkg/native/dbworker.go index 4d78efbefb..7127eac776 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/native/dbworker.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/native/dbworker.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package native import ( @@ -8,6 +18,7 @@ import ( "strings" "time" + "dbm-services/common/go-pubpkg/cmutil" "dbm-services/common/go-pubpkg/logger" "dbm-services/mysql/db-tools/dbactuator/pkg/util" "dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil" @@ -46,7 +57,7 @@ func NewDbWorker(dsn string) (*DbWorker, error) { } // NewDbWorkerNoPing mysql-proxy supports very few queries, which do not include PINGs -// In this case, we do not ping after a connection is built, +// In this case, we do not ping after a connection is build, // and let the function caller to decide if the connection is healthy func NewDbWorkerNoPing(host, user, password string) (*DbWorker, error) { var err error @@ -92,15 +103,25 @@ func (h *DbWorker) ExecWithTimeout(dura time.Duration, query string, args ...int } // ExecMore 执行一堆sql +// 会在同一个连接里执行 +// 空元素会跳过 func (h *DbWorker) ExecMore(sqls []string) (rowsAffectedCount int64, err error) { var c int64 - for _, args := range sqls { - ret, err := h.Db.Exec(args) + db, err := h.Db.Conn(context.Background()) + if err != nil { + return 0, err + } + defer db.Close() + for _, sqlStr := range sqls { + if strings.TrimSpace(sqlStr) == "" { + continue + } + ret, err := db.ExecContext(context.Background(), sqlStr) if err != nil { - return rowsAffectedCount, fmt.Errorf("exec %s failed,err:%w", args, err) + return rowsAffectedCount, fmt.Errorf("exec %s failed,err:%w", sqlStr, err) } if c, err = ret.RowsAffected(); err != nil { - return rowsAffectedCount, fmt.Errorf("exec %s failed,err:%w", args, err) + return rowsAffectedCount, fmt.Errorf("exec %s failed,err:%w", sqlStr, err) } rowsAffectedCount += c } @@ -255,7 +276,7 @@ func (h *DbWorker) ShowSlaveStatus() (resp ShowSlaveStatusResp, err error) { return } -// TotalDelayBinlogSize 获取Slave 延迟的总binlog size +// TotalDelayBinlogSize 获取Slave 延迟的总binlog size,total 单位byte func (h *DbWorker) TotalDelayBinlogSize() (total int, err error) { maxbinlogsize_str, err := h.GetSingleGlobalVar("max_binlog_size") if err != nil { @@ -278,7 +299,7 @@ func (h *DbWorker) TotalDelayBinlogSize() (total int, err error) { if err != nil { return -1, err } - return (masterBinIdx-relayBinIdx)*maxbinlogsize + (ss.ExecMasterLogPos - ss.ReadMasterLogPos), nil + return (masterBinIdx-relayBinIdx)*maxbinlogsize - ss.ExecMasterLogPos, nil } // getIndexFromBinlogFile TODO @@ -320,9 +341,16 @@ func (h *DbWorker) SelectVersion() (version string, err error) { return } +// HasTokudb TODO +func (h *DbWorker) HasTokudb() (has bool, err error) { + var engine string + err = h.Queryxs(&engine, "select engine from information_schema.engines where engine='tokudb';") + return cmutil.IsEmpty(engine), err +} + // SelectNow 获取实例的当前时间。不是获取机器的,因为可能存在时区不一样 func (h *DbWorker) SelectNow() (nowTime string, err error) { - err = h.Queryxs(&nowTime, "select now() as not_time;") + err = h.Queryxs(&nowTime, "select now() as now_time;") return } @@ -661,11 +689,11 @@ func (h *DbWorker) ShowPrivForUser(created bool, userhost string) (grants []stri // CheckSlaveReplStatus TODO // 检查从库的同步状态是否Ok -func (h *DbWorker) CheckSlaveReplStatus() (err error) { +func (h *DbWorker) CheckSlaveReplStatus(fn func() (resp ShowSlaveStatusResp, err error)) (err error) { return util.Retry( util.RetryConfig{Times: 10, DelayTime: 1}, func() error { - ss, err := h.ShowSlaveStatus() + ss, err := fn() if err != nil { return err } @@ -695,6 +723,10 @@ func (h *DbWorker) MySQLVarsCompare(referInsConn *DbWorker, checkVars []string) if err != nil { return err } + return compareDbVariables(referVars, compareVars, checkVars) +} + +func compareDbVariables(referVars, compareVars map[string]string, checkVars []string) (err error) { var errMsg []string for _, varName := range checkVars { referV, r_ok := referVars[varName] @@ -710,7 +742,7 @@ func (h *DbWorker) MySQLVarsCompare(referInsConn *DbWorker, checkVars []string) if len(errMsg) > 0 { return fmt.Errorf(strings.Join(errMsg, "\n")) } - return + return nil } // ResetSlave TODO @@ -860,3 +892,92 @@ func GetTableColumnList(dbworker *DbWorker, dbName, tblName string) ([]string, e } return nil, errors.New("table not found") } + +// ValidateChecksum TODO +func (slaveConn *DbWorker) ValidateChecksum(allowDiffCount int, needCheckSumRd bool) (err error) { + var cnt int + c := fmt.Sprintf( + "select count(distinct db, tbl) as cnt from %s.checksum where ts > date_sub(now(), interval 14 day)", + INFODBA_SCHEMA, + ) + if err = slaveConn.Queryxs(&cnt, c); err != nil { + logger.Error("查询最近14天checkTable总数失败%s", err.Error()) + return err + } + // 如果查询不到 校验记录需要 return error + if cnt == 0 && needCheckSumRd { + logger.Warn("没有查询到最近14天的校验记录") + return fmt.Errorf("主从校验记录为空") + } + + if !needCheckSumRd { + logger.Info("不需要检查校验记录. 获取到的CheckSum Record 总数为%d", cnt) + } + + c = fmt.Sprintf( + `select count(distinct db, tbl,chunk) as cnt from %s.checksum where (this_crc <> master_crc or this_cnt <> master_cnt) + and ts > date_sub(now(), interval 14 day);`, INFODBA_SCHEMA, + ) + + if err = slaveConn.Queryxs(&cnt, c); err != nil { + logger.Error("查询数据校验差异表失败: %s", err.Error()) + return err + } + + if cnt > allowDiffCount { + return fmt.Errorf("checksum 不同值的 chunk 个数是 %d,超过了上限 %d", cnt, allowDiffCount) + } + + return err +} + +// ReplicateDelayCheck TODO +func (slaveConn *DbWorker) ReplicateDelayCheck(allowDelaySec int, behindExecBinLogbyte int) (err error) { + // 检查主从同步delay binlog size + total, err := slaveConn.TotalDelayBinlogSize() + if err != nil { + logger.Error("get total delay binlog size failed %s", err.Error()) + return err + } + if total > behindExecBinLogbyte { + return fmt.Errorf("the total delay binlog size %d 超过了最大允许值 %d", total, behindExecBinLogbyte) + } + var delaySec, beatSec int + c := fmt.Sprintf("select delay_sec, timestampdiff(SECOND, master_time, now()) beat_sec "+ + "from %s.master_slave_heartbeat WHERE slave_server_id=@@server_id", INFODBA_SCHEMA) + if err = slaveConn.Db.QueryRow(c).Scan(&delaySec, &beatSec); err != nil { + logger.Error("查询slave delay sec: %s", err.Error()) + return err + } + if beatSec > 600 { + return errors.Errorf("超过 %ds 没有延迟检测信号", beatSec) + } + if delaySec > allowDelaySec { + return fmt.Errorf("slave 延迟时间 %ds, 超过了上限 %d", delaySec, allowDelaySec) + } + return +} + +// CompareBinlogPos TODO +func CompareBinlogPos(masterStatus MasterStatusResp, slaveStatus ShowSlaveStatusResp) (err error) { + // 比较从库回放到了对应主库的哪个BinLog File + msg := fmt.Sprintf( + "Master Current BinlogFile:%s Slave SQL Thread Exec BinlogFile:%s", + masterStatus.File, slaveStatus.RelayMasterLogFile, + ) + logger.Info(msg) + if strings.Compare(masterStatus.File, slaveStatus.RelayMasterLogFile) != 0 { + return fmt.Errorf("主从同步有差异," + msg) + } + // 比较主库的位点和从库已经回放的位点信息 + // 比较从库回放到了对应主库的哪个BinLog File + msg = fmt.Sprintf( + "Master Current Pos:%d Slave SQL Thread Exec Pos:%d", + masterStatus.Position, slaveStatus.ExecMasterLogPos, + ) + logger.Info(msg) + if masterStatus.Position != slaveStatus.ExecMasterLogPos { + return fmt.Errorf("主从执行的位点信息有差异%s", msg) + } + return err +} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/native/tdbctl.go b/dbm-services/mysql/db-tools/dbactuator/pkg/native/tdbctl.go new file mode 100644 index 0000000000..e3eaaf6b7a --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/native/tdbctl.go @@ -0,0 +1,237 @@ +package native + +import ( + "database/sql" + "encoding/json" + "fmt" + "regexp" + "strings" +) + +// TdbctlDbWork TODO +type TdbctlDbWork struct { + DbWorker +} + +// Server TODO +type Server struct { + ServerName string `db:"Server_name"` + Host string `db:"Host"` + Db string `db:"Db"` + Username string `db:"Username"` + Password string `db:"Password"` + Port int `db:"Port"` + Wrapper string `db:"Wrapper"` +} + +// TsccSchemaChecksum TODO +type TsccSchemaChecksum struct { + Db string `json:"db" db:"db"` + Tbl string `json:"tbl" db:"tbl"` + Status string `json:"status" db:"status"` + ChecksumResult json.RawMessage `json:"checksum_result" db:"checksum_result"` + UpdateTime string `json:"update_time" db:"update_time"` +} + +// SchemaCheckResult TODO +type SchemaCheckResult struct { + ServerName string `json:"Server_name" db:"Server_name"` + Db string `json:"Db" db:"Db"` + Table string `json:"Table" db:"Table"` + Status string `json:"Status" db:"Status"` + Message string `json:"Message" db:"Message"` +} + +// SchemaCheckResults TODO +type SchemaCheckResults []SchemaCheckResult + +// CheckResult TODO +func (rs SchemaCheckResults) CheckResult() (inconsistencyItems []SchemaCheckResult) { + for _, r := range rs { + if strings.Compare(strings.ToUpper(r.Status), SchemaCheckOk) == 0 { + continue + } + inconsistencyItems = append(inconsistencyItems, r) + } + return +} + +const ( + // SchemaCheckOk TODO + SchemaCheckOk = "OK" +) + +// GetAbnormalSchemaChecksum TODO +func (t *TdbctlDbWork) GetAbnormalSchemaChecksum() (checksums []TsccSchemaChecksum, err error) { + err = t.Queryx(&checksums, "select * from infodba_schema.tscc_schema_checksum where status != ? ", SchemaCheckOk) + return +} + +// GetConn TODO +func (s *Server) GetConn() (conn *DbWorker, err error) { + return InsObject{ + Host: s.Host, + Port: s.Port, + User: s.Username, + Pwd: s.Password, + }.Conn() +} + +// Opendb TODO +func (s *Server) Opendb(dbName string) (conn *sql.DB, err error) { + return Opendb(s.GetEndPoint(), s.Username, s.Password, dbName) +} + +// GetEndPoint TODO +func (s *Server) GetEndPoint() string { + return fmt.Sprintf("%s:%d", s.Host, s.Port) +} + +// GetAlterNodeSql TODO +// +// "tdbctl create node wrapper 'mysql_slave' options(user '%s', password '%s', host '%s', port %d, number %d);", +func (s *Server) GetAlterNodeSql(serverName string) (sqlStr string) { + return fmt.Sprintf("TDBCTL ALTER NODE %s options(user '%s', password '%s', host '%s', port %d);", + serverName, + s.Username, + s.Password, s.Host, s.Port) +} + +// SHARDPREFIX TODO +const ( + SHARDPREFIX = "SPT" + SLAVE_SHARDPREFIX = "SPT_SLAVE" + SPIDER_PREFIX = "SPIDER" + SPIDER_SLAVE_PREFIX = "SPIDER_SLAVE" +) + +// SvrNameIsMasterShard TODO +func SvrNameIsMasterShard(svrName string) bool { + return matchPrefixPattern(SHARDPREFIX, svrName) +} + +// SvrNameIsSlaveShard TODO +func SvrNameIsSlaveShard(svrName string) bool { + return matchPrefixPattern(SLAVE_SHARDPREFIX, svrName) +} + +// SvrNameIsSlaveSpiderShard TODO +func SvrNameIsSlaveSpiderShard(svrName string) bool { + return matchPrefixPattern(SPIDER_SLAVE_PREFIX, svrName) +} + +// SvrNameIsMasterSpiderShard TODO +func SvrNameIsMasterSpiderShard(svrName string) bool { + return matchPrefixPattern(SPIDER_PREFIX, svrName) +} + +func matchPrefixPattern(pattern string, svrName string) bool { + re := regexp.MustCompile(fmt.Sprintf(`^%s(\d+)`, pattern)) + return re.MatchString(svrName) +} + +// GetMasterShardNameByShardNum TODO +func GetMasterShardNameByShardNum(num string) string { + return SPIDER_PREFIX + num +} + +// GetSlaveShardNameByShardNum TODO +func GetSlaveShardNameByShardNum(num string) string { + return SLAVE_SHARDPREFIX + num +} + +// GetSlaveShardNameByMasterShardName TODO +func GetSlaveShardNameByMasterShardName(masterShardName string) string { + num := GetShardNumberFromMasterServerName(masterShardName) + return GetSlaveShardNameByShardNum(num) +} + +// GetShardNumberFromMasterServerName TODO +func GetShardNumberFromMasterServerName(serverName string) string { + m := regexp.MustCompile(SHARDPREFIX) + return m.ReplaceAllString(serverName, "") +} + +// GetShardNumberFromSlaveServerName TODO +func GetShardNumberFromSlaveServerName(serverName string) string { + m := regexp.MustCompile(SLAVE_SHARDPREFIX) + return m.ReplaceAllString(serverName, "") +} + +// AlterNode TODO +func (t *TdbctlDbWork) AlterNode(server_name string, user, password, host string, port int) (int64, error) { + return t.Exec("tdbctl alter node ? options(user ?,password ?,host ?,port ?)", server_name, user, password, host, + port) +} + +// SelectServers TODO +func (t *TdbctlDbWork) SelectServers() (servers []Server, err error) { + err = t.Queryx(&servers, "select * from mysql.servers") + return +} + +// get_exec_special_node_cmd TODO +func (t *TdbctlDbWork) get_exec_special_node_cmd(serverName string) string { + return fmt.Sprintf("TDBCTL CONNECT NODE %s EXECUTE", serverName) +} + +// GetSingleGlobalVar TODO +func (t *TdbctlDbWork) GetSingleGlobalVar(serverName, varName string) (val string, err error) { + var item MySQLGlobalVariableItem + if err = t.Queryxs(&item, fmt.Sprintf("%s 'show global variables like \"%s\"'", t.get_exec_special_node_cmd( + serverName), varName)); err != nil { + return "", err + } + return item.Value, nil +} + +// QueryGlobalVariables TODO +func (t *TdbctlDbWork) QueryGlobalVariables(serverName string) (map[string]string, error) { + result := make(map[string]string) + rows, err := t.Db.Query(fmt.Sprintf(" %s 'SHOW GLOBAL VARIABLES'", t.get_exec_special_node_cmd( + serverName))) + if err != nil { + return result, err + } + defer rows.Close() + for rows.Next() { + var key, val string + err := rows.Scan(&key, &val) + if err != nil { + continue + } + result[key] = val + } + return result, nil +} + +// MySQLVarsCompare TODO +func (h *TdbctlDbWork) MySQLVarsCompare(serverName string, referInsConn *DbWorker, checkVars []string) (err error) { + referVars, err := referInsConn.QueryGlobalVariables() + if err != nil { + return err + } + compareVars, err := h.QueryGlobalVariables(serverName) + if err != nil { + return err + } + return compareDbVariables(referVars, compareVars, checkVars) +} + +// ShowSlaveStatus TODO +func (t *TdbctlDbWork) ShowSlaveStatus(serverName string) (data ShowSlaveStatusResp, err error) { + err = t.Queryxs(&data, fmt.Sprintf("%s 'show slave status'", t.get_exec_special_node_cmd(serverName))) + return +} + +// ShowMasterStatus TODO +func (t *TdbctlDbWork) ShowMasterStatus(serverName string) (data MasterStatusResp, err error) { + err = t.Queryxs(&data, fmt.Sprintf("%s 'show master status'", t.get_exec_special_node_cmd(serverName))) + return +} + +// LockTables TODO +func (t *TdbctlDbWork) LockTables(serverName string) (data MasterStatusResp, err error) { + _, err = t.Exec(fmt.Sprintf("%s 'lock table with read lock'", t.get_exec_special_node_cmd(serverName))) + return +} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/native/tdbctl_test.go b/dbm-services/mysql/db-tools/dbactuator/pkg/native/tdbctl_test.go new file mode 100644 index 0000000000..b6ce789e82 --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/native/tdbctl_test.go @@ -0,0 +1,53 @@ +package native_test + +import ( + "log" + "os" + "strconv" + "testing" + + "dbm-services/mysql/db-tools/dbactuator/pkg/native" +) + +var tdbctldbWork *native.TdbctlDbWork + +func init() { + port, _ := strconv.Atoi(os.Getenv("PORT")) + conn, err := native.InsObject{ + Host: os.Getenv("HOST"), + Port: port, + User: os.Getenv("USER"), + Pwd: os.Getenv("PASSWORD"), + }.Conn() + if err != nil { + log.Fatal(err) + } + tdbctldbWork = &native.TdbctlDbWork{DbWorker: *conn} +} + +func TestTdbctlShowAppProcesslist(t *testing.T) { + pcls, err := tdbctldbWork.ShowApplicationProcesslist([]string{"root"}) + if err != nil { + t.Fatal(err) + return + } + t.Log(pcls) +} + +func TestTdbctlGetVariables(t *testing.T) { + val, err := tdbctldbWork.GetSingleGlobalVar("SPT0", "character_set_server") + if err != nil { + t.Fatal(err) + return + } + t.Log(val) +} + +func TestTdbctlAllVariables(t *testing.T) { + val, err := tdbctldbWork.QueryGlobalVariables("SPT0") + if err != nil { + t.Fatal(err) + return + } + t.Log(val) +} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/tools/init.go b/dbm-services/mysql/db-tools/dbactuator/pkg/tools/init.go index 306e3618ad..228c84e398 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/tools/init.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/tools/init.go @@ -48,7 +48,7 @@ const ( var defaultPath = map[ExternalTool]string{ ToolMload: "/home/mysql/dbbackup/MLOAD/MLOAD.pl", ToolXLoad: "/home/mysql/dbbackup/xtrabackup/xload.pl", - ToolQPress: "/home/mysql/dbbackup-go/bin/xtrabackup/qpress", + ToolQPress: "/home/mysql/dbbackup-go/bin/qpress", ToolPv: "/home/mysql/dbbackup-go/bin/pv", ToolMysqlclient: "/usr/local/mysql/bin/mysql", ToolMysqlbinlog: "/usr/local/mysql/bin/mysqlbinlog", diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/util/bkrepo/bkrepo.go b/dbm-services/mysql/db-tools/dbactuator/pkg/util/bkrepo/bkrepo.go deleted file mode 100644 index 2778ce6058..0000000000 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/util/bkrepo/bkrepo.go +++ /dev/null @@ -1,297 +0,0 @@ -// Package bkrepo TODO -package bkrepo - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "log" - "mime/multipart" - "net/http" - "net/url" - "os" - "path" - "path/filepath" - "strconv" - "strings" - - "dbm-services/common/go-pubpkg/logger" - "dbm-services/mysql/db-tools/dbactuator/pkg/util" -) - -/* - API: GET /generic/{project}/{repo}/{path}?download=true - API 名称: download - 功能说明: - - 中文:下载通用制品文件 - English:download generic file - 请求体 此接口请求体为空 -*/ - -// BkRepoClient TODO -type BkRepoClient struct { - Client *http.Client - BkRepoProject string - BkRepoPubBucket string - BkRepoEndpoint string - BkRepoUser string - BkRepoPwd string -} - -// BkRepoRespone TODO -type BkRepoRespone struct { - Code int `json:"code"` - Message string `json:"message"` - Data json.RawMessage `json:"data"` - RequestId string `json:"request_id"` -} - -// getBaseUrl TODO -// -// @receiver b -func (b *BkRepoClient) getBaseUrl() string { - u, err := url.Parse(b.BkRepoEndpoint) - if err != nil { - log.Fatal(err) - } - u.Path = path.Join(u.Path, "generic", b.BkRepoProject, b.BkRepoPubBucket) - return u.String() -} - -// Download 从制品库下载文件 -// -// @receiver b -func (b *BkRepoClient) Download(sqlpath, filename, downloaddir string) (err error) { - uri := b.getBaseUrl() + path.Join("/", sqlpath, filename) + "?download=true" - logger.Info("The download uri %s", uri) - req, err := http.NewRequest(http.MethodGet, uri, nil) - if err != nil { - return err - } - if strings.Contains(filename, "..") { - return fmt.Errorf("%s 存在路径穿越风险", filename) - } - fileAbPath, err := filepath.Abs(path.Join(downloaddir, filename)) - if err != nil { - return err - } - f, err := os.Create(fileAbPath) - if err != nil { - return err - } - req.SetBasicAuth(b.BkRepoUser, b.BkRepoPwd) - resp, err := b.Client.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - size, err := io.Copy(f, resp.Body) - if err != nil { - return err - } - logger.GetLogger().Info(fmt.Sprintf("Downloaded a file %s with size %d", filename, size)) - fileNodeInfo, err := b.QueryFileNodeInfo(sqlpath, filename) - if err != nil { - return err - } - - if size != int64(fileNodeInfo.Size) { - return fmt.Errorf("当前文件&源文件大小不一致,当前文件是:%d,制品库文件是:%d", size, fileNodeInfo.Size) - } - - currentFileMd5, err := util.GetFileMd5(fileAbPath) - if err != nil { - return err - } - if currentFileMd5 != fileNodeInfo.Md5 { - return fmt.Errorf("当前文件&源文件md5b不一致,当前文件是:%s,制品库文件是:%s", currentFileMd5, fileNodeInfo.Md5) - } - return nil -} - -// FileNodeInfo TODO -type FileNodeInfo struct { - Name string `json:"name"` - Sha256 string `json:"sha256"` - Md5 string `json:"md5"` - Size int `json:"size"` - Metadata map[string]string `json:"metadata"` -} - -// QueryFileNodeInfo TODO -// QueryMetaData 查询文件元数据信息 -// -// @receiver b -func (b *BkRepoClient) QueryFileNodeInfo(filepath, filename string) (realData FileNodeInfo, err error) { - var baseResp BkRepoRespone - uri := b.BkRepoEndpoint + path.Join( - "repository/api/node/detail/", b.BkRepoProject, b.BkRepoPubBucket, filepath, - filename, - ) - req, err := http.NewRequest(http.MethodGet, uri, nil) - if err != nil { - return FileNodeInfo{}, err - } - resp, err := b.Client.Do(req) - if err != nil { - return FileNodeInfo{}, err - } - defer resp.Body.Close() - if err = json.NewDecoder(resp.Body).Decode(&baseResp); err != nil { - return FileNodeInfo{}, err - } - if baseResp.Code != 0 { - return FileNodeInfo{}, fmt.Errorf("bkrepo Return Code: %d,Messgae:%s", baseResp.Code, baseResp.Message) - } - if err = json.Unmarshal([]byte(baseResp.Data), &realData); err != nil { - return FileNodeInfo{}, err - } - return -} - -// UploadRespData TODO -type UploadRespData struct { - Sha256 string `json:"sha256"` - Md5 string `json:"md5"` - Size int64 `json:"size"` - FullPath string `json:"fullPath"` - CreateBy string `json:"createBy"` - CreateDate string `json:"createdDate"` - LastModifiedBy string `json:"lastModifiedBy"` - LastModifiedDate string `json:"lastModifiedDate"` - Folder bool `json:"folder"` // 是否为文件夹 - Path string `json:"path"` - Name string `json:"name"` - ProjectId string `json:"projectId"` - RepoName string `json:"repoName"` -} - -// FileServerInfo 文件服务器 -type FileServerInfo struct { - URL string `json:"url"` // 制品库地址 - Bucket string `json:"bucket"` // 目标bucket - Password string `json:"password"` // 制品库 password - Username string `json:"username"` // 制品库 username - Project string `json:"project"` // 制品库 project -} - -func newfileUploadRequest(uri string, params map[string]string, paramName, path string) (*http.Request, error) { - file, err := os.Open(path) - if err != nil { - return nil, err - } - defer file.Close() - - body := &bytes.Buffer{} - writer := multipart.NewWriter(body) - part, err := writer.CreateFormFile(paramName, filepath.Base(path)) - if err != nil { - return nil, err - } - _, err = io.Copy(part, file) - if err != nil { - return nil, err - } - for key, val := range params { - _ = writer.WriteField(key, val) - } - err = writer.Close() - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPut, uri, body) - req.Header.Set("Content-Type", writer.FormDataContentType()) - return req, err -} - -// UploadDirectToBkRepo TODO -func UploadDirectToBkRepo(filepath string, targetURL string, username string, password string) (*BkRepoRespone, error) { - logger.Info("start upload files from %s to %s", filepath, targetURL) - bodyBuf := bytes.NewBufferString("") - bodyWriter := multipart.NewWriter(bodyBuf) - fh, err := os.Open(filepath) - if err != nil { - logger.Info("error opening file") - return nil, err - } - boundary := bodyWriter.Boundary() - closeBuf := bytes.NewBufferString("") - - requestReader := io.MultiReader(bodyBuf, fh, closeBuf) - fi, err := fh.Stat() - if err != nil { - fmt.Printf("Error Stating file: %s", filepath) - return nil, err - } - req, err := http.NewRequest("PUT", targetURL, requestReader) - if err != nil { - return nil, err - } - req.SetBasicAuth(username, password) - // Set headers for multipart, and Content Length - req.Header.Set("Content-Type", "multipart/form-data; boundary="+boundary) - // 文件是否可以被覆盖,默认false - req.Header.Set("X-BKREPO-OVERWRITE", "True") - // 文件默认保留半年 - req.Header.Set("X-BKREPO-EXPIRES", "183") - req.ContentLength = fi.Size() + int64(bodyBuf.Len()) + int64(closeBuf.Len()) - resp, err := http.DefaultClient.Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("返回码非200 %d", resp.StatusCode) - } - var baseResp BkRepoRespone - if err = json.NewDecoder(resp.Body).Decode(&baseResp); err != nil { - return nil, err - } - return &baseResp, err -} - -// UploadFile 上传文件到蓝盾制品库 -// filepath: 本地需要上传文件的路径 -// targetURL: 仓库文件完整路径 -func UploadFile( - filepath string, targetURL string, username string, password string, BkCloudId int, - DBCloudToken string, -) (*BkRepoRespone, error) { - logger.Info("start upload files from %s to %s", filepath, targetURL) - if BkCloudId == 0 { - return UploadDirectToBkRepo(filepath, targetURL, username, password) - } - req, err := newfileUploadRequest( - targetURL, map[string]string{ - "bk_cloud_id": strconv.Itoa(BkCloudId), - "db_cloud_token": DBCloudToken, - }, "file", filepath, - ) - if err != nil { - logger.Error("new request failed %s", err.Error()) - return nil, err - } - resp, err := http.DefaultClient.Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - body := &bytes.Buffer{} - _, err = body.ReadFrom(resp.Body) - if err != nil { - logger.Error("read from body failed %s", err.Error()) - return nil, err - } - logger.Info("respone body:%s", body.String()) - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("返回码非200 %d,Message:%s", resp.StatusCode, body.String()) - } - var baseResp BkRepoRespone - if err = json.NewDecoder(body).Decode(&baseResp); err != nil { - return nil, err - } - return &baseResp, err -} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/util/bkrepo/bkrepo_test.go b/dbm-services/mysql/db-tools/dbactuator/pkg/util/bkrepo/bkrepo_test.go deleted file mode 100644 index 539b30df29..0000000000 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/util/bkrepo/bkrepo_test.go +++ /dev/null @@ -1,21 +0,0 @@ -package bkrepo_test - -import ( - "net/url" - "path" - "testing" - - "dbm-services/mysql/db-tools/dbactuator/pkg/util/bkrepo" -) - -func TestUploadFile(t *testing.T) { - t.Log("start...") - r, err := url.Parse(path.Join("/generic", "/")) - t.Log(r.String()) - resp, err := bkrepo.UploadFile("/tmp/1.sql", "", "", "", 0, "") - if err != nil { - t.Log(err.Error()) - return - } - t.Log(resp) -} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/util/db_table_filter/db_table_filter.go b/dbm-services/mysql/db-tools/dbactuator/pkg/util/db_table_filter/db_table_filter.go index a8dbee7d61..8d0512472c 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/util/db_table_filter/db_table_filter.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/util/db_table_filter/db_table_filter.go @@ -23,6 +23,8 @@ type DbTableFilter struct { } // NewDbTableFilter 构造函数 +// 如果是 mydumper,内置忽略 infodba_schema.conn_log 表 +// NewDbTableFilter 完成后,需要 BuildFilter() func NewDbTableFilter( includeDbPatterns []string, includeTablePatterns []string, @@ -46,12 +48,16 @@ func NewDbTableFilter( return nil, err } - tf.buildDbFilterRegex() - tf.buildTableFilterRegex() - return tf, nil } +// BuildFilter normal build filter +// is different with NewMydumperRegex +func (c *DbTableFilter) BuildFilter() { + c.buildDbFilterRegex() + c.buildTableFilterRegex() +} + func (c *DbTableFilter) validate() error { if len(c.IncludeDbPatterns) == 0 || len(c.IncludeTablePatterns) == 0 { return fmt.Errorf("include patterns can't be empty") @@ -110,7 +116,13 @@ func (c *DbTableFilter) buildTableFilterRegex() { ) } } - + /* + if mydumper { + // ignore infodba_schema.conn_log + // 这里没有放到 ExcludeDbPatterns=[infodba_schema, mysql,test...], ExcludeTablePatterns=[conn_log] 里,因为会拼多 + excludeParts = append(excludeParts, fmt.Sprintf(`%s\.%s$`, native.INFODBA_SCHEMA, "conn_log")) + } + */ c.tableFilterIncludeRegex = buildIncludeRegexp(includeParts) c.tableFilterExcludeRegex = buildExcludeRegexp(excludeParts) } diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/util/db_table_filter/db_table_filter_test.go b/dbm-services/mysql/db-tools/dbactuator/pkg/util/db_table_filter/db_table_filter_test.go index 179baaf619..81004c7b95 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/util/db_table_filter/db_table_filter_test.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/util/db_table_filter/db_table_filter_test.go @@ -18,5 +18,6 @@ func TestDbtableFilter(t *testing.T) { t.Fatal(err) return } + r.BuildFilter() t.Log(r.TableFilterRegex()) } diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/util/db_table_filter/mydumper_regex.go b/dbm-services/mysql/db-tools/dbactuator/pkg/util/db_table_filter/mydumper_regex.go index bb7c63ef3e..6dedec3232 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/util/db_table_filter/mydumper_regex.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/util/db_table_filter/mydumper_regex.go @@ -5,10 +5,27 @@ import ( "strings" ) +func BuildMydumperRegex( + includeDbPatterns []string, + includeTablePatterns []string, + excludeDbPatterns []string, + excludeTablePatterns []string, +) (*DbTableFilter, error) { + tf, err := NewDbTableFilter(includeDbPatterns, includeTablePatterns, excludeDbPatterns, excludeTablePatterns) + if err != nil { + return nil, err + } + + tf.buildDbFilterRegex() + tf.buildTableFilterRegex() + return tf, nil +} + // MyloaderRegex TODO func (c *DbTableFilter) MyloaderRegex(doDr bool) string { if doDr { - sysDBExclude := `^(?!(mysql\.|sys\.|infodba_schema\.|test\.|db_infobase\.))` + // infodba_schema 不忽略,因为需要同步旧库的信息过来,不然replication可能会报错 + sysDBExclude := `^(?!(mysql\.|sys\.|test\.|db_infobase\.))` return sysDBExclude } for i, db := range c.IncludeDbPatterns { @@ -61,7 +78,3 @@ func buildRegexString(patterns []string) string { } return ss } - -func isAllPattern(ss string) bool { - return ss == `.*\.` -} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/util/dbcnf.go b/dbm-services/mysql/db-tools/dbactuator/pkg/util/dbcnf.go index 56a7a4e48c..6ecc2b121a 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/util/dbcnf.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/util/dbcnf.go @@ -1,15 +1,24 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package util import ( "fmt" - "os" "path" "path/filepath" "reflect" - "regexp" "sort" "strings" "sync" + "time" "dbm-services/common/go-pubpkg/cmutil" "dbm-services/common/go-pubpkg/logger" @@ -221,7 +230,7 @@ func (m *CnfFile) GetBinLogDir() (binlogDir, namePrefix string, err error) { if val, err := m.GetMySQLCnfByKey(MysqldSec, k); err == nil { if filepath.IsAbs(val) { if binlogDir, namePrefix, err = m.ParseLogBinBasename(val); err == nil { - return binlogDir, namePrefix, err + return binlogDir, namePrefix, nil } } } @@ -417,7 +426,7 @@ func (m *CnfFile) RenderSection(sectionName, key, val string, isProxy bool) (err if _, err := m.Cfg.Section(sectionName).NewKey(key, shadowv); err != nil { return err } - fmt.Println(",", "M") + // fmt.Println(",", "M") } return nil } @@ -505,10 +514,10 @@ func (m *CnfFile) UpdateKeyValue(section, key, value string) (err error) { if m.isShadowKey(key) { // 如果这些key 是可重复的key err = m.Cfg.Section(section).Key(key).AddShadow(value) - return + return errors.Wrap(err, "update my.cnf KeyValue") } m.Cfg.Section(section).Key(key).SetValue(value) - return + return nil } // GetMyCnfFileName 获取默认 my.cnf 的路径,不检查是否存在 @@ -528,29 +537,16 @@ func GetProxyCnfName(port int) string { // ReplaceValuesToFile 文本替换 my.cnf 里面的 value,如果 key 不存在则插入 [mysqld] 后面 func (m *CnfFile) ReplaceValuesToFile(newItems map[string]string) error { - f, err := os.ReadFile(m.FileName) - if err != nil { - return err + if err := cmutil.OSCopyFile(m.FileName, fmt.Sprintf("%s.bak%s", + m.FileName, time.Now().Format("20060102150405"))); err != nil { + logger.Warn("backup file %s failed, ignore: %s", m.FileName, err.Error()) } - lines := strings.Split(string(f), "\n") - itemsNotFound := make(map[string]string) - for i, lineText := range lines { - for k, v := range newItems { - itemsNotFound[k] = v - reg := regexp.MustCompile(fmt.Sprintf(`^\s*%s\s*=(.*)`, k)) - if reg.MatchString(lineText) { - lines[i] = fmt.Sprintf(`%s = %s`, k, v) - delete(itemsNotFound, k) // found - } + for k, v := range newItems { + if err := m.UpdateKeyValue(MysqldSec, k, v); err != nil { + return err } } - for k, v := range itemsNotFound { - StringsInsertAfter(lines, "[mysqld]", fmt.Sprintf(`%s = %s`, k, v)) - } - if err = os.WriteFile(m.FileName, []byte(strings.Join(lines, "\n")), 0644); err != nil { - return err - } - return nil + return m.SafeSaveFile(false) } // GetMysqldKeyVaule 增加基础方法,获取myconf上面的某个配置参数值,用于做前置校验的对比 diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/util/filelock.go b/dbm-services/mysql/db-tools/dbactuator/pkg/util/filelock.go index 3a44ed004a..3fdf750c05 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/util/filelock.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/util/filelock.go @@ -158,7 +158,6 @@ func (fl *FLock) FileIncrSafe(incr int, retryInterval int) (succ int, err error) return fl.FileIncrSafe(incr, retryInterval) } } - return 1, nil } // FileUnlockIncr TODO diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/change_master.go b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/change_master.go index eb27e9f8f4..5f963d9a85 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/change_master.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/change_master.go @@ -45,13 +45,10 @@ func (c *ChangeMaster) GetSQL() string { ) } else { sql = fmt.Sprintf( - `CHANGE MASTER TO MASTER_HOST='%s', MASTER_PORT=%d, MASTER_USER ='%s', MASTER_PASSWORD='%s',MASTER_LOG_FILE='%s', MASTER_LOG_POS=%d`, - c.MasterHost, - c.MasterPort, - c.MasterUser, - c.MasterPassword, - c.MasterLogFile, - c.MasterLogPos, + "CHANGE MASTER TO MASTER_HOST='%s', MASTER_PORT=%d, MASTER_USER ='%s', MASTER_PASSWORD='%s', "+ + "MASTER_LOG_FILE='%s', MASTER_LOG_POS=%d", + c.MasterHost, c.MasterPort, c.MasterUser, c.MasterPassword, + c.MasterLogFile, c.MasterLogPos, ) } c.ChangeSQL = sql diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/hide_passowrd.go b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/hide_passowrd.go index 1df291a6fd..cdd3d37482 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/hide_passowrd.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/hide_passowrd.go @@ -1,12 +1,21 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package mysqlutil import ( "regexp" - "strings" ) var ( - mysqlRegex = regexp.MustCompile(`mysql.*-u\w+.*\s-p(\w+).*`) + mysqlRegex = regexp.MustCompile(`mysql.*-u(\s*)\w+.*\s-p(\w+).*`) mysqlAdminRegex = regexp.MustCompile(`mysqladmin.*-u\w+.*\s-p(\w+).*`) mysqlPasswordRegex = regexp.MustCompile(`\s-p[^\s]+`) masterPasswordRegexp = regexp.MustCompile(`master_password="[^\s]*"`) @@ -14,6 +23,7 @@ var ( userPasswordRegex = regexp.MustCompile(`\s-u\w+.*\s-p(\w+).*`) dsnRegex = regexp.MustCompile(`\w+:[^\s]*@tcp\([^\s]+\)`) dsnPasswordRegex = regexp.MustCompile(`:[^\s]*@tcp\(`) + passwordRegex = regexp.MustCompile(`password ['|"]*\w+['|"]*`) ) // ClearSensitiveInformation clear sensitive information from input @@ -21,13 +31,19 @@ func ClearSensitiveInformation(input string) string { output := RemoveMysqlCommandPassword(input) output = ClearMasterPasswordInSQL(output) output = RemoveMysqlAdminCommandPassword(output) - output = ClearIdentifyByInSQL(output) + output = clearIdentifyByInSQL(output) output = RemovePasswordInDSN(output) + output = CleanSvrPassword(output) return output } -// ClearIdentifyByInSQL TODO -func ClearIdentifyByInSQL(input string) string { +// CleanSvrPassword TODO +func CleanSvrPassword(input string) string { + return passwordRegex.ReplaceAllString(input, "password 'xxxx'") +} + +// clearIdentifyByInSQL TODO +func clearIdentifyByInSQL(input string) string { output := identifyByRegex.ReplaceAllString(input, `identified by 'xxxx'`) return output } @@ -36,7 +52,7 @@ func ClearIdentifyByInSQL(input string) string { func ClearIdentifyByInSQLs(input []string) []string { output := make([]string, len(input)) for i, s := range input { - output[i] = identifyByRegex.ReplaceAllString(strings.ToLower(s), `identified by 'xxxx'`) + output[i] = clearIdentifyByInSQL(s) } return output } diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/filename_to_tablename.go b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/filename_to_tablename.go new file mode 100644 index 0000000000..c81d6cf2e3 --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/filename_to_tablename.go @@ -0,0 +1,68 @@ +// TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +// Copyright (C) 2017-2023 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 https://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. + +package identifiertrans + +import ( + "dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/internal/tables" + "fmt" +) + +func FilenameToTableName(filename string) (tablename string, err error) { + for idx := 0; idx < len(filename); { + if filename[idx] < 128 && tables.SafeChar[filename[idx]] { + tablename += string(filename[idx]) + idx += 1 + continue + } + + if filename[idx] != '@' { + return "", fmt.Errorf("invalid char filename[%d]=%c", idx, filename[idx]) + } + + if idx+3 > len(filename) { + return "", fmt.Errorf("invalid sequence filename[%d:]=%s", idx, filename[idx:]) + } + + c1 := filename[idx+1] + c2 := filename[idx+2] + + if c1 >= 0x30 && c1 <= 0x7F && c2 >= 0x30 && c2 <= 0x7F { + code := (int(c1)-0x30)*80 + int(c2) - 0x30 + if code < 5994 && tables.ToUni[code] > 0 { + tablename += string(tables.ToUni[code]) + idx += 3 + continue + } + + if c1 == '@' && c2 == '@' { + tablename += string(rune(0)) + idx += 3 + continue + } + } + + if idx+4 > len(filename) { + return "", fmt.Errorf("invalid sequence filename[%d:]=%s", idx, filename[idx:]) + } + + h1 := tables.HexLo(c1) + h2 := tables.HexLo(c2) + if h1 >= 0 && h2 >= 0 { + h3 := tables.HexLo(filename[idx+3]) + h4 := tables.HexLo(filename[idx+4]) + if h3 >= 0 && h4 >= 0 { + code := (h1 << 12) + (h2 << 8) + (h3 << 4) + h4 + tablename += string(rune(code)) + idx += 5 + continue + } + } + } + return tablename, nil +} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/internal/tables/0C00_05FF.go b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/internal/tables/0C00_05FF.go new file mode 100644 index 0000000000..4e13563612 --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/internal/tables/0C00_05FF.go @@ -0,0 +1,161 @@ +// TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +// Copyright (C) 2017-2023 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 https://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. + +package tables + +var Uni0c0005ff = []rune{ + 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, + 0x0029, 0x002A, 0x0067, 0x0068, 0x0069, 0x0000, 0x006B, 0x006C, 0x006D, + 0x006E, 0x006F, 0x0070, 0x0071, 0x008A, 0x0037, 0x0038, 0x0039, 0x003A, + 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, 0x0040, 0x0041, 0x0042, 0x0043, + 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x0087, 0x0088, + 0x0089, 0x0000, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, 0x0090, 0x0091, + 0x0092, 0x0073, 0x0093, 0x0074, 0x0094, 0x0075, 0x0095, 0x0076, 0x0096, + 0x0077, 0x0097, 0x0078, 0x0098, 0x0079, 0x0099, 0x007A, 0x009A, 0x00B7, + 0x00D7, 0x00B8, 0x00D8, 0x00B9, 0x00D9, 0x00BA, 0x00DA, 0x00BB, 0x00DB, + 0x00BC, 0x00DC, 0x00BD, 0x00DD, 0x00BE, 0x00DE, 0x00BF, 0x00DF, 0x00C0, + 0x00E0, 0x00C1, 0x00E1, 0x00C2, 0x00E2, 0x00C3, 0x00E3, 0x00C4, 0x00E4, + 0x00C5, 0x00E5, 0x00C6, 0x00E6, 0x0000, 0x00E7, 0x00C8, 0x00E8, 0x00C9, + 0x00E9, 0x00CA, 0x00EA, 0x0127, 0x0108, 0x0128, 0x0109, 0x0129, 0x010A, + 0x012A, 0x010B, 0x012B, 0x010C, 0x012C, 0x010D, 0x012D, 0x010E, 0x012E, + 0x010F, 0x012F, 0x0130, 0x0111, 0x0131, 0x0112, 0x0132, 0x0113, 0x0133, + 0x0114, 0x0134, 0x0115, 0x0135, 0x0116, 0x0136, 0x0117, 0x0137, 0x0118, + 0x0138, 0x0119, 0x0139, 0x011A, 0x013A, 0x0157, 0x0177, 0x0158, 0x0178, + 0x0159, 0x0179, 0x015A, 0x017A, 0x015B, 0x017B, 0x015C, 0x017C, 0x015D, + 0x017D, 0x015E, 0x017E, 0x015F, 0x017F, 0x0160, 0x0180, 0x0161, 0x0181, + 0x0162, 0x0182, 0x0163, 0x0183, 0x0072, 0x0164, 0x0184, 0x0165, 0x0185, + 0x0166, 0x0186, 0x0187, 0x1161, 0x0A86, 0x07B1, 0x11B1, 0x0801, 0x1201, + 0x0AD6, 0x0851, 0x1251, 0x0B76, 0x0BC6, 0x08A1, 0x12A1, 0x12F1, 0x0D52, + 0x0C66, 0x0D06, 0x0941, 0x1341, 0x0857, 0x0947, 0x1391, 0x0B27, 0x0AD7, + 0x09E1, 0x13E1, 0x1431, 0x1481, 0x0D07, 0x07B8, 0x14D1, 0x08A8, 0x0B21, + 0x1521, 0x0B71, 0x1571, 0x0BC1, 0x15C1, 0x0C18, 0x0C11, 0x1611, 0x0D08, + 0x1661, 0x16B1, 0x0D01, 0x1701, 0x0859, 0x0D51, 0x1751, 0x08F9, 0x0949, + 0x0762, 0x1162, 0x07B2, 0x11B2, 0x0B79, 0x0802, 0x1202, 0x1252, 0x12A2, + 0x0992, 0x1392, 0x1342, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x09E2, + 0x0000, 0x13E2, 0x0A32, 0x0000, 0x1432, 0x0A82, 0x0000, 0x1482, 0x0AD2, + 0x14D2, 0x0B22, 0x1522, 0x0B72, 0x1572, 0x0BC2, 0x15C2, 0x0C12, 0x1612, + 0x0C62, 0x1662, 0x0CB2, 0x16B2, 0x0D02, 0x1702, 0x1752, 0x0763, 0x1163, + 0x07B3, 0x11B3, 0x0803, 0x1203, 0x0853, 0x1253, 0x08A3, 0x12A3, 0x08F3, + 0x12F3, 0x0943, 0x1343, 0x0993, 0x1393, 0x09E3, 0x13E3, 0x1433, 0x0A83, + 0x0000, 0x1483, 0x0AD3, 0x14D3, 0x0991, 0x0000, 0x0B23, 0x1523, 0x0B73, + 0x1573, 0x0BC3, 0x15C3, 0x0C13, 0x1613, 0x0C63, 0x1663, 0x0CB3, 0x16B3, + 0x0D03, 0x1703, 0x0D53, 0x1753, 0x0764, 0x1164, 0x07B4, 0x11B4, 0x0804, + 0x1204, 0x0854, 0x1254, 0x08A4, 0x12A4, 0x08F4, 0x12F4, 0x0944, 0x1344, + 0x0994, 0x1394, 0x09E4, 0x13E4, 0x0A34, 0x1434, 0x0A84, 0x1484, 0x0AD4, + 0x14D4, 0x0AD1, 0x1524, 0x0B74, 0x1574, 0x0BC4, 0x15C4, 0x0C14, 0x1614, + 0x0C64, 0x1664, 0x0CB4, 0x16B4, 0x0D04, 0x1704, 0x0D54, 0x1754, 0x0765, + 0x1165, 0x07B5, 0x11B5, 0x1205, 0x1255, 0x12A5, 0x12F5, 0x1345, 0x1395, + 0x09E5, 0x0A35, 0x1435, 0x0A31, 0x0A85, 0x14D5, 0x1525, 0x0C19, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x1396, 0x13E6, 0x1436, 0x1486, 0x14D6, + 0x1526, 0x1576, 0x15C6, 0x1616, 0x1666, 0x16B6, 0x1706, 0x1756, 0x1167, + 0x11B7, 0x1207, 0x1257, 0x12A7, 0x12F7, 0x1347, 0x1397, 0x13E7, 0x1437, + 0x1487, 0x14D7, 0x1527, 0x1577, 0x15C7, 0x1617, 0x1667, 0x16B7, 0x1707, + 0x1757, 0x1168, 0x11B8, 0x1208, 0x1258, 0x12A8, 0x12F8, 0x1348, 0x1398, + 0x13E8, 0x1438, 0x1488, 0x14D8, 0x1528, 0x1578, 0x15C8, 0x1618, 0x1668, + 0x16B8, 0x1708, 0x1758, 0x1169, 0x11B9, 0x1209, 0x1259, 0x12A9, 0x12F9, + 0x1349, 0x1399, 0x13E9, 0x1439, 0x1489, 0x14D9, 0x1529, 0x1579, 0x15C9, + 0x1619, 0x1669, 0x16B9, 0x1709, 0x1759, 0x116A, 0x11BA, 0x120A, 0x125A, + 0x12AA, 0x12FA, 0x134A, 0x139A, 0x13EA, 0x143A, 0x148A, 0x14DA, 0x152A, + 0x157A, 0x15CA, 0x161A, 0x166A, 0x16BA, 0x170A, 0x175A, 0x116B, 0x11BB, + 0x120B, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x01F7, + 0x0000, 0x01F8, 0x01F9, 0x01FA, 0x0000, 0x0253, 0x0000, 0x0254, 0x0255, + 0x01D9, 0x01FC, 0x0257, 0x01FE, 0x01FF, 0x0200, 0x0201, 0x0202, 0x0258, + 0x0204, 0x02A7, 0x0206, 0x0207, 0x0208, 0x0209, 0x020A, 0x0299, 0x0248, + 0x0000, 0x02A9, 0x024B, 0x024C, 0x0298, 0x024E, 0x024F, 0x0250, 0x0251, + 0x0252, 0x0217, 0x0218, 0x0219, 0x021A, 0x021B, 0x021C, 0x021D, 0x021E, + 0x021F, 0x0220, 0x0221, 0x0222, 0x0223, 0x0224, 0x0225, 0x0226, 0x0227, + 0x0228, 0x0229, 0x022A, 0x0267, 0x0268, 0x0269, 0x026A, 0x026B, 0x026C, + 0x026D, 0x026E, 0x026F, 0x0270, 0x0271, 0x0272, 0x0273, 0x0274, 0x0275, + 0x0000, 0x0277, 0x0278, 0x0259, 0x025A, 0x0297, 0x02B8, 0x02B9, 0x02BA, + 0x0000, 0x02BB, 0x029C, 0x02BC, 0x029D, 0x02BD, 0x029E, 0x02BE, 0x029F, + 0x02BF, 0x02A0, 0x02C0, 0x02A1, 0x02C1, 0x02A2, 0x02C2, 0x02A3, 0x02C3, + 0x02A4, 0x02C4, 0x02A5, 0x02C5, 0x02A6, 0x02C6, 0x02C7, 0x02C8, 0x02C9, + 0x02CA, 0x0000, 0x0307, 0x0308, 0x0000, 0x0309, 0x0000, 0x0000, 0x030A, + 0x030B, 0x02EC, 0x02ED, 0x02EE, 0x0AF1, 0x0B41, 0x0B91, 0x0BE1, 0x0C31, + 0x0C81, 0x0CD1, 0x0D21, 0x0732, 0x0782, 0x07D2, 0x0822, 0x0872, 0x08C2, + 0x0912, 0x0962, 0x0730, 0x0780, 0x07D0, 0x0820, 0x0870, 0x08C0, 0x0910, + 0x0960, 0x09B0, 0x0A00, 0x0A50, 0x0AA0, 0x0AF0, 0x0B40, 0x0B90, 0x0BE0, + 0x0C30, 0x0C80, 0x0CD0, 0x0D20, 0x0731, 0x0781, 0x07D1, 0x0821, 0x0871, + 0x08C1, 0x0911, 0x0961, 0x09B1, 0x0A01, 0x0A51, 0x0AA1, 0x1130, 0x1180, + 0x11D0, 0x1220, 0x1270, 0x12C0, 0x1310, 0x1360, 0x13B0, 0x1400, 0x1450, + 0x14A0, 0x14F0, 0x1540, 0x1590, 0x15E0, 0x1630, 0x1680, 0x16D0, 0x1720, + 0x1131, 0x1181, 0x11D1, 0x1221, 0x1271, 0x12C1, 0x1311, 0x1361, 0x13B1, + 0x1401, 0x1451, 0x14A1, 0x14F1, 0x1541, 0x1591, 0x15E1, 0x1631, 0x1681, + 0x16D1, 0x1721, 0x1132, 0x1182, 0x11D2, 0x1222, 0x1272, 0x12C2, 0x1312, + 0x1362, 0x09B2, 0x13B2, 0x0A02, 0x1402, 0x0A52, 0x1452, 0x0AA2, 0x14A2, + 0x0AF2, 0x14F2, 0x0B42, 0x1542, 0x0B92, 0x1592, 0x0BE2, 0x15E2, 0x0C32, + 0x1632, 0x0C82, 0x1682, 0x0CD2, 0x16D2, 0x0D22, 0x1722, 0x0733, 0x1133, + 0x0783, 0x1183, 0x07D3, 0x11D3, 0x0823, 0x1223, 0x0873, 0x1273, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0913, 0x1313, + 0x0963, 0x1363, 0x09B3, 0x13B3, 0x0A03, 0x1403, 0x0A53, 0x1453, 0x0AA3, + 0x14A3, 0x0AF3, 0x14F3, 0x0B43, 0x1543, 0x0B93, 0x1593, 0x0BE3, 0x15E3, + 0x0C33, 0x1633, 0x0C83, 0x1683, 0x0CD3, 0x16D3, 0x0D23, 0x1723, 0x0734, + 0x1134, 0x0784, 0x1184, 0x07D4, 0x11D4, 0x0824, 0x1224, 0x0874, 0x1274, + 0x08C4, 0x12C4, 0x0914, 0x1314, 0x0964, 0x1364, 0x09B4, 0x13B4, 0x0A04, + 0x1404, 0x0A54, 0x1454, 0x0AA4, 0x14A4, 0x0AF4, 0x14F4, 0x0B44, 0x0B94, + 0x1594, 0x0BE4, 0x15E4, 0x0C34, 0x1634, 0x0C84, 0x1684, 0x0CD4, 0x16D4, + 0x0D24, 0x1724, 0x0735, 0x1135, 0x0000, 0x07D5, 0x11D5, 0x0825, 0x1225, + 0x0875, 0x1275, 0x08C5, 0x12C5, 0x0915, 0x1315, 0x0965, 0x1365, 0x09B5, + 0x13B5, 0x0A05, 0x1405, 0x0A55, 0x1455, 0x0AA5, 0x14A5, 0x0AF5, 0x14F5, + 0x0B45, 0x1545, 0x0B95, 0x1595, 0x0BE5, 0x15E5, 0x0C35, 0x1635, 0x0C85, + 0x1685, 0x0CD5, 0x16D5, 0x0D25, 0x1725, 0x0736, 0x1136, 0x0786, 0x1186, + 0x07D6, 0x11D6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0A06, + 0x1406, 0x0A56, 0x1456, 0x0AA6, 0x14A6, 0x0AF6, 0x14F6, 0x0B46, 0x1546, + 0x0B96, 0x1596, 0x0BE6, 0x15E6, 0x0C36, 0x1636, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0787, 0x07D7, 0x0827, 0x0877, 0x08C7, 0x0917, + 0x0967, 0x09B7, 0x0A07, 0x0A57, 0x0AA7, 0x0AF7, 0x0B47, 0x0B97, 0x0BE7, + 0x0C37, 0x0C87, 0x0CD7, 0x0D27, 0x0738, 0x0788, 0x07D8, 0x0828, 0x0878, + 0x08C8, 0x0918, 0x0968, 0x09B8, 0x0A08, 0x0A58, 0x0AA8, 0x0AF8, 0x0B48, + 0x0B98, 0x0BE8, 0x0C38, 0x0C88, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1187, 0x11D7, 0x1227, + 0x1277, 0x12C7, 0x1317, 0x1367, 0x13B7, 0x1407, 0x1457, 0x14A7, 0x14F7, + 0x1547, 0x1597, 0x15E7, 0x1637, 0x1687, 0x16D7, 0x1727, 0x1138, 0x1188, + 0x11D8, 0x1228, 0x1278, 0x12C8, 0x1318, 0x1368, 0x13B8, 0x1408, 0x1458, + 0x14A8, 0x14F8, 0x1548, 0x1598, 0x15E8, 0x1638, 0x1688, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/internal/tables/1E00_1FFF.go b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/internal/tables/1E00_1FFF.go new file mode 100644 index 0000000000..5523351398 --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/internal/tables/1E00_1FFF.go @@ -0,0 +1,68 @@ +// TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +// Copyright (C) 2017-2023 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 https://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. + +package tables + +var Uni1e001fff = []rune{ + 0x076C, 0x116C, 0x07BC, 0x11BC, 0x080C, 0x120C, 0x085C, 0x125C, 0x08AC, + 0x12AC, 0x08FC, 0x12FC, 0x094C, 0x134C, 0x099C, 0x139C, 0x09EC, 0x13EC, + 0x0A3C, 0x143C, 0x0A8C, 0x148C, 0x0ADC, 0x14DC, 0x0B2C, 0x152C, 0x0B7C, + 0x157C, 0x0BCC, 0x15CC, 0x0C1C, 0x161C, 0x0C6C, 0x166C, 0x0CBC, 0x16BC, + 0x0D0C, 0x170C, 0x0D5C, 0x175C, 0x076D, 0x116D, 0x07BD, 0x11BD, 0x080D, + 0x120D, 0x085D, 0x125D, 0x08AD, 0x12AD, 0x08FD, 0x12FD, 0x094D, 0x134D, + 0x099D, 0x139D, 0x09ED, 0x13ED, 0x0A3D, 0x143D, 0x0A8D, 0x148D, 0x0ADD, + 0x14DD, 0x0B2D, 0x152D, 0x0B7D, 0x157D, 0x0BCD, 0x15CD, 0x0C1D, 0x161D, + 0x0C6D, 0x166D, 0x0CBD, 0x16BD, 0x0D0D, 0x170D, 0x0D5D, 0x175D, 0x076E, + 0x116E, 0x07BE, 0x11BE, 0x080E, 0x120E, 0x085E, 0x125E, 0x08AE, 0x12AE, + 0x08FE, 0x12FE, 0x094E, 0x134E, 0x099E, 0x139E, 0x0770, 0x13EE, 0x0A3E, + 0x143E, 0x0A8E, 0x148E, 0x0ADE, 0x14DE, 0x0B2E, 0x152E, 0x0B7E, 0x157E, + 0x0BCE, 0x15CE, 0x0C1E, 0x161E, 0x0C6E, 0x166E, 0x0CBE, 0x16BE, 0x0D0E, + 0x170E, 0x0D5E, 0x175E, 0x076F, 0x116F, 0x07BF, 0x11BF, 0x080F, 0x120F, + 0x085F, 0x125F, 0x08AF, 0x12AF, 0x08FF, 0x12FF, 0x094F, 0x134F, 0x099F, + 0x139F, 0x09EF, 0x13EF, 0x0A3F, 0x143F, 0x0A8F, 0x148F, 0x0ADF, 0x14DF, + 0x0B2F, 0x152F, 0x0B7F, 0x157F, 0x0BCF, 0x15CF, 0x161F, 0x166F, 0x16BF, + 0x170F, 0x175F, 0x1170, 0x0000, 0x0000, 0x0000, 0x0000, 0x0900, 0x1300, + 0x0950, 0x1350, 0x09A0, 0x13A0, 0x09F0, 0x13F0, 0x0A40, 0x1440, 0x0A90, + 0x1490, 0x0AE0, 0x14E0, 0x0B30, 0x1530, 0x0B80, 0x1580, 0x0BD0, 0x15D0, + 0x0C20, 0x1620, 0x0C70, 0x1670, 0x0CC0, 0x16C0, 0x0D10, 0x1710, 0x0D60, + 0x1760, 0x0771, 0x1171, 0x07C1, 0x11C1, 0x0811, 0x1211, 0x0861, 0x1261, + 0x08B1, 0x12B1, 0x0901, 0x1301, 0x0951, 0x1351, 0x09A1, 0x13A1, 0x09F1, + 0x13F1, 0x0A41, 0x1441, 0x0A91, 0x1491, 0x0AE1, 0x14E1, 0x0B31, 0x1531, + 0x0B81, 0x1581, 0x0BD1, 0x15D1, 0x0C21, 0x1621, 0x0C71, 0x1671, 0x0CC1, + 0x16C1, 0x0D11, 0x1711, 0x0D61, 0x1761, 0x0772, 0x1172, 0x07C2, 0x11C2, + 0x0812, 0x1212, 0x0862, 0x1262, 0x08B2, 0x12B2, 0x0902, 0x1302, 0x0952, + 0x1352, 0x09A2, 0x13A2, 0x09F2, 0x13F2, 0x0A42, 0x1442, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x1173, 0x11C3, 0x1213, 0x1263, 0x12B3, + 0x1303, 0x1353, 0x13A3, 0x0773, 0x07C3, 0x0813, 0x0863, 0x08B3, 0x0903, + 0x0953, 0x09A3, 0x13F3, 0x1443, 0x1493, 0x14E3, 0x1533, 0x1583, 0x0000, + 0x0000, 0x09F3, 0x0A43, 0x0A93, 0x0AE3, 0x0B33, 0x0B83, 0x0000, 0x0000, + 0x1713, 0x1763, 0x1174, 0x11C4, 0x1214, 0x1264, 0x12B4, 0x1304, 0x0D13, + 0x0D63, 0x0774, 0x07C4, 0x0814, 0x0864, 0x08B4, 0x0904, 0x1354, 0x13A4, + 0x13F4, 0x1444, 0x1494, 0x14E4, 0x1534, 0x1584, 0x0954, 0x09A4, 0x09F4, + 0x0A44, 0x0A94, 0x0AE4, 0x0B34, 0x0B84, 0x15D4, 0x1624, 0x1674, 0x16C4, + 0x1714, 0x1764, 0x0000, 0x0000, 0x0BD4, 0x0C24, 0x0C74, 0x0CC4, 0x0D14, + 0x0D64, 0x0000, 0x0000, 0x12B5, 0x1305, 0x1355, 0x13A5, 0x13F5, 0x1445, + 0x1495, 0x14E5, 0x0000, 0x0905, 0x0000, 0x09A5, 0x0000, 0x0A45, 0x0000, + 0x0AE5, 0x1675, 0x16C5, 0x1715, 0x1765, 0x1176, 0x11C6, 0x1216, 0x1266, + 0x0C75, 0x0CC5, 0x0D15, 0x0D65, 0x0776, 0x07C6, 0x0816, 0x0866, 0x12B6, + 0x1306, 0x1356, 0x13A6, 0x13F6, 0x1446, 0x1496, 0x14E6, 0x1536, 0x1586, + 0x15D6, 0x1626, 0x1676, 0x16C6, 0x0000, 0x0000, 0x1177, 0x11C7, 0x1217, + 0x1267, 0x12B7, 0x1307, 0x1357, 0x13A7, 0x0777, 0x07C7, 0x0817, 0x0867, + 0x08B7, 0x0907, 0x0957, 0x09A7, 0x13F7, 0x1447, 0x1497, 0x14E7, 0x1537, + 0x1587, 0x15D7, 0x1627, 0x09F7, 0x0A47, 0x0A97, 0x0AE7, 0x0B37, 0x0B87, + 0x0BD7, 0x0C27, 0x1677, 0x16C7, 0x1717, 0x1767, 0x1178, 0x11C8, 0x1218, + 0x1268, 0x0C77, 0x0CC7, 0x0D17, 0x0D67, 0x0778, 0x07C8, 0x0818, 0x0868, + 0x12B8, 0x1308, 0x1358, 0x13A8, 0x13F8, 0x0000, 0x1498, 0x14E8, 0x08B8, + 0x0908, 0x08B6, 0x0906, 0x09A8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1538, 0x1588, 0x15D8, 0x0000, 0x1678, 0x16C8, 0x0956, 0x09A6, 0x09F6, + 0x0A46, 0x0B88, 0x0000, 0x0000, 0x0000, 0x1718, 0x1768, 0x1179, 0x11C9, + 0x0000, 0x0000, 0x12B9, 0x1309, 0x0D18, 0x0D68, 0x0A96, 0x0AE6, 0x0000, + 0x0000, 0x0000, 0x0000, 0x13A9, 0x13F9, 0x1449, 0x1499, 0x14E9, 0x1539, + 0x1589, 0x15D9, 0x09A9, 0x09F9, 0x0BD6, 0x0C26, 0x0B39, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x16C9, 0x1719, 0x0000, 0x0000, 0x11CA, 0x121A, + 0x0B36, 0x0B86, 0x0C76, 0x0CC6, 0x0D19, 0x0000, 0x0000, 0x0000} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/internal/tables/2160_217F.go b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/internal/tables/2160_217F.go new file mode 100644 index 0000000000..2349e38c1f --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/internal/tables/2160_217F.go @@ -0,0 +1,15 @@ +// TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +// Copyright (C) 2017-2023 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 https://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. + +package tables + +var Uni2160217f = []rune{ + 0x0739, 0x0789, 0x07D9, 0x0829, 0x0879, 0x08C9, 0x0919, 0x0969, + 0x09B9, 0x0A09, 0x0A59, 0x0AA9, 0x0AF9, 0x0B49, 0x0B99, 0x0BE9, + 0x1139, 0x1189, 0x11D9, 0x1229, 0x1279, 0x12C9, 0x1319, 0x1369, + 0x13B9, 0x1409, 0x1459, 0x14A9, 0x14F9, 0x1549, 0x1599, 0x15E9} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/internal/tables/24B0_24EF.go b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/internal/tables/24B0_24EF.go new file mode 100644 index 0000000000..ea6ccc9a30 --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/internal/tables/24B0_24EF.go @@ -0,0 +1,19 @@ +// TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +// Copyright (C) 2017-2023 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 https://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. + +package tables + +var Uni24b024ef = []rune{ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0511, 0x0512, + 0x0513, 0x0514, 0x0515, 0x0516, 0x0517, 0x0518, 0x0519, 0x051A, + 0x051B, 0x051C, 0x051D, 0x051E, 0x051F, 0x0520, 0x0521, 0x0522, + 0x0523, 0x0524, 0x0525, 0x0526, 0x0527, 0x0528, 0x0529, 0x052A, + 0x0531, 0x0532, 0x0533, 0x0534, 0x0535, 0x0536, 0x0537, 0x0538, + 0x0539, 0x053A, 0x053B, 0x053C, 0x053D, 0x053E, 0x053F, 0x0540, + 0x0541, 0x0542, 0x0543, 0x0544, 0x0545, 0x0546, 0x0547, 0x0548, + 0x0549, 0x054A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/internal/tables/FF20_FF5F.go b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/internal/tables/FF20_FF5F.go new file mode 100644 index 0000000000..7e69caa9e8 --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/internal/tables/FF20_FF5F.go @@ -0,0 +1,19 @@ +// TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +// Copyright (C) 2017-2023 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 https://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. + +package tables + +var UniFf20Ff5f = []rune{ + 0x0000, 0x0560, 0x05B0, 0x0600, 0x0650, 0x06A0, 0x06F0, 0x0740, + 0x0790, 0x07E0, 0x0830, 0x0880, 0x08D0, 0x0920, 0x0970, 0x09C0, + 0x0A10, 0x0A60, 0x0AB0, 0x0B00, 0x0B50, 0x0BA0, 0x0BF0, 0x0C40, + 0x0C90, 0x0CE0, 0x0D30, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0F60, 0x0FB0, 0x1000, 0x1050, 0x10A0, 0x10F0, 0x1140, + 0x1190, 0x11E0, 0x1230, 0x1280, 0x12D0, 0x1320, 0x1370, 0x13C0, + 0x1410, 0x1460, 0x14B0, 0x1500, 0x1550, 0x15A0, 0x15F0, 0x1640, + 0x1690, 0x16E0, 0x1730, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/internal/tables/Hex_Lo_Digit.go b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/internal/tables/Hex_Lo_Digit.go new file mode 100644 index 0000000000..d00f0ca63d --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/internal/tables/Hex_Lo_Digit.go @@ -0,0 +1,48 @@ +// TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +// Copyright (C) 2017-2023 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 https://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. + +package tables + +var hexLoDigit = []int{ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* ................ */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* ................ */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* !"#$%&'()*+,-./ */ + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, -1, -1, -1, -1, -1, -1, /* 0123456789:;<=>? */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* @ABCDEFGHIJKLMNO */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* PQRSTUVWXYZ[\]^_ */ + -1, 10, 11, 12, 13, 14, 15, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* `abcdefghijklmno */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* pqrstuvwxyz{|}~. */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* ................ */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* ................ */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* ................ */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* ................ */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* ................ */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* ................ */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* ................ */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* ................ */ +} + +func HexLo(x uint8) int { + return hexLoDigit[x] +} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/internal/tables/To_UNI.go b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/internal/tables/To_UNI.go new file mode 100644 index 0000000000..003e047d4d --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/internal/tables/To_UNI.go @@ -0,0 +1,677 @@ +// TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +// Copyright (C) 2017-2023 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 https://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. + +package tables + +var ToUni = []rune{ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00C0, 0x00C1, 0x00C2, 0x00C3, + 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, + 0x00CD, 0x00CE, 0x00CF, 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x00F0, + 0x00F1, 0x00F2, 0x00F3, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x00D4, 0x00D5, 0x00D6, 0x0000, 0x00D8, + 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x0178, 0x0100, 0x0102, + 0x0104, 0x0106, 0x0108, 0x010A, 0x010C, 0x010E, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x00F4, 0x00F5, 0x00F6, 0x00DF, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, + 0x00FD, 0x00FE, 0x00FF, 0x0101, 0x0103, 0x0105, 0x0107, 0x0109, 0x010B, + 0x010D, 0x010F, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0110, 0x0112, 0x0114, 0x0116, 0x0118, 0x011A, + 0x011C, 0x011E, 0x0120, 0x0122, 0x0124, 0x0126, 0x0128, 0x012A, 0x012C, + 0x012E, 0x0000, 0x0132, 0x0134, 0x0136, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0111, + 0x0113, 0x0115, 0x0117, 0x0119, 0x011B, 0x011D, 0x011F, 0x0121, 0x0123, + 0x0125, 0x0127, 0x0129, 0x012B, 0x012D, 0x012F, 0x0131, 0x0133, 0x0135, + 0x0137, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0139, 0x013B, 0x013D, 0x013F, 0x0141, 0x0143, + 0x0145, 0x0147, 0x0000, 0x014A, 0x014C, 0x014E, 0x0150, 0x0152, 0x0154, + 0x0156, 0x0158, 0x015A, 0x015C, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0138, 0x013A, + 0x013C, 0x013E, 0x0140, 0x0142, 0x0144, 0x0146, 0x0148, 0x0149, 0x014B, + 0x014D, 0x014F, 0x0151, 0x0153, 0x0155, 0x0157, 0x0159, 0x015B, 0x015D, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x015E, 0x0160, 0x0162, 0x0164, 0x0166, 0x0168, 0x016A, 0x016C, + 0x016E, 0x0170, 0x0172, 0x0174, 0x0176, 0x0179, 0x017B, 0x017D, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x015F, 0x0161, 0x0163, + 0x0165, 0x0167, 0x0169, 0x016B, 0x016D, 0x016F, 0x0171, 0x0173, 0x0175, + 0x0177, 0x017A, 0x017C, 0x017E, 0x017F, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0390, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0386, + 0x0388, 0x0389, 0x038A, 0x0000, 0x0391, 0x0000, 0x0393, 0x0394, 0x0395, + 0x0396, 0x0397, 0x0000, 0x0399, 0x0000, 0x039B, 0x039C, 0x039D, 0x039E, + 0x039F, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x03AC, 0x03AD, 0x03AE, 0x03AF, 0x03B0, + 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, + 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x03A1, + 0x0000, 0x0000, 0x03A4, 0x03A5, 0x0000, 0x03A7, 0x03A8, 0x03A9, 0x03AA, + 0x03AB, 0x038C, 0x038E, 0x038F, 0x0000, 0x0392, 0x0398, 0x03D2, 0x03D3, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, + 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, + 0x0000, 0x03D0, 0x03D1, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x03D4, 0x03A6, 0x03A0, + 0x0000, 0x0000, 0x03DA, 0x03DC, 0x03DE, 0x03E0, 0x03E2, 0x03E4, 0x03E6, + 0x03E8, 0x03EA, 0x03EC, 0x03EE, 0x039A, 0x0000, 0x03A3, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x03D5, 0x03D6, 0x03D7, 0x03D9, 0x03DB, 0x03DD, + 0x03DF, 0x03E1, 0x03E3, 0x03E5, 0x03E7, 0x03E9, 0x03EB, 0x03ED, 0x03EF, + 0x03F0, 0x03F1, 0x03F2, 0x03F3, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x03FD, 0x03FE, 0x03FF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x03F5, 0x03F6, 0x03F8, 0x03FB, 0x03FC, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x24B6, 0x24B7, 0x24B8, 0x24B9, 0x24BA, 0x24BB, 0x24BC, 0x24BD, + 0x24BE, 0x24BF, 0x24C0, 0x24C1, 0x24C2, 0x24C3, 0x24C4, 0x24C5, 0x24C6, + 0x24C7, 0x24C8, 0x24C9, 0x24CA, 0x24CB, 0x24CC, 0x24CD, 0x24CE, 0x24CF, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x24D0, 0x24D1, 0x24D2, + 0x24D3, 0x24D4, 0x24D5, 0x24D6, 0x24D7, 0x24D8, 0x24D9, 0x24DA, 0x24DB, + 0x24DC, 0x24DD, 0x24DE, 0x24DF, 0x24E0, 0x24E1, 0x24E2, 0x24E3, 0x24E4, + 0x24E5, 0x24E6, 0x24E7, 0x24E8, 0x24E9, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFF21, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFF22, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFF23, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFF24, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xFF25, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0xFF26, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0410, 0x0424, 0x0408, 0x0478, 0x04A6, + 0x04CD, 0x04F4, 0x0000, 0x0544, 0x2160, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0xFF27, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x01B3, 0x01DE, 0x0208, 0x0230, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x1E00, 0x1E28, 0x1E50, 0x1E78, 0x1E60, 0x1EBE, 0x1EE6, 0x1F08, + 0x1F2A, 0x0000, 0x1F6C, 0x1F88, 0x1FAC, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0411, 0x0425, 0x0409, 0x047A, 0x04A8, 0x0000, + 0x04F6, 0x0531, 0x0545, 0x2161, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0xFF28, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0182, 0x01B5, + 0x01E0, 0x020A, 0x0232, 0x0000, 0x0000, 0x019D, 0x0000, 0x0000, 0x0000, + 0x1E02, 0x1E2A, 0x1E52, 0x1E7A, 0x0000, 0x1EC0, 0x1EE8, 0x1F09, 0x1F2B, + 0x0000, 0x1F6D, 0x1F89, 0x1FAD, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0412, 0x0426, 0x040A, 0x047C, 0x04AA, 0x04D0, 0x04F8, + 0x0532, 0x0546, 0x2162, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xFF29, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0184, 0x01B8, 0x01E2, + 0x020C, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1E04, + 0x1E2C, 0x1E54, 0x1E7C, 0x0000, 0x1EC2, 0x1EEA, 0x1F0A, 0x1F2C, 0x0000, + 0x1F6E, 0x1F8A, 0x1FAE, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0413, 0x0427, 0x040B, 0x047E, 0x04AC, 0x04D2, 0x0000, 0x0533, + 0x0547, 0x2163, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFF2A, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0187, 0x0000, 0x01E4, 0x020E, + 0x0000, 0x0000, 0x0193, 0x0000, 0x01AE, 0x0000, 0x0000, 0x1E06, 0x1E2E, + 0x1E56, 0x1E7E, 0x0000, 0x1EC4, 0x1EEC, 0x1F0B, 0x1F2D, 0x0000, 0x1F6F, + 0x1F8B, 0x1FAF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0414, 0x0428, 0x040C, 0x0480, 0x04AE, 0x04D4, 0x0000, 0x0534, 0x0548, + 0x2164, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFF2B, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x018B, 0x0000, 0x01E6, 0x0210, 0x0000, + 0x0000, 0x0000, 0x019F, 0x0000, 0x0000, 0x0000, 0x1E08, 0x1E30, 0x1E58, + 0x1E80, 0x0000, 0x1EC6, 0x1EEE, 0x1F0C, 0x1F2E, 0x0000, 0x1FBA, 0x1F8C, + 0x1FB8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0415, + 0x0429, 0x040D, 0x0000, 0x04B0, 0x04D6, 0x0000, 0x0535, 0x0549, 0x2165, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFF2C, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x01E8, 0x0212, 0x0000, 0x0000, + 0x0000, 0x0000, 0x01B1, 0x0000, 0x0000, 0x1E0A, 0x1E32, 0x1E5A, 0x1E82, + 0x1EA0, 0x1EC8, 0x1EF0, 0x1F0D, 0x1F2F, 0x1F59, 0x1FBB, 0x1F8D, 0x1FB9, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0416, 0x042A, + 0x040E, 0x048A, 0x04B2, 0x04D8, 0x0000, 0x0536, 0x054A, 0x2166, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFF2D, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0191, 0x0000, 0x01EA, 0x0214, 0x0000, 0x0000, 0x0194, + 0x0000, 0x01B2, 0x0000, 0x0000, 0x1E0C, 0x1E34, 0x1E5C, 0x1E84, 0x1EA2, + 0x1ECA, 0x1EF2, 0x1F0E, 0x1F38, 0x0000, 0x1FC8, 0x1F8E, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0417, 0x042B, 0x040F, + 0x048C, 0x04B4, 0x04DA, 0x0000, 0x0537, 0x054B, 0x2167, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xFF2E, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x01F6, 0x01BC, 0x01EC, 0x0216, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x1E0E, 0x1E36, 0x1E5E, 0x1E86, 0x1EA4, 0x1ECC, + 0x1EF4, 0x1F0F, 0x1F39, 0x1F5B, 0x1FC9, 0x1F8F, 0x1FBC, 0x1FE8, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0418, 0x042C, 0x0460, 0x048E, + 0x04B6, 0x04DC, 0x0000, 0x0538, 0x054C, 0x2168, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0xFF2F, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0198, 0x01C4, 0x01EE, 0x0218, 0x023A, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x1E10, 0x1E38, 0x0000, 0x1E88, 0x1EA6, 0x1ECE, 0x1EF6, + 0x1F18, 0x1F3A, 0x0000, 0x1FCA, 0x1F98, 0x0000, 0x1FE9, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0419, 0x042D, 0x0462, 0x0490, 0x04B8, + 0x04DE, 0x0500, 0x0539, 0x054D, 0x2169, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0xFF30, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x023D, + 0x01C7, 0x0000, 0x021A, 0x023B, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x1E12, 0x1E3A, 0x1E62, 0x1E8A, 0x1EA8, 0x1ED0, 0x1EF8, 0x1F19, + 0x1F3B, 0x1F5D, 0x1FCB, 0x1F99, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x041A, 0x042E, 0x0464, 0x0492, 0x04BA, 0x04E0, + 0x0502, 0x053A, 0x054E, 0x216A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0xFF31, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x01CA, + 0x01F1, 0x021C, 0x023E, 0x0181, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1E14, 0x1E3C, 0x1E64, 0x1E8C, 0x1EAA, 0x1ED2, 0x0000, 0x1F1A, 0x1F3C, + 0x0000, 0x1FDA, 0x1F9A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x041B, 0x042F, 0x0466, 0x0494, 0x04BC, 0x04E2, 0x0504, + 0x053B, 0x054F, 0x216B, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xFF32, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0220, 0x01CD, 0x01F4, + 0x021E, 0x0000, 0x0186, 0x0197, 0x0000, 0x0000, 0x0000, 0x0000, 0x1E16, + 0x1E3E, 0x1E66, 0x1E8E, 0x1EAC, 0x1ED4, 0x0000, 0x1F1B, 0x1F3D, 0x1F5F, + 0x1FDB, 0x1F9B, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x041C, 0x0400, 0x0468, 0x0496, 0x04BE, 0x04E4, 0x0506, 0x053C, + 0x0550, 0x216C, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFF33, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x01A0, 0x01CF, 0x01F8, 0x0000, + 0x0000, 0x0000, 0x0196, 0x0000, 0x0000, 0x0000, 0x0000, 0x1E18, 0x1E40, + 0x1E68, 0x1E90, 0x1EAE, 0x1ED6, 0x0000, 0x1F1C, 0x1F3E, 0x0000, 0x1FF8, + 0x1F9C, 0x0000, 0x1FEC, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x041D, 0x0401, 0x046A, 0x0498, 0x04C0, 0x04E6, 0x0508, 0x053D, 0x0551, + 0x216D, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFF34, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x01A2, 0x01D1, 0x01FA, 0x0222, 0x0000, + 0x0189, 0x0000, 0x0000, 0x01B7, 0x0000, 0x0000, 0x1E1A, 0x1E42, 0x1E6A, + 0x1E92, 0x1EB0, 0x1ED8, 0x0000, 0x1F1D, 0x1F3F, 0x0000, 0x1FF9, 0x1F9D, + 0x1FCC, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x041E, + 0x0402, 0x046C, 0x049A, 0x04C1, 0x04E8, 0x050A, 0x053E, 0x0552, 0x216E, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFF35, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x01A4, 0x01D3, 0x01FC, 0x0224, 0x0000, 0x018A, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1E1C, 0x1E44, 0x1E6C, 0x1E94, + 0x1EB2, 0x1EDA, 0x0000, 0x0000, 0x1F48, 0x0000, 0x1FEA, 0x1F9E, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x041F, 0x0403, + 0x046E, 0x049C, 0x04C3, 0x04EA, 0x050C, 0x053F, 0x0553, 0x216F, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFF36, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x01A7, 0x01D5, 0x01FE, 0x0226, 0x0000, 0x0000, 0x0000, + 0x01A6, 0x0241, 0x0000, 0x0000, 0x1E1E, 0x1E46, 0x1E6E, 0x0000, 0x1EB4, + 0x1EDC, 0x0000, 0x0000, 0x1F49, 0x0000, 0x1FEB, 0x1F9F, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0420, 0x0404, 0x0470, + 0x049E, 0x04C5, 0x04EC, 0x050E, 0x0540, 0x0554, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xFF37, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x01D7, 0x0200, 0x0228, 0x0000, 0x018F, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x1E20, 0x1E48, 0x1E70, 0x0000, 0x1EB6, 0x1EDE, + 0x0000, 0x0000, 0x1F4A, 0x1F68, 0x1FFA, 0x1FA8, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0421, 0x0405, 0x0472, 0x04A0, + 0x04C7, 0x04EE, 0x0000, 0x0541, 0x0555, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0xFF38, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x01D9, 0x0202, 0x022A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x1E22, 0x1E4A, 0x1E72, 0x0000, 0x1EB8, 0x1EE0, 0x0000, + 0x0000, 0x1F4B, 0x1F69, 0x1FFB, 0x1FA9, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0422, 0x0406, 0x0474, 0x04A2, 0x04C9, + 0x04F0, 0x0000, 0x0542, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0xFF39, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x01AC, + 0x01DB, 0x0204, 0x022C, 0x0000, 0x0190, 0x019C, 0x01A9, 0x0000, 0x0000, + 0x0000, 0x1E24, 0x1E4C, 0x1E74, 0x0000, 0x1EBA, 0x1EE2, 0x0000, 0x1F28, + 0x1F4C, 0x1F6A, 0x0000, 0x1FAA, 0x1FD8, 0x1FFC, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0423, 0x0407, 0x0476, 0x04A4, 0x04CB, 0x04F2, + 0x0000, 0x0543, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0xFF3A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x01AF, 0x018E, + 0x0206, 0x022E, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1E26, 0x1E4E, 0x1E76, 0x0000, 0x1EBC, 0x1EE4, 0x0000, 0x1F29, 0x1F4D, + 0x1F6B, 0x0000, 0x1FAB, 0x1FD9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0xFF41, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0xFF42, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0xFF43, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xFF44, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFF45, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFF46, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0430, + 0x0444, 0x0458, 0x0479, 0x04A7, 0x04CE, 0x04F5, 0x0000, 0x0574, 0x2170, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFF47, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0180, 0x01B4, 0x01DF, 0x0209, 0x0231, 0x0000, + 0x025D, 0x0271, 0x0285, 0x0299, 0x02AD, 0x1E01, 0x1E29, 0x1E51, 0x1E79, + 0x1E9B, 0x1EBF, 0x1EE7, 0x1F00, 0x1F22, 0x0000, 0x1F64, 0x1F80, 0x1FA4, + 0x1FD2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0431, 0x0445, + 0x0459, 0x047B, 0x04A9, 0x0000, 0x04F7, 0x0561, 0x0575, 0x2171, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFF48, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0183, 0x01B6, 0x01E1, 0x020B, 0x0233, 0x0000, 0x025E, + 0x0272, 0x0286, 0x029A, 0x02AE, 0x1E03, 0x1E2B, 0x1E53, 0x1E7B, 0x0000, + 0x1EC1, 0x1EE9, 0x1F01, 0x1F23, 0x0000, 0x1F65, 0x1F81, 0x1FA5, 0x1FD3, + 0x1FF6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0432, 0x0446, 0x045A, + 0x047D, 0x04AB, 0x04D1, 0x04F9, 0x0562, 0x0576, 0x2172, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xFF49, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0185, 0x01B9, 0x01E3, 0x020D, 0x0234, 0x0000, 0x025F, 0x0273, + 0x0287, 0x029B, 0x02AF, 0x1E05, 0x1E2D, 0x1E55, 0x1E7D, 0x0000, 0x1EC3, + 0x1EEB, 0x1F02, 0x1F24, 0x0000, 0x1F66, 0x1F82, 0x1FA6, 0x0000, 0x1FF7, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0433, 0x0447, 0x045B, 0x047F, + 0x04AD, 0x04D3, 0x0000, 0x0563, 0x0577, 0x2173, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0xFF4A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0188, 0x01BA, 0x01E5, 0x020F, 0x0235, 0x0000, 0x0260, 0x0274, 0x0288, + 0x029C, 0x0000, 0x1E07, 0x1E2F, 0x1E57, 0x1E7F, 0x0000, 0x1EC5, 0x1EED, + 0x1F03, 0x1F25, 0x0000, 0x1F67, 0x1F83, 0x1FA7, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0434, 0x0448, 0x045C, 0x0481, 0x04AF, + 0x04D5, 0x0000, 0x0564, 0x0578, 0x2174, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0xFF4B, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x018C, + 0x01BB, 0x01E7, 0x0211, 0x0236, 0x0000, 0x0261, 0x0275, 0x0289, 0x029D, + 0x0000, 0x1E09, 0x1E31, 0x1E59, 0x1E81, 0x0000, 0x1EC7, 0x1EEF, 0x1F04, + 0x1F26, 0x1F50, 0x1F70, 0x1F84, 0x1FB0, 0x1FD6, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0435, 0x0449, 0x045D, 0x0000, 0x04B1, 0x04D7, + 0x0000, 0x0565, 0x0579, 0x2175, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0xFF4C, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x018D, 0x0000, + 0x01E9, 0x0213, 0x0237, 0x0000, 0x0262, 0x0276, 0x028A, 0x029E, 0x0000, + 0x1E0B, 0x1E33, 0x1E5B, 0x1E83, 0x1EA1, 0x1EC9, 0x1EF1, 0x1F05, 0x1F27, + 0x1F51, 0x1F71, 0x1F85, 0x1FB1, 0x1FD7, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0436, 0x044A, 0x045E, 0x048B, 0x04B3, 0x04D9, 0x0000, + 0x0566, 0x057A, 0x2176, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xFF4D, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0192, 0x01BE, 0x01EB, + 0x0215, 0x0238, 0x0000, 0x0263, 0x0277, 0x028B, 0x029F, 0x0000, 0x1E0D, + 0x1E35, 0x1E5D, 0x1E85, 0x1EA3, 0x1ECB, 0x1EF3, 0x1F06, 0x1F30, 0x1F52, + 0x1F72, 0x1F86, 0x1FB2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0437, 0x044B, 0x045F, 0x048D, 0x04B5, 0x04DB, 0x0000, 0x0567, + 0x057B, 0x2177, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFF4E, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0195, 0x01BD, 0x01ED, 0x0217, + 0x0239, 0x0250, 0x0264, 0x0278, 0x028C, 0x02A0, 0x0000, 0x1E0F, 0x1E37, + 0x1E5F, 0x1E87, 0x1EA5, 0x1ECD, 0x1EF5, 0x1F07, 0x1F31, 0x1F53, 0x1F73, + 0x1F87, 0x1FB3, 0x1FE0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0438, 0x044C, 0x0461, 0x048F, 0x04B7, 0x04DD, 0x0000, 0x0568, 0x057C, + 0x2178, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFF4F, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0199, 0x01C6, 0x01EF, 0x0219, 0x0000, + 0x0251, 0x0265, 0x0279, 0x028D, 0x02A1, 0x0000, 0x1E11, 0x1E39, 0x1E61, + 0x1E89, 0x1EA7, 0x1ECF, 0x1EF7, 0x1F10, 0x1F32, 0x1F54, 0x1F74, 0x1F90, + 0x1FB4, 0x1FE1, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0439, + 0x044D, 0x0463, 0x0491, 0x04B9, 0x04DF, 0x0501, 0x0569, 0x057D, 0x2179, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFF50, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x019A, 0x01C9, 0x01F0, 0x021B, 0x023C, 0x0252, + 0x0266, 0x027A, 0x028E, 0x02A2, 0x0000, 0x1E13, 0x1E3B, 0x1E63, 0x1E8B, + 0x1EA9, 0x1ED1, 0x1EF9, 0x1F11, 0x1F33, 0x1F55, 0x1F75, 0x1F91, 0x0000, + 0x1FE2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x043A, 0x044E, + 0x0465, 0x0493, 0x04BB, 0x04E1, 0x0503, 0x056A, 0x057E, 0x217A, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFF51, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x019B, 0x01CC, 0x01F3, 0x021D, 0x0000, 0x0253, 0x0267, + 0x027B, 0x028F, 0x02A3, 0x0000, 0x1E15, 0x1E3D, 0x1E65, 0x1E8D, 0x1EAB, + 0x1ED3, 0x0000, 0x1F12, 0x1F34, 0x1F56, 0x1F76, 0x1F92, 0x1FB6, 0x1FE3, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x043B, 0x044F, 0x0467, + 0x0495, 0x04BD, 0x04E3, 0x0505, 0x056B, 0x057F, 0x217B, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xFF52, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x019E, 0x01CE, 0x01F5, 0x021F, 0x023F, 0x0254, 0x0268, 0x027C, + 0x0290, 0x02A4, 0x0000, 0x1E17, 0x1E3F, 0x1E67, 0x1E8F, 0x1EAD, 0x1ED5, + 0x0000, 0x1F13, 0x1F35, 0x1F57, 0x1F77, 0x1F93, 0x1FB7, 0x1FE4, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x043C, 0x0450, 0x0469, 0x0497, + 0x04BF, 0x04E5, 0x0507, 0x056C, 0x0580, 0x217C, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0xFF53, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x01A1, 0x01D0, 0x01F9, 0x0221, 0x0240, 0x0255, 0x0269, 0x027D, 0x0291, + 0x02A5, 0x0000, 0x1E19, 0x1E41, 0x1E69, 0x1E91, 0x1EAF, 0x1ED7, 0x0000, + 0x1F14, 0x1F36, 0x0000, 0x1F78, 0x1F94, 0x1FC2, 0x1FE5, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x043D, 0x0451, 0x046B, 0x0499, 0x0000, + 0x04E7, 0x0509, 0x056D, 0x0581, 0x217D, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0xFF54, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x01A3, + 0x01D2, 0x01FB, 0x0223, 0x0000, 0x0256, 0x026A, 0x027E, 0x0292, 0x02A6, + 0x0000, 0x1E1B, 0x1E43, 0x1E6B, 0x1E93, 0x1EB1, 0x1ED9, 0x0000, 0x1F15, + 0x1F37, 0x0000, 0x1F79, 0x1F95, 0x1FC3, 0x1FE6, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x043E, 0x0452, 0x046D, 0x049B, 0x04C2, 0x04E9, + 0x050B, 0x056E, 0x0582, 0x217E, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0xFF55, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x01A5, 0x01D4, + 0x01FD, 0x0225, 0x0000, 0x0257, 0x026B, 0x027F, 0x0293, 0x02A7, 0x0000, + 0x1E1D, 0x1E45, 0x1E6D, 0x1E95, 0x1EB3, 0x1EDB, 0x0000, 0x0000, 0x1F40, + 0x0000, 0x1F7A, 0x1F96, 0x1FC4, 0x1FE7, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x043F, 0x0453, 0x046F, 0x049D, 0x04C4, 0x04EB, 0x050D, + 0x056F, 0x0583, 0x217F, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xFF56, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x01A8, 0x01D6, 0x01FF, + 0x0227, 0x0000, 0x0258, 0x026C, 0x0280, 0x0294, 0x02A8, 0x0000, 0x1E1F, + 0x1E47, 0x1E6F, 0x1E96, 0x1EB5, 0x1EDD, 0x0000, 0x0000, 0x1F41, 0x0000, + 0x1F7B, 0x1F97, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0440, 0x0454, 0x0471, 0x049F, 0x04C6, 0x04ED, 0x050F, 0x0570, + 0x0584, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFF57, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x01AA, 0x01D8, 0x0201, 0x0229, + 0x0000, 0x0259, 0x026D, 0x0281, 0x0295, 0x02A9, 0x0000, 0x1E21, 0x1E49, + 0x1E71, 0x1E97, 0x1EB7, 0x1EDF, 0x0000, 0x0000, 0x1F42, 0x1F60, 0x1F7C, + 0x1FA0, 0x1FC6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0441, 0x0455, 0x0473, 0x04A1, 0x04C8, 0x04EF, 0x0000, 0x0571, 0x0585, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFF58, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x01AB, 0x01DA, 0x0203, 0x022B, 0x0000, + 0x025A, 0x026E, 0x0282, 0x0296, 0x02AA, 0x0000, 0x1E23, 0x1E4B, 0x1E73, + 0x1E98, 0x1EB9, 0x1EE1, 0x0000, 0x0000, 0x1F43, 0x1F61, 0x1F7D, 0x1FA1, + 0x1FC7, 0x1FF2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0442, + 0x0456, 0x0475, 0x04A3, 0x04CA, 0x04F1, 0x0000, 0x0572, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFF59, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x01AD, 0x01DC, 0x0205, 0x022D, 0x0000, 0x025B, + 0x026F, 0x0283, 0x0297, 0x02AB, 0x0000, 0x1E25, 0x1E4D, 0x1E75, 0x1E99, + 0x1EBB, 0x1EE3, 0x0000, 0x1F20, 0x1F44, 0x1F62, 0x0000, 0x1FA2, 0x1FD0, + 0x1FF3, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0443, 0x0457, + 0x0477, 0x04A5, 0x04CC, 0x04F3, 0x0000, 0x0573, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFF5A, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x01B0, 0x01DD, 0x0207, 0x022F, 0x0000, 0x025C, 0x0270, + 0x0284, 0x0298, 0x02AC, 0x0000, 0x1E27, 0x1E4F, 0x1E77, 0x1E9A, 0x1EBD, + 0x1EE5, 0x0000, 0x1F21, 0x1F45, 0x1F63, 0x0000, 0x1FA3, 0x1FD1, 0x1FF4} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/internal/tables/init.go b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/internal/tables/init.go new file mode 100644 index 0000000000..a454e58737 --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/internal/tables/init.go @@ -0,0 +1,32 @@ +// TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +// Copyright (C) 2017-2023 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 https://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. + +package tables + +/* + File system encoding components: + +Code range Pattern Number Used Unused Blocks +----------------------------------------------------------------------------- +00C0..017F [.][0..4][g..z] 5*20= 100 97 3 Latin1 Supplement + Ext A +0370..03FF [.][5..9][g..z] 5*20= 100 88 12 Greek + Coptic +0400..052F [.][g..z][0..6] 20*7= 140 140 137 Cyrillic +0530..058F [.][g..z][7..8] 20*2= 40 38 2 Armenian +2160..217F [.][g..z][9] 20*1= 20 16 4 Number Forms +0180..02AF [.][g..z][a..k] 28*11=220 203 17 Latin Ext B + IPA +1E00..0EFF [.][g..z][l..r] 20*7= 140 136 4 Latin Additional Extended +1F00..1FFF [.][g..z][s..z] 20*8= 160 144 16 Greek Extended +.... .... [.][a..f][g..z] 6*20= 120 0 120 RESERVED +24B6..24E9 [.][@][a..z] 26 26 0 Enclosed Alphanumerics +FF21..FF5A [.][a..z][@] 26 26 0 Full Width forms + +All other characters are encoded using five bytes: + +[.][0..9a..z][0..9a..z][0..9a..z][0..9a..z] + +*/ diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/internal/tables/safe_char.go b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/internal/tables/safe_char.go new file mode 100644 index 0000000000..9cccd74c9b --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/internal/tables/safe_char.go @@ -0,0 +1,20 @@ +// TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +// Copyright (C) 2017-2023 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 https://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. + +package tables + +var SafeChar = []bool{ + true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, /* ................ */ + false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, /* ................ */ + false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, /* !"#$%&'()*+,-./ */ + true, true, true, true, true, true, true, true, true, true, false, false, false, false, false, false, /* 0123456789:;<=>? */ + false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, /* @ABCDEFGHIJKLMNO */ + true, true, true, true, true, true, true, true, true, true, true, false, false, false, false, true, /* PQRSTUVWXYZ[\]^_ */ + false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, /* `abcdefghijklmno */ + true, true, true, true, true, true, true, true, true, true, true, false, false, false, false, false, /* pqrstuvwxyz{|}~. */ +} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/tablename_to_filename.go b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/tablename_to_filename.go new file mode 100644 index 0000000000..0779fbd66b --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/tablename_to_filename.go @@ -0,0 +1,50 @@ +// TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +// Copyright (C) 2017-2023 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 https://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. + +package identifiertrans + +import ( + "dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/identifiertrans/internal/tables" + "fmt" +) + +func TablenameToFilename(tablename string) (filename string) { + for _, c := range tablename { + if c < 128 && tables.SafeChar[c] { + filename += string(c) + continue + } + + filename += "@" + + if (c >= 0x00C0 && c <= 0x05FF) || + (c >= 0x1E00 && c <= 0x1FFF) || + (c >= 0x2160 && c <= 0x217F) || + (c >= 0x24B0 && c <= 0x24EF) || + (c >= 0xFF20 && c <= 0xFF5F) { + var code rune + if c >= 0x00C0 && c <= 0x05FF { + code = tables.Uni0c0005ff[c-0x00C0] + } else if c >= 0x1E00 && c <= 0x1FFF { + code = tables.Uni1e001fff[c-0x1E00] + } else if c >= 0x2160 && c <= 0x217F { + code = tables.Uni2160217f[c-0x2160] + } else if c >= 0x24B0 && c <= 0x24EF { + code = tables.Uni24b024ef[c-0x24B0] + } else if c >= 0xFF20 && c <= 0xFF5F { + code = tables.UniFf20Ff5f[c-0xFF20] + } + filename += string(code/80 + 0x30) + filename += string(code%80 + 0x30) + continue + } + + filename += fmt.Sprintf("%04x", c) + } + return filename +} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/mysql_dumper.go b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/mysql_dumper.go index 54e71b3e4b..05b35d5de8 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/mysql_dumper.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/mysql_dumper.go @@ -26,15 +26,15 @@ type Dumper interface { type MySQLDumpOption struct { /* DumpSchema bool DumpData bool */ - NoData bool - AddDropTable bool // 默认 false 代表添加 --skip-add-drop-table 选项 - NeedUseDb bool - NoCreateDb bool - NoCreateTb bool - DumpRoutine bool // 默认 false 代表添加不导出存储过程,True导出存储过程 - DumpTrigger bool // 默认 false 代表添加不导出触发器 - DumpEvent bool // 默认 false 导出 event - + NoData bool + AddDropTable bool // 默认 false 代表添加 --skip-add-drop-table 选项 + NeedUseDb bool + NoCreateDb bool + NoCreateTb bool + DumpRoutine bool // 默认 false 代表添加不导出存储过程,True导出存储过程 + DumpTrigger bool // 默认 false 代表添加不导出触发器 + DumpEvent bool // 默认 false 导出 event + GtidPurgedOff bool // --set-gtid-purged=OFF } type runtimectx struct { @@ -238,6 +238,9 @@ func (m *MySQLDumper) getDumpCmd(dbName, outputFile, errFile, dumpOption string) if m.DumpEvent { dumpOption += " --events" } + if m.GtidPurgedOff { + dumpOption += " --set-gtid-purged=OFF" + } dumpCmd = fmt.Sprintf( `%s -h%s diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/mysql_os.go b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/mysql_os.go index 6759479c67..629a1a3d43 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/mysql_os.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/mysql_os.go @@ -71,40 +71,15 @@ func IsSudo() bool { return false } -// InnodbDataFilePathValue TODO -func InnodbDataFilePathValue(value string) string { - result := regexp.MustCompile(`(\d+)(m|M|g|G)`).FindStringSubmatch(value) - if len(result) > 0 && - regexp.MustCompile("(?i)M").MatchString(result[2]) { - return fmt.Sprintf("%sM:autoextend\n", result[1]) - } else if len(result) > 0 && - regexp.MustCompile("(?i)G").MatchString(result[2]) { - size, err := strconv.Atoi(result[1]) - if err != nil { - logger.Info("%s convert to int get an error:%s", result[1], err.Error()) - return "" - } - var ( - ibDataStr = "" - index = 1 - ) - for ; size > 10; size -= 10 { - ibDataStr += fmt.Sprintf("ibdata%d:10G;", index) - index++ - } - ibDataStr += fmt.Sprintf("ibdata%d:%dG:autoextend", index, size) - return ibDataStr - } - - return "" -} +// return "" +// } // MySQLVersionParse (): // input: select version() 获取到的string // output: 获取tmysql中的mysql前缀版本 // example: // 5.7.20-tmysql-3.1.5-log ==> 5*1000000 + 7*1000 + 20 ==> 5007020 -// MySQL5.1.13 ==> 5*1000000+1*1000+13 ==> 5001013 +// 5.1.13 ==> 5*1000000+1*1000+13 ==> 5001013 func MySQLVersionParse(version string) uint64 { re := regexp.MustCompile(`([\d]+).?([\d]+)?.?([\d]+)?`) return mysqlVersionParse(re, version) diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/mysql_os_test.go b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/mysql_os_test.go index 0c1a131e94..b8226697c5 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/mysql_os_test.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/mysql_os_test.go @@ -17,5 +17,5 @@ func TestMySQLVersionParse(t *testing.T) { } func TestMajorVersion(t *testing.T) { - t.Logf("major Version:%s", mysqlutil.GetMajorVersion(10003007)) + t.Logf("major Version:%s", mysqlutil.GetMajorVersion(5006024)) } diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/mysqlclient_exec.go b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/mysqlclient_exec.go index 2cb57c5627..eae3936bd2 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/mysqlclient_exec.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/mysqlclient_exec.go @@ -1,20 +1,31 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package mysqlutil import ( + "bytes" + "context" "database/sql" "fmt" "io" "os" "os/exec" "path" - "regexp" + "strings" "sync" "time" "dbm-services/common/go-pubpkg/logger" "dbm-services/mysql/db-tools/dbactuator/pkg/core/cst" "dbm-services/mysql/db-tools/dbactuator/pkg/util" - "dbm-services/mysql/db-tools/dbactuator/pkg/util/osutil" ) // ExecuteSqlAtLocal TODO @@ -54,7 +65,7 @@ func (e ExecuteSqlAtLocal) CreateLoadSQLCommand() (command string) { if util.StrIsEmpty(e.Socket) { return fmt.Sprintf( `%s %s --safe_updates=0 -u %s %s -h%s -P %d %s -vvv `, - mysqlclient, forceStr, e.User, passwd, e.Host, e.Port, e.Charset, + mysqlclient, forceStr, e.User, passwd, e.Host, e.Port, connCharset, ) } return fmt.Sprintf( @@ -92,30 +103,29 @@ func (e ExecuteSqlAtLocal) ExcuteSqlByMySQLClientOne(sqlfile string, db string) // ExcuteCommand TODO func (e ExecuteSqlAtLocal) ExcuteCommand(command string) (err error) { + var stderrBuf bytes.Buffer var errStdout, errStderr error logger.Info("The Command Is %s", ClearSensitiveInformation(command)) cmd := exec.Command("/bin/bash", "-c", command) stdoutIn, _ := cmd.StdoutPipe() stderrIn, _ := cmd.StderrPipe() - stdout := osutil.NewCapturingPassThroughWriter(os.Stdout) - stderr := osutil.NewCapturingPassThroughWriter(os.Stderr) - defer func() { - // 写入error 文件 - ef, errO := os.OpenFile(e.ErrFile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) - defer ef.Close() - if errO != nil { - logger.Warn("打开日志时失败! %s", errO.Error()) - return - } - _, errW := ef.Write(stderr.Bytes()) - if errW != nil { - logger.Warn("写错误日志时失败! %s", err.Error()) - } - }() + + // 写入error 文件 + ef, errO := os.OpenFile(e.ErrFile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) + if errO != nil { + logger.Warn("打开日志时失败! %s", errO.Error()) + return + } + defer ef.Close() + defer ef.Sync() + stdout := io.MultiWriter(os.Stdout) + stderr := io.MultiWriter(os.Stderr, &stderrBuf, ef) + if err = cmd.Start(); err != nil { logger.Error("start command failed:%s", err.Error()) return } + var wg sync.WaitGroup wg.Add(1) @@ -127,24 +137,17 @@ func (e ExecuteSqlAtLocal) ExcuteCommand(command string) (err error) { _, errStderr = io.Copy(stderr, stderrIn) wg.Wait() - if err = cmd.Wait(); err != nil { - logger.Error("cmd.wait failed:%s", err.Error()) - return - } - if errStdout != nil || errStderr != nil { logger.Error("failed to capture stdout or stderr\n") return } - outStr, errStr := string(stdout.Bytes()), string(stderr.Bytes()) - re, err := regexp.Compile(`((?i)\s*error\s+\d+)|No such file or directory`) - if err != nil { - return err - } - logger.Info("outstr:%s,errstr:%s", outStr, errStr) - if re.MatchString(outStr + errStr) { // @todo 这里的写法不够细致,可能匹配表结构里的关键字 - return fmt.Errorf("执行sql的输出含有error") + + if err = cmd.Wait(); err != nil { + errStr := string(stderrBuf.Bytes()) + logger.Error("exec failed:%s,stderr: %s", err.Error(), errStr) + return } + return nil } @@ -156,7 +159,14 @@ func (e ExecuteSqlAtLocal) ExcutePartitionByMySQLClient( logger.Info("The partitionsql is %s", ClearSensitiveInformation(partitionsql)) err = util.Retry( util.RetryConfig{Times: 2, DelayTime: 2 * time.Second}, func() error { - _, err = dbw.Exec(partitionsql) + db, err := dbw.Conn(context.Background()) + if err != nil { + return err + } + partitionsqls := strings.Split(partitionsql, ";;;") + for _, psql := range partitionsqls { + _, err = db.ExecContext(context.Background(), psql) + } return err }, ) diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/mysqlclient_exec_test.go b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/mysqlclient_exec_test.go index c5e4d521af..0327890311 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/mysqlclient_exec_test.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/mysqlclient_exec_test.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package mysqlutil_test import ( diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/mysqlutil.go b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/mysqlutil.go index 8e5a9dcdcb..7098e61d22 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/mysqlutil.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/util/mysqlutil/mysqlutil.go @@ -1,2 +1,33 @@ // Package mysqlutil TODO package mysqlutil + +import ( + "bytes" + "fmt" + "os/exec" + "regexp" + "strings" + + "github.com/pkg/errors" +) + +// ExecCommandMySQLShell 执行 mysql / mysqladmin 专用shell +// 会移除 password insecure 信息 +func ExecCommandMySQLShell(param string) (stdoutStr string, err error) { + cmd := exec.Command("bash", "-c", param) + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + err = cmd.Run() + if err != nil { + return stderr.String(), errors.WithMessage(err, stderr.String()) + } + errStr := stderr.String() + reg := regexp.MustCompile(`(?U)\n?.*Using a password on the command line interface can be insecure.`) + errStr = strings.TrimSpace(reg.ReplaceAllString(errStr, "")) + if len(errStr) > 0 { + err = fmt.Errorf("execute command(%s) has stderr:%s", ClearSensitiveInformation(param), errStr) + return errStr, err + } + return stdout.String(), nil +} diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/util/osutil/cmdexec.go b/dbm-services/mysql/db-tools/dbactuator/pkg/util/osutil/cmdexec.go index 3adea59f9e..fb7c0854ac 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/util/osutil/cmdexec.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/util/osutil/cmdexec.go @@ -141,10 +141,9 @@ func ExecShellCommand(isSudo bool, param string) (stdoutStr string, err error) { } if len(stderr.String()) > 0 { - err = fmt.Errorf("execute shell command(%s) error:%s", param, stderr.String()) + err = fmt.Errorf("execute shell command(%s) has stderr:%s", param, stderr.String()) return stderr.String(), err } - return stdout.String(), nil } diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/util/osutil/osutil.go b/dbm-services/mysql/db-tools/dbactuator/pkg/util/osutil/osutil.go index f65b7657d9..352872d057 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/util/osutil/osutil.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/util/osutil/osutil.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + // Package osutil TODO package osutil @@ -619,31 +629,6 @@ func PrintFileSizeIncr( } } -// CapturingPassThroughWriter is a writer that remembers -// data written to it and passes it to w -type CapturingPassThroughWriter struct { - buf bytes.Buffer - w io.Writer -} - -// NewCapturingPassThroughWriter creates new CapturingPassThroughWriter -func NewCapturingPassThroughWriter(w io.Writer) *CapturingPassThroughWriter { - return &CapturingPassThroughWriter{ - w: w, - } -} - -// Write 用于常见IO -func (w *CapturingPassThroughWriter) Write(d []byte) (int, error) { - w.buf.Write(d) - return w.w.Write(d) -} - -// Bytes returns bytes written to the writer -func (w *CapturingPassThroughWriter) Bytes() []byte { - return w.buf.Bytes() -} - // ReadFileString TODO func ReadFileString(filename string) (string, error) { if body, err := os.ReadFile(filename); err != nil { diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/util/slice.go b/dbm-services/mysql/db-tools/dbactuator/pkg/util/slice.go index 32e9926704..5812ef6cac 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/util/slice.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/util/slice.go @@ -10,9 +10,9 @@ import ( "github.com/pkg/errors" ) -// IntsHas check the []int contains the given value -func IntsHas(ints []int, val int) bool { - for _, ele := range ints { +// 判断val 是否在elems 中 +func ContainElem[T int | int64 | string](elems []T, val T) bool { + for _, ele := range elems { if ele == val { return true } @@ -20,24 +20,19 @@ func IntsHas(ints []int, val int) bool { return false } +// IntsHas check the []int contains the given value +func IntsHas(ints []int, val int) bool { + return ContainElem(ints, val) +} + // Int64sHas check the []int64 contains the given value func Int64sHas(ints []int64, val int64) bool { - for _, ele := range ints { - if ele == val { - return true - } - } - return false + return ContainElem(ints, val) } // StringsHas check the []string contains the given element func StringsHas(ss []string, val string) bool { - for _, ele := range ss { - if ele == val { - return true - } - } - return false + return ContainElem(ss, val) } // StringsHasICase check the []string contains the given element. insensitive case @@ -51,36 +46,28 @@ func StringsHasICase(ss []string, val string) bool { return false } -// UniqueStrings Returns unique items in a slice -func UniqueStrings(slice []string) []string { - // create a map with all the values as key - uniqMap := make(map[string]struct{}) +func UniqueSlice[T string | int](slice []T) []T { + uniqMap := make(map[T]struct{}) for _, v := range slice { uniqMap[v] = struct{}{} } // turn the map keys into a slice - uniqSlice := make([]string, 0, len(uniqMap)) + uniqSlice := make([]T, 0, len(uniqMap)) for v := range uniqMap { uniqSlice = append(uniqSlice, v) } return uniqSlice } +// UniqueStrings Returns unique items in a slice +func UniqueStrings(slice []string) []string { + return UniqueSlice(slice) +} + // UniqueInts Returns unique items in a slice func UniqueInts(slice []int) []int { - // create a map with all the values as key - uniqMap := make(map[int]struct{}) - for _, v := range slice { - uniqMap[v] = struct{}{} - } - - // turn the map keys into a slice - uniqSlice := make([]int, 0, len(uniqMap)) - for v := range uniqMap { - uniqSlice = append(uniqSlice, v) - } - return uniqSlice + return UniqueSlice(slice) } // IsConsecutiveStrings 是否是连续数字 @@ -202,7 +189,7 @@ func StringsInsertIndex(ss []string, index int, new string) []string { // @return dst func FilterOutStringSlice(src []string, filters []string) (dst []string) { for _, v := range src { - if !StringsHas(filters, v) { + if !ContainElem(filters, v) { dst = append(dst, v) } } diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/util/templates/cmd_groups.go b/dbm-services/mysql/db-tools/dbactuator/pkg/util/templates/cmd_groups.go index e153e2ee69..827e6185f2 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/util/templates/cmd_groups.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/util/templates/cmd_groups.go @@ -1,3 +1,13 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + package templates import ( diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/util/util.go b/dbm-services/mysql/db-tools/dbactuator/pkg/util/util.go index f78e1abfbe..bcce9daf9e 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/util/util.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/util/util.go @@ -8,9 +8,7 @@ import ( "io" "math/rand" "net" - "net/url" "os" - "path" "reflect" "regexp" "runtime" @@ -313,16 +311,6 @@ func GetFileModifyTime(filename string) (bool, int64) { return false, 0 } -// UrlJoinPath utl.JoinPath go1.919 -func UrlJoinPath(p, subPath string) (string, error) { - u, err := url.Parse(p) - if err != nil { - return "", err - } - u.Path = path.Join(u.Path, subPath) - return u.String(), nil -} - // FileIsEmpty TODO func FileIsEmpty(path string) error { fileInfo, err := os.Stat(path) diff --git a/dbm-services/mysql/db-tools/mysql-crond/cmd/root.go b/dbm-services/mysql/db-tools/mysql-crond/cmd/root.go index f3f011a61a..5b799f4890 100644 --- a/dbm-services/mysql/db-tools/mysql-crond/cmd/root.go +++ b/dbm-services/mysql/db-tools/mysql-crond/cmd/root.go @@ -6,7 +6,6 @@ import ( "os" "path" "sync" - "time" "dbm-services/mysql/db-tools/mysql-crond/pkg/config" "dbm-services/mysql/db-tools/mysql-crond/pkg/crond" @@ -62,8 +61,8 @@ var rootCmd = &cobra.Command{ go func() { <-quit - time.Sleep(10 * time.Second) - crond.Stop() + //time.Sleep(10 * time.Second) + //crond.Stop() slog.Info("quit mysql-crond") os.Exit(0) }() diff --git a/dbm-services/mysql/db-tools/mysql-crond/cmd/subcmd_add.go b/dbm-services/mysql/db-tools/mysql-crond/cmd/subcmd_add.go index 1c37e7f304..5f46fd0206 100644 --- a/dbm-services/mysql/db-tools/mysql-crond/cmd/subcmd_add.go +++ b/dbm-services/mysql/db-tools/mysql-crond/cmd/subcmd_add.go @@ -16,7 +16,7 @@ import ( var addJobCmd = &cobra.Command{ Use: "addJob", Short: "add crond entry", - Long: `add crond entry`, + Long: `add crond entry, permanent`, RunE: func(cmd *cobra.Command, args []string) error { var jobEntry api.JobDefine if body, _ := cmd.Flags().GetString("body"); body != "" { diff --git a/dbm-services/mysql/db-tools/mysql-crond/cmd/subcmd_del.go b/dbm-services/mysql/db-tools/mysql-crond/cmd/subcmd_del.go new file mode 100644 index 0000000000..03bda6248f --- /dev/null +++ b/dbm-services/mysql/db-tools/mysql-crond/cmd/subcmd_del.go @@ -0,0 +1,73 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package cmd + +import ( + "encoding/json" + "fmt" + "os" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + + "dbm-services/mysql/db-tools/mysql-crond/api" + "dbm-services/mysql/db-tools/mysql-crond/pkg/config" +) + +// versionCmd represents the version command +var delJobCmd = &cobra.Command{ + Use: "delJob", + Short: "del crond entry", + Long: `del crond entry`, + RunE: func(cmd *cobra.Command, args []string) error { + var jobEntry api.JobDefine + if body, _ := cmd.Flags().GetString("body"); body != "" { + if err := json.Unmarshal([]byte(body), &jobEntry); err != nil { + return err + } + } else { + jobName, _ := cmd.Flags().GetString("name") + jobEntry = api.JobDefine{ + Name: jobName, + } + } + return delEntry(jobEntry) + }, +} + +func init() { + delJobCmd.PersistentFlags().StringP("config", "c", "", "config file") + _ = delJobCmd.MarkPersistentFlagRequired("config") + _ = viper.BindPFlag("del-config", delJobCmd.PersistentFlags().Lookup("config")) + + delJobCmd.Flags().StringP("name", "n", "", "name") + delJobCmd.Flags().Bool("permanent", false, "permanent delete, default false") + _ = delJobCmd.MarkFlagRequired("name") + + rootCmd.AddCommand(delJobCmd) +} + +func delEntry(entry api.JobDefine) error { + // init config to get listen ip:port + var err error + apiUrl := "" + if apiUrl, err = config.GetApiUrlFromConfig(viper.GetString("del-config")); err != nil { + fmt.Fprintln(os.Stderr, "read config error", err.Error()) + os.Exit(1) + } + manager := api.NewManager(apiUrl) + //logger.Info("removing job_item to crond: %+v", jobItem) + _, err = manager.Delete(entry.Name, true) + if err != nil { + return err + } + return nil +} diff --git a/dbm-services/mysql/db-tools/mysql-crond/cmd/subcmd_disable.go b/dbm-services/mysql/db-tools/mysql-crond/cmd/subcmd_disable.go new file mode 100644 index 0000000000..9794dbed5e --- /dev/null +++ b/dbm-services/mysql/db-tools/mysql-crond/cmd/subcmd_disable.go @@ -0,0 +1,73 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package cmd + +import ( + "encoding/json" + "fmt" + "os" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + + "dbm-services/mysql/db-tools/mysql-crond/api" + "dbm-services/mysql/db-tools/mysql-crond/pkg/config" +) + +// versionCmd represents the version command +var disableJobCmd = &cobra.Command{ + Use: "disableJob", + Short: "disable crond entry", + Long: `disable crond entry`, + RunE: func(cmd *cobra.Command, args []string) error { + var jobEntry api.JobDefine + if body, _ := cmd.Flags().GetString("body"); body != "" { + if err := json.Unmarshal([]byte(body), &jobEntry); err != nil { + return err + } + } else { + jobName, _ := cmd.Flags().GetString("name") + jobEntry = api.JobDefine{ + Name: jobName, + } + } + return disableEntry(jobEntry) + }, +} + +func init() { + disableJobCmd.PersistentFlags().StringP("config", "c", "", "config file") + _ = disableJobCmd.MarkPersistentFlagRequired("config") + _ = viper.BindPFlag("disable-config", disableJobCmd.PersistentFlags().Lookup("config")) + + disableJobCmd.Flags().StringP("name", "n", "", "name") + disableJobCmd.Flags().Bool("permanent", false, "permanent disable, default false") + _ = delJobCmd.MarkFlagRequired("name") + + rootCmd.AddCommand(disableJobCmd) +} + +func disableEntry(entry api.JobDefine) error { + // init config to get listen ip:port + var err error + apiUrl := "" + if apiUrl, err = config.GetApiUrlFromConfig(viper.GetString("disable-config")); err != nil { + fmt.Fprintln(os.Stderr, "read config error", err.Error()) + os.Exit(1) + } + manager := api.NewManager(apiUrl) + //logger.Info("removing job_item to crond: %+v", jobItem) + _, err = manager.Disable(entry.Name, true) + if err != nil { + return err + } + return nil +} diff --git a/dbm-services/mysql/db-tools/mysql-crond/cmd/subcmd_list.go b/dbm-services/mysql/db-tools/mysql-crond/cmd/subcmd_list.go index 4f8185f3de..b5f29147aa 100644 --- a/dbm-services/mysql/db-tools/mysql-crond/cmd/subcmd_list.go +++ b/dbm-services/mysql/db-tools/mysql-crond/cmd/subcmd_list.go @@ -21,7 +21,7 @@ var listEntriesCmd = &cobra.Command{ Short: "list active crond entries", Long: `list active crond entries`, Run: func(cmd *cobra.Command, args []string) { - listEntries() + listEntries(cmd) }, } @@ -33,7 +33,7 @@ func init() { rootCmd.AddCommand(listEntriesCmd) } -func listEntries() { +func listEntries(cmd *cobra.Command) { // init config to get listen ip:port var err error apiUrl := "" @@ -50,18 +50,21 @@ func listEntries() { } sort.Sort(api.SimpleEntryList(entries)) // 自定义排序展示 table := tablewriter.NewWriter(os.Stdout) - table.SetAutoWrapText(false) - table.SetRowLine(false) + table.SetAutoWrapText(true) + table.SetRowLine(true) table.SetAutoFormatHeaders(false) - table.SetHeader([]string{"ID", "Schedule", "Command", "Args", "WorkDir", "Enable"}) + + table.SetHeader([]string{"ID", "JobName", "Schedule", "Command", "Args", "WorkDir", "Enable"}) for _, e := range entries { table.Append([]string{ cast.ToString(e.ID), + e.Job.Name, e.Job.Schedule, e.Job.Command, strings.Join(e.Job.Args, " "), e.Job.WorkDir, cast.ToString(e.Job.Enable)}) } + table.Render() } diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/Makefile b/dbm-services/mysql/db-tools/mysql-dbbackup/Makefile index ab823afd72..a44d4c1b65 100644 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/Makefile +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/Makefile @@ -1,8 +1,9 @@ +DIST = $(error please set DIST flag to txsql or community) PROJ_BIN="dbbackup" PROJ="dbbackup-go" OUTPUT_DIR = build PKG_DIR=${OUTPUT_DIR}/${PROJ} -PROJ_PKG=${PROJ}.tar.gz +PROJ_PKG=${PROJ}-${DIST}.tar.gz .PHONY: build build: @@ -13,20 +14,19 @@ build: .PHONY: package package: @echo "Building ${OUTPUT_DIR}/${PROJ_BIN}" - @go build -o ${OUTPUT_DIR}/${PROJ_BIN} + @GOOS=linux GOARCH=amd64 go build -o ${OUTPUT_DIR}/${PROJ_BIN} @echo "Packaging ${PROJ_PKG}" @mkdir -p ${PKG_DIR} - @cp ${OUTPUT_DIR}/${PROJ_BIN} ${PKG_DIR}/ && cp dbbackup_main.sh ${PKG_DIR}/ - @cp -r lib/ ${PKG_DIR} && cp -r bin/ ${PKG_DIR} + @cp ${OUTPUT_DIR}/${PROJ_BIN} ${PKG_DIR}/ + @cp mydumper_for_tdbctl.cnf ${PKG_DIR}/ + @cp dbbackup_main.sh ${PKG_DIR}/ + @cp -r dbbackup-go-deps/* ${PKG_DIR}/ @chmod +x ${PKG_DIR}/*.sh && chmod +x ${PKG_DIR}/dbbackup @chmod +x ${PKG_DIR}/bin/* && chmod +x ${PKG_DIR}/bin/*/* + @tar -C ${OUTPUT_DIR} -zcvf ${OUTPUT_DIR}/${PROJ_PKG} ${PROJ} .PHONY: clean clean: @rm -rf ${OUTPUT_DIR} @rm -rf ${PROJ} - - - - diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/build.sh b/dbm-services/mysql/db-tools/mysql-dbbackup/build.sh index 1b5cfb31cc..d813c32aa8 100755 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/build.sh +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/build.sh @@ -1,22 +1,74 @@ #!/bin/bash +# release_type only used for release package name +# we use dependency dir 'dbbackup-go-deps/' to tar, so make sure this dir has correct deps + +#### precheck section +usage() { + echo "Usage:" + echo "./build.sh [-s] [-t your_release_type]" + echo -e "Description:\n" + echo " -s: skip go build, use build/dbbackup binary" + echo " -t: set release_type, allowed: txsql,community" + echo "" + exit 1 +} + +release_type="" +go_build=1 +while getopts 't:sh' OPT; do + case $OPT in + t) + release_type="$OPTARG" + ;; + s) + go_build=0 + ;; + h) usage;; + ?) usage;; + esac +done +if [ "$release_type" != "txsql" -a "$release_type" != "community" ];then + echo "unknown release_type. allowed: txsql,community" + exit 1 +else + echo "release_type=$release_type" +fi + +#### build section proj_bin=dbbackup build_dir=build proj=dbbackup-go pkg_dir=${build_dir}/${proj} -proj_pkg=${proj}.tar.gz +proj_pkg=${proj}-${release_type}.tar.gz -rm -rf build/dbbackup-go +if [ $go_build -ne 0 ];then + echo "run go build" + rm -rf $pkg_dir ; mkdir -p $pkg_dir + rm -f ${build_dir}/${proj_bin} + go build -o ${build_dir}/${proj_bin} + if [ $? -gt 0 ];then + echo "build dbbackup failed" + exit 1 + fi +else + echo "skip run go build. use binary build/dbbackup" + rm -rf $pkg_dir ; mkdir -p $pkg_dir + if [ ! -f ${build_dir}/${proj_bin} ];then + echo "${build_dir}/${proj_bin} not exists" + exit 1 + fi +fi -go build -o ${pkg_dir}/${proj_bin} +cp -r dbbackup-go-deps/* ${pkg_dir}/ if [ $? -gt 0 ];then - echo "build dbbackup failed" + echo "copy dbbackup-go-deps failed" exit 1 fi -cp -r lib ${pkg_dir}/ -cp -r bin ${pkg_dir}/ -cp dbbackup_main.sh ${pkg_dir}/ +cp -a dbbackup_main.sh ${pkg_dir}/ +cp -a mydumper_for_tdbctl.cnf ${pkg_dir}/ +cp -a ${build_dir}/${proj_bin} ${pkg_dir}/ chmod +x ${pkg_dir}/*.sh && chmod +x ${pkg_dir}/dbbackup chmod +x ${pkg_dir}/bin/* && chmod +x ${pkg_dir}/bin/*/* diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/cmd/root.go b/dbm-services/mysql/db-tools/mysql-dbbackup/cmd/root.go index b6de534b07..78870071dc 100644 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/cmd/root.go +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/cmd/root.go @@ -8,10 +8,12 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" + + "dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/logger" ) // DbbackupVersion TODO -var DbbackupVersion = "1.0.2" +var DbbackupVersion = "1.0.3" var cnfFile string @@ -71,5 +73,6 @@ func initConfig(confFile string, v interface{}) error { if err != nil { log.Fatalf("parse config failed: %v", err) } + logger.Log.Infof("initConfig success %s", viper.ConfigFileUsed()) return nil } diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/cmd/subcmd_dump.go b/dbm-services/mysql/db-tools/mysql-dbbackup/cmd/subcmd_dump.go index 0fa3a50366..56c2c34d70 100644 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/cmd/subcmd_dump.go +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/cmd/subcmd_dump.go @@ -42,6 +42,9 @@ var dumpCmd = &cobra.Command{ Long: `Run backup using config, include logical and physical`, RunE: func(cmd *cobra.Command, args []string) error { var err error + if err = logger.InitLog("dbbackup_dump.log"); err != nil { + return err + } cnfFiles, _ := cmd.Flags().GetStringSlice("config") if len(cnfFiles) == 0 { if cnfFiles, err = filepath.Glob("dbbackup.*.ini"); err != nil { @@ -60,8 +63,8 @@ var dumpCmd = &cobra.Command{ logger.Log.Error("Create Dbbackup: fail to parse ", f) continue } - cnf.BackupClient.DoChecksum = true - cnf.BackupClient.Enable = true + //cnf.BackupClient.DoChecksum = true + //cnf.BackupClient.Enable = true err := backupData(&cnf) if err != nil { diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/cmd/subcmd_load.go b/dbm-services/mysql/db-tools/mysql-dbbackup/cmd/subcmd_load.go index a9dd74735c..cfe1d13696 100644 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/cmd/subcmd_load.go +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/cmd/subcmd_load.go @@ -19,11 +19,15 @@ var loadCmd = &cobra.Command{ Short: "Run load backup", Long: `Run load backup using config, include logical and physical`, RunE: func(cmd *cobra.Command, args []string) error { + var err error + if err = logger.InitLog("dbbackup_load.log"); err != nil { + return err + } var cnf = config.BackupConfig{} - if err := initConfig(cnfFile, &cnf); err != nil { + if err = initConfig(cnfFile, &cnf); err != nil { return err } - err := loadData(&cnf) + err = loadData(&cnf) if err != nil { logger.Log.Error("Load Dbbackup: Failure") return err diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/cmd/subcmd_spider.go b/dbm-services/mysql/db-tools/mysql-dbbackup/cmd/subcmd_spider.go index 6b608c167b..4f719fa2b5 100644 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/cmd/subcmd_spider.go +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/cmd/subcmd_spider.go @@ -1,82 +1,197 @@ package cmd import ( + "fmt" + "path/filepath" + "sort" + "strings" + "github.com/pkg/errors" + "github.com/spf13/cast" "github.com/spf13/cobra" "github.com/spf13/viper" + "dbm-services/common/go-pubpkg/cmutil" + "dbm-services/common/go-pubpkg/mysqlcomm" "dbm-services/mysql/db-tools/mysql-dbbackup/pkg/config" "dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/logger" "dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/spider" "dbm-services/mysql/db-tools/mysql-dbbackup/pkg/util" ) -func init() { - // spiderCmd - spiderCmd.Flags().Bool("schedule", false, "schedule back tasks") - spiderCmd.Flags().Bool("wait", false, "wait task done when using --schedule") - _ = viper.BindPFlag("schedule.wait", spiderCmd.Flags().Lookup("wait")) - - spiderCmd.Flags().String("backup-id", "", "overwrite Public.BackupId") - _ = viper.BindPFlag("Public.BackupId", dumpCmd.Flags().Lookup("backup-id")) - - spiderCmd.Flags().Bool("check", false, "check backup tasks to run") - spiderCmd.Flags().StringSliceP("config", "c", []string{}, "config files to check, comma separated. "+ - "if not given, will find dbbackup.*.ini. Using with --check") - spiderCmd.Flags().Bool("run", false, "run dbbackup if --check have tasks") - _ = viper.BindPFlag("check.run", spiderCmd.Flags().Lookup("run")) - - spiderCmd.MarkFlagsMutuallyExclusive("schedule", "check") - // spiderCmd.Flags().Bool("addSchedule", false, "add schedule to mysql-crond") - // spiderCmd.Flags().Bool("delSchedule", false, "del schedule from mysql-crond") - // spiderCmd.MarkFlagsMutuallyExclusive("addSchedule", "delSchedule") -} - var spiderCmd = &cobra.Command{ Use: "spiderbackup", Short: "Run spider backup", Long: `Run spider backup task`, - RunE: func(cmd *cobra.Command, args []string) error { - var err error - cnfFiles, _ := cmd.Flags().GetStringSlice("config") - if len(cnfFiles) == 0 { - if cnfFiles, err = util.FindBackupConfigFiles(""); err != nil { - return err - } else if len(cnfFiles) == 0 { - return errors.New("no dbbackup.*.ini found") - } - } - logger.Log.Infof("using config files: %v", cnfFiles) +} - if ok, _ := cmd.Flags().GetBool("schedule"); ok { - if len(cnfFiles) != 1 { - return errors.Errorf("--schedule expect one config, but got:%v", cnfFiles) - } - var cnf = config.BackupConfig{} - if err := initConfig(cnfFiles[0], &cnf); err != nil { - return err - } - if backupId := viper.GetString("backup-id"); backupId != "" { - cnf.Public.BackupId = backupId +func init() { + + // spiderbackup schedule + spiderScheduleCmd.Flags().Bool("wait", false, "wait task done") + _ = viper.BindPFlag("schedule.wait", spiderScheduleCmd.Flags().Lookup("wait")) + spiderScheduleCmd.Flags().String("backup-id", "", "overwrite Public.BackupId") + spiderScheduleCmd.Flags().StringSliceP("config", "c", []string{}, + "spider dbbackup ini file. If not given, will auto-detect dbbackup.*.ini to determine spider dbbackup ini") + + // spiderbackup check + spiderCheckCmd.Flags().StringSliceP("config", "c", []string{}, + "config files to check, comma separated. if not given, will find dbbackup.*.ini. Using with --check") + spiderCheckCmd.Flags().Bool("run", false, "run dbbackup if --check have tasks") + _ = viper.BindPFlag("check.run", spiderCheckCmd.Flags().Lookup("run")) + + // spiderbackup query + spiderQueryCmd.Flags().StringSlice("BackupStatus", []string{}, "BackupStatus filter, comma separated") + spiderQueryCmd.Flags().String("BackupId", "", "BackupId filter") + // format 只允许 json,table + formatOpt, _ := cmutil.NewPflagEnum("format", "table", []string{"table", "json"}) + spiderQueryCmd.Flags().Var(formatOpt, formatOpt.Name(), + fmt.Sprintf("output format, allowed %v", formatOpt.Choices())) + _ = formatOpt.SetChoices(spiderQueryCmd.Flags()) + _ = viper.BindPFlag("query.format", spiderQueryCmd.Flags().Lookup("format")) + + //spiderCmd.MarkFlagsMutuallyExclusive("schedule", "check", "query") + //spiderCmd.MarkFlagRequired("config) + + spiderCmd.AddCommand(spiderScheduleCmd) + spiderCmd.AddCommand(spiderCheckCmd) + spiderCmd.AddCommand(spiderQueryCmd) +} + +func findSpiderBackupConfigFile(cnfFiles []string) (string, error) { + if len(cnfFiles) == 1 { + return cnfFiles[0], nil + } else if len(cnfFiles) != 2 { + return "", errors.New("unable to determine spider dbbackup ini ") + } else { + var ports []int + for _, cf := range cnfFiles { + var port int + if ps := strings.Split(filepath.Base(cf), "."); len(ps) != 3 { + logger.Log.Warn("invalid backup config file name %s", cf) + } else { + port = cast.ToInt(ps[1]) } - err = spider.ScheduleBackup(&cnf.Public) - } else if ok, _ = cmd.Flags().GetBool("check"); ok { - publicConfigs, err := batchParseCnfFiles(cnfFiles) - if err != nil { - return err + if port != 0 { + ports = append(ports, port) + } else { + return "", errors.Errorf("invalid backup config file name %s", cf) } - return spider.RunBackupTasks(publicConfigs) - } else { - return errors.New("need --schedule or --check") } + sort.Ints(ports) + spiderPort := ports[0] + tdbctlPortExpect := mysqlcomm.GetTdbctlPortBySpider(spiderPort) + if ports[1] != tdbctlPortExpect { + return "", errors.Errorf("tdbctl port expect %d but got %d", tdbctlPortExpect, ports[1]) + } + // expect dbbackup.*.ini + configFile := fmt.Sprintf("dbbackup.%d.ini", spiderPort) + logger.Log.Infof("found spider dbbackup ini: %s", configFile) + return configFile, nil + } +} + +var spiderScheduleCmd = &cobra.Command{ + Use: "schedule", + Short: "spiderbackup schedule", + Long: `Start spider global backup. Will initialize backup tasks using one backup-id, only run on spider master`, + RunE: func(cmd *cobra.Command, args []string) error { + if err := logger.InitLog("dbbackup_spider.log"); err != nil { + return err + } + // 本地应该有 spider 和 tdbctl 2 个 backup ini, 自动找到 spider port + // 如果只有一个 backup ini,则认为它就是 spider port + cnfFiles, err := spiderCmdHandleConfig(cmd) + if err != nil { + return err + } + configFile, err := findSpiderBackupConfigFile(cnfFiles) + if err != nil { + return err + } + var cnf = config.BackupConfig{} + if err := initConfig(configFile, &cnf); err != nil { + return err + } + cnf.Public.BackupId, _ = cmd.Flags().GetString("backup-id") + err = spider.ScheduleBackup(&cnf.Public) + if err != nil { + logger.Log.Error("Spider Schedule: Failure") + return err + } + return nil + }, +} + +var spiderCheckCmd = &cobra.Command{ + Use: "check", + Short: "spiderbackup check", + Long: `Check or run backup todo tasks`, + RunE: func(cmd *cobra.Command, args []string) error { + if err := logger.InitLog("dbbackup_spider.log"); err != nil { + return err + } + cnfFiles, err := spiderCmdHandleConfig(cmd) + if err != nil { + return err + } + publicConfigs, err := batchParseCnfFiles(cnfFiles) + if err != nil { + return err + } + err = spider.RunBackupTasks(publicConfigs) + if err != nil { + logger.Log.Error("Spider Check: Failure") + return err + } + return nil + }, +} + +var spiderQueryCmd = &cobra.Command{ + Use: "query", + Short: "spiderbackup query", + Long: `Query spider backup task status, only run on spider master`, + RunE: func(cmd *cobra.Command, args []string) error { + if err := logger.InitLog("dbbackup_spider.log"); err != nil { + return err + } + cnfFiles, err := spiderCmdHandleConfig(cmd) + if err != nil { + return err + } + configFile, err := findSpiderBackupConfigFile(cnfFiles) + if err != nil { + return err + } + var cnf = config.BackupConfig{} + if err := initConfig(configFile, &cnf); err != nil { + return err + } + cnf.Public.BackupId, _ = cmd.Flags().GetString("BackupId") + backupStatus, _ := cmd.Flags().GetStringSlice("BackupStatus") + err = spider.QueryBackup(&cnf.Public, backupStatus) if err != nil { - logger.Log.Error("Spider backup: Failure") + logger.Log.Error("Spider Query: Failure") return err } return nil }, } +func spiderCmdHandleConfig(cmd *cobra.Command) (cnfFiles []string, err error) { + cnfFiles, _ = cmd.Flags().GetStringSlice("config") + if len(cnfFiles) == 0 { + if cnfFiles, err = util.FindBackupConfigFiles(""); err != nil { + return nil, err + } else if len(cnfFiles) == 0 { + return nil, errors.New("no dbbackup.*.ini found") + } + } + logger.Log.Infof("using config files: %v", cnfFiles) + return cnfFiles, nil +} + func batchParseCnfFiles(cnfFiles []string) ([]*config.Public, error) { var publicConfigs []*config.Public var backupId = viper.GetString("backup-id") diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/bin/.gitkeep b/dbm-services/mysql/db-tools/mysql-dbbackup/dbbackup-go-deps/bin/.gitkeep similarity index 100% rename from dbm-services/mysql/db-tools/mysql-dbbackup/bin/.gitkeep rename to dbm-services/mysql/db-tools/mysql-dbbackup/dbbackup-go-deps/bin/.gitkeep diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/lib/.gitkeep b/dbm-services/mysql/db-tools/mysql-dbbackup/dbbackup-go-deps/lib/.gitkeep similarity index 100% rename from dbm-services/mysql/db-tools/mysql-dbbackup/lib/.gitkeep rename to dbm-services/mysql/db-tools/mysql-dbbackup/dbbackup-go-deps/lib/.gitkeep diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/dbbackup_main.sh b/dbm-services/mysql/db-tools/mysql-dbbackup/dbbackup_main.sh index dd1749dd97..110ecac007 100755 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/dbbackup_main.sh +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/dbbackup_main.sh @@ -93,7 +93,7 @@ okPorts="" for conf_file in $configFiles do #port=`echo $conf_file |awk -F. '{print $(NF-1)}'` - port=`grep MysqlPort $conf_file |head -1|grep -v "#" |cut -d= -f2` + port=`grep MysqlPort $conf_file |grep -Ev "#|MysqlPort = 0" |head -1 | cut -d= -f2` echo "now doing dbbackup for config file=$conf_file port=$port" echo "${scriptDir}/dbbackup dumpbackup --config=$conf_file $dbbackupOpt 2>&1 >> $logfile" ${scriptDir}/dbbackup dumpbackup --config=$conf_file $dbbackupOpt 2>&1 >> $logfile diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/docs/spiderbackup.md b/dbm-services/mysql/db-tools/mysql-dbbackup/docs/spiderbackup.md index b0baf8a779..171845146e 100644 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/docs/spiderbackup.md +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/docs/spiderbackup.md @@ -4,13 +4,15 @@ 在 spider master (即 tdbctl master) 节点运行 ``` -./dbbackup spiderbackup -c dbbackup.25000.ini --schedule +./dbbackup spiderbackup -c dbbackup.25000.ini schedule +./dbbackup spiderbackup -c dbbackup.25000.ini schedule --wait +./dbbackup spiderbackup -c dbbackup.25000.ini schedule --wait --backup-id=xx-xx-xx ``` 会生成一个 uuid 写入 `infodba_schema.global_backup` 表,也可以指定 `--backup-id`。 ## 各节点执行备份 ``` -cd /home/mysql/dbbackup-go/dbbackup && ./dbbackup spiderbackup --check --run -c dbbackup.20000.ini,dbbackup.20001.ini +cd /home/mysql/dbbackup-go/dbbackup && ./dbbackup spiderbackup check --run -c dbbackup.20000.ini,dbbackup.20001.ini ``` 会对指定的备份配置文件(没有指定时,在当前目录find dbbackup.*.ini),检查是否有备份任务,如果有则执行备份。 @@ -21,9 +23,9 @@ cd /home/mysql/dbbackup-go/dbbackup && ./dbbackup spiderbackup --check --run -c - remote_master, remote_slave, spider_master ``` cd /home/mysql/mysql-crond -./mysql-crond addJob --name spiderbackup-run -c runtime.yaml \ +./mysql-crond addJob --name spiderbackup-check -c runtime.yaml \ --command /home/mysql/dbbackup-go/dbbackup \ - --args spiderbackup,--check,--run \ + --args spiderbackup,check,--run \ --work_dir /home/mysql/dbbackup-go \ --schedule "*/1 * * * *" \ --creator xxx \ @@ -38,7 +40,7 @@ cd /home/mysql/mysql-crond cd /home/mysql/mysql-crond ./mysql-crond addJob --name spiderbackup-schedule -c runtime.yaml \ --command /home/mysql/dbbackup-go/dbbackup \ - --args spiderbackup,--schedule,-c,dbbackup.25000.ini \ + --args spiderbackup,schedule,-c,dbbackup.25000.ini \ --work_dir /home/mysql/dbbackup-go \ --schedule "0 3 * * *" \ --creator xxx \ @@ -62,20 +64,30 @@ cd /home/mysql/mysql-crond - failed 备份失败。在部分情况会有 `failed: no pid` 标准失败原因 - quit - 可以手动 update 任务状态为 quit,在`--check`轮询时发现这个状态的任务,会强制把对应的 TaskPid kill 掉,达到批量终止备份任务的效果 + 可以手动 update 任务状态为 quit,在`check`轮询时发现这个状态的任务,会强制把对应的 TaskPid kill 掉,达到批量终止备份任务的效果 -每轮的 `--check` 操作会检查 running 状态的任务,如果 TaskPid 不存在,则会标记为 failed 。这在备份过程中实例挂掉后,能够继续维护任务状态流转。 +每轮的 `check` 操作会检查 running 状态的任务,如果 TaskPid 不存在,则会标记为 failed 。这在备份过程中实例挂掉后,能够继续维护任务状态流转。 -在 spider master 指定 `--schedule --wait` 时会同步等待本次任务在其他节点全部结束才退出,如果有任意节点 failed,则 wait exit code=1。 -在 `--wait` 过程如果 spider master 自身挂掉,并不会马上退出,而是会持续检查,直到尝试 120 次后再退出。 +在 spider master 指定 `schedule --wait` 时会同步等待本次任务在其他节点全部结束才退出,如果有任意节点 failed,则 wait exit code=1。 +- 在 `--wait` 过程如果 spider master 自身挂掉,并不会马上退出,而是会持续检查,直到尝试 120 次后再退出 +- wait 会同时检查 remote slave 上的 global_backup,因为在 spider master node 无法查询到 remote slave 的表,需要从中控节点轮询 -`--schedule` 时也能指定 `--backup-id` 参数,则不会自动生成 BackupId 而是使用指定值 -`--check --run` 也能指定 `--backup-id` 参数,表示运行指定 backup-id 的任务。 + +`spiderbackup schedule` 时也能指定 `--backup-id` 参数,则不会自动生成 BackupId 而是使用指定值 +`spiderbackup check --run` 也能指定 `--backup-id` 参数,表示运行指定 backup-id 的任务。 备份任务是按 机器 + backup-id 来调度的: 1. 比如机器有 4 个实例,一下子接受到 2 个备份任务(2 个backup-id) -2. `--check` 会先把本机所有实例 init 状态的任务取出,再按时间排序,取最早的任务对应的 backup-id +2. `spiderbackup check` 会先把本机所有实例 init 状态的任务取出,再按时间排序,取最早的任务对应的 backup-id 3. 再根据这个 backup-id 找到所有需要备份的实例列表,顺序逐个进行备份。逐个备份前会检查当前备份任务的状态是否发生变化 -4. 周期性的 `--check` 任务发现本机有任一 running 状态的任务,则跳过本轮。 当没有running状态的任务时,则继续第二个 backup-id 任务 +4. 周期性的 `spiderbackup check` 任务发现本机有任一 running 状态的任务,则跳过本轮。 当没有running状态的任务时,则继续第二个 backup-id 任务 5. 单个实例备份失败,不会影响其它实例备份,但整个操作会 exit code=1 -6. 周期性 `--check` 任务还会处理备份超过 48h 的任务 \ No newline at end of file +6. 周期性 `spiderbackup check` 任务还会处理备份超过 48h 的任务 + +## 查询备份状态 + +``` +./dbbackup spiderbackup query +./dbbackup spiderbackup query --backupId=xx-xx-xx +./dbbackup spiderbackup query --backupStatus=running +``` \ No newline at end of file diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/go.mod b/dbm-services/mysql/db-tools/mysql-dbbackup/go.mod index d4133a77fc..5409c0489d 100644 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/go.mod +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/go.mod @@ -7,6 +7,7 @@ require ( github.com/go-sql-driver/mysql v1.7.1 github.com/google/uuid v1.3.0 github.com/jmoiron/sqlx v1.3.5 + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 github.com/olekukonko/tablewriter v0.0.5 github.com/shirou/gopsutil v3.21.11+incompatible github.com/sirupsen/logrus v1.9.0 diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/go.sum b/dbm-services/mysql/db-tools/mysql-dbbackup/go.sum index 584678daff..ed23906c99 100644 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/go.sum +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/go.sum @@ -160,6 +160,8 @@ github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRU github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us= diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/main.go b/dbm-services/mysql/db-tools/mysql-dbbackup/main.go index 08da6bde02..da313dcd56 100644 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/main.go +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/main.go @@ -2,10 +2,10 @@ package main import ( "dbm-services/mysql/db-tools/mysql-dbbackup/cmd" - "dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/logger" ) func main() { - logger.InitLog() + //logger.InitLog("") + // logger 在子 command 里面设置,不同的日志名 cmd.Execute() } diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/mydumper_for_tdbctl.cnf b/dbm-services/mysql/db-tools/mysql-dbbackup/mydumper_for_tdbctl.cnf new file mode 100644 index 0000000000..07fe86705f --- /dev/null +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/mydumper_for_tdbctl.cnf @@ -0,0 +1,2 @@ +[mydumper_session_variables] +tc_admin=0 \ No newline at end of file diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/config/backup_client.go b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/config/backup_client.go index b67ca8131d..72b2416efe 100644 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/config/backup_client.go +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/config/backup_client.go @@ -10,8 +10,12 @@ package config // BackupClient the config of backupclient type BackupClient struct { - Enable bool `ini:"Enable"` + // Enable 是否启用备份 + Enable bool `ini:"Enable"` + // FileTag 启用备份时上报文件使用哪个 FileTag FileTag string `ini:"FileTag"` RemoteFileSystem string `ini:"RemoteFileSystem"` DoChecksum bool `ini:"DoChecksum"` + // BackupClientBin 备份客户端路径,默认 /usr/local/backup_client/bin/backup_client + BackupClientBin string `ini:"BackupClientBin"` } diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/config/logical.go b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/config/logical.go index 2accec79db..846a331244 100644 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/config/logical.go +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/config/logical.go @@ -9,8 +9,9 @@ package config // LogicalBackup the config of logical backup +// data or schema is controlled by Public.DataSchemaGrant type LogicalBackup struct { - ChunkFileSize uint64 `ini:"ChunkFilesize"` // split tables into chunks of this output file size. This value is in MB + ChunkFilesize uint64 `ini:"ChunkFilesize"` // split tables into chunks of this output file size. This value is in MB Regex string `ini:"Regex"` Threads int `ini:"Threads"` DisableCompress bool `ini:"DisableCompress"` @@ -21,15 +22,23 @@ type LogicalBackup struct { // LogicalLoad the config of logical loading type LogicalLoad struct { - MysqlHost string `ini:"MysqlHost"` - MysqlPort int `ini:"MysqlPort"` - MysqlUser string `ini:"MysqlUser"` - MysqlPasswd string `ini:"MysqlPasswd"` - MysqlCharset string `ini:"MysqlCharset"` - MysqlLoadDir string `ini:"MysqlLoadDir"` - Threads int `ini:"Threads"` - Regex string `ini:"Regex"` - EnableBinlog bool `ini:"EnableBinlog"` + MysqlHost string `ini:"MysqlHost"` + MysqlPort int `ini:"MysqlPort"` + MysqlUser string `ini:"MysqlUser"` + MysqlPasswd string `ini:"MysqlPasswd"` + MysqlCharset string `ini:"MysqlCharset"` + MysqlLoadDir string `ini:"MysqlLoadDir"` + Threads int `ini:"Threads"` + Regex string `ini:"Regex"` + EnableBinlog bool `ini:"EnableBinlog"` + // SchemaOnly import schema,trigger,func,proc (--no-data) + // if you want only table schema, use ExtraOpt = -skip-triggers --skip-post + // mydumper doest not support data only currently, you should backup only data for your purpose + SchemaOnly bool `ini:"SchemaOnly"` IndexFilePath string `ini:"IndexFilePath" validate:"required"` ExtraOpt string `ini:"ExtraOpt"` // other myloader options string to be appended + // DBListDropIfExists will run drop database if exists db_xxx before load data. comma separated + DBListDropIfExists string `ini:"DBListDropIfExists"` + // CreateTableIfNotExists true will add --append-if-not-exist for myloader + CreateTableIfNotExists bool `ini:"CreateTableIfNotExists"` } diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/config/public.go b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/config/public.go index 4378771742..ee71c19a7a 100644 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/config/public.go +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/config/public.go @@ -20,18 +20,19 @@ import ( ) type Public struct { - BkBizId int `ini:"BkBizId" validate:"required"` - BkCloudId int `ini:"BkCloudId"` - BillId string `ini:"BillId"` - BackupId string `ini:"BackupId"` - ClusterId int `ini:"ClusterId"` - ClusterAddress string `ini:"ClusterAddress"` - ShardValue int `ini:"ShardValue"` // 分片 id,仅 spider 有用 - MysqlHost string `ini:"MysqlHost" validate:"required,ip"` - MysqlPort int `ini:"MysqlPort" validate:"required"` - MysqlUser string `ini:"MysqlUser" validate:"required"` - MysqlPasswd string `ini:"MysqlPasswd"` - DataSchemaGrant string `ini:"DataSchemaGrant" validate:"required"` // data,grant,priv,all + BkBizId int `ini:"BkBizId" validate:"required"` + BkCloudId int `ini:"BkCloudId"` + BillId string `ini:"BillId"` + BackupId string `ini:"BackupId"` + ClusterId int `ini:"ClusterId"` + ClusterAddress string `ini:"ClusterAddress"` + ShardValue int `ini:"ShardValue"` // 分片 id,仅 spider 有用 + MysqlHost string `ini:"MysqlHost" validate:"required,ip"` + MysqlPort int `ini:"MysqlPort" validate:"required"` + MysqlUser string `ini:"MysqlUser" validate:"required"` + MysqlPasswd string `ini:"MysqlPasswd"` + // DataSchemaGrant data,grant,schema,priv,all,写了 data 则只备data,不备份 schema + DataSchemaGrant string `ini:"DataSchemaGrant" validate:"required"` BackupDir string `ini:"BackupDir" validate:"required"` MysqlRole string `ini:"MysqlRole" validate:"required"` // oneof=master slave MysqlCharset string `ini:"MysqlCharset"` @@ -47,41 +48,6 @@ type Public struct { targetName string } -// ParseDataSchemaGrant Check whether data|schema|grant is backed up -//func (c *Public) ParseDataSchemaGrant() error { -// valueAllowed := []string{cst.BackupGrant, cst.BackupSchema, cst.BackupData, cst.BackupAll} -// arr := strings.Split(c.DataSchemaGrant, ",") -// set := make(map[string]struct{}, len(arr)) -// for _, v := range arr { -// v = strings.ToLower(strings.TrimSpace(v)) -// if !cmutil.StringsHas(valueAllowed, v) { -// return fmt.Errorf("the part of param DataSchemaGrant [%s] is wrong", v) -// } -// set[v] = struct{}{} -// } -// if _, found := set[cst.BackupData]; found { -// common.BackupData = true -// } -// if _, found := set[cst.BackupSchema]; found { -// common.BackupSchema = true -// } -// if _, found := set[cst.BackupGrant]; found { -// common.BackupGrant = true -// } -// if _, found := set[cst.BackupAll]; found { -// // all is alias to 'grant,schema,data' -// common.BackupGrant = true -// common.BackupData = true -// common.BackupSchema = true -// } -// -// if !common.BackupData && !common.BackupSchema && !common.BackupGrant { -// return fmt.Errorf("need to backup at least one of %v", valueAllowed) -// } -// -// return nil -//} - // GetCnfFileName TODO func (c *Public) GetCnfFileName() string { return c.cnfFilename diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/cst/const.go b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/cst/const.go index 01774f86e2..0a68588f32 100644 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/cst/const.go +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/cst/const.go @@ -47,3 +47,8 @@ const ( // SpiderTaskMaxRunHours TODO SpiderTaskMaxRunHours = 48 ) + +// SpiderNodeShardValue spider 全局备份,对 spider 节点备份假设的 shard_value 值 +// 比如 为 0,则 spider node 备份任务会写到 spt0 节点,spider node备份任务查询spider表时,实际是从 spt0 获取的 +// 注意不能指定负数,因为 -1 mod 2 的结果也为负数,会无法写入数据 +const SpiderNodeShardValue = 0 diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/backupexe/dumper_logical.go b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/backupexe/dumper_logical.go index 16ced7565d..63505b0155 100644 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/backupexe/dumper_logical.go +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/backupexe/dumper_logical.go @@ -110,6 +110,7 @@ func (l *LogicalDumper) initConfig() error { // return nil //} +// Execute excute dumping backup with logical backup tool func (l *LogicalDumper) Execute(enableTimeOut bool) error { binPath := filepath.Join(l.dbbackupHome, "/bin/mydumper") args := []string{ @@ -120,7 +121,7 @@ func (l *LogicalDumper) Execute(enableTimeOut bool) error { "-o", filepath.Join(l.cnf.Public.BackupDir, l.cnf.Public.TargetName()), fmt.Sprintf("--long-query-retries=%d", l.cnf.LogicalBackup.FlushRetryCount), fmt.Sprintf("--set-names=%s", l.cnf.Public.MysqlCharset), - fmt.Sprintf("--chunk-filesize=%d", l.cnf.LogicalBackup.ChunkFileSize), + fmt.Sprintf("--chunk-filesize=%d", l.cnf.LogicalBackup.ChunkFilesize), fmt.Sprintf("--threads=%d", l.cnf.LogicalBackup.Threads), "--trx-consistency-only", "--long-query-retry-interval=10", diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/backupexe/dumper_physical.go b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/backupexe/dumper_physical.go index beb55bfa8c..e0df64168f 100644 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/backupexe/dumper_physical.go +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/backupexe/dumper_physical.go @@ -24,6 +24,7 @@ type PhysicalDumper struct { mysqlVersion string innodbCmd InnodbCommand storageEngine string + isOfficial bool } func (p *PhysicalDumper) initConfig() error { @@ -46,14 +47,16 @@ func (p *PhysicalDumper) initConfig() error { if verErr != nil { return verErr } - p.mysqlVersion = util.VersionParser(versionStr) + p.mysqlVersion, p.isOfficial = util.VersionParser(versionStr) p.storageEngine, err = mysqlconn.GetStorageEngine(db) if err != nil { return err } p.storageEngine = strings.ToLower(p.storageEngine) - p.innodbCmd.ChooseXtrabackupTool(p.mysqlVersion) + if err := p.innodbCmd.ChooseXtrabackupTool(p.mysqlVersion, p.isOfficial); err != nil { + return err + } return nil } @@ -137,6 +140,7 @@ func (p *PhysicalDumper) initConfig() error { // return nil //} +// Execute excute dumping backup with physical backup tool func (p *PhysicalDumper) Execute(enableTimeOut bool) error { if p.storageEngine != "innodb" { err := fmt.Errorf("%s engine not support", p.storageEngine) @@ -144,7 +148,7 @@ func (p *PhysicalDumper) Execute(enableTimeOut bool) error { return err } - binPath := filepath.Join(p.dbbackupHome, "/bin/xtrabackup", p.innodbCmd.innobackupexBin) + binPath := filepath.Join(p.dbbackupHome, p.innodbCmd.innobackupexBin) args := []string{ fmt.Sprintf("--defaults-file=%s", p.cnf.PhysicalBackup.DefaultsFile), fmt.Sprintf("--host=%s", p.cnf.Public.MysqlHost), @@ -152,7 +156,7 @@ func (p *PhysicalDumper) Execute(enableTimeOut bool) error { fmt.Sprintf("--user=%s", p.cnf.Public.MysqlUser), fmt.Sprintf("--password=%s", p.cnf.Public.MysqlPasswd), fmt.Sprintf( - "--ibbackup=%s", filepath.Join(p.dbbackupHome, "/bin/xtrabackup", p.innodbCmd.xtrabackupBin)), + "--ibbackup=%s", filepath.Join(p.dbbackupHome, p.innodbCmd.xtrabackupBin)), "--no-timestamp", "--compress", "--lazy-backup-non-innodb", @@ -171,7 +175,7 @@ func (p *PhysicalDumper) Execute(enableTimeOut bool) error { if p.cnf.PhysicalBackup.Threads > 0 { args = append(args, []string{ - fmt.Sprintf("--compress-thread=%d", p.cnf.PhysicalBackup.Threads), + fmt.Sprintf("--compress-threads=%d", p.cnf.PhysicalBackup.Threads), fmt.Sprintf("--parallel=%d", p.cnf.PhysicalBackup.Threads), }...) } @@ -188,6 +192,10 @@ func (p *PhysicalDumper) Execute(enableTimeOut bool) error { }...) } + if strings.Compare(p.mysqlVersion, "008000000") >= 0 && p.isOfficial { + args = append(args, "--skip-strict") + } + // ToDo extropt var cmd *exec.Cmd @@ -211,7 +219,7 @@ func (p *PhysicalDumper) Execute(enableTimeOut bool) error { filepath.Join( p.dbbackupHome, "logs", - fmt.Sprintf("mydumper_%d.log", int(time.Now().Weekday())))) + fmt.Sprintf("xtrabackup_%d.log", int(time.Now().Weekday())))) if err != nil { logger.Log.Error("create log file failed: ", err) return err diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/backupexe/env.go b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/backupexe/env.go index 44428b5dc6..5a379893a3 100644 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/backupexe/env.go +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/backupexe/env.go @@ -8,6 +8,8 @@ import ( "strings" "time" + "dbm-services/mysql/db-tools/mysql-dbbackup/pkg/util" + "dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/logger" ) @@ -18,44 +20,92 @@ type InnodbCommand struct { } // ChooseXtrabackupTool Decide the version of xtrabackup tool -func (i *InnodbCommand) ChooseXtrabackupTool(mysqlVersion string) { - i.innobackupexBin = "innobackupex.pl" - i.xtrabackupBin = "xtrabackup" - if strings.Compare(mysqlVersion, "005006000") >= 0 && - strings.Compare(mysqlVersion, "005007000") < 0 { - i.innobackupexBin = "innobackupex_56.pl" - i.xtrabackupBin = "xtrabackup_56" - } else if strings.Compare(mysqlVersion, "005007000") >= 0 && - strings.Compare(mysqlVersion, "008000000") < 0 { - i.innobackupexBin = "xtrabackup_57" - i.xtrabackupBin = "xtrabackup_57" - } else if strings.Compare(mysqlVersion, "008000000") >= 0 { - i.innobackupexBin = "xtrabackup_80" - i.xtrabackupBin = "xtrabackup_80" +func (i *InnodbCommand) ChooseXtrabackupTool(mysqlVersion string, isOfficial bool) error { + if !isOfficial { + if strings.Compare(mysqlVersion, "005005000") >= 0 && + strings.Compare(mysqlVersion, "005006000") < 0 { + // tmysql 5.5 + i.innobackupexBin = "/bin/xtrabackup/innobackupex_55.pl" + i.xtrabackupBin = "/bin/xtrabackup/xtrabackup_55" + } else if strings.Compare(mysqlVersion, "005006000") >= 0 && + strings.Compare(mysqlVersion, "005007000") < 0 { + // tmysql 5.6 + i.innobackupexBin = "/bin/xtrabackup/innobackupex_56.pl" + i.xtrabackupBin = "/bin/xtrabackup/xtrabackup_56" + } else if strings.Compare(mysqlVersion, "005007000") >= 0 && + strings.Compare(mysqlVersion, "008000000") < 0 { + // tmysql 5.7 + i.innobackupexBin = "/bin/xtrabackup/xtrabackup_57" + i.xtrabackupBin = "/bin/xtrabackup/xtrabackup_57" + } else if strings.Compare(mysqlVersion, "008000000") >= 0 { + // tmysql 8.0 + i.innobackupexBin = "/bin/xtrabackup/xtrabackup_80" + i.xtrabackupBin = "/bin/xtrabackup/xtrabackup_80" + } else { + return fmt.Errorf("unrecognizable mysql version") + } + } else { + if strings.Compare(mysqlVersion, "005007000") >= 0 && + strings.Compare(mysqlVersion, "008000000") < 0 { + // official_mysql_5.7 + i.innobackupexBin = "/bin/xtrabackup_official/xtrabackup_57/xtrabackup" + i.xtrabackupBin = "/bin/xtrabackup_official/xtrabackup_57/xtrabackup" + } else if strings.Compare(mysqlVersion, "008000000") >= 0 { + //official_mysql_8.0 + i.innobackupexBin = "/bin/xtrabackup_official/xtrabackup_80/xtrabackup" + i.xtrabackupBin = "/bin/xtrabackup_official/xtrabackup_80/xtrabackup" + } else { + return fmt.Errorf("unrecognizable mysql version") + } } + return nil } // SetEnv set env variables -func SetEnv() error { - exepath, err := os.Executable() +func SetEnv(backupType string, mysqlVersionStr string) error { + exePath, err := os.Executable() if err != nil { return err } - exepath = filepath.Dir(exepath) - libpath := filepath.Join(exepath, "lib/libmydumper") - libpath2 := filepath.Join(exepath, "lib/libxtra") - libpath3 := filepath.Join(exepath, "lib/libxtra_80") - binpath := filepath.Join(exepath, "bin/xtrabackup") + exePath = filepath.Dir(exePath) + var libPath []string + var binPath []string + if strings.ToLower(backupType) == "logical" { + libPath = append(libPath, filepath.Join(exePath, "lib/libmydumper")) + } else if strings.ToLower(backupType) == "physical" { + _, isOfficial := util.VersionParser(mysqlVersionStr) + if !isOfficial { + libPath = append(libPath, filepath.Join(exePath, "lib/libxtra")) + libPath = append(libPath, filepath.Join(exePath, "lib/libxtra_80")) + + binPath = append(binPath, filepath.Join(exePath, "bin/xtrabackup")) + } else { + libPath = append(libPath, filepath.Join(exePath, "lib/libxtra_57_official/private")) + libPath = append(libPath, filepath.Join(exePath, "lib/libxtra_57_official/plugin")) + libPath = append(libPath, filepath.Join(exePath, "lib/libxtra_80_official/private")) + libPath = append(libPath, filepath.Join(exePath, "lib/libxtra_80_official/plugin")) + + binPath = append(binPath, filepath.Join(exePath, "bin/xtrabackup_official")) + } + } else { + return fmt.Errorf("setEnv: unknown backupType") + } + // xtrabackup --decompress 需要找到 qpress 命令 + binPath = append(binPath, filepath.Join(exePath, "bin")) + + logger.Log.Info(fmt.Sprintf("export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:%s", strings.Join(libPath, ":"))) + logger.Log.Info(fmt.Sprintf("export PATH=$PATH:%s", strings.Join(binPath, ":"))) oldLibs := strings.Split(os.Getenv("LD_LIBRARY_PATH"), ":") - oldLibs = append(oldLibs, libpath, libpath2, libpath3) + oldLibs = append(oldLibs, libPath...) err = os.Setenv("LD_LIBRARY_PATH", strings.Join(oldLibs, ":")) + if err != nil { logger.Log.Error("failed to set env variable", err) return err } oldPaths := strings.Split(os.Getenv("PATH"), ":") - oldPaths = append(oldPaths, binpath) + oldPaths = append(oldPaths, binPath...) err = os.Setenv("PATH", strings.Join(oldPaths, ":")) if err != nil { logger.Log.Error("failed to set env variable", err) diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/backupexe/execute_dump.go b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/backupexe/execute_dump.go index c60ea2d2ca..2ea0bd1576 100644 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/backupexe/execute_dump.go +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/backupexe/execute_dump.go @@ -1,6 +1,7 @@ package backupexe import ( + "dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/mysqlconn" "strings" "dbm-services/mysql/db-tools/mysql-dbbackup/pkg/config" @@ -9,7 +10,19 @@ import ( // ExecuteBackup execute dump backup command func ExecuteBackup(cnf *config.BackupConfig) error { - if envErr := SetEnv(); envErr != nil { + // get mysql version from mysql server, and then set env variables + db, err := mysqlconn.InitConn(&cnf.Public) + if err != nil { + return err + } + defer func() { + _ = db.Close() + }() + versionStr, verErr := mysqlconn.GetMysqlVersion(db) + if verErr != nil { + return verErr + } + if envErr := SetEnv(cnf.Public.BackupType, versionStr); envErr != nil { return envErr } diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/backupexe/execute_load.go b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/backupexe/execute_load.go index 49dc89e5bb..f734bbb494 100644 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/backupexe/execute_load.go +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/backupexe/execute_load.go @@ -16,10 +16,6 @@ func ExecuteLoad(cnf *config.BackupConfig) error { } } - if envErr := SetEnv(); envErr != nil { - return envErr - } - var indexPath string if cnf.LogicalLoad.IndexFilePath != "" { indexPath = cnf.LogicalLoad.IndexFilePath @@ -32,6 +28,10 @@ func ExecuteLoad(cnf *config.BackupConfig) error { return err } + if envErr := SetEnv(indexFileContent.BackupType, indexFileContent.MysqlVersion); envErr != nil { + return envErr + } + loader, err := BuildLoader(cnf, indexFileContent.BackupType) if err != nil { return err diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/backupexe/grant.go b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/backupexe/grant.go index 1ae878fb7b..34349f6630 100644 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/backupexe/grant.go +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/backupexe/grant.go @@ -63,6 +63,7 @@ func GrantBackup(cfg *config.Public) error { writer := bufio.NewWriter(file) version, verErr := mysqlconn.GetMysqlVersion(db) + verStr, _ := util.VersionParser(version) if verErr != nil { return verErr } @@ -75,7 +76,7 @@ func GrantBackup(cfg *config.Public) error { } var grantInfo string - if strings.Compare(util.VersionParser(version), "005007000") >= 0 { // mysql.version >=5.7 + if strings.Compare(verStr, "005007000") >= 0 { // mysql.version >=5.7 sqlString := strings.Join([]string{"show create user `", user, "`@`", host, "`"}, "") gRows, err := db.Query(sqlString) if err != nil { @@ -132,7 +133,7 @@ func GrantBackup(cfg *config.Public) error { return err } - if strings.Compare(util.VersionParser(version), "005007000") >= 0 { // mysql.version >=5.7 + if strings.Compare(verStr, "005007000") >= 0 { // mysql.version >=5.7 cmdStr := fmt.Sprintf(`sed -i 's/CREATE USER IF NOT EXISTS /CREATE USER /g' %s`, filepath) err := exec.Command("/bin/bash", "-c", cmdStr).Run() if err != nil { diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/backupexe/loader_logical.go b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/backupexe/loader_logical.go index de6e84a486..2d4de120a5 100644 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/backupexe/loader_logical.go +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/backupexe/loader_logical.go @@ -1,23 +1,28 @@ package backupexe import ( + "database/sql" "fmt" "os" - "os/exec" "path/filepath" "strconv" "strings" + "time" "github.com/pkg/errors" + "dbm-services/common/go-pubpkg/cmutil" "dbm-services/mysql/db-tools/mysql-dbbackup/pkg/config" "dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/logger" + "dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/mysqlconn" ) // LogicalLoader this is used to load logical backup type LogicalLoader struct { cnf *config.BackupConfig dbbackupHome string + dbConn *sql.DB + initConnect string } func (l *LogicalLoader) initConfig(_ *IndexContent) error { @@ -33,50 +38,84 @@ func (l *LogicalLoader) initConfig(_ *IndexContent) error { return nil } -//// CreateCommand Create LogicalLoad Cmd -//func (l *LogicalLoader) CreateCommand() (string, error) { -// binPath := filepath.Join(l.dbbackupHome, "bin/myloader") -// var buffer bytes.Buffer -// -// buffer.WriteString(binPath) -// buffer.WriteString(" -h " + l.cnf.LogicalLoad.MysqlHost) -// buffer.WriteString(" -P " + l.cnf.LogicalLoad.MysqlPort) -// buffer.WriteString(" -u " + l.cnf.LogicalLoad.MysqlUser) -// buffer.WriteString(" -p " + l.cnf.LogicalLoad.MysqlPasswd) -// buffer.WriteString(" -d " + l.cnf.LogicalLoad.MysqlLoadDir) -// buffer.WriteString(" --threads " + strconv.Itoa(l.cnf.LogicalLoad.Threads)) -// buffer.WriteString(" --set-names " + l.cnf.LogicalLoad.MysqlCharset) -// if l.cnf.LogicalLoad.EnableBinlog { -// buffer.WriteString(" --enable-binlog ") -// } -// if l.cnf.LogicalLoad.Regex != "" { -// buffer.WriteString(fmt.Sprintf(` -x '%s'`, l.cnf.LogicalLoad.Regex)) -// } -// if l.cnf.LogicalLoad.ExtraOpt != "" { -// buffer.WriteString(fmt.Sprintf(` %s `, l.cnf.LogicalLoad.ExtraOpt)) -// } -// cmdStr := buffer.String() -// logger.Log.Info("logical loader cmd: ", cmdStr) -// return cmdStr, nil -//} -// -//// Execute execute myloader command -//func (l *LogicalLoader) Execute() error { -// cmdStr, err := l.CreateCommand() -// if err != nil { -// logger.Log.Error("Failed to create the cmd_line of loading backup, error: ", err) -// return err -// } -// res, exeErr := exec.Command("/bin/bash", "-c", cmdStr).CombinedOutput() -// if exeErr != nil { -// logger.Log.Error("Failed to execute loading backup, error: ", exeErr, string(res)) -// return errors.Wrap(exeErr, string(res)) -// } -// logger.Log.Info("execute loading backup, result: ", string(res)) -// return nil -//} +func (l *LogicalLoader) preExecute() error { + // 临时清理 init_connect + dbListDrop := l.cnf.LogicalLoad.DBListDropIfExists + var initConnect string + if err := l.dbConn.QueryRow("select @@init_connect").Scan(&initConnect); err != nil { + return err + } + l.initConnect = initConnect + if l.initConnect != "" && strings.TrimSpace(dbListDrop) != "" { + logger.Log.Info("set global init_connect='' for safe") + if _, err := l.dbConn.Exec("set global init_connect=''"); err != nil { + return err + } + } + + // handle DBListDropIfExists + // 如果有设置这个选项,会在运行前执行 drop database if exists 命令,来清理脏库 + if strings.TrimSpace(dbListDrop) != "" { + logger.Log.Info("load logical DBListDropIfExists:", dbListDrop) + if strings.Contains(dbListDrop, "`") { + return errors.Errorf("DBListDropIfExists has invalid character %s", dbListDrop) + } + SysDBs := []string{"mysql", "sys", "information_schema", "performance_schema", "test"} + dblist := strings.Split(dbListDrop, ",") + dblistNew := []string{} + for _, dbName := range dblist { + dbName = strings.TrimSpace(dbName) + if dbName == "" { + continue + } else if cmutil.StringsHas(SysDBs, dbName) { + return errors.Errorf("DBListDropIfExists should not contain sys db: %s", dbListDrop) + } else { + dblistNew = append(dblistNew, dbName) + } + } + + for _, dbName := range dblistNew { + dropDbSql := fmt.Sprintf("DROP DATABASE IF EXISTS `%s`", dbName) + logger.Log.Warn("DBListDropIfExists sql:", dropDbSql) + if _, err := l.dbConn.Exec(dropDbSql); err != nil { + return errors.Wrap(err, "DBListDropIfExists err") + } + } + return nil + } + return nil +} + +// Execute execute loading backup with logical backup tool +func (l *LogicalLoader) Execute() (err error) { + cnfPublic := config.Public{ + MysqlHost: l.cnf.LogicalLoad.MysqlHost, + MysqlPort: l.cnf.LogicalLoad.MysqlPort, + MysqlUser: l.cnf.LogicalLoad.MysqlUser, + MysqlPasswd: l.cnf.LogicalLoad.MysqlPasswd, + MysqlCharset: l.cnf.LogicalLoad.MysqlCharset, + } + l.dbConn, err = mysqlconn.InitConn(&cnfPublic) + if err != nil { + return err + } + defer func() { + _ = l.dbConn.Close() + }() + if err = l.preExecute(); err != nil { + return err + } + + defer func() { + if l.initConnect != "" { + logger.Log.Info("set global init_connect back:", l.initConnect) + if _, err = l.dbConn.Exec(fmt.Sprintf(`set global init_connect="%s"`, l.initConnect)); err != nil { + //return err + logger.Log.Warn("fail set global init_connect back:", l.initConnect) + } + } + }() -func (l *LogicalLoader) Execute() error { binPath := filepath.Join(l.dbbackupHome, "bin/myloader") args := []string{ "-h", l.cnf.LogicalLoad.MysqlHost, @@ -90,24 +129,28 @@ func (l *LogicalLoader) Execute() error { if l.cnf.LogicalLoad.EnableBinlog { args = append(args, "--enable-binlog") } + if l.cnf.LogicalLoad.SchemaOnly { + args = append(args, "--no-data") + } + if l.cnf.LogicalLoad.CreateTableIfNotExists { + args = append(args, "--append-if-not-exist") + } if l.cnf.LogicalLoad.Regex != "" { - args = append(args, []string{ - "-x", fmt.Sprintf(`'%s'`, l.cnf.LogicalLoad.Regex), - }...) + args = append(args, "-x", fmt.Sprintf(`'%s'`, l.cnf.LogicalLoad.Regex)) } // ToDo extraOpt + // myloader 日志输出到当前目录的 logs/myloader_xx.log + pwd, _ := os.Getwd() + logfile := filepath.Join(pwd, "logs", fmt.Sprintf("myloader_%d.log", int(time.Now().Weekday()))) + _ = os.MkdirAll(filepath.Dir(logfile), 0755) - cmd := exec.Command("sh", "-c", - fmt.Sprintf(`%s %s`, binPath, strings.Join(args, " "))) - - logger.Log.Info("load logical command: ", cmd.String()) - - output, err := cmd.CombinedOutput() + args = append(args, ">>", logfile, "2>&1") + logger.Log.Info("load logical command:", binPath, strings.Join(args, " ")) + outStr, errStr, err := cmutil.ExecCommand(true, "", binPath, args...) if err != nil { - logger.Log.Error("load backup failed: ", err, string(output)) - return errors.Wrap(err, string(output)) + logger.Log.Error("load backup failed: ", err, errStr) + return errors.Wrap(err, errStr) } - - logger.Log.Info("load backup success: ", string(output)) + logger.Log.Info("load backup success: ", outStr) return nil } diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/backupexe/loader_physical.go b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/backupexe/loader_physical.go index e16044c763..e50e101d02 100644 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/backupexe/loader_physical.go +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/backupexe/loader_physical.go @@ -3,10 +3,11 @@ package backupexe import ( "fmt" "os" - "os/exec" "path/filepath" "strings" + "time" + "dbm-services/common/go-pubpkg/cmutil" "dbm-services/mysql/db-tools/mysql-dbbackup/pkg/config" "dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/logger" "dbm-services/mysql/db-tools/mysql-dbbackup/pkg/util" @@ -15,12 +16,14 @@ import ( ) // PhysicalLoader this is used to load physical backup +// decompress, apply, recover type PhysicalLoader struct { cnf *config.BackupConfig dbbackupHome string mysqlVersion string storageEngine string innodbCmd InnodbCommand + isOfficial bool } func (p *PhysicalLoader) initConfig(indexContent *IndexContent) error { @@ -33,130 +36,15 @@ func (p *PhysicalLoader) initConfig(indexContent *IndexContent) error { p.dbbackupHome = filepath.Dir(cmdPath) } - p.mysqlVersion = util.VersionParser(indexContent.MysqlVersion) + p.mysqlVersion, p.isOfficial = util.VersionParser(indexContent.MysqlVersion) p.storageEngine = strings.ToLower(indexContent.StorageEngine) - p.innodbCmd.ChooseXtrabackupTool(p.mysqlVersion) + if err := p.innodbCmd.ChooseXtrabackupTool(p.mysqlVersion, p.isOfficial); err != nil { + return err + } return nil } -// createPhysicalLoadInnodbCmd Create PhysicalBackup Cmd(Innodb) -//func (p *PhysicalLoader) createPhysicalLoadInnodbCmd() (string, error) { -// var buffer bytes.Buffer -// binpath := filepath.Join(p.dbbackupHome, "/bin/xtrabackup", p.innodbCmd.innobackupexBin) -// binpath2 := filepath.Join(p.dbbackupHome, "/bin/xtrabackup", p.innodbCmd.xtrabackupBin) -// buffer.WriteString(binpath) -// buffer.WriteString(" --defaults-file=" + p.cnf.PhysicalLoad.DefaultsFile) -// buffer.WriteString(" --ibbackup=" + binpath2) -// if p.cnf.PhysicalLoad.CopyBack { -// buffer.WriteString(" --copy-back") -// } else { -// buffer.WriteString(" --move-back") -// } -// if p.cnf.PhysicalLoad.ExtraOpt != "" { -// buffer.WriteString(fmt.Sprintf(` %s `, p.cnf.PhysicalLoad.ExtraOpt)) -// } -// // targetPath := filepath.Join(cnf.Public.BackupDir, TargetName) -// if strings.Compare(p.mysqlVersion, "005007000") < 0 { -// buffer.WriteString(" " + p.cnf.PhysicalLoad.MysqlLoadDir) -// } else { -// buffer.WriteString(" --target-dir=" + p.cnf.PhysicalLoad.MysqlLoadDir) -// } -// -// cmdStr := buffer.String() -// logger.Log.Info(fmt.Sprintf("load physical backup cmd: %s", cmdStr)) -// return cmdStr, nil -//} - -// createDecompressInnodbCmd create decompress cmd -//func (p *PhysicalLoader) createDecompressInnodbCmd() (string, error) { -// var buffer bytes.Buffer -// binpath := filepath.Join(p.dbbackupHome, "/bin/xtrabackup", p.innodbCmd.innobackupexBin) -// buffer.WriteString(binpath) -// buffer.WriteString(" --decompress") -// buffer.WriteString(" --qpress=" + filepath.Join(p.dbbackupHome, "/bin/xtrabackup", "qpress")) -// buffer.WriteString(" --parallel=" + strconv.Itoa(p.cnf.PhysicalLoad.Threads)) -// // targetPath := filepath.Join(cnf.Public.BackupDir, TargetName) -// if strings.Compare(p.mysqlVersion, "005007000") < 0 { -// buffer.WriteString(" " + p.cnf.PhysicalLoad.MysqlLoadDir) -// } else { -// buffer.WriteString(" --target-dir=" + p.cnf.PhysicalLoad.MysqlLoadDir) -// } -// cmdStr := buffer.String() -// logger.Log.Info(fmt.Sprintf("decompress cmd: %s", cmdStr)) -// return cmdStr, nil -//} - -//// createApplyInnodbCmd Create ApplyInnodb Cmd -//func (p *PhysicalLoader) createApplyInnodbCmd() (string, error) { -// var buffer bytes.Buffer -// binpath := filepath.Join(p.dbbackupHome, "/bin/xtrabackup", p.innodbCmd.innobackupexBin) -// binpath2 := filepath.Join(p.dbbackupHome, "/bin/xtrabackup", p.innodbCmd.xtrabackupBin) -// buffer.WriteString(binpath) -// buffer.WriteString(" --parallel=" + strconv.Itoa(p.cnf.PhysicalLoad.Threads)) -// buffer.WriteString(" --ibbackup=" + binpath2) -// buffer.WriteString(" --use-memory=1GB") -// // targetPath := filepath.Join(cnf.Public.BackupDir, TargetName) -// if strings.Compare(p.mysqlVersion, "005007000") < 0 { -// buffer.WriteString(" --apply-log") -// } else { -// buffer.WriteString(" --prepare") -// } -// if strings.Compare(p.mysqlVersion, "005007000") < 0 { -// buffer.WriteString(" " + p.cnf.PhysicalLoad.MysqlLoadDir) -// } else { -// buffer.WriteString(" --target-dir=" + p.cnf.PhysicalLoad.MysqlLoadDir) -// } -// cmdStr := buffer.String() -// logger.Log.Info(fmt.Sprintf("apply-log cmd: %s", cmdStr)) -// return cmdStr, nil -//} - -//// ExecuteInnodbLoader TODO -//func (p *PhysicalLoader) ExecuteInnodbLoader() error { -// var cmdStr string -// var err error -// if cmdStr, err = p.createDecompressInnodbCmd(); err != nil { -// logger.Log.Error("Failed to create the cmd_line of loading backup, error: ", err) -// return err -// } -// if err := util.ExeCommand(cmdStr); err != nil { -// return err -// } -// -// if cmdStr, err = p.createApplyInnodbCmd(); err != nil { -// logger.Log.Error("Failed to create the cmd_line of loading backup, error: ", err) -// return err -// } -// if err = util.ExeCommand(cmdStr); err != nil { -// return err -// } -// -// if cmdStr, err = p.createPhysicalLoadInnodbCmd(); err != nil { -// logger.Log.Error("Failed to create the cmd_line of loading backup, error: ", err) -// return err -// } -// if err = util.ExeCommand(cmdStr); err != nil { -// return err -// } -// return nil -//} -// -//// Execute execute multiple commands to load physicalbackup -//func (p *PhysicalLoader) Execute() error { -// if p.storageEngine == "innodb" { -// err := p.ExecuteInnodbLoader() -// if err != nil { -// logger.Log.Error("Failed to create the cmd_line of loading backup, error: ", err) -// return err -// } -// } else { -// logger.Log.Error(fmt.Sprintf("This is a unknown StorageEngine: %s", p.storageEngine)) -// err := fmt.Errorf("unknown StorageEngine: %s", p.storageEngine) -// return err -// } -// return nil -//} - +// Execute excute loading backup with physical backup tool func (p *PhysicalLoader) Execute() error { if p.storageEngine != "innodb" { err := fmt.Errorf("%s engine not supported", p.storageEngine) @@ -183,11 +71,11 @@ func (p *PhysicalLoader) Execute() error { } func (p *PhysicalLoader) decompress() error { - binPath := filepath.Join(p.dbbackupHome, "/bin/xtrabackup", p.innodbCmd.innobackupexBin) + binPath := filepath.Join(p.dbbackupHome, p.innodbCmd.innobackupexBin) args := []string{ "--decompress", - fmt.Sprintf("--qpress=%s", filepath.Join(p.dbbackupHome, "/bin/xtrabackup", "qpress")), + //fmt.Sprintf("--qpress=%s", filepath.Join(p.dbbackupHome, "/bin", "qpress")), fmt.Sprintf("--parallel=%d", p.cnf.PhysicalLoad.Threads), } if strings.Compare(p.mysqlVersion, "005007000") < 0 { @@ -197,26 +85,33 @@ func (p *PhysicalLoader) decompress() error { fmt.Sprintf("--target-dir=%s", p.cnf.PhysicalLoad.MysqlLoadDir), }...) } + if strings.Compare(p.mysqlVersion, "008000000") >= 0 && p.isOfficial { + args = append(args, "--skip-strict") + } - cmd := exec.Command("sh", "-c", - fmt.Sprintf(`%s %s`, binPath, strings.Join(args, " "))) - logger.Log.Info("decompress command: ", cmd.String()) - - err := cmd.Run() + // decompress 日志输出到当前目录的 logs/xtrabackup_xx.log + pwd, _ := os.Getwd() + logfile := filepath.Join(pwd, "logs", fmt.Sprintf("xtrabackup_%d.log", int(time.Now().Weekday()))) + _ = os.MkdirAll(filepath.Dir(logfile), 0755) + // decompress 会把正常日志打印到错误输出 + args = append(args, ">>", logfile, "2>&1") + logger.Log.Info("decompress command:", binPath, strings.Join(args, " ")) + outStr, errStr, err := cmutil.ExecCommand(true, "", binPath, args...) if err != nil { - logger.Log.Error("decompress failed: ", err) - return err + logger.Log.Error("decompress failed: ", err, errStr) + return errors.Wrap(err, errStr) } + logger.Log.Info("decompress success: ", outStr) return nil } func (p *PhysicalLoader) apply() error { - binPath := filepath.Join(p.dbbackupHome, "/bin/xtrabackup", p.innodbCmd.innobackupexBin) + binPath := filepath.Join(p.dbbackupHome, p.innodbCmd.innobackupexBin) args := []string{ fmt.Sprintf("--parallel=%d", p.cnf.PhysicalLoad.Threads), fmt.Sprintf( - "--ibbackup=%s", filepath.Join(p.dbbackupHome, "/bin/xtrabackup", p.innodbCmd.xtrabackupBin)), + "--ibbackup=%s", filepath.Join(p.dbbackupHome, p.innodbCmd.xtrabackupBin)), "--use-memory=1GB", } @@ -234,31 +129,39 @@ func (p *PhysicalLoader) apply() error { }...) } - cmd := exec.Command("sh", "-c", - fmt.Sprintf(`%s %s`, binPath, strings.Join(args, " "))) - logger.Log.Info("apply command: ", cmd.String()) + if strings.Compare(p.mysqlVersion, "008000000") >= 0 && p.isOfficial { + args = append(args, "--skip-strict") + } + + // apply 日志输出到当前目录的 logs/xtrabackup_xx.log + pwd, _ := os.Getwd() + logfile := filepath.Join(pwd, "logs", fmt.Sprintf("xtrabackup_%d.log", int(time.Now().Weekday()))) + _ = os.MkdirAll(filepath.Dir(logfile), 0755) - err := cmd.Run() + args = append(args, ">>", logfile, "2>&1") + logger.Log.Info("physical apply command:", binPath, strings.Join(args, " ")) + outStr, errStr, err := cmutil.ExecCommand(true, "", binPath, args...) if err != nil { - logger.Log.Error("apply failed: ", err) - return err + logger.Log.Error("physical apply failed: ", err, errStr) + return errors.Wrap(err, errStr) } + logger.Log.Info("physical apply success: ", outStr) return nil } func (p *PhysicalLoader) load() error { - binPath := filepath.Join(p.dbbackupHome, "/bin/xtrabackup", p.innodbCmd.innobackupexBin) + binPath := filepath.Join(p.dbbackupHome, p.innodbCmd.innobackupexBin) args := []string{ fmt.Sprintf("--defaults-file=%s", p.cnf.PhysicalLoad.DefaultsFile), fmt.Sprintf( - "--ibbackup=%s", filepath.Join(p.dbbackupHome, "/bin/xtrabackup", p.innodbCmd.xtrabackupBin)), + "--ibbackup=%s", filepath.Join(p.dbbackupHome, p.innodbCmd.xtrabackupBin)), } if p.cnf.PhysicalLoad.CopyBack { - args = append(args, "--copy-backup") + args = append(args, "--copy-back") } else { - args = append(args, "--move-backup") + args = append(args, "--move-back") } if strings.Compare(p.mysqlVersion, "005007000") < 0 { @@ -269,16 +172,23 @@ func (p *PhysicalLoader) load() error { }...) } - // ToDo extraopt - - cmd := exec.Command("sh", "-c", - fmt.Sprintf(`%s %s`, binPath, strings.Join(args, " "))) - logger.Log.Info("load command: ", cmd.String()) + if strings.Compare(p.mysqlVersion, "008000000") >= 0 && p.isOfficial { + args = append(args, "--skip-strict") + } - err := cmd.Run() + // ToDo extraopt + // xtrabackup 日志输出到当前目录的 logs/xtrabackup_xx.log + pwd, _ := os.Getwd() + logfile := filepath.Join(pwd, "logs", fmt.Sprintf("xtrabackup_%d.log", int(time.Now().Weekday()))) + _ = os.MkdirAll(filepath.Dir(logfile), 0755) + + args = append(args, ">>", logfile, "2>&1") + logger.Log.Info("xtrabackup recover command:", binPath, strings.Join(args, " ")) + outStr, errStr, err := cmutil.ExecCommand(true, "", binPath, args...) if err != nil { - logger.Log.Error("load failed: ", err) - return err + logger.Log.Error("xtrabackup recover failed: ", err, errStr) + return errors.Wrap(err, errStr) } + logger.Log.Info("xtrabackup recover success: ", outStr) return nil } diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/dbareport/backup_result.go b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/dbareport/backup_result.go index 3df72a0da2..d0a221bdb4 100644 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/dbareport/backup_result.go +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/dbareport/backup_result.go @@ -25,7 +25,7 @@ type BackupResult struct { TimeZone string `json:"time_zone"` ClusterId int `json:"cluster_id"` ClusterAddress string `json:"cluster_address"` - ShardValue int `ini:"shard_value"` // 分片 id,仅 spider 有用 + ShardValue int `json:"shard_value"` // 分片 id,仅 spider 有用 MysqlHost string `json:"mysql_host"` MysqlPort int `json:"mysql_port"` MasterHost string `json:"master_host"` @@ -108,7 +108,7 @@ func (b *BackupResult) PrepareXtraBackupInfo(cnf *config.BackupConfig) error { return err } exepath = filepath.Dir(exepath) - binpath := filepath.Join(exepath, "/bin/xtrabackup", "qpress") + binpath := filepath.Join(exepath, "/bin", "qpress") // parse xtrabackup_info if err := parseXtraInfo(b, binpath, xtrabackupInfoFileName, tmpFileName); err != nil { diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/dbareport/prepareinfo.go b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/dbareport/prepareinfo.go index 0b22eab354..00e8c7c446 100644 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/dbareport/prepareinfo.go +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/dbareport/prepareinfo.go @@ -93,8 +93,8 @@ func parseMydumperMetadata(metadataFile string) (*mydumperMetadata, error) { kv := strings.SplitN(l, "=", 2) key := strings.TrimSpace(strings.TrimLeft(kv[0], "#")) valTmp := strings.SplitN(kv[1], "# ", 2) - val := strings.TrimSpace(strings.Trim(valTmp[0], "'")) - logger.Log.Infof("key=%s val=%s", key, val) + val := strings.TrimSpace(strings.Trim(valTmp[0], "' ")) + logger.Log.Debugf("key=%s val=%s", key, val) if flagMaster { metadata.MasterStatus[key] = val } else if flagSlave { @@ -162,7 +162,7 @@ func parseXtraTimestamp(backupResult *BackupResult, binpath string, fileName str xtrabackupTimestampFileExist := true if err != nil { xtrabackupTimestampFileExist = false - return nil + //return nil } defer func() { _ = tmpFile.Close() @@ -177,9 +177,12 @@ func parseXtraTimestamp(backupResult *BackupResult, binpath string, fileName str } backupResult.ConsistentBackupTime = consistentTime.Format("2006-01-02 15:04:05") } - } /* else { - b.ConsistentBackupTime = startTime - } */ + } else { + // 此时刚备份完成,还没有开始打包,这里把当前时间认为是 consistent_time,不完善! + logger.Log.Warnf("xtrabackup_info file not found: %s, use current time as Consistent Time", fileName) + // TODO 时区问题,待处理 + backupResult.ConsistentBackupTime = time.Now().Format("2006-01-02 15:04:05") + } return nil } diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/dbareport/report.go b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/dbareport/report.go index 2ec790a2fa..d95124b7ee 100644 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/dbareport/report.go +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/dbareport/report.go @@ -12,6 +12,7 @@ import ( "strings" "time" + "dbm-services/common/go-pubpkg/backupclient" "dbm-services/common/go-pubpkg/cmutil" "dbm-services/mysql/db-tools/dbactuator/pkg/core/cst" "dbm-services/mysql/db-tools/mysql-dbbackup/pkg/config" @@ -120,6 +121,26 @@ func (r *Reporter) ReportBackupStatus(status string) error { // ExecuteBackupClient execute backup_client which sends files to backup system func (r *Reporter) ExecuteBackupClient(fileName string) (taskid string, err error) { + if r.cfg.BackupClient.Enable { + backupClient, err := backupclient.New(r.cfg.BackupClient.BackupClientBin, "", r.cfg.BackupClient.FileTag) + if err != nil { + return "", err + } + logger.Log.Infof("upload register file %s", fileName) + taskid, err = backupClient.Upload(fileName) + logger.Log.Infof("upload register file %s with taskid=%s, err=%v", fileName, taskid, err) + if err != nil { + return "", err + } + } else { + taskid = "-1" + logger.Log.Infof("backup_client is not enabled: %s taskid=%s", fileName, taskid) + } + return taskid, nil +} + +// ExecuteBackupClient2 execute backup_client which sends files to backup system +func (r *Reporter) ExecuteBackupClient2(fileName string) (taskid string, err error) { var checksumStr string var filesystemStr string if r.cfg.BackupClient.Enable { @@ -165,6 +186,7 @@ func (r *Reporter) ExecuteBackupClient(fileName string) (taskid string, err erro } // ReportBackupResult Report BackupResult info +// 会执行 ExecuteBackupClient upload func (r *Reporter) ReportBackupResult(backupBaseResult BackupResult) error { var backupResultArray []BackupResult @@ -189,7 +211,8 @@ func (r *Reporter) ReportBackupResult(backupBaseResult BackupResult) error { if match { // execute backup_client, and send file to backup system var taskId string - if taskId, err = r.ExecuteBackupClient(entry.Name()); err != nil { + fileName := filepath.Join(r.cfg.Public.BackupDir, entry.Name()) + if taskId, err = r.ExecuteBackupClient(fileName); err != nil { return err } backupTaskResult := backupBaseResult diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/logger/dbbackuplog.go b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/logger/dbbackuplog.go index 790869654e..425609ea84 100644 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/logger/dbbackuplog.go +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/logger/dbbackuplog.go @@ -5,6 +5,7 @@ import ( "os" "path/filepath" + "github.com/pkg/errors" "github.com/sirupsen/logrus" "gopkg.in/natefinch/lumberjack.v2" @@ -14,8 +15,10 @@ import ( // Log TODO var Log *logrus.Logger +const DefaultLogFileName = "dbbackup.log" + // InitLog Initialize dbbackupLog -func InitLog() (err error) { +func InitLog(logFileName string) (err error) { Log = logrus.New() Log.SetFormatter(&logrus.TextFormatter{ FullTimestamp: true, @@ -26,8 +29,18 @@ func InitLog() (err error) { if !cmutil.IsDirectory(logDir) { _ = os.Mkdir(logDir, 0755) } + if logFileName == "" { + logFileName = DefaultLogFileName + } + logFile := filepath.Join(logDir, logFileName) + // lumberjack 强制写死的新文件权限是 0644,但会继承已经存在的文件权限,所以提前创建文件 + if f, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644); err != nil { + return errors.Wrap(err, "open log file") + } else { + f.Close() + } Log.SetOutput(&lumberjack.Logger{ - Filename: filepath.Join(logDir, "dbbackup.log"), + Filename: logFile, MaxSize: 50, // megabytes MaxBackups: 3, MaxAge: 28, // days diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/mysqlconn/conn.go b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/mysqlconn/conn.go index ddc3058eff..b7bf57765f 100644 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/mysqlconn/conn.go +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/mysqlconn/conn.go @@ -11,6 +11,7 @@ import ( "github.com/pkg/errors" "github.com/spf13/cast" + "dbm-services/common/go-pubpkg/mysqlcomm" "dbm-services/mysql/db-tools/mysql-dbbackup/pkg/config" "dbm-services/mysql/db-tools/mysql-dbbackup/pkg/cst" "dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/logger" @@ -213,10 +214,11 @@ func IsPrimarySpider(spiderInst InsObject) (bool, error) { return isPrimary, err } +// GetTdbctlInst build tdbctl instance with spider port + 1000 func GetTdbctlInst(spiderInst InsObject) InsObject { ctlInst := InsObject{ Host: spiderInst.Host, - Port: spiderInst.Port + 1000, // tdbctl port = spider_port + 1000 + Port: mysqlcomm.GetTdbctlPortBySpider(spiderInst.Port), User: spiderInst.User, Pwd: spiderInst.Pwd, } diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/precheck/check_charset.go b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/precheck/check_charset.go index 192b4b4aa3..6cd21fd93e 100644 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/precheck/check_charset.go +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/precheck/check_charset.go @@ -29,7 +29,8 @@ func CheckCharset(cnf *config.Public) error { if verErr != nil { return verErr } - if strings.Compare(util.VersionParser(version), "005005003") == -1 { // mysql_version <5.5.3 + verStr, _ := util.VersionParser(version) + if strings.Compare(verStr, "005005003") == -1 { // mysql_version <5.5.3 superCharset = "utf8" } else { superCharset = "utf8mb4" diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/spider/global_backup.go b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/spider/global_backup.go index 404d189be7..e84adb66ba 100644 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/spider/global_backup.go +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/spider/global_backup.go @@ -17,23 +17,33 @@ import ( ) const ( - // StatusInit TODO + // StatusInit 刚初始化还未开始的备份任务 StatusInit = "init" - // StatusSuccess TODO + // StatusSuccess 备份成功 StatusSuccess = "success" - // StatusRunning TODO + // StatusRunning 备份运行中 StatusRunning = "running" - // StatusFailed TODO + // StatusFailed 备份失败 StatusFailed = "failed" // StatusQuit 手动设置为 quit 状态的任务,会自动kill掉 StatusQuit = "quit" + // StatusReplicated master 发给从库的备份任务,相当于从库的 init. replicated 状态在remote master上不会变化 + StatusReplicated = "replicated" // StatusUnknown TODO StatusUnknown = "unknown" ) +// isBackupStatusInit init 和 replicated 都是待执行备份 +// 但都需要检查当前备份任务的 host:port 是否是自身实例 +// +// 因为 remote master 实例里会存在 master(自身 init)、slave(从库 replicated) 两个任务 +func isBackupStatusInit(backupStatus string) bool { + return backupStatus == StatusInit || backupStatus == StatusReplicated +} + // GlobalBackupModel TODO type GlobalBackupModel struct { - ServerName string `json:"Server_name" db:"Server_name"` + ServerName string `json:"ServerName" db:"ServerName"` Host string `json:"Host" db:"Host"` Port int `json:"Port" db:"Port"` Wrapper string `json:"Wrapper" db:"Wrapper"` @@ -50,12 +60,15 @@ type GlobalBackupModel struct { type GlobalBackup struct { *GlobalBackupModel - retries int - localLog *logrus.Entry + retries int + localLog *logrus.Entry + // instObj spider instance object instObj *mysqlconn.InsObject cnfFile string cnfObj config.Public shardValue int + // tdbctlInstObj 中控实例 + tdbctlInstObj *mysqlconn.InsObject } // TableName TODO @@ -80,7 +93,7 @@ func (e GlobalBackupList) Len() int { // Less 用于排序 func (e GlobalBackupList) Less(i, j int) bool { - if e[i].CreatedAt > e[j].CreatedAt { + if e[i].CreatedAt < e[j].CreatedAt { return true } return false @@ -110,18 +123,18 @@ func buildSchema(db *sqlx.DB) string { logger.Log.Errorf("buildSchema getSpiderPartitions: %v", err) return "" } - return globalBackup("SPIDER", partValues) + return GetGlobalBackupSchema("SPIDER", partValues) } else { - return globalBackup("InnoDB", nil) + return GetGlobalBackupSchema("InnoDB", nil) } } -func globalBackup(tableEngine string, partValues []int) string { +func GetGlobalBackupSchema(tableEngine string, partValues []int) string { // 真正的唯一性是:BackupId,Host,Port // ShardValue 是为了路由到对应的分片 createTable := fmt.Sprintf(` CREATE TABLE IF NOT EXISTS %s.global_backup ( - Server_name varchar(10) NOT NULL DEFAULT '', + ServerName varchar(10) NOT NULL DEFAULT '', Wrapper varchar(20) NOT NULL DEFAULT '', Host varchar(60) NOT NULL DEFAULT '', Port int(4) NOT NULL DEFAULT 0, diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/spider/mysql_servers.go b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/spider/mysql_servers.go index 3280b7f5e5..ab85297b3d 100644 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/spider/mysql_servers.go +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/spider/mysql_servers.go @@ -19,6 +19,8 @@ type MysqlServer struct { ServerName string `json:"Server_name" db:"Server_name"` Host string `json:"Host" db:"Host"` Port int `json:"Port" db:"Port"` + Username string `json:"Username" db:"Username"` + Password string `json:"Password" db:"Password"` Wrapper string `json:"Wrapper" db:"Wrapper"` PartValue int // PARTITION_DESCRIPTION } diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/spider/spider.go b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/spider/spider.go index b11064fff3..3868c5d0ce 100644 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/spider/spider.go +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/spider/spider.go @@ -3,6 +3,7 @@ package spider import ( "bytes" + "encoding/json" "fmt" "os" "os/exec" @@ -18,7 +19,6 @@ import ( "github.com/spf13/viper" "dbm-services/common/go-pubpkg/cmutil" - "dbm-services/mysql/db-tools/mysql-dbbackup/pkg/cst" "github.com/jmoiron/sqlx" @@ -49,10 +49,13 @@ func ScheduleBackup(cnf *config.Public) error { return err } defer dbw.Close() + // cnf.BackupId 可能是用户传进来的 backup-id var b = GlobalBackupModel{Host: spiderInst.Host, Port: spiderInst.Port, BackupId: cnf.BackupId} + tdbctlInstObj := mysqlconn.GetTdbctlInst(spiderInst) var globalBackup = GlobalBackup{ GlobalBackupModel: &b, instObj: &spiderInst, + tdbctlInstObj: &tdbctlInstObj, // 用于在中控查询 slave backup_status 信息 localLog: logger.Log.WithField("Port", cnf.MysqlPort), } var backupId string @@ -66,7 +69,7 @@ func ScheduleBackup(cnf *config.Public) error { if viper.GetBool("schedule.wait") { ch := make(chan error, 1) go func() { - err := globalBackup.waitBackupDone(backupId, dbw.Db) + err := globalBackup.waitBackupDone(backupId) ch <- err }() select { @@ -85,6 +88,43 @@ func ScheduleBackup(cnf *config.Public) error { return nil } +// QueryBackup query backup status +func QueryBackup(cnf *config.Public, backupStatus []string) error { + spiderInst := mysqlconn.InsObject{ + Host: cnf.MysqlHost, + Port: cnf.MysqlPort, + User: cnf.MysqlUser, + Pwd: cnf.MysqlPasswd, + } + isPrimary, err := mysqlconn.IsPrimarySpider(spiderInst) + if err != nil { + logger.Log.Warn(err.Error()) + return err + } else if isPrimary { + logger.Log.Infof("current host spider and tdbctl is primary") + dbw, err := spiderInst.Conn() + if err != nil { + return err + } + defer dbw.Close() + var b = GlobalBackupModel{Host: spiderInst.Host, Port: spiderInst.Port, BackupId: cnf.BackupId} + tdbctlInstObj := mysqlconn.GetTdbctlInst(spiderInst) + var globalBackup = GlobalBackup{ + GlobalBackupModel: &b, + instObj: &spiderInst, + tdbctlInstObj: &tdbctlInstObj, // 用于在中控查询 slave backup_status 信息 + localLog: logger.Log.WithField("Port", cnf.MysqlPort), + } + tasks, err := globalBackup.queryBackupStatusById(cnf.BackupId, backupStatus) + if err != nil { + return err + } + sort.Sort(sort.Reverse(GlobalBackupList(tasks))) + printBackup(tasks, viper.GetString("query.format")) + } + return nil +} + // InstBackupTask 记录某个 instance 的所有备份任务信息 type InstBackupTask struct { tasks []*GlobalBackupModel @@ -157,7 +197,11 @@ func RunBackupTasks(cnfList []*config.Public) error { return err } } else { - printBackup(backupIdTasks) + var tasks []*GlobalBackupModel + for i, t := range backupIdTasks { + tasks[i] = t.earliestBackupTask + } + printBackup(tasks, "") } } else { logger.Log.Info("no backup tasks for this host") @@ -168,25 +212,31 @@ func RunBackupTasks(cnfList []*config.Public) error { return nil } -func printBackup(tasks []InstBackupTask) { +func printBackup(tasks []*GlobalBackupModel, format string) { + if format == "json" { + jsonBytes, _ := json.Marshal(tasks) + fmt.Println(string(jsonBytes)) + return + } table := tablewriter.NewWriter(os.Stdout) table.SetAutoWrapText(false) - table.SetRowLine(false) table.SetAutoFormatHeaders(false) - table.SetHeader([]string{"BackupId", "BackupStatus", "Host", "Port", "ShardValue", "CreatedAt"}) - + table.SetAutoMergeCellsByColumnIndex([]int{0}) + table.SetRowLine(true) + table.SetHeader([]string{"BackupId", "ServerName", "BackupStatus", "Host", "Port", "ShardValue", "CreatedAt"}) for _, t := range tasks { - if t.earliestBackupTask != nil { - b := t.earliestBackupTask + if t != nil { table.Append([]string{ - b.BackupId, - b.BackupStatus, - b.Host, - cast.ToString(b.Port), - cast.ToString(b.ShardValue), - b.CreatedAt}) + t.BackupId, + t.ServerName, + t.BackupStatus, + t.Host, + cast.ToString(t.Port), + cast.ToString(t.ShardValue), + t.CreatedAt}) } } + table.SetFooter([]string{"Rows", cast.ToString(table.NumLines()), "", "", "", "", ""}) table.Render() } func runBackup(tasks []InstBackupTask) error { @@ -231,12 +281,12 @@ func (g GlobalBackup) runBackup(task InstBackupTask) error { if backupStatusInDB, err := g.checkBackupStatus(dbw.Db); err != nil { return errors.WithMessage(err, "checkBackupStatus") } else { - if backupStatusInDB != StatusInit { + if !isBackupStatusInit(backupStatusInDB) { g.localLog.Infof("backupStatus changed, in db:%s ", backupStatusInDB) } if backupStatusInDB == StatusSuccess { return nil - } else if backupStatusInDB != StatusInit { + } else if !isBackupStatusInit(backupStatusInDB) { return errors.Errorf("backupStatus changed, got %s", backupStatusInDB) } } @@ -358,6 +408,11 @@ func (instTask *InstBackupTask) filterBackupTasks(cnf *config.Public) (err error instTask.shardValue = cnf.ShardValue // 配置文件里面不一定有配置上 ShardValue b := GlobalBackupModel{Host: instObj.Host, Port: instObj.Port} + if strings.HasPrefix(cnf.MysqlRole, "spider_") { + // 如果当前节点是 spider node,查询备份的时候为了避免跨分片查询所有后端,这里优化下指定 wrapper=SPIDER + // 在 queryBackupTasks 会作为条件 + b.Wrapper = cst.WrapperSpider + } instTask.tasks, err = b.queryBackupTasks(0, dbw.Db) if err != nil { return err @@ -370,7 +425,7 @@ func (instTask *InstBackupTask) filterBackupTasks(cnf *config.Public) (err error if instTask.shardValue < 0 { // 从 global_backup 中获取 ShardValue 信息,理论上这 2 个应该是相同的 instTask.shardValue = instTask.tasks[0].ShardValue // 这里一定会有实例,不会panic } - if t.BackupStatus == StatusInit { + if isBackupStatusInit(t.BackupStatus) { instTask.taskInit = append(instTask.taskInit, t) if p, ok := instTask.backupTaskInit[t.BackupId]; ok { return errors.Errorf("backup_id %s has to ports [%d,%d] in this instance", t.BackupId, p, t.Port) @@ -389,54 +444,107 @@ func (instTask *InstBackupTask) filterBackupTasks(cnf *config.Public) (err error return nil } -func (g GlobalBackup) waitBackupDone(backupId string, db *sqlx.DB) error { - var dbWorkersCollect []*mysqlconn.DbWorker - defer func() { - for _, ele := range dbWorkersCollect { - _ = ele.Close +// getBackupStatusByWrapper wrapper = mysql|mysql_slave +func (g GlobalBackup) getBackupStatusByWrapper(backupId string, wrapper string) ([]*GlobalBackupModel, error) { + tdbctlDbw, err := g.tdbctlInstObj.Conn() + if err != nil { + logger.Log.Warnf("getBackupStatusByWrapper connect tdbctl error:%s", err.Error()) + return nil, err + } + defer tdbctlDbw.Close() + var tasks []*GlobalBackupModel + slaveSQL := fmt.Sprintf("select BackupId,ServerName,Wrapper,Host,Port,ShardValue,BackupStatus,CreatedAt "+ + "from %s where Wrapper='%s'", g.GlobalBackupModel.TableName(), wrapper) + if backupId != "" { + slaveSQL += fmt.Sprintf(" and BackupId='%s'", backupId) + } + logger.Log.Warnf("TdbctlQueryByRoleWithMerge sql:%s", slaveSQL) + + if err := TdbctlQueryByRoleWithMerge(&tasks, wrapper, slaveSQL, tdbctlDbw.Db); err != nil { + logger.Log.Warnf("TdbctlQueryByRoleWithMerge error:%s", err.Error()) + return nil, err + } + logger.Log.Warnf("TdbctlQueryByRoleWithMerge slave tasks:%+v", tasks) + return tasks, nil +} + +// getBackupStatusMaster 获取 SPIDER, mysql(remote master)的备份状态 +// 通过是否是 StatusReplicated 来区分,即不返回 replicated 状态的任务 +func (g GlobalBackup) getBackupStatusMaster(backupId string) ([]*GlobalBackupModel, error) { + spiderDbw, err := g.instObj.Conn() + if err != nil { + logger.Log.Warnf("getBackupStatusMaster connect spider error:%s", err.Error()) + return nil, err + } + defer spiderDbw.Close() + sqlBuilder := sq.Select("*"). + From(g.GlobalBackupModel.TableName()).Where("BackupStatus != ?", StatusReplicated) + if backupId != "" { + sqlBuilder = sqlBuilder.Where("BackupId = ?", backupId) + } + sqlStr, sqlArgs := sqlBuilder.MustSql() + var tasks []*GlobalBackupModel + if err = spiderDbw.Db.Select(&tasks, sqlStr, sqlArgs...); err != nil { + logger.Log.Warnf("getBackupStatusMaster error:%s", err.Error()) + return nil, err + } + return tasks, nil +} + +func (g GlobalBackup) queryBackupStatusById(backupId string, backupStatus []string) ([]*GlobalBackupModel, error) { + tasks, err := g.getBackupStatusMaster(backupId) + slaveTasks, err1 := g.getBackupStatusByWrapper(backupId, cst.WrapperRemoteSlave) + if err == nil && err1 == nil { + tasks = append(tasks, slaveTasks...) + } else if err != nil || err1 != nil { + return nil, err + } + if len(backupStatus) > 0 { + tasksFiltered := make([]*GlobalBackupModel, len(tasks)) + for _, t := range tasks { + backupStatusTrimmed := strings.SplitN(t.BackupStatus, ":", 2)[0] + if cmutil.StringsHas(backupStatus, backupStatusTrimmed) { + tasksFiltered = append(tasksFiltered, t) + } } - }() + return tasksFiltered, nil + } else { + return tasks, nil + } +} +// waitBackupDone wait a backupId to done +// need to check remote master and remote slave +func (g GlobalBackup) waitBackupDone(backupId string) error { for true { - time.Sleep(1 * time.Minute) + time.Sleep(60 * time.Second) var statusTasks = map[string]int{ - StatusInit: 0, - StatusRunning: 0, - StatusFailed: 0, - StatusSuccess: 0, - StatusUnknown: 0, + StatusInit: 0, + StatusRunning: 0, + StatusFailed: 0, + StatusSuccess: 0, + StatusUnknown: 0, + StatusReplicated: 0, } - sqlBuilder := sq.Select("Server_name", "Wrapper", "Host", "Port", "ShardValue", "BackupStatus"). - From(g.GlobalBackupModel.TableName()). - Where("BackupId = ?", backupId) - sqlStr, sqlArgs, err := sqlBuilder.ToSql() - if err != nil { - return err - } - var tasks []*GlobalBackupModel - if err = db.Select(&tasks, sqlStr, sqlArgs...); err != nil { - logger.Log.Warnf("waitBackupDone error:%s", err.Error()) + + tasks, err := g.getBackupStatusMaster(backupId) + slaveTasks, err1 := g.getBackupStatusByWrapper(backupId, cst.WrapperRemoteSlave) + if err == nil && err1 == nil { + tasks = append(tasks, slaveTasks...) + } else if err != nil || err1 != nil { if g.retries > 120 { - return errors.Errorf("backup[%s] waitBackupDone failed", backupId) - } - if cmutil.NewMySQLError(err).Code == 2002 { - _ = db.Close() - dbw, err := g.instObj.Conn() - if err == nil { - db = dbw.Db - //defer dbw.Close() - dbWorkersCollect = append(dbWorkersCollect, dbw) - } else { - logger.Log.Warnf("reconnect failed: %s", err.Error()) - } + return errors.Errorf("backup progress [%s] waitBackupDone failed", backupId) + } else { + logger.Log.Warnf("get backup status for wait_done failed: err=%v, err1=%v", err, err1) + g.retries += 1 + continue } - g.retries += 1 - continue - // return err } + logger.Log.Warnf("all backupTasks for backupId=%s:%+v", backupId, tasks) for _, t := range tasks { - if t.BackupStatus == StatusSuccess || t.BackupStatus == StatusRunning || t.BackupStatus == StatusInit { + if t.BackupStatus == StatusSuccess || t.BackupStatus == StatusRunning || + t.BackupStatus == StatusInit || t.BackupStatus == StatusReplicated { statusTasks[t.BackupStatus] += 1 } else if strings.HasPrefix(t.BackupStatus, StatusFailed) || strings.HasPrefix(t.BackupStatus, StatusQuit) { statusTasks[StatusFailed] += 1 @@ -444,8 +552,8 @@ func (g GlobalBackup) waitBackupDone(backupId string, db *sqlx.DB) error { statusTasks[StatusUnknown] += 1 } } - if statusTasks[StatusInit]+statusTasks[StatusRunning] == 0 { - finishInfo := fmt.Sprintf("backup[%s] finish with: %v", backupId, statusTasks) + if statusTasks[StatusInit]+statusTasks[StatusRunning]+statusTasks[StatusReplicated] == 0 { + finishInfo := fmt.Sprintf("backup progress [%s] finish with: %v", backupId, statusTasks) if statusTasks[StatusSuccess] != len(tasks) { logger.Log.Warn(finishInfo) return errors.New(finishInfo) @@ -456,7 +564,7 @@ func (g GlobalBackup) waitBackupDone(backupId string, db *sqlx.DB) error { } else { nowTime := time.Now() if nowTime.Minute()%10 == 0 { // 每 10 分钟打印一次日志 - logger.Log.Infof("backup[%s] running: %v", backupId, statusTasks) + logger.Log.Infof("backup progress [%s] status: %v", backupId, statusTasks) } } } diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/spider/spider_service.go b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/spider/spider_service.go index acec1eda56..8ab5258674 100644 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/spider/spider_service.go +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/spider/spider_service.go @@ -12,6 +12,7 @@ import ( "github.com/spf13/cast" "dbm-services/mysql/db-tools/mysql-dbbackup/pkg/cst" + "dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/dbareport" "dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/logger" "dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/mysqlconn" ) @@ -39,7 +40,6 @@ func (g GlobalBackup) prepareBackup(tdbctlInst mysqlconn.InsObject) (string, []M if s.Wrapper == cst.WrapperRemote || s.Wrapper == cst.WrapperRemoteSlave { if strings.HasPrefix(s.ServerName, "SPT_SLAVE") { s.PartValue = cast.ToInt(strings.TrimPrefix(s.ServerName, "SPT_SLAVE")) - logger.Log.Warnf("s.PartValue=%d, s.ServerName=%s", s.PartValue, s.ServerName) } else if strings.HasPrefix(s.ServerName, "SPT") { s.PartValue = cast.ToInt(strings.TrimPrefix(s.ServerName, "SPT")) } else { @@ -48,6 +48,7 @@ func (g GlobalBackup) prepareBackup(tdbctlInst mysqlconn.InsObject) (string, []M backupServers = append(backupServers, s) } else if s.Host == g.Host && s.Wrapper == cst.WrapperSpider { // primary spider / tdbctl + s.PartValue = cst.SpiderNodeShardValue backupServers = append(backupServers, s) } } @@ -55,7 +56,7 @@ func (g GlobalBackup) prepareBackup(tdbctlInst mysqlconn.InsObject) (string, []M if g.BackupId != "" { backupId = g.BackupId } else { - if backupId, err = dbw.GetOneValue(`select uuid()`); err != nil { + if backupId, err = dbareport.GenerateUUid(); err != nil { return "", nil, err } g.BackupId = backupId @@ -65,9 +66,13 @@ func (g GlobalBackup) prepareBackup(tdbctlInst mysqlconn.InsObject) (string, []M func (g GlobalBackup) initializeBackup(backupServers []MysqlServer, dbw *mysqlconn.DbWorker) error { sqlI := sq.Insert(g.GlobalBackupModel.TableName()). - Columns("Server_name", "Wrapper", "Host", "Port", "ShardValue", "BackupId", "BackupStatus") + Columns("ServerName", "Wrapper", "Host", "Port", "ShardValue", "BackupId", "BackupStatus") for _, s := range backupServers { - sqlI = sqlI.Values(s.ServerName, s.Wrapper, s.Host, s.Port, s.PartValue, g.BackupId, StatusInit) + if strings.HasPrefix(s.ServerName, "SPT_SLAVE") { + sqlI = sqlI.Values(s.ServerName, s.Wrapper, s.Host, s.Port, s.PartValue, g.BackupId, StatusReplicated) + } else { + sqlI = sqlI.Values(s.ServerName, s.Wrapper, s.Host, s.Port, s.PartValue, g.BackupId, StatusInit) + } } sqlStr, sqlArgs := sqlI.MustSql() logger.Log.Infof("init backup tasks:%+v, %+v", sqlStr, sqlArgs) @@ -104,13 +109,17 @@ func (b GlobalBackupModel) checkBackupStatus(db *sqlx.DB) (string, error) { // queryBackupTasks 以本机 ip:port 来查询本实例的备份任务 func (b GlobalBackupModel) queryBackupTasks(retries int, db *sqlx.DB) (backupTasks []*GlobalBackupModel, err error) { - sqlBuilder := sq.Select("BackupId", "Host", "Port", "BackupStatus", "ShardValue", "CreatedAt"). + sqlBuilder := sq.Select("BackupId", "ServerName", "Host", "Port", "BackupStatus", "ShardValue", "CreatedAt"). From(b.TableName()). Where("Host = ? and Port = ?", b.Host, b.Port). - Where(sq.Eq{"BackupStatus": []string{StatusInit, StatusRunning}}) + Where(sq.Eq{"BackupStatus": []string{StatusInit, StatusReplicated, StatusRunning}}) // isBackupStatusInit if b.BackupId != "" { sqlBuilder = sqlBuilder.Where("BackupId = ?", b.BackupId) } + if b.Wrapper == cst.WrapperSpider { // 可以避免在 spider node 上备份时,跨分片查询 + sqlBuilder = sqlBuilder.Where("ShardValue = ?", cst.SpiderNodeShardValue) + b.Wrapper = "" // 避免后续可能干扰后续查询条件 + } sqlStr, sqlArgs, err := sqlBuilder.ToSql() if err != nil { return nil, err @@ -118,7 +127,7 @@ func (b GlobalBackupModel) queryBackupTasks(retries int, db *sqlx.DB) (backupTas logger.Log.Infof("queryBackupTasks port=%d, sqlStr:%s, sqlArgs:%v", b.Port, sqlStr, sqlArgs) if err = db.Select(&backupTasks, sqlStr, sqlArgs...); err != nil { - logger.Log.Warnf("fail to queryBackupTasks: %s", err.Error()) + logger.Log.Warnf("fail to queryBackupTasks: %s, retries %d", err.Error(), retries) if err2 := migrateBackupSchema(err, db); err2 != nil { return nil, errors.WithMessagef(err, "migrateBackupSchema failed:%s", err2.Error()) } else { @@ -127,7 +136,7 @@ func (b GlobalBackupModel) queryBackupTasks(retries int, db *sqlx.DB) (backupTas retries += 1 return b.queryBackupTasks(retries, db) } else { - return nil, errors.New("retry queryBackupTasks too much") + return nil, errors.Wrap(err, "retry queryBackupTasks too much") } // return nil, nil } diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/spider/tdbctl.go b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/spider/tdbctl.go new file mode 100644 index 0000000000..d7293effa6 --- /dev/null +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/spider/tdbctl.go @@ -0,0 +1,134 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +package spider + +import ( + "fmt" + "reflect" + + "github.com/jmoiron/sqlx" + "github.com/mohae/deepcopy" + "github.com/pkg/errors" + + "dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/logger" + "dbm-services/mysql/db-tools/mysql-dbbackup/pkg/src/mysqlconn" +) + +// TdbctlQueryOnRemote 在中控指定server 执行命令 +// 注意执行的 sql 不能尽量不要包含 双引号,或者提前转义 +// need ADMIN privileges +func TdbctlQueryOnRemote(serverName string, query string, db *sqlx.DB) (*sqlx.Rows, error) { + sqlStr := fmt.Sprintf("TDBCTL CONNECT NODE '%s' EXECUTE \"%s\"", serverName, query) + rows, err := db.Queryx(sqlStr) + if err != nil { + logger.Log.Errorf("TdbctlQueryOnRemote sql:%s", sqlStr) + } + return rows, err +} + +func TdbctlQueryByRole(dest interface{}, wrapper string, query string, db *sqlx.DB) (map[string]interface{}, error) { + queryServers := fmt.Sprintf("select Server_name,Host,Port,Wrapper "+ + "from mysql.servers where Wrapper='%s'", wrapper) + var servers []*MysqlServer + if err := db.Select(&servers, queryServers); err != nil { + return nil, err + } + destRes := make(map[string]interface{}) // {spt0: result, spt1: result} + for _, s := range servers { + rows, err := TdbctlQueryOnRemote(s.ServerName, query, db) + if err != nil { + return nil, errors.Wrapf(err, "query on %s", s.ServerName) + } + destTmp := deepcopy.Copy(dest) + for rows.Next() { + if err = rows.Scan(destTmp); err != nil { + return nil, errors.Wrapf(err, "scan result on %s", s.ServerName) + } + } + destRes[s.ServerName] = destTmp + } + if len(destRes) != len(servers) { + return nil, errors.Errorf("TdbctlQueryByRole result error: %v", destRes) + } + return destRes, nil +} + +// TdbctlQueryByRoleWithMerge 中控上对某个 serverRole(Wrapper) 遍历执行一个查询语句,并合并结果返回 +// dest must be a ptr +func TdbctlQueryByRoleWithMerge(dest interface{}, serverRole string, query string, db *sqlx.DB) error { + if reflect.TypeOf(dest).Kind() != reflect.Ptr { + return errors.New("merge result need type ptr") + } + destValue := reflect.ValueOf(dest) + destDirect := reflect.Indirect(destValue) + + destResult, err := TdbctlQueryByRoleNative(dest, serverRole, query, db) + if err != nil { + return err + } + var dests []interface{} // not used currently + for _, res := range destResult { + resType := reflect.TypeOf(res) + switch resType.Kind() { + case reflect.Ptr: + resValue := reflect.ValueOf(res) + direct := reflect.Indirect(resValue) + for i := 0; i < direct.Len(); i++ { + // 将子分片的结果集,追加到 dest 中 + destDirect.Set(reflect.Append(destDirect, direct.Index(i))) + dests = append(dests, direct.Index(i).Interface()) + } + default: + // unreachable code + dests = append(dests, res) + } + } + return nil +} + +// TdbctlQueryByRoleNative dest is a ptr ref interface{} +// 但 dest 只用于 deepcopy 模板,生成新的 ptr->interface{},作为 map[string]interface{} 的 value +func TdbctlQueryByRoleNative(dest interface{}, serverRole string, query string, + db *sqlx.DB) (map[string]interface{}, error) { + queryServers := fmt.Sprintf("select Server_name,Host,Port,Wrapper,Username,Password "+ + "from mysql.servers where Wrapper='%s'", serverRole) + var servers []*MysqlServer + if err := db.Select(&servers, queryServers); err != nil { + return nil, err + } + destRes := make(map[string]interface{}) // {spt0: result, spt1: result} + for _, s := range servers { + instObj := mysqlconn.InsObject{ + Host: s.Host, + Port: s.Port, + User: s.Username, + Pwd: s.Password, + } + dbw, err := instObj.Conn() + if err != nil { + return nil, errors.Wrapf(err, "connect %s", s.ServerName) + } + //defer dbw.Close() + // 因为 dest 是从最外层传进来的,主要用于scan to struct,但不能直接覆盖 dest ptr,所以使用 deepcopy + destTmp := deepcopy.Copy(dest) + err = dbw.Db.Select(destTmp, query) + if err != nil { + dbw.Close() + return nil, errors.Wrapf(err, "query %s", s.ServerName) + } + dbw.Close() + destRes[s.ServerName] = destTmp + } + if len(destRes) != len(servers) { + return nil, errors.Errorf("TdbctlQueryByRole result error: %v", destRes) + } + return destRes, nil +} diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/util/misc.go b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/util/misc.go index 0f90553108..b46499621b 100644 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/util/misc.go +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/pkg/util/misc.go @@ -201,10 +201,12 @@ func StringSliceToInterfaceSlice(ids []string) []interface{} { return result } -// VersionParser parse mysql version -// example: 5.7.20-tmysql-3.4.2-log -> 005007020 -func VersionParser(version string) string { - var parse = "000000" +// VersionParser parse mysql version. +// example: +// tmysql-version: 5.7.20-tmysql-3.4.2-log -> return 005007020, false +// official-version: 5.7.42-log -> return 005007042, true +func VersionParser(version string) (parse string, isOfficial bool) { + parse = "000000" reg := regexp.MustCompile(`^\s*(\d+)\.(\d+)\.(\d+)`) temp := reg.FindStringSubmatch(version) if len(temp) > 0 { @@ -212,7 +214,12 @@ func VersionParser(version string) string { newTemp := StringSliceToInterfaceSlice(temp) parse = fmt.Sprintf("%03s%03s%03s", newTemp...) } - return parse + if strings.Contains(version, "tmysql") { + isOfficial = false + } else { + isOfficial = true + } + return parse, isOfficial } // FindBackupConfigFiles TODO diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/release_package.sh b/dbm-services/mysql/db-tools/mysql-dbbackup/release_package.sh new file mode 100755 index 0000000000..858e78b8ef --- /dev/null +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/release_package.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +# release txsql and community dbbackup-go-XXX.tar.gz +# need run in current dir + +depsDir=dbbackup-go-deps +function build_package() { + rm -rf $depsDir + tar -zxf dbbackup-go-deps-${1}.tar.gz && mv dbbackup-go-deps-${1} $depsDir + if [ $? -gt 0 ];then + echo "release package for $1 failed: tar" + exit 1 + fi + # skip go build, we hope txsql/community has the same dbbackup binary + sh ./build.sh -s -t $1 +} + +echo "go build dbbackup" +rm -f build/dbbackup +make && [ -f build/dbbackup ] +if [ $? -gt 0 ];then + echo "go build dbbackup failed" + exit 1 +fi + +# txsql +echo +echo "#################### build txsql ####################" +echo "build package for txsql..." +[ -f dbbackup-go-deps-txsql.tar.gz ] && build_package txsql +[ $? -gt 0 ] && exit 1 +ls -l build/dbbackup-go-txsql.tar.gz + +# community +echo +echo "#################### build community ####################" +echo "build package for community..." +[ -f dbbackup-go-deps-community.tar.gz ] && build_package community +[ $? -gt 0 ] && exit 1 +ls -l build/dbbackup-go-community.tar.gz \ No newline at end of file diff --git a/dbm-services/mysql/db-tools/mysql-dbbackup/test.2000.ini b/dbm-services/mysql/db-tools/mysql-dbbackup/test.2000.ini index daaa3c7d25..790a8d9138 100644 --- a/dbm-services/mysql/db-tools/mysql-dbbackup/test.2000.ini +++ b/dbm-services/mysql/db-tools/mysql-dbbackup/test.2000.ini @@ -25,10 +25,10 @@ StatusReportPath= /data/git-code/dbbackup [BackupClient] Enable = false -RemoteFileSystem = hdfs +RemoteFileSystem = cos FileTag = MYSQL_FULL_BACKUP DoChecksum = true -BackupClientBin = /usr/local/bin/backup_client +BackupClientBin = /usr/local/backup_client/bin/backup_client [LogicalBackup] ChunkFilesize = 2048 @@ -58,6 +58,8 @@ EnableBinlog = false MysqlLoadDir = /data/git-code/dbbackup/file/1111_VM-165-14-centos_127.0.0.1_12000_20230423_173959_logical IndexFilePath = /data/git-code/dbbackup/file/1111_VM-165-14-centos_127.0.0.1_12000_20230423_173959_logical.index ExtraOpt = +DBListDropIfExists = db1,dbx +CreateTableIfNotExists = false [PhysicalLoad] Threads = 4 diff --git a/dbm-services/mysql/db-tools/mysql-monitor/.gitignore b/dbm-services/mysql/db-tools/mysql-monitor/.gitignore index cef0d0d82b..e29ebc0966 100644 --- a/dbm-services/mysql/db-tools/mysql-monitor/.gitignore +++ b/dbm-services/mysql/db-tools/mysql-monitor/.gitignore @@ -1,4 +1,5 @@ build .idea logs -.DS_Store \ No newline at end of file +.DS_Store +cmd/generator \ No newline at end of file diff --git a/dbm-services/mysql/db-tools/mysql-monitor/Makefile b/dbm-services/mysql/db-tools/mysql-monitor/Makefile index fb28551f69..e17dedbda0 100644 --- a/dbm-services/mysql/db-tools/mysql-monitor/Makefile +++ b/dbm-services/mysql/db-tools/mysql-monitor/Makefile @@ -10,18 +10,16 @@ DEV_BUILD_FLAG = "-X ${MODULE}/cmd.version="develop" -X ${MODULE}/cmd.buildStamp .PHONY: release-bin release-bin: @CGO_ENABLE=0 GOARCH=amd64 GOOS=linux go build -ldflags ${RELEASE_BUILD_FLAG} -o ${OUTPUT_DIR}/${$PROJ} - @cp config.yaml.go.tpl ${OUTPUT_DIR}/config.yaml.go.tpl @cp pt-config-diff ${OUTPUT_DIR}/pt-config-diff @cp pt-summary ${OUTPUT_DIR}/pt-summary - @tar -C ${OUTPUT_DIR} -zcf ${OUTPUT_DIR}/${PKG} mysql-monitor config.yaml.go.tpl pt-config-diff pt-summary + @tar -C ${OUTPUT_DIR} -zcf ${OUTPUT_DIR}/${PKG} mysql-monitor pt-config-diff pt-summary .PHONY: dev-bin dev-bin: @go build -ldflags ${DEV_BUILD_FLAG} -o ${OUTPUT_DIR}/${PROJ} - @cp config.yaml.go.tpl ${OUTPUT_DIR}/config.yaml.go.tpl @cp pt-config-diff ${OUTPUT_DIR}/pt-config-diff @cp pt-summary ${OUTPUT_DIR}/pt-summary - @tar -C ${OUTPUT_DIR} -zcf ${OUTPUT_DIR}/${PKG} mysql-monitor config.yaml.go.tpl pt-config-diff pt-summary + @tar -C ${OUTPUT_DIR} -zcf ${OUTPUT_DIR}/${PKG} mysql-monitor pt-config-diff pt-summary .PHONY: clean clean: diff --git a/dbm-services/mysql/db-tools/mysql-monitor/README.md b/dbm-services/mysql/db-tools/mysql-monitor/README.md index d7919fdc3c..f858fe0a16 100644 --- a/dbm-services/mysql/db-tools/mysql-monitor/README.md +++ b/dbm-services/mysql/db-tools/mysql-monitor/README.md @@ -15,6 +15,21 @@ ## _clean_ * 执行 `mysql-monitor -c runtime.yaml clean` 会删除所有相关的 `mysql-crond entry` +* 会触发 `监控心跳丢失` 的告警 +* 一般只用于下架场景 +* 如果临时停止监控需求, 用下面的 `disable-all` + +## _disable-all_ +`mysql-monitor disable-all -c monitor-config_20000.yaml --staff somebody --with-db-up` +* 保留 `监控心跳` +* 停掉包括`db-up` 在内的所有监控项 + +如果不使用 `--with-db-up`, 则会保留 `db-up` 监控项 + +不修改任何配置文件, _disable_ 不会持久化, 可以随时使用上面提到的 _reschedule_ 恢复回来 + + + ## 硬编码项 目前有两个硬编码项 1. 执行心跳 diff --git a/dbm-services/mysql/db-tools/mysql-monitor/cmd/reschedule_items.go b/dbm-services/mysql/db-tools/mysql-monitor/cmd/reschedule_items.go new file mode 100644 index 0000000000..acde4c0979 --- /dev/null +++ b/dbm-services/mysql/db-tools/mysql-monitor/cmd/reschedule_items.go @@ -0,0 +1,125 @@ +package cmd + +import ( + "fmt" + "strings" + + ma "dbm-services/mysql/db-tools/mysql-crond/api" + "dbm-services/mysql/db-tools/mysql-monitor/pkg/config" + + "golang.org/x/exp/slog" +) + +func reschedule(configFileDir, configFileName, staff string) error { + manager := ma.NewManager(config.MonitorConfig.ApiUrl) + entries, err := manager.Entries() + if err != nil { + slog.Error("reschedule list entries", err) + return err + } + + for _, entry := range entries { + if strings.HasPrefix( + entry.Job.Name, + fmt.Sprintf("mysql-monitor-%d", config.MonitorConfig.Port), + ) { + eid, err := manager.Delete(entry.Job.Name, true) + if err != nil { + slog.Error( + "reschedule delete entry", err, + slog.String("name", entry.Job.Name), + ) + return err + } + slog.Info( + "reschedule delete entry", + slog.String("name", entry.Job.Name), + slog.Int("ID", eid), + ) + } + } + + var hardCodeItems []*config.MonitorItem + itemGroups := make(map[string][]*config.MonitorItem) + for _, ele := range config.ItemsConfig { + // 硬编码监控项先排除掉 + if ele.Name == "db-up" || ele.Name == config.HeartBeatName { + if ele.IsEnable() { + hardCodeItems = append(hardCodeItems, ele) + } + continue + } + + if ele.IsEnable() && ele.IsMatchMachineType() && ele.IsMatchRole() { + var key string + + if ele.Schedule == nil { + key = config.MonitorConfig.DefaultSchedule + } else { + key = *ele.Schedule + } + + if _, ok := itemGroups[key]; !ok { + itemGroups[key] = []*config.MonitorItem{} + } + itemGroups[key] = append(itemGroups[key], ele) + } + } + + for k, v := range itemGroups { + var itemNames []string + for _, j := range v { + itemNames = append(itemNames, j.Name) + } + args := []string{ + "run", + "--items", strings.Join(itemNames, ","), + "-c", configFileName, // use WorkDir + } + eid, err := manager.CreateOrReplace( + ma.JobDefine{ + Name: fmt.Sprintf("mysql-monitor-%d-%s", config.MonitorConfig.Port, k), + Command: executable, + Args: args, + Schedule: k, + Creator: staff, //viper.GetString("staff"), + Enable: true, + WorkDir: configFileDir, + }, true, + ) + if err != nil { + slog.Error("reschedule add entry", err) + return err + } + slog.Info("reschedule add entry", slog.Int("entry id", eid)) + } + + // 注册 hardcode + for _, j := range hardCodeItems { + args := []string{ + "hardcode-run", + "--items", j.Name, //strings.Join(itemNames, ","), + "-c", configFileName, + } + + eid, err := manager.CreateOrReplace( + ma.JobDefine{ + Name: fmt.Sprintf( + "mysql-monitor-%d-hardcode-%s", config.MonitorConfig.Port, j.Name), + Command: executable, + Args: args, + Schedule: config.HardCodeSchedule, + Creator: staff, //viper.GetString("staff"), + Enable: true, + WorkDir: configFileDir, + }, true, + ) + if err != nil { + slog.Error("reschedule add hardcode entry", err) + return err + } + slog.Info("reschedule add hardcode entry", slog.Int("entry id", eid)) + } + + return nil +} diff --git a/dbm-services/mysql/db-tools/mysql-monitor/cmd/subcmd_disable_all.go b/dbm-services/mysql/db-tools/mysql-monitor/cmd/subcmd_disable_all.go new file mode 100644 index 0000000000..25c2c9d25c --- /dev/null +++ b/dbm-services/mysql/db-tools/mysql-monitor/cmd/subcmd_disable_all.go @@ -0,0 +1,86 @@ +package cmd + +import ( + "os" + "path/filepath" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + "golang.org/x/exp/slog" + + "dbm-services/mysql/db-tools/mysql-monitor/pkg/config" +) + +var subCmdDisableAll = &cobra.Command{ + Use: "disable-all", + Short: "disable-all items", + Long: "disable-all items", + RunE: func(cmd *cobra.Command, args []string) error { + configPath := viper.GetString("disable-config") + if !filepath.IsAbs(configPath) { + cwd, err := os.Getwd() + if err != nil { + slog.Error("disable-all get config abs path", err) + return err + } + configPath = filepath.Join(cwd, configPath) + } + configFileDir, configFileName := filepath.Split(configPath) + + err := config.InitConfig(configPath) + if err != nil { + return err + } + initLogger(config.MonitorConfig.Log) + + emptyItemsConfig, err := os.CreateTemp("/tmp", "empty-items.yaml") + if err != nil { + slog.Error("disable-all create empty items config", slog.String("error", err.Error())) + return err + } + defer func() { + _ = emptyItemsConfig.Close() + _ = os.Remove(emptyItemsConfig.Name()) + }() + slog.Info("disable-all create empty items config success") + + config.MonitorConfig.ItemsConfigFile = emptyItemsConfig.Name() + + err = config.LoadMonitorItemsConfig() + if err != nil { + slog.Error("disable-all load items", err) + return err + } + + disableDbUp := viper.GetBool("with-db-up") + if !disableDbUp { + config.InjectMonitorDbUpItem() + } + config.InjectMonitorHeartBeatItem() + + slog.Info("disable-all", + slog.String("staff", viper.GetString("staff"))) + err = reschedule(configFileDir, configFileName, viper.GetString("disable-staff")) + if err != nil { + slog.Error("disable-all sub-cmd", slog.String("error", err.Error())) + return err + } + + return nil + }, +} + +func init() { + subCmdDisableAll.PersistentFlags().StringP("config", "c", "", "config file") + _ = subCmdDisableAll.MarkPersistentFlagRequired("config") + _ = viper.BindPFlag("disable-config", subCmdDisableAll.PersistentFlags().Lookup("config")) + + subCmdDisableAll.PersistentFlags().StringP("staff", "", "", "staff name") + _ = subCmdDisableAll.MarkPersistentFlagRequired("staff") + _ = viper.BindPFlag("disable-staff", subCmdDisableAll.PersistentFlags().Lookup("staff")) + + subCmdDisableAll.PersistentFlags().BoolP("with-db-up", "", false, "also disable db-up") + _ = viper.BindPFlag("with-db-up", subCmdDisableAll.PersistentFlags().Lookup("with-db-up")) + + rootCmd.AddCommand(subCmdDisableAll) +} diff --git a/dbm-services/mysql/db-tools/mysql-monitor/cmd/subcmd_reschedule.go b/dbm-services/mysql/db-tools/mysql-monitor/cmd/subcmd_reschedule.go index 7e403da7c3..2bf6494d36 100644 --- a/dbm-services/mysql/db-tools/mysql-monitor/cmd/subcmd_reschedule.go +++ b/dbm-services/mysql/db-tools/mysql-monitor/cmd/subcmd_reschedule.go @@ -1,12 +1,9 @@ package cmd import ( - "fmt" "os" "path/filepath" - "strings" - ma "dbm-services/mysql/db-tools/mysql-crond/api" "dbm-services/mysql/db-tools/mysql-monitor/pkg/config" "github.com/spf13/cobra" @@ -46,7 +43,8 @@ var subCmdReschedule = &cobra.Command{ return err } - config.InjectHardCodeItem() + config.InjectMonitorDbUpItem() + config.InjectMonitorHeartBeatItem() err = config.WriteMonitorItemsBack() if err != nil { @@ -54,115 +52,12 @@ var subCmdReschedule = &cobra.Command{ return err } - manager := ma.NewManager(config.MonitorConfig.ApiUrl) - entries, err := manager.Entries() + err = reschedule(configFileDir, configFileName, viper.GetString("reschedule-staff")) if err != nil { - slog.Error("reschedule list entries", err) + slog.Error("reschedule sub-cmd", slog.String("error", err.Error())) return err } - for _, entry := range entries { - if strings.HasPrefix( - entry.Job.Name, - fmt.Sprintf("mysql-monitor-%d", config.MonitorConfig.Port), - ) { - eid, err := manager.Delete(entry.Job.Name, true) - if err != nil { - slog.Error( - "reschedule delete entry", err, - slog.String("name", entry.Job.Name), - ) - return err - } - slog.Info( - "reschedule delete entry", - slog.String("name", entry.Job.Name), - slog.Int("ID", eid), - ) - } - } - - var hardCodeItems []*config.MonitorItem - itemGroups := make(map[string][]*config.MonitorItem) - for _, ele := range config.ItemsConfig { - // 硬编码监控项先排除掉 - if ele.Name == "db-up" || ele.Name == config.HeartBeatName { - if ele.IsEnable() { - hardCodeItems = append(hardCodeItems, ele) - } - continue - } - - if ele.IsEnable() && ele.IsMatchMachineType() && ele.IsMatchRole() { - var key string - - if ele.Schedule == nil { - key = config.MonitorConfig.DefaultSchedule - } else { - key = *ele.Schedule - } - - if _, ok := itemGroups[key]; !ok { - itemGroups[key] = []*config.MonitorItem{} - } - itemGroups[key] = append(itemGroups[key], ele) - } - } - - for k, v := range itemGroups { - var itemNames []string - for _, j := range v { - itemNames = append(itemNames, j.Name) - } - args := []string{ - "run", - "--items", strings.Join(itemNames, ","), - "-c", configFileName, // use WorkDir - } - eid, err := manager.CreateOrReplace( - ma.JobDefine{ - Name: fmt.Sprintf("mysql-monitor-%d-%s", config.MonitorConfig.Port, k), - Command: executable, - Args: args, - Schedule: k, - Creator: viper.GetString("staff"), - Enable: true, - WorkDir: configFileDir, - }, true, - ) - if err != nil { - slog.Error("reschedule add entry", err) - return err - } - slog.Info("reschedule add entry", slog.Int("entry id", eid)) - } - - // 注册 hardcode - var itemNames []string - for _, j := range hardCodeItems { - itemNames = append(itemNames, j.Name) - } - args = []string{ - "hardcode-run", - "--items", strings.Join(itemNames, ","), - "-c", configPath, - } - eid, err := manager.CreateOrReplace( - ma.JobDefine{ - Name: fmt.Sprintf("mysql-monitor-%d-hardcode", config.MonitorConfig.Port), - Command: executable, - Args: args, - Schedule: config.HardCodeSchedule, - Creator: viper.GetString("staff"), - Enable: true, - }, true, - ) - if err != nil { - slog.Error("reschedule add hardcode entry", err) - return err - } - slog.Info("reschedule add hardcode entry", slog.Int("entry id", eid)) - return nil }, } @@ -174,7 +69,7 @@ func init() { subCmdReschedule.PersistentFlags().StringP("staff", "", "", "staff name") _ = subCmdReschedule.MarkPersistentFlagRequired("staff") - _ = viper.BindPFlag("staff", subCmdReschedule.PersistentFlags().Lookup("staff")) + _ = viper.BindPFlag("reschedule-staff", subCmdReschedule.PersistentFlags().Lookup("staff")) rootCmd.AddCommand(subCmdReschedule) } diff --git a/dbm-services/mysql/db-tools/mysql-monitor/config.yaml b/dbm-services/mysql/db-tools/mysql-monitor/config.yaml index d866da2e87..1ef44dc654 100644 --- a/dbm-services/mysql/db-tools/mysql-monitor/config.yaml +++ b/dbm-services/mysql/db-tools/mysql-monitor/config.yaml @@ -1,20 +1,23 @@ -ip: 127.0.0.1 -port: 20000 +bk_biz_id: +ip: x.x.x.x +port: yyyyy +bk_instance_id: 1111 immute_domain: aaa.bbb.ccc -machine_type: storage -role: master -bk_cloud_id: 1 +machine_type: spider +role: spider_master +bk_cloud_id: log: console: true - log_file_dir: /Users/xfwduke/mysql-monitor/logs + log_file_dir: log debug: true source: true json: false +items_config_file: items-config.yaml api_url: http://127.0.0.1:9999 -items_config_file: /Users/xfwduke/mysql-monitor/items-config.yaml auth: - user: root - password: 123 -dba_sys_dbs: ["mysql", "test", "information_schema", "performance_schema", "db_infobase", "sys"] + mysql: + user: + password: +dba_sys_dbs: [mysql test infodba_schema performance_schema information_schema sys] interact_timeout: 2s default_schedule: '@every 1m' \ No newline at end of file diff --git a/dbm-services/mysql/db-tools/mysql-monitor/config.yaml.go.tpl b/dbm-services/mysql/db-tools/mysql-monitor/config.yaml.go.tpl deleted file mode 100644 index 733de08750..0000000000 --- a/dbm-services/mysql/db-tools/mysql-monitor/config.yaml.go.tpl +++ /dev/null @@ -1,20 +0,0 @@ -ip: {{ .IP }} -port: {{ .Port }} -immute_domain: {{ .ImmuteDomain }} -machine_type: {{ .MachineType }} -role: {{ .Role }} -bk_cloud_id: {{ .BkCloudId }} -log: - console: true - log_file_dir: {{ .LogPath }} - debug: true - source: true - json: false -api_url: http://127.0.0.1:9999 -items_config_file: {{ .ItemsConfigPath }} -auth: - user: {{ .User }} - password: {{ .Password }} -dba_sys_dbs: {{ .DbaSysDbs }} -interact_timeout: 2s -default_schedule: '@every 1m' \ No newline at end of file diff --git a/dbm-services/mysql/db-tools/mysql-monitor/config2sql.pl b/dbm-services/mysql/db-tools/mysql-monitor/config2sql.pl new file mode 100644 index 0000000000..8fee6dc449 --- /dev/null +++ b/dbm-services/mysql/db-tools/mysql-monitor/config2sql.pl @@ -0,0 +1,23 @@ +use strict; +use warnings; +use YAML::XS qw(LoadFile); +use JSON qw(encode_json); + +my $items_config = LoadFile('items-config.yaml'); + +foreach my $item (@$items_config) { + my $sql = sprintf(q#REPLACE INTO + tb_config_name_def( + namespace, conf_type, conf_file, conf_name, + value_type, value_default, value_allowed, value_type_sub, + flag_status, flag_disable, flag_locked, flag_encrypt, need_restart) + VALUES( + 'tendb', 'mysql_monitor', 'items-config.yaml', '%s', + 'STRING', '%s', '', 'MAP', + 1, 0, 0, 0, 1);#, + $item->{name}, encode_json($item)); + # 没什么实际的意义, 只是让输入的 sql 好看些 + $sql =~ s/\n//g; + $sql =~ s/\s+/ /g; + print $sql . "\n"; +} \ No newline at end of file diff --git a/dbm-services/mysql/db-tools/mysql-monitor/items-config.tpl.yaml b/dbm-services/mysql/db-tools/mysql-monitor/items-config.tpl.yaml deleted file mode 100644 index f9b34460e0..0000000000 --- a/dbm-services/mysql/db-tools/mysql-monitor/items-config.tpl.yaml +++ /dev/null @@ -1,130 +0,0 @@ -- name: character-consistency - enable: true - schedule: 0 0 14 * * 1 - machine_type: - - backend - - remote - - spider - role: [] -- name: routine-definer - enable: true - schedule: 0 0 15 * * 1 - machine_type: - - backend - - remote - role: [] -- name: view-definer - enable: true - schedule: 0 0 15 * * 1 - machine_type: - - backend - - remote - role: [] -- name: trigger-definer - enable: true - schedule: 0 0 15 * * 1 - machine_type: - - backend - - remote - role: [] -- name: engine - enable: true - schedule: 0 0 12 * * * - machine_type: - - backend - - remote - role: [] -- name: ext3-check - enable: true - schedule: 0 0 16 * * 1 - machine_type: - - backend - - remote - role: [] -- name: slave-status - enable: true - schedule: '@every 1m' - machine_type: - - backend - - remote - role: - - slave - - repeater -- name: mysql-err-notice - enable: true - schedule: '@every 1m' - machine_type: - - backend - - remote - - spider - role: [] -- name: mysql-err-critical - enable: true - schedule: '@every 1m' - machine_type: - - backend - - remote - - spider - role: [] -- name: mysql-lock - enable: true - schedule: '@every 1m' - machine_type: - - backend - - remote - - spider - role: [] -- name: mysql-inject - enable: true - schedule: '@every 1m' - machine_type: - - backend - - remtoe - - spider - role: [] -- name: rotate-slowlog - enable: true - schedule: 0 55 23 * * * - machine_type: - - backend - - remote - - spider - role: [] -- name: mysql-connlog-size - enable: true - schedule: 0 0 12 * * * - machine_type: - - backend - - remote - - spider - role: [] -- name: mysql-connlog-rotate - enable: true - schedule: 0 30 23 * * * - machine_type: - - backend - - remote - - spider - role: [] -- name: mysql-connlog-report - enable: true - schedule: 0 40 23 * * * - machine_type: - - backend - - remote - - spider - role: [] -- name: mysql-config-diff - enable: true - schedule: '@every 1m' - machine_type: - - backend - - remote - - spider - role: [] -- name: ctl-replicate - enable: true - schedule: '@every 1m' - machine_type: - - spider - role: [] diff --git a/dbm-services/mysql/db-tools/mysql-monitor/items-config.yaml b/dbm-services/mysql/db-tools/mysql-monitor/items-config.yaml index f9b34460e0..e26550527d 100644 --- a/dbm-services/mysql/db-tools/mysql-monitor/items-config.yaml +++ b/dbm-services/mysql/db-tools/mysql-monitor/items-config.yaml @@ -2,6 +2,7 @@ enable: true schedule: 0 0 14 * * 1 machine_type: + - single - backend - remote - spider @@ -10,6 +11,7 @@ enable: true schedule: 0 0 15 * * 1 machine_type: + - single - backend - remote role: [] @@ -17,6 +19,7 @@ enable: true schedule: 0 0 15 * * 1 machine_type: + - single - backend - remote role: [] @@ -24,6 +27,7 @@ enable: true schedule: 0 0 15 * * 1 machine_type: + - single - backend - remote role: [] @@ -31,6 +35,7 @@ enable: true schedule: 0 0 12 * * * machine_type: + - single - backend - remote role: [] @@ -38,93 +43,180 @@ enable: true schedule: 0 0 16 * * 1 machine_type: + - single - backend - remote role: [] -- name: slave-status +- name: ibd-statistic enable: true - schedule: '@every 1m' + schedule: 0 0 14 * * 1 machine_type: + - single - backend - remote role: - slave - - repeater -- name: mysql-err-notice +- name: master-slave-heartbeat enable: true schedule: '@every 1m' machine_type: - backend - remote - spider - role: [] -- name: mysql-err-critical + role: + - master + - repeater + - slave + - spider_master +- name: mysql-config-diff enable: true schedule: '@every 1m' machine_type: + - single - backend - remote - spider role: [] -- name: mysql-lock +- name: mysql-connlog-size enable: true - schedule: '@every 1m' + schedule: 0 0 12 * * * machine_type: + - single - backend - remote - spider role: [] -- name: mysql-inject +- name: mysql-connlog-rotate enable: true - schedule: '@every 1m' + schedule: 0 30 23 * * * machine_type: + - single - backend - - remtoe + - remote - spider role: [] -- name: rotate-slowlog +- name: mysql-connlog-report enable: true - schedule: 0 55 23 * * * + schedule: 0 40 23 * * * machine_type: + - single - backend - remote - spider role: [] -- name: mysql-connlog-size +- name: mysql-err-notice enable: true - schedule: 0 0 12 * * * + schedule: '@every 1m' machine_type: + - single - backend - remote - - spider role: [] -- name: mysql-connlog-rotate +- name: mysql-err-critical enable: true - schedule: 0 30 23 * * * + schedule: '@every 1m' machine_type: + - single - backend - remote + role: [] +- name: spider-err-notice + enable: true + schedule: '@every 1m' + machine_type: - spider role: [] -- name: mysql-connlog-report +- name: spider-err-warn enable: true - schedule: 0 40 23 * * * + schedule: '@every 1m' + machine_type: + - spider + role: [] +- name: spider-err-critical + enable: true + schedule: '@every 1m' + machine_type: + - spider + role: [] +- name: mysql-lock + enable: true + schedule: '@every 1m' machine_type: + - single - backend - remote - spider role: [] -- name: mysql-config-diff +- name: mysql-inject enable: true schedule: '@every 1m' machine_type: + - single + - backend + - spider + role: [] +- name: proxy-backend + enable: true + schedule: '@every 1m' + machine_type: + - proxy + role: [] +- name: proxy-user-list + enable: true + schedule: '@every 1m' + machine_type: + - proxy + role: [] +- name: rotate-slowlog + enable: true + schedule: 0 55 23 * * * + machine_type: + - single - backend - remote - spider role: [] +- name: slave-status + enable: true + schedule: '@every 1m' + machine_type: + - backend + - remote + role: + - slave + - repeater - name: ctl-replicate enable: true schedule: '@every 1m' machine_type: - spider + role: + - spider_master +- name: spider-remote + enable: true + schedule: '@every 1m' + machine_type: + - spider + role: [] +- name: spider-table-schema-consistency + enable: true + schedule: 0 10 1 * * * + machine_type: + - spider + role: + - spider_master +- name: dbha-heartbeat + enable: true + schedule: '@every 1m' + machine_type: + - spider + - remote + - backend role: [] +- name: unique-ctl-master + enable: true + schedule: '@every 1m' + machine_type: + - spider + role: + - spider_master \ No newline at end of file diff --git a/dbm-services/mysql/db-tools/mysql-monitor/pkg/config/init.go b/dbm-services/mysql/db-tools/mysql-monitor/pkg/config/init.go index 99af42a4ce..08cd4f3c5a 100644 --- a/dbm-services/mysql/db-tools/mysql-monitor/pkg/config/init.go +++ b/dbm-services/mysql/db-tools/mysql-monitor/pkg/config/init.go @@ -80,6 +80,32 @@ func LoadMonitorItemsConfig() error { return nil } +func InjectMonitorHeartBeatItem() { + enable := true + heartBeatItem := &MonitorItem{ + Name: HeartBeatName, + Enable: &enable, + Schedule: &HardCodeSchedule, //&MonitorConfig.DefaultSchedule, + MachineType: []string{MonitorConfig.MachineType}, + Role: nil, + } + ItemsConfig = injectItem(heartBeatItem, ItemsConfig) + slog.Debug("inject hardcode", slog.Any("items", ItemsConfig)) +} + +func InjectMonitorDbUpItem() { + enable := true + dbUpItem := &MonitorItem{ + Name: "db-up", + Enable: &enable, + Schedule: &HardCodeSchedule, //&MonitorConfig.DefaultSchedule, + MachineType: []string{MonitorConfig.MachineType}, + Role: nil, + } + ItemsConfig = injectItem(dbUpItem, ItemsConfig) + slog.Debug("inject hardcode", slog.Any("items", ItemsConfig)) +} + // InjectHardCodeItem 注入硬编码的心跳和db-up监控 func InjectHardCodeItem() { enable := true diff --git a/dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/dbhaheartbeat/checker.go b/dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/dbhaheartbeat/checker.go new file mode 100644 index 0000000000..1b7e3574e9 --- /dev/null +++ b/dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/dbhaheartbeat/checker.go @@ -0,0 +1,69 @@ +package dbhaheartbeat + +import ( + "context" + "database/sql" + "fmt" + "time" + + "github.com/jmoiron/sqlx" + "golang.org/x/exp/slog" + + "dbm-services/mysql/db-tools/mysql-monitor/pkg/config" + "dbm-services/mysql/db-tools/mysql-monitor/pkg/monitoriteminterface" +) + +/* +1. 超过 2min 没心跳就产生 event +2. 监控平台上连续重复 5 次再告警 +这个监控重要, 但是不需要过于敏感 +*/ + +var name = "dbha-heartbeat" + +type Checker struct { + db *sqlx.DB +} + +func (c *Checker) Run() (msg string, err error) { + ctx, cancel := context.WithTimeout(context.Background(), config.MonitorConfig.InteractTimeout) + defer cancel() + + var res sql.NullTime + err = c.db.QueryRowxContext( + ctx, + `SELECT MAX(ck_time) FROM infodba_schema.check_heartbeat`, + ).Scan(&res) + + if v, err := res.Value(); err != nil { + return "", err + } else { + if v == nil { + return fmt.Sprintf("empty dbha heartbeat ck_time"), nil + } + + slog.Info("dbha-heartbeat", + slog.Time("latest heartbeat", res.Time)) + + // 最近2分钟没有新的心跳 + if res.Time.Add(2 * time.Minute).Before(time.Now()) { + return fmt.Sprintf("last heartbeat time: %s", res.Time.In(time.Local)), nil + } + } + + return "", nil +} + +func (c *Checker) Name() string { + return name +} + +func NewChecker(cc *monitoriteminterface.ConnectionCollect) monitoriteminterface.MonitorItemInterface { + return &Checker{ + db: cc.MySqlDB, + } +} + +func Register() (string, monitoriteminterface.MonitorItemConstructorFuncType) { + return name, NewChecker +} diff --git a/dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/ibdstatistic/ibd_statistic.go b/dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/ibdstatistic/ibd_statistic.go index 01a6d73c0a..c6c4e4a038 100644 --- a/dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/ibdstatistic/ibd_statistic.go +++ b/dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/ibdstatistic/ibd_statistic.go @@ -31,7 +31,7 @@ var partitionPattern *regexp.Regexp func init() { ibdExt = ".ibd" - partitionPattern = regexp.MustCompile(`^(.*)#P#.*\.ibd`) + partitionPattern = regexp.MustCompile(`^(.*)#[pP]#.*\.ibd`) } diff --git a/dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/items_collect.go b/dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/items_collect.go index 3934eeb056..ef82ef6d40 100644 --- a/dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/items_collect.go +++ b/dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/items_collect.go @@ -13,6 +13,7 @@ import ( "fmt" "dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/characterconsistency" + "dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/dbhaheartbeat" "dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/definer" "dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/engine" "dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/ext3check" @@ -26,6 +27,9 @@ import ( "dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/proxyuserlist" "dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/rotateslowlog" "dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/slavestatus" + "dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/spiderremote" + "dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/tscc" + "dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/uniquectlmaster" mi "dbm-services/mysql/db-tools/mysql-monitor/pkg/monitoriteminterface" "golang.org/x/exp/slog" @@ -79,4 +83,8 @@ func init() { _ = registerItemConstructor(proxybackend.Register()) _ = registerItemConstructor(ibdstatistic.Register()) _ = registerItemConstructor(slavestatus.RegisterCtlReplicateChecker()) + _ = registerItemConstructor(spiderremote.Register()) + _ = registerItemConstructor(tscc.Register()) + _ = registerItemConstructor(dbhaheartbeat.Register()) + _ = registerItemConstructor(uniquectlmaster.Register()) } diff --git a/dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/masterslaveheartbeat/master_slave_heartbeat.go b/dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/masterslaveheartbeat/master_slave_heartbeat.go index 2923cf22dd..61b2244811 100644 --- a/dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/masterslaveheartbeat/master_slave_heartbeat.go +++ b/dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/masterslaveheartbeat/master_slave_heartbeat.go @@ -19,6 +19,19 @@ import ( var ( name = "master-slave-heartbeat" checkTable = "master_slave_heartbeat" + + HeartBeatTable = fmt.Sprintf("%s.%s", cst.DBASchema, checkTable) + DropTableSQL = fmt.Sprintf("DROP TABLE IF EXISTS %s", HeartBeatTable) + CreateTableSQL = fmt.Sprintf(`CREATE TABLE IF NOT EXISTS %s ( + master_server_id varchar(40) COMMENT 'server_id that run this update', + slave_server_id varchar(40) COMMENT 'slave server_id', + master_time varchar(32) COMMENT 'the time on master', + slave_time varchar(32) COMMENT 'the time on slave', + delay_sec int DEFAULT 0 COMMENT 'the slave delay to master', + PRIMARY KEY (master_server_id) + ) ENGINE=InnoDB`, HeartBeatTable, + ) + // beat_sec int DEFAULT 0 COMMENT 'the beat since master heartbeat:timestampdiff(SECOND, master_time, now())', ) // Checker TODO @@ -55,13 +68,16 @@ func (c *Checker) updateHeartbeat() error { _ = conn.Close() }() + if config.MonitorConfig.MachineType == "spider" { + _, err := conn.ExecContext(ctx, "set tc_admin=0") + if err != nil { + slog.Error("master-slave-heartbeat", err) + return err + } + } + txrrSQL := "SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ" // SET SESSION transaction_isolation = 'REPEATABLE-READ' binlogSQL := "SET SESSION binlog_format='STATEMENT'" - updateSQL := fmt.Sprintf( - `UPDATE %s SET -master_time=now(), slave_time=sysdate(),delay_sec=timestampdiff(SECOND, now(),sysdate()) -WHERE slave_server_id=@@server_id and master_server_id= '%s'`, - c.heartBeatTable, masterServerId) insertSQL := fmt.Sprintf( `REPLACE INTO %s(master_server_id, slave_server_id, master_time, slave_time, delay_sec) VALUES('%s', @@server_id, now(), sysdate(), timestampdiff(SECOND, now(),sysdate()))`, @@ -78,9 +94,10 @@ VALUES('%s', @@server_id, now(), sysdate(), timestampdiff(SECOND, now(),sysdate( return err } - res, err := conn.ExecContext(ctx, updateSQL) + res, err := conn.ExecContext(ctx, insertSQL) if err != nil { - if merr, ok := err.(*mysql.MySQLError); ok { + var merr *mysql.MySQLError + if errors.As(err, &merr) { if merr.Number == 1146 || merr.Number == 1054 { slog.Debug("master-slave-heartbeat table not found") // ERROR 1054 (42S22): Unknown colum res, err = c.initTableHeartbeat() @@ -90,20 +107,11 @@ VALUES('%s', @@server_id, now(), sysdate(), timestampdiff(SECOND, now(),sysdate( } slog.Debug("master-slave-heartbeat init table success") } - } else { - slog.Error("master-slave-heart beat", err) - return err } - } - - num, _ := res.RowsAffected() - slog.Debug("master-slave-heartbeat", slog.String("update rows", name)) - if num == 0 { - if _, err = conn.ExecContext(ctx, insertSQL); err != nil { - slog.Error("master-slave-heartbeat insert", err) - return err + } else { + if num, _ := res.RowsAffected(); num > 0 { + slog.Debug("master-slave-heartbeat insert success") } - slog.Debug("master-slave-heartbeat insert success") } /* // 正常只在 slave 上才需要 update slave beat_sec,但 repeater 也需要更新,所以可以直接忽略角色 @@ -122,21 +130,8 @@ VALUES('%s', @@server_id, now(), sysdate(), timestampdiff(SECOND, now(),sysdate( } func (c *Checker) initTableHeartbeat() (sql.Result, error) { - dropTable := fmt.Sprintf("DROP TABLE IF EXISTS %s", c.heartBeatTable) - _, _ = c.db.Exec(dropTable) // we do not care if table drop success, but care if table create success or not - createTable := fmt.Sprintf( - `CREATE TABLE IF NOT EXISTS %s ( - master_server_id varchar(40) COMMENT 'server_id that run this update', - slave_server_id varchar(40) COMMENT 'slave server_id', - master_time varchar(32) COMMENT 'the time on master', - slave_time varchar(32) COMMENT 'the time on slave', - delay_sec int DEFAULT 0 COMMENT 'the slave delay to master', - PRIMARY KEY (master_server_id) - ) ENGINE=InnoDB`, - c.heartBeatTable, - ) - // beat_sec int DEFAULT 0 COMMENT 'the beat since master heartbeat:timestampdiff(SECOND, master_time, now())', - return c.db.Exec(createTable) + _, _ = c.db.Exec(DropTableSQL) // we do not care if table drop success, but care if table create success or not + return c.db.Exec(CreateTableSQL) } // Run TODO @@ -152,7 +147,17 @@ func (c *Checker) Name() string { // New TODO func New(cc *monitoriteminterface.ConnectionCollect) monitoriteminterface.MonitorItemInterface { - return &Checker{db: cc.MySqlDB, heartBeatTable: fmt.Sprintf("%s.%s", cst.DBASchema, checkTable)} + if config.MonitorConfig.MachineType == "spider" && *config.MonitorConfig.Role == "spider_master" { + return &Checker{ + db: cc.CtlDB, + heartBeatTable: HeartBeatTable, + } + } else { + return &Checker{ + db: cc.MySqlDB, + heartBeatTable: HeartBeatTable, + } + } } // Register TODO diff --git a/dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/proxybackend/proxy_backend.go b/dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/proxybackend/proxy_backend.go index b53325d595..a444403a4c 100644 --- a/dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/proxybackend/proxy_backend.go +++ b/dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/proxybackend/proxy_backend.go @@ -14,6 +14,8 @@ import ( "context" "database/sql" "fmt" + "math/big" + "net" "os" "path/filepath" "regexp" @@ -21,6 +23,7 @@ import ( "dbm-services/mysql/db-tools/mysql-monitor/pkg/config" "dbm-services/mysql/db-tools/mysql-monitor/pkg/monitoriteminterface" + "dbm-services/mysql/db-tools/mysql-monitor/pkg/utils" "github.com/jmoiron/sqlx" "github.com/pkg/errors" @@ -97,8 +100,25 @@ func (c *Checker) Run() (msg string, err error) { if backendAddr == "" || !backendInfo.Address.Valid || backendAddr != backendInfo.Address.String { msg = fmt.Sprintf("cnf.backend=%s, mem.backend=%s", backendAddr, backendInfo.Address.String) + return msg, nil } + backendIp := strings.Split(backendAddr, ":")[0] + if net.ParseIP(backendIp) == nil { + msg = fmt.Sprintf("%s not a valid ip", backendIp) + return msg, nil + } + if net.ParseIP(backendIp).To4() == nil { + msg = fmt.Sprintf("%s not a v4 ip", backendIp) + return msg, nil + } + + utils.SendMonitorMetrics( + "proxy_backend_ip", + big.NewInt(0).SetBytes(net.ParseIP(backendIp).To4()).Int64(), + nil, + ) + return msg, nil } diff --git a/dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/slavestatus/ctl_replicate.go b/dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/slavestatus/ctl_replicate.go index 756b8c1a08..7f537012b0 100644 --- a/dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/slavestatus/ctl_replicate.go +++ b/dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/slavestatus/ctl_replicate.go @@ -10,10 +10,8 @@ package slavestatus import ( "context" - "database/sql" "fmt" - "github.com/pkg/errors" "golang.org/x/exp/slog" "dbm-services/mysql/db-tools/mysql-monitor/pkg/config" @@ -61,20 +59,33 @@ func (c *ctlReplicateChecker) isPrimary() (bool, error) { ctx, cancel := context.WithTimeout(context.Background(), config.MonitorConfig.InteractTimeout) defer cancel() - var tcIsPrimary sql.NullInt32 - err := c.db.GetContext(ctx, &tcIsPrimary, `SELECT @@tc_is_primary`) - if err != nil { - slog.Error("select @@tc_is_primary", err) - return false, err + var getPrimaryRes []struct { + ServerName string `db:"SERVER_NAME"` + Host string `db:"HOST"` + Port string `db:"PORT"` + IsThis int `db:"IS_THIS_SERVER"` } - if !tcIsPrimary.Valid { - err := errors.Errorf("invalide tc_is_primary: %v", tcIsPrimary) - slog.Error("select @@tc_is_primary", err) + err := c.db.SelectContext(ctx, &getPrimaryRes, `TDBCTL GET PRIMARY`) + if err != nil { + slog.Error("TDBCTL GET PRIMARY", err) return false, err } - return tcIsPrimary.Int32 == 1, nil + //var tcIsPrimary sql.NullInt32 + //err := c.db.GetContext(ctx, &tcIsPrimary, `SELECT @@tc_is_primary`) + //if err != nil { + // slog.Error("select @@tc_is_primary", err) + // return false, err + //} + // + //if !tcIsPrimary.Valid { + // err := errors.Errorf("invalide tc_is_primary: %v", tcIsPrimary) + // slog.Error("select @@tc_is_primary", err) + // return false, err + //} + + return getPrimaryRes[0].IsThis == 1, nil } // Name 监控项名 diff --git a/dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/spiderremote/spider_remote.go b/dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/spiderremote/spider_remote.go new file mode 100644 index 0000000000..8848303a40 --- /dev/null +++ b/dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/spiderremote/spider_remote.go @@ -0,0 +1,93 @@ +// TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +// Copyright (C) 2017-2023 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 https://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. + +package spiderremote + +import ( + "context" + "encoding/json" + "fmt" + "hash/crc32" + + "github.com/jmoiron/sqlx" + "github.com/pkg/errors" + "golang.org/x/exp/slog" + + "dbm-services/mysql/db-tools/mysql-monitor/pkg/config" + "dbm-services/mysql/db-tools/mysql-monitor/pkg/monitoriteminterface" + "dbm-services/mysql/db-tools/mysql-monitor/pkg/utils" +) + +/* +检查spider的mysql.servers表,要求 +1. 各分片的remote ip一致 +2. 上报remote ip +3. 指标增加自定义维度 role, 监控平台配置增加 role 聚合, 就可以兼容 spider_slave +*/ + +var name = "spider-remote" + +type spiderRemoteCheck struct { + db *sqlx.DB +} + +func (c *spiderRemoteCheck) Run() (msg string, err error) { + ctx, cancel := context.WithTimeout(context.Background(), config.MonitorConfig.InteractTimeout) + defer cancel() + + var res []struct { + ServerName string `db:"Server_name"` + Host string `db:"Host"` + Port int `db:"Port"` + } + err = c.db.SelectContext( + ctx, + &res, + `SELECT Server_name, Host, Port FROM mysql.servers WHERE Wrapper = 'mysql' ORDER BY Server_name`) + if err != nil { + return "", errors.Wrap(err, "query mysql.servers") + } + + if len(res) <= 0 { + msg = fmt.Sprintf("remote routine not found in mysql.servers") + return msg, nil + } + + b, err := json.Marshal(res) + if err != nil { + slog.Error("spider remote marshal res", err, slog.Any("res", res)) + return "", err + } + + remoteCrc := crc32.ChecksumIEEE(b) + slog.Info("spider remote", + slog.String("remote info", string(b)), + slog.Int64("remote crc", int64(remoteCrc))) + + utils.SendMonitorMetrics( + "spider_remote_ip", + int64(remoteCrc), + map[string]interface{}{ + "role": *config.MonitorConfig.Role, + }, + ) + + return msg, nil +} + +func (c *spiderRemoteCheck) Name() string { + return name +} + +func New(cc *monitoriteminterface.ConnectionCollect) monitoriteminterface.MonitorItemInterface { + return &spiderRemoteCheck{db: cc.MySqlDB} +} + +func Register() (string, monitoriteminterface.MonitorItemConstructorFuncType) { + return name, New +} diff --git a/dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/tscc/tscc.go b/dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/tscc/tscc.go new file mode 100644 index 0000000000..5a825376e6 --- /dev/null +++ b/dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/tscc/tscc.go @@ -0,0 +1,231 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * Copyright (C) 2017-2023 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 https://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. + */ + +// Package tscc TODO +package tscc + +import ( + "encoding/json" + "fmt" + "strings" + "time" + + "github.com/jmoiron/sqlx" + "golang.org/x/exp/slog" + + "dbm-services/mysql/db-tools/mysql-monitor/pkg/monitoriteminterface" +) + +var name = "spider-table-schema-consistency" + +// 记录未执行检查的表 +var schemas = []string{ + `create table if not exists infodba_schema.tscc_pending_execute_tbl( + db varchar(64) NOT NULL DEFAULT '', + tbl varchar(64) NOT NULL, + create_time datetime DEFAULT NULL, + PRIMARY KEY (db,tbl) + );`, + `CREATE TABLE if not exists infodba_schema.tscc_schema_checksum( + db char(64) NOT NULL, + tbl char(64) NOT NULL, + status char(32) NOT NULL DEFAULT "" COMMENT "检查结果,一致:ok,不一致:inconsistent", + checksum_result json COMMENT "差异表结构信息,tdbctl checksum table 的结果", + update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (db,tbl) + );`} +var ignoreDbs = []string{"sys", "information_schema", "mysql", "performance_schema", "infodba_schema", "test"} + +const ( + // SchemaCheckOk TODO + SchemaCheckOk = "OK" +) + +type tableSchemaConsistencyCheck struct { + ctldb *sqlx.DB +} + +// Name TODO +func (c *tableSchemaConsistencyCheck) Name() string { + return name +} + +// TsccPendingExecuteTbl TODO +type TsccPendingExecuteTbl struct { + Db string `db:"db"` + Tbl string `db:"tbl"` + CreateTime time.Time `db:"create_time"` +} + +// SchemaCheckResult TODO +type SchemaCheckResult struct { + ServerName string `json:"Server_name" db:"Server_name"` + Db string `json:"Db" db:"Db"` + Table string `json:"Table" db:"Table"` + Status string `json:"Status" db:"Status"` + Message string `json:"Message" db:"Message"` +} + +// PrimaryCtlInfo TODO +type PrimaryCtlInfo struct { + ServerName string `db:"SERVER_NAME"` + Host string `db:"HOST"` + Port int `db:"PORT"` + IsThisServer int `db:"IS_THIS_SERVER"` +} + +// SchemaCheckResults TODO +type SchemaCheckResults []SchemaCheckResult + +// CheckResult TODO +func (rs SchemaCheckResults) CheckResult() (inconsistencyItems []SchemaCheckResult) { + for _, r := range rs { + if strings.Compare(strings.ToUpper(r.Status), SchemaCheckOk) == 0 { + continue + } + inconsistencyItems = append(inconsistencyItems, r) + } + return +} + +// Run TODO +func (c *tableSchemaConsistencyCheck) Run() (msg string, err error) { + slog.Info("start check cluster schema ....") + defer c.ctldb.Close() + // 如果不是主节点,无需运行 + var ps PrimaryCtlInfo + err = c.ctldb.Get(&ps, "TDBCTL GET PRIMARY") + if err != nil { + slog.Error("execute TDBCTL GET PRIMARY failed", err) + return + } + if ps.IsThisServer == 0 { + slog.Info("is not primary tdbctl,no need to run") + return "", nil + } + initSQLs := []string{"set tc_admin = 0;", "use infodba_schema;"} + initSQLs = append(initSQLs, schemas...) + for _, sqlStr := range initSQLs { + if _, err = c.ctldb.Exec(sqlStr); err != nil { + slog.Error("exec init sql:", sqlStr, "err: %v", sqlStr, err) + return + } + } + var count int + err = c.ctldb.Get(&count, "select count(*) from tscc_pending_execute_tbl") + if err != nil { + slog.Error("query pending execute check table failed", err) + return + } + if count < 10 { + query, args, err := sqlx.In( + "insert ignore into tscc_pending_execute_tbl select TABLE_SCHEMA,TABLE_NAME,CREATE_TIME from information_schema.tables where TABLE_SCHEMA not in (?) ", ignoreDbs) + if err != nil { + slog.Error("get check tables failed", err) + return msg, err + } + _, err = c.ctldb.Exec(c.ctldb.Rebind(query), args...) + if err != nil { + return msg, err + } + } + finish := time.After(2 * time.Hour) + errChan := make(chan struct{}, 1) + stopsignal := 0 + var errCnt int + for { + select { + case <-finish: + stopsignal = 1 + // return "", nil + case <-errChan: + errCnt++ + if errCnt >= 100 { + return "there are too many exceptions quit ", nil + } + default: + if stopsignal == 1 { + slog.Info("the run time has been used up,bye ~") + return "", nil + } + var tblRows []TsccPendingExecuteTbl + err = c.ctldb.Select(&tblRows, "select * from tscc_pending_execute_tbl limit 500") + if err != nil { + slog.Error("failed to query the table to be verified", err) + return + } + if len(tblRows) < 1 { + return "done", nil + } + for _, tblRow := range tblRows { + var result SchemaCheckResults + c.ctldb.Exec("set tc_admin = 1;") + err = c.ctldb.Select(&result, fmt.Sprintf("tdbctl checksum `%s`.`%s`;", tblRow.Db, tblRow.Tbl)) + if err != nil { + errChan <- struct{}{} + slog.Error("exec tdbctl checksum table failed", err) + continue + } + c.ctldb.Exec("set tc_admin = 0;") + inconsistentItems := result.CheckResult() + if err = c.atomUpdateCheckResult(tblRow.Db, tblRow.Tbl, inconsistentItems); err == nil { + slog.Info("update checkresult ok") + } + time.Sleep(200 * time.Millisecond) + } + } + } +} + +func (c *tableSchemaConsistencyCheck) atomUpdateCheckResult(db, tbl string, inconsistentItems []SchemaCheckResult) ( + err error) { + var status string + status = SchemaCheckOk + tx, err := c.ctldb.Begin() + if err != nil { + return err + } + checkResult := []byte("{}") + if len(inconsistentItems) != 0 { + checkResult, err = json.Marshal(inconsistentItems) + if err != nil { + slog.Error("json marshal failed %s", err.Error()) + return + } + status = "" + } + _, err = tx.Exec("replace into infodba_schema.tscc_schema_checksum values(?,?,?,?,?)", db, tbl, status, + checkResult, + time.Now()) + if err != nil { + slog.Error("replace checksum record failed", err) + return + } + _, err = tx.Exec("delete from infodba_schema.tscc_pending_execute_tbl where db = ? and tbl = ? ", db, tbl) + if err != nil { + slog.Warn("delete pending tbl record failed", err) + return + } + if err = tx.Commit(); err != nil { + slog.Warn("commit error: ", err) + return + } + return +} + +// New TODO +func New(cc *monitoriteminterface.ConnectionCollect) monitoriteminterface.MonitorItemInterface { + return &tableSchemaConsistencyCheck{ctldb: cc.CtlDB} +} + +// Register TODO +func Register() (string, monitoriteminterface.MonitorItemConstructorFuncType) { + return name, New +} diff --git a/dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/uniquectlmaster/checker.go b/dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/uniquectlmaster/checker.go new file mode 100644 index 0000000000..4025054e53 --- /dev/null +++ b/dbm-services/mysql/db-tools/mysql-monitor/pkg/itemscollect/uniquectlmaster/checker.go @@ -0,0 +1,64 @@ +package uniquectlmaster + +import ( + "math/big" + "net" + + "github.com/jmoiron/sqlx" + "golang.org/x/exp/slog" + + "dbm-services/mysql/db-tools/mysql-monitor/pkg/monitoriteminterface" + "dbm-services/mysql/db-tools/mysql-monitor/pkg/utils" +) + +/* +1. 每一个 spider master 上的中控节点都上报自己看到的 ctl master +2. 在监控平台做 count 计数, > 1 则告警 +*/ + +var name = "unique-ctl-master" + +type Checker struct { + db *sqlx.DB +} + +func (c *Checker) Run() (msg string, err error) { + var res struct { + ServerName string `db:"SERVER_NAME"` + Host string `db:"HOST"` + Port uint32 `db:"PORT"` + IsThisServer uint32 `db:"IS_THIS_SERVER"` + } + err = c.db.QueryRowx(`tdbctl get primary`).StructScan(&res) + if err != nil { + return "", err + } + + slog.Info("unique-ctl-master", + slog.String("ctl master", res.Host)) + + ret := big.NewInt(0) + ret.SetBytes(net.ParseIP(res.Host).To4()) + + utils.SendMonitorMetrics( + "unique_ctl_master", + ret.Int64(), + map[string]interface{}{ + "ctl-master": res.Host, + }, + ) + + return "", nil +} + +func (c *Checker) Name() string { + return name +} + +func NewChecker(cc *monitoriteminterface.ConnectionCollect) monitoriteminterface.MonitorItemInterface { + return &Checker{db: cc.CtlDB} +} + +func Register() (string, monitoriteminterface.MonitorItemConstructorFuncType) { + return name, NewChecker +} diff --git a/dbm-services/mysql/db-tools/mysql-monitor/pkg/monitoriteminterface/connection_collect.go b/dbm-services/mysql/db-tools/mysql-monitor/pkg/monitoriteminterface/connection_collect.go index 7cf6d1f590..5e8c4707da 100644 --- a/dbm-services/mysql/db-tools/mysql-monitor/pkg/monitoriteminterface/connection_collect.go +++ b/dbm-services/mysql/db-tools/mysql-monitor/pkg/monitoriteminterface/connection_collect.go @@ -88,7 +88,8 @@ func NewConnectionCollect() (*ConnectionCollect, error) { config.MonitorConfig.Auth.ProxyAdmin, ) if err != nil { - if merr, ok := err.(*mysql.MySQLError); ok { + var merr *mysql.MySQLError + if errors.As(err, &merr) { if merr.Number == 1105 { // 连接 proxy 管理端肯定在这里返回 return &ConnectionCollect{ProxyDB: db1, ProxyAdminDB: db2}, nil @@ -118,19 +119,24 @@ func NewConnectionCollect() (*ConnectionCollect, error) { return nil, err } - ctlPort := config.MonitorConfig.Port + 1000 - db2, err := connectDB( - config.MonitorConfig.Ip, - ctlPort, - config.MonitorConfig.Auth.Mysql, - ) - if err != nil { - slog.Error( - "connect ctl", err, - slog.String("ip", config.MonitorConfig.Ip), - slog.Int("port", ctlPort), + // spider_slave 不建立到中控的连接 + // 所以要小心 + var db2 *sqlx.DB + if *config.MonitorConfig.Role == "spider_master" { + ctlPort := config.MonitorConfig.Port + 1000 + db2, err = connectDB( + config.MonitorConfig.Ip, + ctlPort, + config.MonitorConfig.Auth.Mysql, ) - return nil, err + if err != nil { + slog.Error( + "connect ctl", err, + slog.String("ip", config.MonitorConfig.Ip), + slog.Int("port", ctlPort), + ) + return nil, err + } } return &ConnectionCollect{MySqlDB: db1, CtlDB: db2}, nil diff --git a/dbm-services/mysql/db-tools/mysql-rotatebinlog/Makefile b/dbm-services/mysql/db-tools/mysql-rotatebinlog/Makefile index 4ac38012e2..e7335773f3 100644 --- a/dbm-services/mysql/db-tools/mysql-rotatebinlog/Makefile +++ b/dbm-services/mysql/db-tools/mysql-rotatebinlog/Makefile @@ -13,7 +13,8 @@ release: @CGO_ENABLE=0 GOARCH=amd64 GOOS=linux go build -ldflags ${RELEASE_BUILD_FLAG} -o ${OUTPUT_DIR}/${PROJ}/${PROJ_BIN} @cp config.example.yaml ${OUTPUT_DIR}/${PROJ}/config.yaml.example @tar -C ${OUTPUT_DIR} -zcf ${OUTPUT_DIR}/${PROJ_PKG} ${PROJ}/ - +release-bin: + @CGO_ENABLE=0 GOARCH=amd64 GOOS=linux go build -ldflags ${RELEASE_BUILD_FLAG} -o ${OUTPUT_DIR}/${PROJ}/${PROJ_BIN} .PHONY: beta beta: @cd ${BASE_DIR}/cmd && go build -ldflags ${BETA_BUILD_FLAG} -o ${OUTPUT_DIR}/${PROJ}/${PROJ_BIN} diff --git a/dbm-services/mysql/db-tools/mysql-rotatebinlog/README.md b/dbm-services/mysql/db-tools/mysql-rotatebinlog/README.md index e2b33117db..f1e08ae88e 100644 --- a/dbm-services/mysql/db-tools/mysql-rotatebinlog/README.md +++ b/dbm-services/mysql/db-tools/mysql-rotatebinlog/README.md @@ -52,7 +52,7 @@ rotate_binlog 可以作为独立程序 crontab 来运行,也可以注册到 my rotate_binlog 通过 sqlite 本地 db 记录处理过的 binlog 状态,代替一起通过文本文件的方式。 ## sqlite migrations -每次允许时都会允许 sqlite migrate,是为了避免对存量 DB 实例 如果更新了 binlog_rotate sqlite 表结构,避免重建 sqlite 库,会导致已处理的 binlog 混乱。 +为了避免对存量 DB 实例 如果更新了 binlog_rotate sqlite 表结构,避免重建 sqlite 库,会导致已处理的 binlog 混乱,需要手动做 sql migration 如果涉及字段变更,因为 sqlite 不支持 drop column, change column 语法,migrations 里面要重建表,例如: ``` PRAGMA foreign_keys=off; @@ -75,7 +75,7 @@ CREATE TABLE IF NOT EXISTS binlog_rotate ( stop_time varchar(32) default '', backup_status integer default -2, backup_status_info varchar(120) not null default '', - backup_taskid varchar(64) default '', + task_id varchar(64) default '', created_at varchar(32) default '', updated_at varchar(32) default '', PRIMARY KEY(cluster_id,filename,host,port) @@ -85,10 +85,10 @@ CREATE INDEX idx_status ON binlog_rotate (backup_status); INSERT INTO binlog_rotate ( - bk_biz_id, cluster_id, db_role,host,port,filename,filesize,file_mtime,start_time,stop_time,backup_status,backup_status_info,backup_taskid,created_at,updated_at + bk_biz_id, cluster_id, db_role,host,port,filename,filesize,file_mtime,start_time,stop_time,backup_status,backup_status_info,task_id,created_at,updated_at ) SELECT - bk_biz_id, cluster_id, db_role,host,port,filename,filesize,file_mtime,start_time,stop_time,backup_status,backup_status_info,backup_taskid,created_at,updated_at + bk_biz_id, cluster_id, db_role,host,port,filename,filesize,file_mtime,start_time,stop_time,backup_status,backup_status_info,task_id,created_at,updated_at FROM binlog_rotate_old; DROP TABLE IF EXISTS binlog_rotate_old; diff --git a/dbm-services/mysql/db-tools/mysql-rotatebinlog/pkg/backup/backup_cos.go b/dbm-services/mysql/db-tools/mysql-rotatebinlog/pkg/backup/backup_cos.go index 7ff47c2e3b..a23f550c45 100644 --- a/dbm-services/mysql/db-tools/mysql-rotatebinlog/pkg/backup/backup_cos.go +++ b/dbm-services/mysql/db-tools/mysql-rotatebinlog/pkg/backup/backup_cos.go @@ -1,20 +1,31 @@ package backup +import "dbm-services/common/go-pubpkg/backupclient" + // COSBackupClient TODO type COSBackupClient struct { + ToolPath string `mapstructure:"tool_path" json:"tool_path" validate:"required"` + FileTag string `mapstructure:"file_tag" json:"file_tag" validate:"required"` + AuthFile string `mapstructure:"auth_file" json:"auth_file"` + backupClient *backupclient.BackupClient } // Init TODO func (o *COSBackupClient) Init() error { + if backupClient, err := backupclient.New(o.ToolPath, o.AuthFile, o.FileTag); err != nil { + return err + } else { + o.backupClient = backupClient + } return nil } // Upload TODO func (o *COSBackupClient) Upload(fileName string) (string, error) { - return "", nil + return o.backupClient.Upload(fileName) } // Query TODO func (o *COSBackupClient) Query(taskId string) (int, error) { - return 0, nil + return o.backupClient.Query(taskId) } diff --git a/dbm-services/mysql/db-tools/mysql-rotatebinlog/pkg/backup/backup_ibs.go b/dbm-services/mysql/db-tools/mysql-rotatebinlog/pkg/backup/backup_ibs.go index c671d55bc4..e269e02f7e 100644 --- a/dbm-services/mysql/db-tools/mysql-rotatebinlog/pkg/backup/backup_ibs.go +++ b/dbm-services/mysql/db-tools/mysql-rotatebinlog/pkg/backup/backup_ibs.go @@ -6,11 +6,11 @@ import ( "strconv" "strings" + "github.com/pkg/errors" + + "dbm-services/common/go-pubpkg/cmutil" "dbm-services/common/go-pubpkg/logger" "dbm-services/common/go-pubpkg/validate" - "dbm-services/mysql/db-tools/mysql-rotatebinlog/pkg/util" - - "github.com/pkg/errors" ) const ( @@ -54,7 +54,7 @@ func (o *IBSBackupClient) Upload(fileName string) (taskId string, err error) { logger.Info("backup upload to ibs: %s", fileName) backupCmd := fmt.Sprintf(`%s -f %s`, o.ibsUploadCmd, fileName) var stdout, stderr string - if stdout, stderr, err = util.ExecCommand(true, "", backupCmd); err != nil { + if stdout, stderr, err = cmutil.ExecCommand(true, "", "", backupCmd); err != nil { return "", errors.Wrapf(err, "upload failed:%s", stderr) } reTaskId := regexp.MustCompile(`taskid:(\d+)`) @@ -74,7 +74,7 @@ func (o *IBSBackupClient) Query(taskid string) (taskStatus int, err error) { // queryCmd := fmt.Sprintf(`%s -q --taskid=%s`, o.IBSBackup, taskid) queryCmd := fmt.Sprintf(`%s -q --taskid %s`, o.ibsQueryCmd, taskid) var stdout, stderr string - if stdout, stderr, err = util.ExecCommand(true, "", queryCmd); err != nil { + if stdout, stderr, err = cmutil.ExecCommand(true, "", "", queryCmd); err != nil { return 0, errors.Wrapf(err, "query failed:%s", stderr) } outLines := strings.Split(stdout, "\n") diff --git a/dbm-services/mysql/db-tools/mysql-rotatebinlog/pkg/backup/init.go b/dbm-services/mysql/db-tools/mysql-rotatebinlog/pkg/backup/init.go index 8bfb5c962b..d13d230a3e 100644 --- a/dbm-services/mysql/db-tools/mysql-rotatebinlog/pkg/backup/init.go +++ b/dbm-services/mysql/db-tools/mysql-rotatebinlog/pkg/backup/init.go @@ -12,22 +12,28 @@ import ( func InitBackupClient() (backupClient BackupClient, err error) { backupClients := viper.GetStringMap("backup_client") for name, cfgClient := range backupClients { - if name == "ibs" && viper.GetBool("backup_client.ibs.enable") { + if name == "ibs" { + if !viper.GetBool("backup_client.ibs.enable") { + continue + } var ibsClient IBSBackupClient if err := mapstructure.Decode(cfgClient, &ibsClient); err != nil { return nil, err } else { backupClient = &ibsClient } - } else if name == "cos" && viper.GetBool("backup_client.cos.enable") { - var ibsClient COSBackupClient - if err := mapstructure.Decode(cfgClient, &ibsClient); err != nil { + } else if name == "cos" { + if !viper.GetBool("backup_client.cos.enable") { + continue + } + var cosClient COSBackupClient + if err := mapstructure.Decode(cfgClient, &cosClient); err != nil { return nil, err } else { - backupClient = &ibsClient + backupClient = &cosClient } } else { - logger.Warn("unknown backup_client", name) + logger.Error("unknown backup_client %s", name) // return nil, errors.Errorf("unknown backup_client: %s", name) } } diff --git a/dbm-services/mysql/db-tools/mysql-rotatebinlog/pkg/models/dbmodel.go b/dbm-services/mysql/db-tools/mysql-rotatebinlog/pkg/models/dbmodel.go index ac6fd1ef19..03129bfc0a 100644 --- a/dbm-services/mysql/db-tools/mysql-rotatebinlog/pkg/models/dbmodel.go +++ b/dbm-services/mysql/db-tools/mysql-rotatebinlog/pkg/models/dbmodel.go @@ -84,14 +84,14 @@ type BinlogFileModel struct { StopTime string `json:"stop_time" db:"stop_time"` BackupStatus int `json:"backup_status,omitempty" db:"backup_status"` BackupStatusInfo string `json:"backup_status_info" db:"backup_status_info"` - BackupTaskid string `json:"backup_taskid,omitempty" db:"backup_taskid"` + BackupTaskid string `json:"task_id,omitempty" db:"task_id"` *ModelAutoDatetime } // String 用于打印 func (m *BinlogFileModel) String() string { return fmt.Sprintf( - "{filename:%s, start_time: %s, stop_time: %s, backup_status:%d, backup_taskid: %s}", + "{filename:%s, start_time: %s, stop_time: %s, backup_status:%d, task_id: %s}", m.Filename, m.StartTime, m.StopTime, m.BackupStatus, m.BackupTaskid, ) } @@ -137,7 +137,7 @@ func (m *BinlogFileModel) Save(db *sqlx.DB) error { sqlBuilder := sq.Insert("").Into(m.TableName()). Columns( "bk_biz_id", "cluster_id", "cluster_domain", "db_role", "host", "port", "filename", - "filesize", "start_time", "stop_time", "file_mtime", "backup_status", "backup_taskid", + "filesize", "start_time", "stop_time", "file_mtime", "backup_status", "task_id", "created_at", "updated_at", ). Values( @@ -167,7 +167,7 @@ func (m *BinlogFileModel) BatchSave(models []*BinlogFileModel, db *sqlx.DB) erro sqlBuilder := sq.Insert("").Into(m.TableName()). Columns( "bk_biz_id", "cluster_id", "cluster_domain", "db_role", "host", "port", "filename", - "filesize", "start_time", "stop_time", "file_mtime", "backup_status", "backup_taskid", + "filesize", "start_time", "stop_time", "file_mtime", "backup_status", "task_id", "created_at", "updated_at", ) for _, o := range models { @@ -199,7 +199,7 @@ func (m *BinlogFileModel) Update(db *sqlx.DB) error { Set("backup_status_info", m.BackupStatusInfo). Set("updated_at", m.UpdatedAt) if m.BackupTaskid != "" { - sqlBuilder = sqlBuilder.Set("backup_taskid", m.BackupTaskid) + sqlBuilder = sqlBuilder.Set("task_id", m.BackupTaskid) } if m.StartTime != "" { sqlBuilder = sqlBuilder.Set("start_time", m.StartTime) @@ -332,7 +332,7 @@ func (m *BinlogFileModel) Query(db *sqlx.DB, pred interface{}, params ...interfa var files []*BinlogFileModel sqlBuilder := sq.Select( "bk_biz_id", "cluster_id", "cluster_domain", "db_role", "host", "port", "filename", - "filesize", "start_time", "stop_time", "file_mtime", "backup_status", "backup_taskid", + "filesize", "start_time", "stop_time", "file_mtime", "backup_status", "task_id", ). From(m.TableName()).Where(m.instanceWhere()) sqlBuilder = sqlBuilder.Where(pred, params...).OrderBy("filename asc") diff --git a/dbm-services/mysql/db-tools/mysql-rotatebinlog/pkg/models/migrations/000001_create_table.up.sql b/dbm-services/mysql/db-tools/mysql-rotatebinlog/pkg/models/migrations/000001_create_table.up.sql index b3c4a2c445..66ea3af9c2 100644 --- a/dbm-services/mysql/db-tools/mysql-rotatebinlog/pkg/models/migrations/000001_create_table.up.sql +++ b/dbm-services/mysql/db-tools/mysql-rotatebinlog/pkg/models/migrations/000001_create_table.up.sql @@ -12,7 +12,7 @@ CREATE TABLE IF NOT EXISTS binlog_rotate ( stop_time varchar(32) default '', backup_status integer default -2, backup_status_info varchar(120) not null default '', - backup_taskid varchar(64) default '', + task_id varchar(64) default '', created_at varchar(32) default '', updated_at varchar(32) default '', PRIMARY KEY(cluster_id,filename,host,port) diff --git a/dbm-services/mysql/db-tools/mysql-rotatebinlog/pkg/rotate/main.go b/dbm-services/mysql/db-tools/mysql-rotatebinlog/pkg/rotate/main.go index 21562e41bc..0f2226cda9 100644 --- a/dbm-services/mysql/db-tools/mysql-rotatebinlog/pkg/rotate/main.go +++ b/dbm-services/mysql/db-tools/mysql-rotatebinlog/pkg/rotate/main.go @@ -70,6 +70,7 @@ func (c *RotateBinlogComp) Start() (err error) { if err = models.SetupTable(); err != nil { return err } + // 在需要的地方初始化 var backupClient backup.BackupClient if backupClient, err = backup.InitBackupClient(); err != nil { return err diff --git a/dbm-services/mysql/db-tools/mysql-rotatebinlog/pkg/rotate/rotate.go b/dbm-services/mysql/db-tools/mysql-rotatebinlog/pkg/rotate/rotate.go index cc81ff3a31..0164902a69 100644 --- a/dbm-services/mysql/db-tools/mysql-rotatebinlog/pkg/rotate/rotate.go +++ b/dbm-services/mysql/db-tools/mysql-rotatebinlog/pkg/rotate/rotate.go @@ -286,7 +286,7 @@ func (r *BinlogRotate) Backup() error { } } else { // 等待上传的,查询上传结果 if f.BackupTaskid == "" { - logger.Error("binlog backup_taskid should not empty %s", f.Filename) + logger.Error("binlog backup task_id should not empty %s", f.Filename) f.BackupStatus = models.IBStatusFail } else { taskStatus, err := r.backupClient.Query(f.BackupTaskid) diff --git a/dbm-services/mysql/db-tools/mysql-rotatebinlog/pkg/util/util.go b/dbm-services/mysql/db-tools/mysql-rotatebinlog/pkg/util/util.go index 3bec96317d..32be1a7894 100644 --- a/dbm-services/mysql/db-tools/mysql-rotatebinlog/pkg/util/util.go +++ b/dbm-services/mysql/db-tools/mysql-rotatebinlog/pkg/util/util.go @@ -2,19 +2,15 @@ package util import ( - "bytes" "fmt" - "os" - "os/exec" "regexp" "strconv" "strings" - "dbm-services/common/go-pubpkg/cmutil" - "dbm-services/common/go-pubpkg/logger" - "github.com/pkg/errors" "github.com/spf13/cast" + + "dbm-services/common/go-pubpkg/cmutil" ) // DiskDfResult disk df output @@ -41,6 +37,7 @@ func (d DiskDfResult) String() string { } // GetDiskPartitionWithDir TODO +// todo replace with cmutil.GetDiskPartInfo func GetDiskPartitionWithDir(dirName string) (*DiskDfResult, error) { /* $ df -m /data/dbbak/data1 @@ -52,7 +49,7 @@ func GetDiskPartitionWithDir(dirName string) (*DiskDfResult, error) { return nil, errors.New("df -m dirName should not be empty") } cmdArgs := []string{"-m", dirName} - stdout, stderr, err := ExecCommand(false, "df", cmdArgs...) + stdout, stderr, err := cmutil.ExecCommand(false, "", "df", cmdArgs...) if err != nil { return nil, errors.Wrapf(err, "dir:%s, err:%+v", dirName, stderr) } @@ -79,6 +76,7 @@ func GetDiskPartitionWithDir(dirName string) (*DiskDfResult, error) { // GetDirectorySizeMB du 获取 binlog 目录大小 // 如果 binlog 目录有其它文件,会一起计算 +// TODO replace with cmutil.DirSize() func GetDirectorySizeMB(binlogDir string) (int64, error) { /* du -sm /data/ @@ -87,7 +85,7 @@ func GetDirectorySizeMB(binlogDir string) (int64, error) { */ // cmdArgs := fmt.Sprintf("-sm %s", binlogDir) // du -sh /xx cmdArgs := []string{"-sm", binlogDir} - stdout, stderr, err := ExecCommand(false, "du", cmdArgs...) + stdout, stderr, err := cmutil.ExecCommand(false, "", "du", cmdArgs...) errStr := strings.SplitN(stderr, "\n", 1)[0] if err != nil { if strings.Contains(stdout, binlogDir) && strings.Contains(stderr, "lost+found") { @@ -113,38 +111,3 @@ func squashSpace(ss string) string { reSpaces := regexp.MustCompile(`\s+`) return reSpaces.ReplaceAllString(ss, " ") } - -// ExecCommand bash=true: bash -c 'cmdName args', bash=false: ./cmdName args list -// ExecCommand(false, "df", "-k /data") will get `df '-k /data'` error command. you need change it to (false, "df", "-k", "/data") or (true, "df -k /data") -// bash=false need PATH -func ExecCommand(bash bool, cmdName string, args ...string) (string, string, error) { - var cmd *exec.Cmd - if bash { - if cmdName != "" { - cmdName += " " - } - cmdStr := fmt.Sprintf(`%s%s`, cmdName, strings.Join(args, " ")) - cmd = exec.Command("bash", "-c", cmdStr) - } else { - if cmdName == "" { - return "", "", errors.Errorf("command name should not be empty:%v", args) - } - // args should be list - cmd = exec.Command(cmdName, args...) - } - cmd.Env = []string{ - fmt.Sprintf( - "PATH=%s:/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin", - os.Getenv("PATH"), - ), - } - //logger.Info("PATH:%s cmd.Env:%v", os.Getenv("PATH"), cmd.Env) - var stdout, stderr bytes.Buffer - cmd.Stdout = &stdout - cmd.Stderr = &stderr - if err := cmd.Run(); err != nil { - logger.Error("stdout:%s, stderr:%s, cmd:%s", stdout.String(), stderr.String(), cmd.String()) - return stdout.String(), stdout.String(), errors.Wrap(err, cmd.String()) - } - return stdout.String(), stderr.String(), nil -} diff --git a/dbm-services/mysql/db-tools/mysql-table-checksum/go.mod b/dbm-services/mysql/db-tools/mysql-table-checksum/go.mod index 75576d7c92..344ba6e751 100644 --- a/dbm-services/mysql/db-tools/mysql-table-checksum/go.mod +++ b/dbm-services/mysql/db-tools/mysql-table-checksum/go.mod @@ -6,7 +6,6 @@ require ( github.com/go-sql-driver/mysql v1.7.1 github.com/jmoiron/sqlx v1.3.5 github.com/juju/fslock v0.0.0-20160525022230-4d5c94c67b4b - github.com/pkg/errors v0.9.1 github.com/spf13/cobra v1.7.0 github.com/spf13/viper v1.15.0 golang.org/x/exp v0.0.0-20230418202329-0354be287a23 diff --git a/dbm-services/mysql/db-tools/mysql-table-checksum/go.sum b/dbm-services/mysql/db-tools/mysql-table-checksum/go.sum index 6321230ad5..80b30bd947 100644 --- a/dbm-services/mysql/db-tools/mysql-table-checksum/go.sum +++ b/dbm-services/mysql/db-tools/mysql-table-checksum/go.sum @@ -155,7 +155,6 @@ github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us= github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= diff --git a/dbm-services/mysql/db-tools/mysql-table-checksum/pkg/checker/run_command.go b/dbm-services/mysql/db-tools/mysql-table-checksum/pkg/checker/run_command.go index 4555df6301..dc5efdd884 100644 --- a/dbm-services/mysql/db-tools/mysql-table-checksum/pkg/checker/run_command.go +++ b/dbm-services/mysql/db-tools/mysql-table-checksum/pkg/checker/run_command.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/json" + "errors" "fmt" "os" "os/exec" @@ -11,7 +12,6 @@ import ( "dbm-services/mysql/db-tools/mysql-table-checksum/pkg/config" - "github.com/pkg/errors" "golang.org/x/exp/slog" ) @@ -33,17 +33,20 @@ func (r *Checker) Run() error { time.Sleep(2 * time.Second) // 故意休眠 2s, 让时间往前走一下, mysql 时间戳精度不够, 这里太快了会有问题 err := command.Run() if err != nil { - if _, ok := err.(*exec.ExitError); !ok { + var exitError *exec.ExitError + if !errors.As(err, &exitError) { slog.Error("run pt-table-checksum got unexpected error", err) return err } } - ptErr, _ := err.(*exec.ExitError) - slog.Info( - "run pt-table-checksum success", - slog.Any("pt err", ptErr), - ) + var ptErr *exec.ExitError + _ = errors.As(err, &ptErr) + if ptErr != nil { + slog.Info("run pt-table-checksum success", slog.String("pt err", ptErr.String())) + } else { + slog.Info("run pt-table-checksum success without any err") + } /* 这一段是最难受的逻辑, 根据 pt-table-checksum 的文档 @@ -140,7 +143,7 @@ func (r *Checker) Run() error { if ptErr != nil && (ptErr.ExitCode()&1 != 0 || ptErr.ExitCode()&2 != 0 || ptErr.ExitCode()&4 != 0) { err = errors.New(string(ojson)) slog.Error("run pt-table-checksum bad flag found", err) - fmt.Fprintf(os.Stderr, string(ojson)) + _, _ = fmt.Fprintf(os.Stderr, string(ojson)) return err } diff --git a/dbm-services/redis/db-tools/dbactuator/example/bkdbmon_install.example.md b/dbm-services/redis/db-tools/dbactuator/example/bkdbmon_install.example.md index 4d18a8bf7d..2b740cd205 100644 --- a/dbm-services/redis/db-tools/dbactuator/example/bkdbmon_install.example.md +++ b/dbm-services/redis/db-tools/dbactuator/example/bkdbmon_install.example.md @@ -59,9 +59,9 @@ bk-dbmon安装: "bk_cloud_id":"246", "app":"testapp", "app_name":"测试app", - "cluster_domain":"tendisx.aaaa.testapp.db", + "cluster_domain":"cache.aaaa.testapp.db", "cluster_name":"aaaa", - "cluster_type":"PredixyTendisplusCluster", + "cluster_type":"TwemproxyRedisInstance", "meta_role":"redis_master", "server_ip":"127.0.0.1", "server_ports":[ @@ -69,7 +69,14 @@ bk-dbmon安装: 30001, 30002, 30003 - ] + ], + "server_shards":{ + "a.a.a.a:12000":"0-104999", + "a.a.a.a:12001":"105000-209999", + "a.a.a.a:12002":"210000-314999", + "a.a.a.a:12003":"315000-419999" + }, + "cache_backup_mode":"rdb" }, { "bk_biz_id":"200500194", @@ -86,7 +93,9 @@ bk-dbmon安装: 31001, 31002, 31003 - ] + ], + "server_shards":{}, + "cache_backup_mode":"" } ] } diff --git a/dbm-services/redis/db-tools/dbactuator/example/clustermeet_checkfinish.json b/dbm-services/redis/db-tools/dbactuator/example/clustermeet_checkfinish.json new file mode 100644 index 0000000000..87ef6fda1e --- /dev/null +++ b/dbm-services/redis/db-tools/dbactuator/example/clustermeet_checkfinish.json @@ -0,0 +1,17 @@ +{ + "password":"xxxx", + "replica_pairs":[ + { + "master_ip":"xxxx", + "master_port":40000 + }, + { + "master_ip":"xxxx", + "master_port":40001 + }, + { + "master_ip":"xxxx", + "master_port":40002 + } + ] +} \ No newline at end of file diff --git a/dbm-services/redis/db-tools/dbactuator/example/redis_backup.example.md b/dbm-services/redis/db-tools/dbactuator/example/redis_backup.example.md index f781470afb..45a962070c 100644 --- a/dbm-services/redis/db-tools/dbactuator/example/redis_backup.example.md +++ b/dbm-services/redis/db-tools/dbactuator/example/redis_backup.example.md @@ -19,6 +19,7 @@ "without_to_backup_sys":true, //是否上传到备份系统,默认false "ssd_log_count":{ // tendisssd 重建slave做备份需设置相关参数,普通备份不传值 或 传0即可 "log-count":8000000, + "log-keep-count":800000, "slave-log-keep-count":5000000 } } diff --git a/dbm-services/redis/db-tools/dbactuator/example/redis_data_query.json b/dbm-services/redis/db-tools/dbactuator/example/redis_data_query.json new file mode 100644 index 0000000000..cac0ea04fd --- /dev/null +++ b/dbm-services/redis/db-tools/dbactuator/example/redis_data_query.json @@ -0,0 +1,20 @@ +{ + "source_ip":"127.0.0.x", + "source_ports":[30001,30000], + "new_temp_ip":"127.0.0.x", + "new_temp_ports":[ + 30000, + 30001 + ], + "recovery_time_point":"2023-08-03 16:53:00", + "is_precheck":true, + "tendis_type":"TendisSSDInstance", + "user":"xxxx", + "password":"xxxx", + "base_info":{ + "url":"http://127.0.0.x/backupApi", + "sys_id":"xxxx", + "key":"xxxx" + } + +} \ No newline at end of file diff --git a/dbm-services/redis/db-tools/dbactuator/example/redis_dts_datarepair.example.md b/dbm-services/redis/db-tools/dbactuator/example/redis_dts_datarepair.example.md new file mode 100644 index 0000000000..8e13da1a70 --- /dev/null +++ b/dbm-services/redis/db-tools/dbactuator/example/redis_dts_datarepair.example.md @@ -0,0 +1,36 @@ +### redis_dts_datacheck +bk-dbmon安装: +```sh +./dbactuator_redis --uid={{uid}} --root_id={{root_id}} --node_id={{node_id}} --version_id={{version_id}} --atom-job-list="redis_dts_datarepair" --payload='{{payload_base64}}' +``` + +dts_type是迁移类型,有四个值: +- one_app_diff_cluster: 一个业务下的不同集群间迁移 +- diff_app_diff_cluster: 不同业务下的不同集群间迁移 +- sync_to_other_system: 同步到其他系统,如迁移到腾讯云 +- user_built_to_dbm: 用户自建redis到dbm系统 + +原始payload: +```json +{ + "pkg":"dbtools.tar.gz", + "pkg_md5":"ced0fa280c63cb31536fefc1845f3ff0", + "bk_biz_id":"testapp", + "dts_type":"one_app_diff_cluster", + "src_redis_ip":"127.0.0.1", //源redis信息 + "src_redis_port_segmentlist":[ + { + "port":30000, + "seg_start":-1, + "seg_end":-1 + } + ], + "src_hash_tag":false, //是否开启hash_tag + "src_redis_password":"xxxxx", //是redis的密码,非srcCluster proxy得密码 + "src_cluster_addr":"tendisx.aaaa.testapp.db:50000", + "dst_cluster_addr":"tendisx.bbbb.testapp.db:50000", //目的集群addr + "dst_cluster_password":"yyyy", //目的集群proxy密码 + "key_white_regex":"*", + "key_black_regex":"" +} +``` \ No newline at end of file diff --git a/dbm-services/redis/db-tools/dbactuator/example/redis_dts_datarepaire.example.md b/dbm-services/redis/db-tools/dbactuator/example/redis_dts_datarepaire.example.md deleted file mode 100644 index df0e19a973..0000000000 --- a/dbm-services/redis/db-tools/dbactuator/example/redis_dts_datarepaire.example.md +++ /dev/null @@ -1,36 +0,0 @@ -### redis_dts_datacheck -bk-dbmon安装: -```sh -./dbactuator_redis --uid={{uid}} --root_id={{root_id}} --node_id={{node_id}} --version_id={{version_id}} --atom-job-list="redis_dts_datarepaire" --payload='{{payload_base64}}' -``` - -dts_type是迁移类型,有四个值: -- one_app_diff_cluster: 一个业务下的不同集群间迁移 -- diff_app_diff_cluster: 不同业务下的不同集群间迁移 -- sync_to_other_system: 同步到其他系统,如迁移到腾讯云 -- user_built_to_dbm: 用户自建redis到dbm系统 - -原始payload: -```json -{ - "pkg":"dbtools.tar.gz", - "pkg_md5":"ced0fa280c63cb31536fefc1845f3ff0", - "bk_biz_id":"testapp", - "dts_type":"one_app_diff_cluster", - "src_redis_ip":"127.0.0.1", //源redis信息 - "src_redis_port_segmentlist":[ - { - "port":30000, - "seg_start":-1, - "seg_end":-1 - } - ], - "src_hash_tag":false, //是否开启hash_tag - "src_redis_password":"xxxxx", //是redis的密码,非srcCluster proxy得密码 - "src_cluster_addr":"tendisx.aaaa.testapp.db:50000", - "dst_cluster_addr":"tendisx.bbbb.testapp.db:50000", //目的集群addr - "dst_cluster_password":"yyyy", //目的集群proxy密码 - "key_white_regex":"*", - "key_black_regex":"" -} -``` \ No newline at end of file diff --git a/dbm-services/redis/db-tools/dbactuator/example/redis_dts_online_switch.example.md b/dbm-services/redis/db-tools/dbactuator/example/redis_dts_online_switch.example.md new file mode 100644 index 0000000000..9b7d3e8244 --- /dev/null +++ b/dbm-services/redis/db-tools/dbactuator/example/redis_dts_online_switch.example.md @@ -0,0 +1,64 @@ +### redis_dts_online_switch +dts 在线切换: +``` +./dbactuator_redis --uid={{uid}} --root_id={{root_id}} --node_id={{node_id}} --version_id={{version_id}} --atom-job-list="redis_dts_online_switch" --payload='{{payload_base64}}' +``` + +原始payload +```json +{ + "dst_proxy_pkg":{ + "pkg":"predixy-1.4.0.tar.gz", + "pkg_md5":"24aba4a96dcf7f8581d2fde89d062455" + }, + "dts_bill_id":1111, + "src_proxy_ip": "xx.xx.xx.xx", + "src_proxy_port":50000, + "src_proxy_password":"passxxxx", + "src_cluster_type":"TwemproxyRedisInstance", + "dst_proxy_ip":"yy.yy.yy.yy", + "dst_proxy_port":50100, + "dst_proxy_password":"passyyyy", + "dst_cluster_type":"PredixyTendisplusCluster", + "dst_redis_ip":"a.a.a.a", + "dst_redis_port":30000, + "dst_proxy_config_content":" +Bind yy.yy.yy.yy:50100 +WorkerThreads 8 +ClientTimeout 0 +Authority { + Auth \"passyyyy\" { + Mode write + } +} +Log /data/predixy/50100/logs/log +LogRotate 1d +ClusterServerPool { + Password xxxxxx + RefreshInterval 1 + ServerFailureLimit 10 + ServerRetryTimeout 1 + ServerTimeout 0 + KeepAlive 0 + Servers { + + a.a.a.a:30000 + + b.b.b.b:30000 + + + } +} +LatencyMonitor all { + Commands { + + all + } + TimeSpan { + + 100 + + 500 + + 1000 + + 5000 + + 10000 + } +} + " +} +``` \ No newline at end of file diff --git a/dbm-services/redis/db-tools/dbactuator/go.mod b/dbm-services/redis/db-tools/dbactuator/go.mod index 8dfe8b3bae..fb758236ee 100644 --- a/dbm-services/redis/db-tools/dbactuator/go.mod +++ b/dbm-services/redis/db-tools/dbactuator/go.mod @@ -11,10 +11,10 @@ require ( github.com/google/go-cmp v0.5.9 github.com/panjf2000/ants/v2 v2.7.2 github.com/pkg/errors v0.9.1 - github.com/shirou/gopsutil/v3 v3.23.2 + github.com/shirou/gopsutil/v3 v3.23.7 github.com/smartystreets/goconvey v1.7.2 github.com/spf13/cobra v1.7.0 - golang.org/x/sys v0.7.0 + golang.org/x/sys v0.11.0 gopkg.in/yaml.v2 v2.4.0 ) @@ -37,7 +37,9 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/tklauser/go-sysconf v0.3.11 // indirect github.com/tklauser/numcpus v0.6.0 // indirect - github.com/yusufpapurcu/wmi v1.2.2 // indirect - golang.org/x/crypto v0.8.0 // indirect - golang.org/x/text v0.9.0 // indirect + github.com/yusufpapurcu/wmi v1.2.3 // indirect + golang.org/x/crypto v0.12.0 // indirect + golang.org/x/net v0.14.0 // indirect + golang.org/x/sync v0.3.0 // indirect + golang.org/x/text v0.12.0 // indirect ) diff --git a/dbm-services/redis/db-tools/dbactuator/go.sum b/dbm-services/redis/db-tools/dbactuator/go.sum index c756d1aa31..2991d573b5 100644 --- a/dbm-services/redis/db-tools/dbactuator/go.sum +++ b/dbm-services/redis/db-tools/dbactuator/go.sum @@ -64,8 +64,10 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/shirou/gopsutil/v3 v3.23.2 h1:PAWSuiAszn7IhPMBtXsbSCafej7PqUOvY6YywlQUExU= -github.com/shirou/gopsutil/v3 v3.23.2/go.mod h1:gv0aQw33GLo3pG8SiWKiQrbDzbRY1K80RyZJ7V4Th1M= +github.com/shirou/gopsutil/v3 v3.23.7 h1:C+fHO8hfIppoJ1WdsVm1RoI0RwXoNdfTK7yWXV0wVj4= +github.com/shirou/gopsutil/v3 v3.23.7/go.mod h1:c4gnmoRC0hQuaLqvxnx1//VXQ0Ms/X9UnJF8pddY5z4= +github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= +github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= @@ -80,32 +82,35 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM= github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= -github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= -github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= +github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= -golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/dbm-services/redis/db-tools/dbactuator/models/myredis/client.go b/dbm-services/redis/db-tools/dbactuator/models/myredis/client.go index 3bf0e572e6..1d0e12da77 100644 --- a/dbm-services/redis/db-tools/dbactuator/models/myredis/client.go +++ b/dbm-services/redis/db-tools/dbactuator/models/myredis/client.go @@ -1,8 +1,10 @@ package myredis import ( + "bufio" "context" "fmt" + "os/exec" "path/filepath" "regexp" "strconv" @@ -710,8 +712,8 @@ func (db *RedisClient) Sscan(keyname string, cursor uint64, match string, count fields, retCursor, err = db.InstanceClient.SScan(context.TODO(), keyname, cursor, match, count).Result() } if err != nil && err != redis.Nil { - mylog.Logger.Error("Redis 'sscan %s %d match %s count %s' command fail,err:%v,addr:%s", keyname, cursor, match, count, - err, db.Addr) + mylog.Logger.Error("Redis 'sscan %s %d match %s count %d' command fail,err:%v,addr:%s", + keyname, cursor, match, count, err, db.Addr) return fields, 0, err } return fields, retCursor, nil @@ -1680,3 +1682,112 @@ func (db *RedisClient) Close() { db.InstanceClient = nil return } + +// CmdWhiteList 这些命令是dba工具 or 监控发起的,不是用户请求 +// monitor命令本身会产生一个OK结果,所以也忽略 +var CmdWhiteList = []string{ + "OK", + "auth", + "info", + "ping", + "cluster", + "slowlog", + "CONFIG", + "binlogflush", + "INCRSYNC", + "readonly", + "adminset", +} + +// IsRedisUsing 通过执行monitor命令确认redis是否在使用(过滤掉dba执行的命令) +// go-redis不支持monitor命令,所以我们必须传入 redis-cli 客户端位置 +func (db *RedisClient) IsRedisUsing(redisCli string, timeout time.Duration) (isUsing bool, cmds []string, err error) { + ctx01, cancel := context.WithTimeout(context.TODO(), timeout) + defer cancel() + + cmdWhiteStr := strings.Join(CmdWhiteList, "|") + list01 := strings.Split(db.Addr, ":") + + monitorCmd := fmt.Sprintf("timeout %d %s -h %s -p %s -a %s monitor 2>/dev/null | grep -viP %q", + int64(timeout.Seconds()), redisCli, list01[0], list01[1], db.Password, cmdWhiteStr) + logCmd := fmt.Sprintf("timeout %d %s -h %s -p %s -a xxxxx monitor 2>/dev/null | grep -viP %q", + int64(timeout.Seconds()), redisCli, list01[0], list01[1], cmdWhiteStr) + + msg := fmt.Sprintf("IsRedisUsing start...,cmd:%s,time:%s", logCmd, timeout.String()) + mylog.Logger.Info(msg) + + cmd := exec.CommandContext(ctx01, "bash", "-c", monitorCmd) + stdout, _ := cmd.StdoutPipe() + err = cmd.Start() + if err != nil { + err = fmt.Errorf("IsRedisUsing cmd.Start fail,err:%v,cmd:%s", err, logCmd) + mylog.Logger.Error(err.Error()) + return + } + scanner := bufio.NewScanner(stdout) + scanner.Split(bufio.ScanLines) + for scanner.Scan() { + m01 := scanner.Text() + cmds = append(cmds, m01) + if len(cmds) > 15 { + return true, cmds, nil + } + } + cmd.Wait() + if len(cmds) > 0 { + return true, cmds, nil + } + return false, cmds, nil +} + +// ClusterReset 执行cluster reset(主要用于暂停slave) +func (db *RedisClient) ClusterReset() (err error) { + _, err = db.InstanceClient.ClusterResetSoft(context.TODO()).Result() + if err != nil { + err = fmt.Errorf("ClusterReset fail,err:%v,addr:%s", err, db.Addr) + mylog.Logger.Error(err.Error()) + return + } + return nil +} + +// GetClusterNodesStr 获取tendisplus集群cluster nodes命令结果,并返回字符串 +func (db *RedisClient) GetClusterNodesStr() (ret string, err error) { + ret, err = db.InstanceClient.ClusterNodes(context.TODO()).Result() + if err != nil { + err = fmt.Errorf("GetClusterNodesStr fail,err:%v,clusterAddr:%s", err, db.Addr) + mylog.Logger.Error(err.Error()) + return + } + return +} + +// RedisClusterGetMasterNode 获取master节点信息(如果 addr是master则返回它的node信息,否则找到它的masterID,进而找到master的node信息) +func (db *RedisClient) RedisClusterGetMasterNode(addr string) (masterNode *ClusterNodeData, err error) { + addrToNodes, err := db.GetAddrMapToNodes() + if err != nil { + return + } + myNode, ok := addrToNodes[addr] + if !ok { + err = fmt.Errorf("addr:%s not found in cluster nodes", addr) + mylog.Logger.Error(err.Error()) + return + } + if myNode.GetRole() == consts.RedisMasterRole { + masterNode = myNode + return + } + masterNodeID := myNode.MasterID + idToNode, err := db.GetNodeIDMapToNodes() + if err != nil { + return + } + masterNode, ok = idToNode[masterNodeID] + if !ok { + err = fmt.Errorf("masterNodeID:%s not found in cluster nodes", masterNodeID) + mylog.Logger.Error(err.Error()) + return + } + return +} diff --git a/dbm-services/redis/db-tools/dbactuator/models/myredis/proxy_backends.go b/dbm-services/redis/db-tools/dbactuator/models/myredis/proxy_backends.go new file mode 100644 index 0000000000..39cf26619b --- /dev/null +++ b/dbm-services/redis/db-tools/dbactuator/models/myredis/proxy_backends.go @@ -0,0 +1,193 @@ +package myredis + +import ( + "context" + "fmt" + "strconv" + "strings" + + "dbm-services/redis/db-tools/dbactuator/mylog" + "dbm-services/redis/db-tools/dbactuator/pkg/util" + "dbm-services/redis/db-tools/dbmon/pkg/consts" +) + +// TwemproxyBackendItem twemproxy backend 项 +type TwemproxyBackendItem struct { + Addr string `json:"addr"` + App string `json:"app"` + SegStart int `json:"segStart"` + SegEnd int `json:"segEnd"` + Weight int `json:"weight"` +} + +// String 用于打印 +func (backend *TwemproxyBackendItem) String() string { + return fmt.Sprintf("%s %s %d-%d %d", + backend.Addr, backend.App, backend.SegStart, backend.SegEnd, backend.Weight) +} + +// StringWithoutApp 不带app信息 +func (backend *TwemproxyBackendItem) StringWithoutApp() string { + return fmt.Sprintf("%s %d-%d %d", + backend.Addr, backend.SegStart, backend.SegEnd, backend.Weight) +} + +// StringWithoutWeight 不带weight信息 +func (backend *TwemproxyBackendItem) StringWithoutWeight() string { + return fmt.Sprintf("%s %s %d-%d", + backend.Addr, backend.App, backend.SegStart, backend.SegEnd) +} + +// DecodeTwemproxyBackends 解析twmproxy backends,如: +// a.a.a.a:30006 myapp 0-2624 1 +// a.a.a.a:30007 myapp 2625-5249 1 +// b.b.b.b:30010 myapp 5250-7874 1 +// c.c.c.c:30000 myapp 7875-10499 1 +func DecodeTwemproxyBackends(backendsStr string) ( + backendList []TwemproxyBackendItem, + backendAddrMap map[string]TwemproxyBackendItem, + err error, +) { + backendAddrMap = make(map[string]TwemproxyBackendItem) + backendsStr = strings.TrimSpace(backendsStr) + lines := strings.Split(backendsStr, "\n") + for _, line01 := range lines { + line01 = strings.TrimSpace(line01) + list01 := strings.Fields(line01) + if len(list01) != 4 { + err = fmt.Errorf("twemproxy backend:%s format not correct", line01) + mylog.Logger.Error(err.Error()) + return + } + backendItem := TwemproxyBackendItem{} + backendItem.Addr = list01[0] + backendItem.App = list01[1] + segs := strings.Split(list01[2], "-") + backendItem.SegStart, err = strconv.Atoi(segs[0]) + if err != nil { + err = fmt.Errorf("twemproxy backend:%s not corret,segStart strconv.Atoi fail,err:%s", line01, err) + mylog.Logger.Error(err.Error()) + return + } + backendItem.SegEnd, err = strconv.Atoi(segs[1]) + if err != nil { + err = fmt.Errorf("twemproxy backend:%s not corret,segEnd strconv.Atoi fail,err:%s", line01, err) + mylog.Logger.Error(err.Error()) + return + } + backendItem.Weight, _ = strconv.Atoi(list01[3]) + backendList = append(backendList, backendItem) + backendAddrMap[backendItem.Addr] = backendItem + } + return +} + +// GetTwemproxyBackendsRaw 获取twemproxy backends原始内容 +func GetTwemproxyBackendsRaw(ip string, port int) (ret string, err error) { + addr := fmt.Sprintf("%s:%d", ip, port+1000) + cmd := "get nosqlproxy servers" + ret, err = util.NetCatTcpClient(addr, cmd) + if err != nil { + err = fmt.Errorf("NetCatTcpClient fail,err:%v,addr:%s,command:%s", err, addr, cmd) + mylog.Logger.Error(err.Error()) + return "", err + } + return +} + +// GetTwemproxyBackendsDecoded 获取twemproxy backends解析后的内容 +func GetTwemproxyBackendsDecoded(ip string, port int) ( + backendList []TwemproxyBackendItem, + backendAddrMap map[string]TwemproxyBackendItem, + err error, +) { + backendsRaw, err := GetTwemproxyBackendsRaw(ip, port) + if err != nil { + return nil, nil, err + } + return DecodeTwemproxyBackends(backendsRaw) +} + +// PredixyInfoServer predixy执行info server结果 +type PredixyInfoServer struct { + Server string `json:"Server"` + Role string `json:"Role"` + Group string `json:"Group"` + DC string `json:"DC"` + CurrentIsFail int `json:"CurrentIsFail"` + Connections int `json:"Connections"` + Connect int `json:"Connect"` + Requests uint64 `json:"Requests"` + Responses uint64 `json:"Responses"` + SendBytes uint64 `json:"SendBytes"` + RecvBytes uint64 `json:"RecvBytes"` +} + +// GetPredixyInfoServersRaw 获取predixy info server原始内容 s +func GetPredixyInfoServersRaw(ip string, port int, password string) (svrsinfo string, err error) { + predixyAddr := fmt.Sprintf("%s:%d", ip, port) + cli01, err := NewRedisClient(predixyAddr, password, 0, consts.TendisTypeRedisInstance) + if err != nil { + return + } + defer cli01.Close() + + svrsinfo, err = cli01.InstanceClient.Info(context.TODO(), "servers").Result() + if err != nil { + err = fmt.Errorf("PredixyInfoServers execute cmd:'info servers' fail,err:%v", err) + mylog.Logger.Error(err.Error()) + return + } + return +} + +// GetPredixyInfoServersDecoded 获取predixy info server结果(已解析) +func GetPredixyInfoServersDecoded(ip string, port int, password string) (rets []*PredixyInfoServer, err error) { + svrsinfo, err := GetPredixyInfoServersRaw(ip, port, password) + if err != nil { + return + } + infoList := strings.Split(svrsinfo, "\n") + item01 := &PredixyInfoServer{} + for _, infoItem := range infoList { + infoItem = strings.TrimSpace(infoItem) + if strings.HasPrefix(infoItem, "#") { + continue + } + if len(infoItem) == 0 { + if item01.Server != "" { + rets = append(rets, item01) + item01 = &PredixyInfoServer{} + } + continue + } + list01 := strings.SplitN(infoItem, ":", 2) + if len(list01) < 2 { + continue + } + if strings.HasPrefix(list01[0], "Server") { + item01.Server = list01[1] + } else if strings.HasPrefix(list01[0], "Role") { + item01.Role = list01[1] + } else if strings.HasPrefix(list01[0], "Group") { + item01.Group = list01[1] + } else if strings.HasPrefix(list01[0], "DC") { + item01.DC = list01[1] + } else if strings.HasPrefix(list01[0], "CurrentIsFail") { + item01.CurrentIsFail, _ = strconv.Atoi(list01[1]) + } else if strings.HasPrefix(list01[0], "Connections") { + item01.Connections, _ = strconv.Atoi(list01[1]) + } else if strings.HasPrefix(list01[0], "Connect") { + item01.Connect, _ = strconv.Atoi(list01[1]) + } else if strings.HasPrefix(list01[0], "Requests") { + item01.Requests, _ = strconv.ParseUint(list01[1], 10, 64) + } else if strings.HasPrefix(list01[0], "Responses") { + item01.Responses, _ = strconv.ParseUint(list01[1], 10, 64) + } else if strings.HasPrefix(list01[0], "SendBytes") { + item01.SendBytes, _ = strconv.ParseUint(list01[1], 10, 64) + } else if strings.HasPrefix(list01[0], "RecvBytes") { + item01.RecvBytes, _ = strconv.ParseUint(list01[1], 10, 64) + } + } + return rets, nil +} diff --git a/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomproxy/twemproxy_check_backends.go b/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomproxy/twemproxy_check_backends.go index b60c3766d4..e251c8a695 100644 --- a/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomproxy/twemproxy_check_backends.go +++ b/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomproxy/twemproxy_check_backends.go @@ -93,7 +93,7 @@ func (job *TwemproxyCheckBackends) Run() (err error) { if len(md5s) > 1 { x, _ := json.Marshal(md5s) - return fmt.Errorf("some proxy failed for servers:{%+v}", x) + return fmt.Errorf("some proxy failed for servers:{%s}", x) } job.runtime.Logger.Info(fmt.Sprintf("all twemproxy %+v got same nosqlproxy servers md5 %+v", job.params, md5s)) @@ -133,10 +133,10 @@ func (job *TwemproxyCheckBackends) getTwemproxyMd5(addr string) string { } // 1.1.x.a:30000 tgalive 0-17499 1 segs := []string{} - for _, seg := range strings.Split(string(rsp), "\n") { + for _, seg := range strings.Split(strings.TrimRight(string(rsp), "\n"), "\n") { segInfo := strings.Split(seg, " ") if len(segInfo) != 4 { - return fmt.Sprintf("GetServersFailed:%s|%+v", addr, seg) + return fmt.Sprintf("GetServersFailed:%s|[%d:%+v]{%s}", addr, len(segInfo), segInfo, rsp) } segs = append(segs, fmt.Sprintf("%s|%s", segInfo[0], segInfo[2])) } diff --git a/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/bkdbmon_install.go b/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/bkdbmon_install.go index 345a3c42ca..6352bc8c76 100644 --- a/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/bkdbmon_install.go +++ b/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/bkdbmon_install.go @@ -22,17 +22,19 @@ import ( // ConfServerItem servers配置项 type ConfServerItem struct { - BkBizID string `json:"bk_biz_id" yaml:"bk_biz_id" validate:"required"` - BkCloudID int64 `json:"bk_cloud_id" yaml:"bk_cloud_id" validate:"required"` - App string `json:"app" yaml:"app" validate:"required"` - AppName string `json:"app_name" yaml:"app_name" validate:"required"` - ClusterDomain string `json:"cluster_domain" yaml:"cluster_domain" validate:"required"` - ClusterName string `json:"cluster_name" yaml:"cluster_name" validate:"required"` - ClusterType string `json:"cluster_type" yaml:"cluster_type" validate:"required"` - MetaRole string `json:"meta_role" yaml:"meta_role" validate:"required"` - ServerIP string `json:"server_ip" yaml:"server_ip" validate:"required"` - ServerPorts []int `json:"server_ports" yaml:"server_ports" validate:"required"` - Shard string `json:"shard" yaml:"shard"` + BkBizID string `json:"bk_biz_id" yaml:"bk_biz_id" validate:"required"` + BkCloudID int64 `json:"bk_cloud_id" yaml:"bk_cloud_id"` + App string `json:"app" yaml:"app" validate:"required"` + AppName string `json:"app_name" yaml:"app_name" validate:"required"` + ClusterDomain string `json:"cluster_domain" yaml:"cluster_domain" validate:"required"` + ClusterName string `json:"cluster_name" yaml:"cluster_name" validate:"required"` + ClusterType string `json:"cluster_type" yaml:"cluster_type" validate:"required"` + MetaRole string `json:"meta_role" yaml:"meta_role" validate:"required"` + ServerIP string `json:"server_ip" yaml:"server_ip" validate:"required"` + ServerPorts []int `json:"server_ports" yaml:"server_ports" validate:"required"` + ServerShards map[string]string `json:"server_shards" yaml:"server_shards"` + CacheBackupMode string `json:"cache_backup_mode" yaml:"cache_backup_mode"` // aof or rdb + Shard string `json:"shard" yaml:"shard"` } // BkDbmonInstallParams 安装参数 @@ -67,9 +69,11 @@ func NewBkDbmonInstall() jobruntime.JobRunner { // Init 初始化 func (job *BkDbmonInstall) Init(m *jobruntime.JobGenericRuntime) error { job.runtime = m - err := json.Unmarshal([]byte(job.runtime.PayloadDecoded), &job.params) + d := json.NewDecoder(strings.NewReader(job.runtime.PayloadDecoded)) + d.UseNumber() + err := d.Decode(&job.params) if err != nil { - job.runtime.Logger.Error(fmt.Sprintf("json.Unmarshal failed,err:%+v", err)) + job.runtime.Logger.Error(fmt.Sprintf("json.Decode failed,err:%+v", err)) return err } // 参数有效性检查 @@ -87,6 +91,30 @@ func (job *BkDbmonInstall) Init(m *jobruntime.JobGenericRuntime) error { return err } } + for _, svrItem := range job.params.Servers { + if len(svrItem.ServerPorts) > 0 { + if svrItem.ServerIP == "" { + job.runtime.Logger.Error("BkDbmonInstall Init params validate failed,err:ServerIP is empty") + return fmt.Errorf("ServerIP is empty") + } + if svrItem.ClusterName == "" { + job.runtime.Logger.Error("BkDbmonInstall Init params validate failed,err:ClusterName is empty") + return fmt.Errorf("ClusterName is empty") + } + if svrItem.ClusterDomain == "" { + job.runtime.Logger.Error("BkDbmonInstall Init params validate failed,err:ClusterDomain is empty") + return fmt.Errorf("ClusterDomain is empty") + } + if svrItem.ClusterType == "" { + job.runtime.Logger.Error("BkDbmonInstall Init params validate failed,err:ClusterType is empty") + return fmt.Errorf("ClusterType is empty") + } + if svrItem.ClusterType == "" { + job.runtime.Logger.Error("BkDbmonInstall Init params validate failed,err:ClusterType is empty") + return fmt.Errorf("ClusterType is empty") + } + } + } return nil } diff --git a/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/cluster_forget.go b/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/cluster_forget.go new file mode 100644 index 0000000000..99c898b18c --- /dev/null +++ b/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/cluster_forget.go @@ -0,0 +1,218 @@ +package atomredis + +import ( + "encoding/json" + "fmt" + "time" + + "dbm-services/redis/db-tools/dbactuator/models/myredis" + "dbm-services/redis/db-tools/dbactuator/pkg/consts" + "dbm-services/redis/db-tools/dbactuator/pkg/jobruntime" + + "github.com/go-playground/validator/v10" +) + +/* + RedisCluster Forget + 1. 支持 slave 节点forget + 2. 支持 master节点但未分配slots 信息 + + // 输入参数 + { + "cluster_meta":{ + "immute_domain":"xx.db", + "cluster_type":"xxx", + "RedisMasterSet":[ + + ], + "StoragePassword":"" + }, + "forget_list":[ + { + "ip":"1.1.a.1", + "port":30000 + } + ] +} +*/ +// ClusterForgetParam 参数说明 +type ClusterForgetParam struct { // NOCC:golint/structcomment(检查工具误报) + ClusterMeta ClusterInfo `json:"cluster_meta" validate:"required"` + ForgetList []InstanceParam `json:"forget_list" validate:"required"` +} + +// RedisClusterForget entry +type RedisClusterForget struct { + runtime *jobruntime.JobGenericRuntime + params *ClusterForgetParam +} + +// NewRedisClusterForget 新建入口 +func NewRedisClusterForget() jobruntime.JobRunner { + return &RedisClusterForget{} +} + +// Init 初始化 +func (job *RedisClusterForget) Init(m *jobruntime.JobGenericRuntime) error { + job.runtime = m + err := json.Unmarshal([]byte(job.runtime.PayloadDecoded), &job.params) + if err != nil { + job.runtime.Logger.Error(fmt.Sprintf("json.Unmarshal failed,err:%+v", err)) + return err + } + + // 参数有效性检查 + validate := validator.New() + err = validate.Struct(job.params) + if err != nil { + if _, ok := err.(*validator.InvalidValidationError); ok { + job.runtime.Logger.Error("ClusterRedoDR Init params validate failed,err:%v,params:%+v", + err, job.params) + return err + } + for _, err := range err.(validator.ValidationErrors) { + job.runtime.Logger.Error("ClusterRedoDR Init params validate failed,err:%v,params:%+v", + err, job.params) + return err + } + } + if !consts.IsClusterDbType(job.params.ClusterMeta.ClusterType) { + job.runtime.Logger.Error("cluster:%s:%s unsupport forget command,", + job.params.ClusterMeta.ImmuteDomain, job.params.ClusterMeta.ClusterType) + return fmt.Errorf("ErrUnSupportForget4:%s", job.params.ClusterMeta.ClusterType) + } + return nil +} + +// Run 执行入口 +func (job *RedisClusterForget) Run() (err error) { + job.runtime.Logger.Info("clusterforget start; params:%+v", job.params) + + clusterNodes, err := job.tryGetClusterNodesInfo() + if err != nil { + return + } + + for _, node := range job.params.ForgetList { + forgetAddr := fmt.Sprintf("%s:%d", node.IP, node.Port) + if _, ok := clusterNodes[forgetAddr]; !ok { + job.runtime.Logger.Info("cluster does not exist node %s:%s", + job.params.ClusterMeta.ImmuteDomain, forgetAddr) + continue + } + + // 允许 master with no slot || slave + forgetDetail := clusterNodes[forgetAddr] + if forgetDetail.Role == consts.RedisMasterRole { + if forgetDetail.SlotSrcStr != "" { + job.runtime.Logger.Error("err try forget node:{%s} role is master , but has slot asigned {%s}", + forgetAddr, forgetDetail.SlotSrcStr) + return fmt.Errorf("ErrNotAllowedMasterWithSlot:{%s}{%s}", forgetAddr, forgetDetail.SlotSrcStr) + } + } + job.runtime.Logger.Info("try 2 forget node:%s role:%s,cluster's detail :%+v", + forgetAddr, forgetDetail.Role, forgetDetail) + + if err := job.clusterForgetNode(clusterNodes, forgetDetail); err != nil { + return err + } + } + + if currNodes, err := job.tryGetClusterNodesInfo(); err != nil { + job.runtime.PipeContextData = currNodes + return err + } + job.runtime.Logger.Info("cluster forget nodes succ ^_^; domain:%s", job.params.ClusterMeta.ImmuteDomain) + return nil +} + +// tryGetClusterNodesInfo 遍历传入的所有节点,尝试建立集群链接并获取集群节点 +func (job *RedisClusterForget) tryGetClusterNodesInfo() ( + clusterNodes map[string]*myredis.ClusterNodeData, err error) { + for idx, nodeAddr := range job.params.ClusterMeta.RedisMasterSet { + var clusterConn *myredis.RedisClient + job.runtime.Logger.Info("try make connect use %s:%s", job.params.ClusterMeta.ImmuteDomain, nodeAddr) + + clusterConn, err = myredis.NewRedisClientWithTimeout(nodeAddr, + job.params.ClusterMeta.StoragePassword, 0, job.params.ClusterMeta.ClusterType, time.Second) + if err != nil { + job.runtime.Logger.Error("connect cluster node [%d]:%s failed:%+v", idx, nodeAddr, err) + continue + } + defer clusterConn.Close() + + clusterNodes, err = clusterConn.GetAddrMapToNodes() + if err != nil { + job.runtime.Logger.Error("get cluster nodes use [%d] addr:%s failed:%+v", idx, nodeAddr, err) + } + + for _, node := range clusterNodes { + job.runtime.Logger.Info("current cluster node : %+v", node) + } + return + } + return nil, fmt.Errorf("can't connection cluster :%+v", err) +} + +// clusterForgetNode 为了将节点从群集中彻底删除,必须将 CLUSTER FORGET 命令发送到所有其余节点,无论他们是Master/Slave。 +// 不允许命令执行的特殊条件, 并在以下情况下返回错误: +// 1. 节点表中找不到指定的节点标识。 +// 2. 接收命令的节点是从属节点,并且指定的节点ID标识其当前主节点。 +// 3. 节点 ID 标识了我们发送命令的同一个节点。 + +// clusterForgetNode 返回值 +// 简单的字符串回复:OK如果命令执行成功,否则返回错误。 +// 我们有一个60秒的窗口来,所以这个函数必须在60s内执行完成 +func (job *RedisClusterForget) clusterForgetNode( + clusterNodes map[string]*myredis.ClusterNodeData, fnode *myredis.ClusterNodeData) error { + beforeStart := time.Now().Unix() + for _, node := range clusterNodes { + if node.NodeID == fnode.NodeID { + continue + } + job.runtime.Logger.Info("exec {cluster forget %s:%s} from [%s]", fnode.Addr, fnode.NodeID, node.Addr) + var ignoreErr bool + if len(node.FailStatus) != 0 { + ignoreErr = true + job.runtime.Logger.Warn("exec forget node maybe fail,will ignore err,%s:%+v", node.Addr, node.FailStatus) + } + + nodeConn, err := myredis.NewRedisClientWithTimeout(node.Addr, + job.params.ClusterMeta.StoragePassword, 0, job.params.ClusterMeta.ClusterType, time.Second) + if err != nil { + if ignoreErr { + job.runtime.Logger.Warn("current node status maybe fail, ignore %s:%+v", node.Addr, err) + continue + } + return err + } + defer nodeConn.Close() + + if err := nodeConn.ClusterForget(fnode.NodeID); err != nil { + job.runtime.Logger.Error("forget node %s:%s failed :+%v", fnode.Addr, fnode.NodeID, err) + if ignoreErr { + job.runtime.Logger.Warn("current node status maybe fail, ignore %s:%+v", node.Addr, err) + continue + } + return fmt.Errorf("ErrForgetNode:%s:%+v", fnode.Addr, err) + } + } + forgetUseSeconds := time.Now().Unix() - beforeStart + job.runtime.Logger.Info("exec cluster forget use %d seconds", forgetUseSeconds) + return nil +} + +// Name 原子任务名 +func (job *RedisClusterForget) Name() string { + return "redis_cluster_forget" +} + +// Retry times +func (job *RedisClusterForget) Retry() uint { + return 2 +} + +// Rollback rollback +func (job *RedisClusterForget) Rollback() error { + return nil +} diff --git a/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/clustermeet_checkfinish.go b/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/clustermeet_checkfinish.go new file mode 100644 index 0000000000..7af83ab62a --- /dev/null +++ b/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/clustermeet_checkfinish.go @@ -0,0 +1,201 @@ +package atomredis + +import ( + "encoding/json" + "fmt" + "strconv" + "time" + + "dbm-services/redis/db-tools/dbactuator/pkg/consts" + "dbm-services/redis/db-tools/dbactuator/pkg/jobruntime" + + "dbm-services/redis/db-tools/dbactuator/models/myredis" + + "github.com/go-playground/validator/v10" +) + +// ClusterNodesItem redis 节点信息 +type ClusterNodesItem struct { + MasterIP string `json:"master_ip" validate:"required"` + MasterPort int `json:"master_port" validate:"required"` +} + +// MasterAddr masteraddr +func (item *ClusterNodesItem) MasterAddr() string { + return item.MasterIP + ":" + strconv.Itoa(item.MasterPort) +} + +// ClusterMeetCheckFinishParams 恢复回档后的集群关系和检查集群状态是否恢复,到这一步前面的回档子任务都全部成功了 +type ClusterMeetCheckFinishParams struct { + Password string `json:"password"` // 如果password为空,则自动从本地获取 + //集群节点信息 + ReplicaPairs []ClusterNodesItem `json:"replica_pairs"` +} + +// ClusterMeetCheckFinish 恢复回档时的集群关系和检查集群状态是否恢复 +type ClusterMeetCheckFinish struct { + runtime *jobruntime.JobGenericRuntime + params ClusterMeetCheckFinishParams + AddrMapCli map[string]*myredis.RedisClient `json:"addr_map_cli"` + Err error `json:"-"` +} + +// 无实际作用,仅确保实现了 jobruntime.JobRunner 接口 +var _ jobruntime.JobRunner = (*ClusterMeetCheckFinish)(nil) + +// NewClusterMeetCheckFinish new +func NewClusterMeetCheckFinish() jobruntime.JobRunner { + return &ClusterMeetCheckFinish{} +} + +// Name 原子任务名 +func (task *ClusterMeetCheckFinish) Name() string { + return "clustermeet_checkfinish" +} + +// Retry times +func (task *ClusterMeetCheckFinish) Retry() uint { + return 2 +} + +// Rollback rollback +func (task *ClusterMeetCheckFinish) Rollback() error { + return nil + +} + +// Init 初始化 +func (task *ClusterMeetCheckFinish) Init(m *jobruntime.JobGenericRuntime) error { + task.runtime = m + err := json.Unmarshal([]byte(task.runtime.PayloadDecoded), &task.params) + if err != nil { + task.runtime.Logger.Error(fmt.Sprintf("json.Unmarshal failed,err:%+v", err)) + return err + } + // 参数有效性检查 + validate := validator.New() + err = validate.Struct(task.params) + if err != nil { + if _, ok := err.(*validator.InvalidValidationError); ok { + task.runtime.Logger.Error("ClusterMeetCheckFinish Init params validate failed,err:%v,params:%+v", + err, task.params) + return err + } + for _, err := range err.(validator.ValidationErrors) { + task.runtime.Logger.Error("ClusterMeetCheckFinish Init params validate failed,err:%v,params:%+v", + err, task.params) + return err + } + } + task.AddrMapCli = make(map[string]*myredis.RedisClient) + task.runtime.Logger.Info("ClusterMeetCheckFinish init success") + return nil +} + +// Run 执行 +func (task *ClusterMeetCheckFinish) Run() (err error) { + err = task.allInstsAbleToConnect() + if err != nil { + return + } + defer task.allInstDisconnect() + + err = task.AddInstsToCluster() + if err != nil { + return + } + var ok bool + maxRetryTimes := 60 // 等待两分钟,等待集群状态ok + i := 0 + for { + i++ + for i > maxRetryTimes { + break + } + ok, err = task.IsClusterStateOK() + if err != nil { + return + } + if !ok { + task.runtime.Logger.Info("redisCluster(%s) cluster_state not ok,sleep 2s and retry...", + task.params.ReplicaPairs[0].MasterAddr()) + time.Sleep(2 * time.Second) + continue + } + break + } + if !ok { + err = fmt.Errorf("wait 120s,redisCluster(%s) cluster_state still not ok", task.params.ReplicaPairs[0].MasterAddr()) + task.runtime.Logger.Error(err.Error()) + return + } + + return nil +} + +// allInstsAbleToConnect 检查所有实例可连接 +func (task *ClusterMeetCheckFinish) allInstsAbleToConnect() (err error) { + instsAddrs := []string{} + for _, item := range task.params.ReplicaPairs { + instsAddrs = append(instsAddrs, item.MasterAddr()) + } + for _, addr01 := range instsAddrs { + cli, err := myredis.NewRedisClient(addr01, task.params.Password, 0, consts.TendisTypeTendisplusInsance) + if err != nil { + return err + } + task.AddrMapCli[addr01] = cli + } + task.runtime.Logger.Info("all tendisplus instances able to connect,(%+v)", instsAddrs) + return nil +} + +// allInstDisconnect 所有实例断开连接 +func (task *ClusterMeetCheckFinish) allInstDisconnect() { + for _, cli := range task.AddrMapCli { + cli.Close() + } +} + +// AddInstsToCluster 添加实例到集群中 +func (task *ClusterMeetCheckFinish) AddInstsToCluster() (err error) { + firstAddr := task.params.ReplicaPairs[0].MasterAddr() + firstIP := task.params.ReplicaPairs[0].MasterIP + firstPort := task.params.ReplicaPairs[0].MasterPort + firstCli := task.AddrMapCli[firstAddr] + addrMap, err := firstCli.GetAddrMapToNodes() + if err != nil { + return + } + for add01, cli := range task.AddrMapCli { + if add01 == firstAddr { + continue + } + node01, ok := addrMap[add01] + if ok && myredis.IsRunningNode(node01) { + continue + } + _, err = cli.ClusterMeet(firstIP, strconv.Itoa(firstPort)) + if err != nil { + return + } + } + time.Sleep(10 * time.Second) + task.runtime.Logger.Info("all tendisplus instances add to cluster") + return nil +} + +// IsClusterStateOK 集群状态是否ok +func (task *ClusterMeetCheckFinish) IsClusterStateOK() (ok bool, err error) { + + firstAddr := task.params.ReplicaPairs[0].MasterAddr() + firstCli := task.AddrMapCli[firstAddr] + clusterInfo, err := firstCli.ClusterInfo() + if err != nil { + return false, err + } + if clusterInfo.ClusterState == consts.ClusterStateOK { + return true, nil + } + return false, nil +} diff --git a/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/redis_add_dts_server.go b/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/redis_add_dts_server.go new file mode 100644 index 0000000000..26451e3673 --- /dev/null +++ b/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/redis_add_dts_server.go @@ -0,0 +1,410 @@ +package atomredis + +import ( + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "path/filepath" + "strconv" + "time" + + "dbm-services/redis/db-tools/dbactuator/pkg/common" + "dbm-services/redis/db-tools/dbactuator/pkg/consts" + "dbm-services/redis/db-tools/dbactuator/pkg/jobruntime" + "dbm-services/redis/db-tools/dbactuator/pkg/util" + + "github.com/flosch/pongo2/v6" + "github.com/go-playground/validator/v10" +) + +// RedisAddDtsServerParams 新增dts_server参数 +type RedisAddDtsServerParams struct { + common.MediaPkg + BkDbmNginxURL string `json:"bk_dbm_nginx_url" validate:"required"` + BkDbmCloudID int64 `json:"bk_dbm_cloud_id"` + BkDbmCloudToken string `json:"bk_dbm_cloud_token" validate:"required"` + SystemUser string `json:"system_user" validate:"required"` + SystemPassword string `json:"system_password" validate:"required"` + CityName string `json:"city_name" validate:"required"` + WarningMsgNotifiers string `json:"warning_msg_notifiers"` +} + +// RedisAddDtsServer redis add dts_server +type RedisAddDtsServer struct { + runtime *jobruntime.JobGenericRuntime + params RedisAddDtsServerParams + DataDir string `json:"data_dir"` + RedisDtsDir string `json:"redis_dts_dir"` + shouldUpdateMedia bool + shouldUpdateConfig bool + tmpConfFile string + currentConfFile string +} + +// 无实际作用,仅确保实现了 jobruntime.JobRunner 接口 +var _ jobruntime.JobRunner = (*RedisAddDtsServer)(nil) + +// NewRedisAddDtsServer new +func NewRedisAddDtsServer() jobruntime.JobRunner { + return &RedisAddDtsServer{} +} + +// Init 初始化 +func (job *RedisAddDtsServer) Init(m *jobruntime.JobGenericRuntime) error { + job.runtime = m + err := json.Unmarshal([]byte(job.runtime.PayloadDecoded), &job.params) + if err != nil { + job.runtime.Logger.Error(fmt.Sprintf("json.Unmarshal failed,err:%+v", err)) + return err + } + // 参数有效性检查 + validate := validator.New() + err = validate.Struct(job.params) + if err != nil { + if _, ok := err.(*validator.InvalidValidationError); ok { + job.runtime.Logger.Error("RedisInstall Init params validate failed,err:%v,params:%+v", + err, job.params) + return err + } + for _, err := range err.(validator.ValidationErrors) { + job.runtime.Logger.Error("RedisInstall Init params validate failed,err:%v,params:%+v", + err, job.params) + return err + } + } + return nil +} + +// Name 原子任务名 +func (job *RedisAddDtsServer) Name() string { + return "redis_add_dts_server" +} + +// Run 执行 +func (job *RedisAddDtsServer) Run() (err error) { + var ok bool + err = job.getDataDir() + if err != nil { + return err + } + if job.isDtsDirExists() && job.isPackageMd5Equal() { + err = job.GenerateTmpConfigFile() + if err != nil { + return err + } + job.shouldUpdateConfig, err = job.isConfigFileUpdated() + if err != nil { + return err + } + if !job.shouldUpdateConfig { + // media md5 not changed and config file not changed + job.runtime.Logger.Info("redis_dts_server package md5 not changed,not need to update") + return nil + } + } + if !job.isDtsDirExists() { + // dts dir not exists + err = job.UntarMedia() + if err != nil { + return err + } + } + err = job.GenerateTmpConfigFile() + if err != nil { + return err + } + job.shouldUpdateConfig, err = job.isConfigFileUpdated() + if err != nil { + return err + } + ok, err = job.ableToStopDtsServer() + if err != nil { + return err + } + if !ok { + err = fmt.Errorf("redis_dts_server other tasks are running,can not stop") + return + } + err = job.stopDtsServer() + if err != nil { + return err + } + if !job.isPackageMd5Equal() { + // dts dir exists but media md5 not equal + err = job.UntarMedia() + if err != nil { + return err + } + } + if job.shouldUpdateConfig { + // update config file + err = job.updateLocalConfigFile() + if err != nil { + return err + } + } + err = job.startDtsServer() + if err != nil { + return err + } + + return nil +} + +func (job *RedisAddDtsServer) getDataDir() (err error) { + job.DataDir = filepath.Join(consts.GetRedisDataDir(), "dbbak") + util.MkDirsIfNotExists([]string{job.DataDir}) + util.LocalDirChownMysql(job.DataDir) + return nil +} + +func (job *RedisAddDtsServer) isDtsDirExists() bool { + pkgBaseName := job.params.GePkgBaseName() + job.RedisDtsDir = filepath.Join(job.DataDir, pkgBaseName) + return util.FileExists(job.RedisDtsDir) +} + +// isPackageMd5Equal 检查当前包的md5是否和上次一致 +func (job *RedisAddDtsServer) isPackageMd5Equal() bool { + baseName := filepath.Join(job.DataDir, job.params.GePkgBaseName()) + // /data/dbbak/redis_dts.tar.gz + currentPackageFile := filepath.Join(job.DataDir, baseName+"tar.gz") + // /data/install/redis_dts.tar.gz + lastPackageFile := job.params.GetAbsolutePath() + if !util.FileExists(currentPackageFile) { + return false + } + currentPackageMd5, err := util.GetFileMd5(currentPackageFile) + if err != nil { + job.runtime.Logger.Error("get current package md5 failed,err:%v,packageFile:%s", err, currentPackageFile) + return false + } + lastPackageMd5, err := util.GetFileMd5(lastPackageFile) + if err != nil { + job.runtime.Logger.Error("get last package md5 failed,err:%v,packageFile:%s", err, lastPackageFile) + return false + } + if currentPackageMd5 != lastPackageMd5 { + job.runtime.Logger.Error("current package file:%s,md5:%s not equal last package file:%s,md5:%s", + currentPackageFile, currentPackageMd5, lastPackageFile, lastPackageMd5) + return false + } + job.runtime.Logger.Error("current package file:%s,md5:%s equal last package file:%s,md5:%s", + currentPackageFile, currentPackageMd5, lastPackageFile, lastPackageMd5) + return false +} + +// GenerateTmpConfigFile 生成临时配置文件 +func (job *RedisAddDtsServer) GenerateTmpConfigFile() (err error) { + job.runtime.Logger.Info("begin to generate redis dts config file") + templateFile := filepath.Join(job.RedisDtsDir, "config-template.yaml") + if !util.FileExists(templateFile) { + err = fmt.Errorf("redis_dts_server config template file:%s not exists", templateFile) + job.runtime.Logger.Error(err.Error()) + return err + } + templdateData, err := ioutil.ReadFile(templateFile) + if err != nil { + err = fmt.Errorf("read redis_dts_server config template file:%s failed,err:%v", templateFile, err) + job.runtime.Logger.Error(err.Error()) + return err + } + tpl, err := pongo2.FromBytes(templdateData) + if err != nil { + err = fmt.Errorf("pongo2.FromString fail,err:%v,dts_server config template file:%s", err, templateFile) + job.runtime.Logger.Error(err.Error()) + return err + } + pctx01 := pongo2.Context{ + "bk_dbm_nginx_url": job.params.BkDbmNginxURL, + "bk_dbm_cloud_id": strconv.FormatInt(job.params.BkDbmCloudID, 10), + "bk_dbm_cloud_token": job.params.BkDbmCloudToken, + "system_user": job.params.SystemUser, + "system_password": job.params.SystemPassword, + "city_name": job.params.CityName, + "warning_msg_notifiers": job.params.WarningMsgNotifiers, + } + confData, err := tpl.Execute(pctx01) + if err != nil { + err = fmt.Errorf("tpl.Execute fail,err:%v,dts_server config template file:%s", err, templateFile) + job.runtime.Logger.Error(err.Error()) + return err + } + job.currentConfFile = filepath.Join(job.RedisDtsDir, "config.yaml") + job.tmpConfFile = filepath.Join(job.RedisDtsDir, "tmp_config.yaml") + err = ioutil.WriteFile(job.tmpConfFile, []byte(confData), 0644) + if err != nil { + err = fmt.Errorf("write redis_dts_server config file:%s failed,err:%v", job.tmpConfFile, err) + job.runtime.Logger.Error(err.Error()) + return err + } + util.LocalDirChownMysql(job.RedisDtsDir) + job.runtime.Logger.Info("generate redis dts config file success") + return nil +} + +// isConfigFileUpdated 判断配置文件是否更新 +func (job *RedisAddDtsServer) isConfigFileUpdated() (updated bool, err error) { + if !util.FileExists(job.tmpConfFile) { + return false, nil + } + if !util.FileExists(job.currentConfFile) { + return true, nil + } + tmpFileMD5, err := util.GetFileMd5(job.tmpConfFile) + if err != nil { + job.runtime.Logger.Error(err.Error()) + return false, err + } + + confFileMD5, err := util.GetFileMd5(job.currentConfFile) + if err != nil { + job.runtime.Logger.Error(err.Error()) + return false, err + } + if tmpFileMD5 != confFileMD5 { + job.runtime.Logger.Info("tmp config file:%s md5:%s not equal current config file:%s md5:%s", + job.tmpConfFile, tmpFileMD5, job.currentConfFile, confFileMD5) + return true, nil + } + return false, nil +} + +func (job *RedisAddDtsServer) updateLocalConfigFile() (err error) { + if !util.FileExists(job.tmpConfFile) { + err = fmt.Errorf("tmp config file:%s not exists", job.tmpConfFile) + job.runtime.Logger.Error(err.Error()) + return err + } + cpCmd := fmt.Sprintf("cp -f %s %s", job.tmpConfFile, job.currentConfFile) + _, err = util.RunBashCmd(cpCmd, "", nil, 10*time.Minute) + if err != nil { + job.runtime.Logger.Error(err.Error()) + return err + } + return nil +} + +// UntarMedia 解压介质,更新介质文件 +func (job *RedisAddDtsServer) UntarMedia() (err error) { + job.runtime.Logger.Info("begin to untar redis media") + defer func() { + if err != nil { + job.runtime.Logger.Error("untar redis_dts_server media fail") + } else { + job.runtime.Logger.Info("untar redis_dts_server media success") + } + }() + err = job.params.Check() + if err != nil { + job.runtime.Logger.Error("UntarMedia failed,err:%v", err) + return + } + lastPkgAbsPath := job.params.GetAbsolutePath() + tarCmd := fmt.Sprintf("tar -zxf %s -C %s", lastPkgAbsPath, job.DataDir) + job.runtime.Logger.Info(tarCmd) + _, err = util.RunBashCmd(tarCmd, "", nil, 10*time.Minute) + if err != nil { + return + } + util.LocalDirChownMysql(job.RedisDtsDir) + + // copy redis_dts.tar.gz to /data/dbbak/ + cpCmd := fmt.Sprintf("cp -f %s %s", lastPkgAbsPath, job.DataDir) + job.runtime.Logger.Info(cpCmd) + _, err = util.RunBashCmd(cpCmd, "", nil, 10*time.Minute) + if err != nil { + return err + } + return nil +} + +// ableToStopDtsServer 是否可以停止dts_server +func (job *RedisAddDtsServer) ableToStopDtsServer() (ok bool, err error) { + psCmd := + "ps aux|grep 'redis_dts'|grep -vE 'dbactuator|grep|./redis_dts_server|redis-sync|redis-shake' || { true; }" + job.runtime.Logger.Info(psCmd) + output, err := util.RunBashCmd(psCmd, "", nil, 10*time.Second) + if err != nil { + job.runtime.Logger.Error("check redis_dts_server process failed,err:%v", err) + return false, err + } + if output != "" { + job.runtime.Logger.Error("redis_dts other tasks are running,can not stop.details:\n%s\n", output) + return false, nil + } + return true, nil +} + +func (job *RedisAddDtsServer) stopDtsServer() (err error) { + job.runtime.Logger.Info("begin to stop redis_dts_server") + defer func() { + if err != nil { + job.runtime.Logger.Error("stop redis_dts_server fail") + } else { + job.runtime.Logger.Info("stop redis_dts_server success") + } + }() + stopCmd := fmt.Sprintf("cd %s && sh stop.sh", job.RedisDtsDir) + job.runtime.Logger.Info(stopCmd) + _, err = util.RunBashCmd(stopCmd, "", nil, 10*time.Second) + if err != nil { + return err + } + return nil +} + +func (job *RedisAddDtsServer) startDtsServer() (err error) { + job.runtime.Logger.Info("begin to start redis_dts_server") + defer func() { + if err != nil { + job.runtime.Logger.Error("start redis_dts_server fail") + } else { + job.runtime.Logger.Info("start redis_dts_server success") + } + }() + util.LocalDirChownMysql(job.RedisDtsDir) + startCmd := fmt.Sprintf("su %s -c \"nohup cd %s && sh start.sh &\"", consts.MysqlAaccount, job.RedisDtsDir) + job.runtime.Logger.Info(startCmd) + _, err = util.RunLocalCmd("su", []string{consts.MysqlAaccount, "-c", "cd " + job.RedisDtsDir + + " && nohup sh start.sh >/dev/null 2>&1 &"}, "", nil, 10*time.Second) + if err != nil { + return err + } + time.Sleep(2 * time.Second) + alive, err := job.isDtsServerAlive() + if err != nil { + return err + } + if !alive { + err = errors.New("redis_dts_server start failed") + job.runtime.Logger.Error(err.Error()) + return err + } + job.runtime.Logger.Info("redis_dts_server start success") + return nil +} + +func (job *RedisAddDtsServer) isDtsServerAlive() (alive bool, err error) { + psCmd := "ps -ef|grep redis_dts_server|grep -ivE 'grep|redis-sync|redis-shake|dbactuator'" + job.runtime.Logger.Info(psCmd) + output, err := util.RunBashCmd(psCmd, "", nil, 10*time.Second) + if err != nil { + return false, err + } + if output == "" { + return false, nil + } + return true, nil +} + +// Retry times +func (job *RedisAddDtsServer) Retry() uint { + return 2 +} + +// Rollback rollback +func (job *RedisAddDtsServer) Rollback() error { + return nil +} diff --git a/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/redis_backup.go b/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/redis_backup.go index e8898d6eb3..78410bd28e 100644 --- a/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/redis_backup.go +++ b/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/redis_backup.go @@ -21,11 +21,13 @@ import ( "github.com/go-playground/validator/v10" "github.com/gofrs/flock" + "gopkg.in/yaml.v2" ) // TendisSSDSetLogCount tendisSSD设置log参数 type TendisSSDSetLogCount struct { LogCount int64 `json:"log-count"` + LogKeepCount int64 `json:"log-keep-count"` SlaveLogKeepCount int64 `json:"slave-log-keep-count"` } @@ -44,9 +46,10 @@ type RedisBackupParams struct { // RedisBackup backup atomjob type RedisBackup struct { - runtime *jobruntime.JobGenericRuntime - params RedisBackupParams - Reporter report.Reporter `json:"-"` + runtime *jobruntime.JobGenericRuntime + params RedisBackupParams + Reporter report.Reporter `json:"-"` + backupClient backupsys.BackupClient } // 无实际作用,仅确保实现了 jobruntime.JobRunner 接口 @@ -126,6 +129,10 @@ func (job *RedisBackup) Run() (err error) { if err != nil { return } + err = job.GetBackupClient() + if err != nil { + return + } toBackSys := "yes" if job.params.WithoutToBackupSys { toBackSys = "no" @@ -138,7 +145,7 @@ func (job *RedisBackup) Run() (err error) { } task := NewFullBackupTask(job.params.BkBizID, job.params.Domain, job.params.IP, port, password, toBackSys, job.params.BackupType, bakDir, - true, consts.BackupTarSplitSize, job.Reporter) + true, consts.BackupTarSplitSize, job.params.SSDLogCount, job.Reporter, job.backupClient) bakTasks = append(bakTasks, task) } @@ -213,43 +220,56 @@ func (job *RedisBackup) GetReporter() (err error) { return } +// GetBackupClient 获取备份系统client +func (job *RedisBackup) GetBackupClient() (err error) { + bkTag := consts.RedisFullBackupTAG + if job.params.BackupType == consts.ForeverBackupType { + bkTag = consts.RedisForeverBackupTAG + } + job.backupClient = backupsys.NewIBSBackupClient(consts.IBSBackupClient, bkTag) + // job.backupClient,err=backupsys.NewCosBackupClient(consts.IBSBackupClient,"", bkTag) + return +} + // BackupTask redis备份task type BackupTask struct { - ReportType string `json:"report_type"` - BkBizID string `json:"bk_biz_id"` - ServerIP string `json:"server_ip"` - ServerPort int `json:"server_port"` - Domain string `json:"domain"` - Password string `json:"-"` - ToBackupSystem string `json:"-"` - DbType string `json:"db_type"` // RedisInstance or TendisplusInstance or TendisSSDInstance - BackupType string `json:"-"` // 常规备份、下线备份 - Role string `json:"role"` - DataSize uint64 `json:"-"` // redis实例数据大小 - DataDir string `json:"-"` - BackupDir string `json:"backup_dir"` - TarSplit bool `json:"-"` // 是否对tar文件做split - TarSplitPartSize string `json:"-"` - BackupFiles []string `json:"backup_files"` // 备份的目标文件,如果文件过大会切割成多个 - BackupFilesSize []int64 `json:"backup_files_size"` // 备份文件大小(已切割 or 已压缩 or 已打包) - BackupTaskIDs []uint64 `json:"backup_taskids"` - BackupMD5s []string `json:"backup_md5s"` // 目前为空 - BackupTag string `json:"backup_tag"` // REDIS_FULL or REDIS_BINLOG + ReportType string `json:"report_type"` + BkBizID string `json:"bk_biz_id"` + ServerIP string `json:"server_ip"` + ServerPort int `json:"server_port"` + Domain string `json:"domain"` + Password string `json:"-"` + ToBackupSystem string `json:"-"` + DbType string `json:"db_type"` // RedisInstance or TendisplusInstance or TendisSSDInstance + BackupType string `json:"-"` // 常规备份、下线备份 + Role string `json:"role"` + DataSize uint64 `json:"-"` // redis实例数据大小 + DataDir string `json:"-"` + BackupDir string `json:"backup_dir"` + TarSplit bool `json:"-"` // 是否对tar文件做split + TarSplitPartSize string `json:"-"` + BackupFile string `json:"backup_file"` // 备份的目标文件,如果文件过大会切割成多个 + BackupFileSize int64 `json:"backup_file_size"` // 备份文件大小(已切割 or 已压缩 or 已打包) + BackupTaskID string `json:"backup_taskid"` + BackupMD5 string `json:"backup_md5"` // 目前为空 + BackupTag string `json:"backup_tag"` // REDIS_FULL or REDIS_BINLOG + ShardValue string `json:"shard_value"` // shard值 // 全备尽管会切成多个文件,但其生成的起始时间、结束时间一样 - StartTime customtime.CustomTime `json:"start_time"` // 生成全备的起始时间 - EndTime customtime.CustomTime `json:"end_time"` // //生成全备的结束时间 - Status string `json:"status"` - Message string `json:"message"` - Cli *myredis.RedisClient `json:"-"` - SSDLogCount TendisSSDSetLogCount `json:"-"` - reporter report.Reporter - Err error `json:"-"` + StartTime customtime.CustomTime `json:"start_time"` // 生成全备的起始时间 + EndTime customtime.CustomTime `json:"end_time"` // //生成全备的结束时间 + Status string `json:"status"` + Message string `json:"message"` + Cli *myredis.RedisClient `json:"-"` + SSDLogCount TendisSSDSetLogCount `json:"-"` + reporter report.Reporter + backupClient backupsys.BackupClient + Err error `json:"-"` } // NewFullBackupTask new backup task func NewFullBackupTask(bkBizID, domain, ip string, port int, password, toBackupSys, backupType, backupDir string, tarSplit bool, tarSplitSize string, - reporter report.Reporter) *BackupTask { + ssdLogCount TendisSSDSetLogCount, reporter report.Reporter, bakCli backupsys.BackupClient) *BackupTask { return &BackupTask{ ReportType: consts.RedisFullBackupReportType, BkBizID: bkBizID, @@ -262,10 +282,12 @@ func NewFullBackupTask(bkBizID, domain, ip string, port int, password, BackupDir: backupDir, TarSplit: tarSplit, TarSplitPartSize: tarSplitSize, - BackupTaskIDs: []uint64{}, - BackupMD5s: []string{}, + BackupTaskID: "", + BackupMD5: "", BackupTag: consts.RedisFullBackupTAG, + SSDLogCount: ssdLogCount, reporter: reporter, + backupClient: bakCli, } } @@ -339,6 +361,10 @@ func (task *BackupTask) GoFullBakcup() { if task.Err != nil { return } + task.getRedisShardVal() + if task.Err != nil { + return + } // 如果有备份正在执行,则先等待其完成 task.Err = task.Cli.WaitForBackupFinish() @@ -350,11 +376,11 @@ func (task *BackupTask) GoFullBakcup() { } else if task.DbType == consts.TendisTypeTendisplusInsance { task.TendisplusInstanceBackup() } else if task.DbType == consts.TendisTypeTendisSSDInsance { - task.TendisSSDInstanceBackup() + task.TendisSSDSetLougCount() if task.Err != nil { return } - task.TendisSSDSetLougCount() + task.TendisSSDInstanceBackup() } if task.Err != nil { return @@ -449,6 +475,54 @@ func (task *BackupTask) PrecheckDisk() { task.Addr(), task.DataSize/1024/1024, task.BackupDir, bakDiskUsg.AvailSize/1024/1024)) } +func (task *BackupTask) getRedisShardVal() { + var enabled bool + var masterNode *myredis.ClusterNodeData + enabled, task.Err = task.Cli.IsClusterEnabled() + if task.Err != nil { + return + } + if enabled { + masterNode, task.Err = task.Cli.RedisClusterGetMasterNode(task.Addr()) + if task.Err != nil { + return + } + task.ShardValue = masterNode.SlotSrcStr + return + } + if util.FileExists(consts.BkDbmonConfFile) { + confData, err := os.ReadFile(consts.BkDbmonConfFile) + if err != nil { + err = fmt.Errorf("read file(%s) fail,err:%v", consts.BkDbmonConfFile, err) + mylog.Logger.Warn(err.Error()) + return + } + if !strings.Contains(string(confData), "server_shards:") { + return + } + type servers struct { + Servers []struct { + ServerShards map[string]string `yaml:"server_shards"` + } `yaml:"servers"` + } + var serversObj servers + err = yaml.Unmarshal(confData, &serversObj) + if err != nil { + err = fmt.Errorf("yaml.Unmarshal fail,err:%v", err) + mylog.Logger.Warn(err.Error()) + return + } + for _, server := range serversObj.Servers { + for ipPort, shardVal := range server.ServerShards { + if ipPort == task.Addr() { + task.ShardValue = shardVal + return + } + } + } + } +} + // RedisInstanceBackup redis(cache)实例备份 func (task *BackupTask) RedisInstanceBackup() { var srcFile string @@ -493,14 +567,14 @@ func (task *BackupTask) RedisInstanceBackup() { return } } - // task.BackupFiles = append(task.BackupFiles, filepath.Base(targetFile)) - task.BackupFiles = append(task.BackupFiles, targetFile) + task.BackupFile = targetFile fileSize, task.Err = util.GetFileSize(targetFile) if task.Err != nil { mylog.Logger.Error(task.Err.Error()) return } - task.BackupFilesSize = append(task.BackupFilesSize, fileSize) + task.BackupFileSize = fileSize + util.LocalFileChmodAllRead(targetFile) util.LocalDirChownMysql(task.BackupDir) mylog.Logger.Info(fmt.Sprintf("redis(%s) local backup success", task.Addr())) return @@ -525,20 +599,17 @@ func (task *BackupTask) TendisplusInstanceBackup() { return } task.EndTime.Time = time.Now().Local() - if task.TarSplit && task.TarSplitPartSize != "" { - task.BackupFiles, task.Err = util.TarAndSplitADir(backupFullDir, task.BackupDir, task.TarSplitPartSize, true) - } else { - tarFile, task.Err = util.TarADir(backupFullDir, task.BackupDir, true) - task.BackupFiles = append(task.BackupFiles, tarFile) - } + tarFile, task.Err = util.TarADir(backupFullDir, task.BackupDir, true) if task.Err != nil { mylog.Logger.Error(task.Err.Error()) return } + task.BackupFile = tarFile task.GetBakFilesSize() if task.Err != nil { return } + util.LocalFileChmodAllRead(task.BackupFile) util.LocalDirChownMysql(task.BackupDir) mylog.Logger.Info(fmt.Sprintf("tendisplus(%s) local backup success", task.Addr())) return @@ -603,20 +674,17 @@ func (task *BackupTask) TendisSSDInstanceBackup() { backupFullDir = fileWithBinlogPos // 只做打包,不做压缩,rocksdb中已经做了压缩 - if task.TarSplit && task.TarSplitPartSize != "" { - task.BackupFiles, task.Err = util.TarAndSplitADir(backupFullDir, task.BackupDir, task.TarSplitPartSize, true) - } else { - tarFile, task.Err = util.TarADir(backupFullDir, task.BackupDir, true) - task.BackupFiles = append(task.BackupFiles, filepath.Join(task.BackupDir, tarFile)) - } + tarFile, task.Err = util.TarADir(backupFullDir, task.BackupDir, true) if task.Err != nil { mylog.Logger.Error(task.Err.Error()) return } + task.BackupFile = tarFile task.GetBakFilesSize() if task.Err != nil { return } + util.LocalFileChmodAllRead(task.BackupFile) util.LocalDirChownMysql(task.BackupDir) mylog.Logger.Info(fmt.Sprintf("tendisSSD(%s) local backup success", task.Addr())) return @@ -625,15 +693,12 @@ func (task *BackupTask) TendisSSDInstanceBackup() { // GetBakFilesSize 获取备份文件大小 func (task *BackupTask) GetBakFilesSize() { var fileSize int64 - task.BackupFilesSize = make([]int64, 0, len(task.BackupFiles)) - for _, bakFile := range task.BackupFiles { - fileSize, task.Err = util.GetFileSize(bakFile) - if task.Err != nil { - mylog.Logger.Error(task.Err.Error()) - return - } - task.BackupFilesSize = append(task.BackupFilesSize, fileSize) + fileSize, task.Err = util.GetFileSize(task.BackupFile) + if task.Err != nil { + mylog.Logger.Error(task.Err.Error()) + return } + task.BackupFileSize = fileSize } // TendisSSDSetLougCount tendisSSD设置log-count参数 @@ -643,49 +708,58 @@ func (task *BackupTask) TendisSSDSetLougCount() { if task.Err != nil { return } + mylog.Logger.Info(fmt.Sprintf("%s config set log-count %d success", task.Addr(), + task.SSDLogCount.LogCount)) + } else { + mylog.Logger.Info(fmt.Sprintf("%s skip config set log-count(%d)...", task.Addr(), + task.SSDLogCount.LogCount)) + } + if task.SSDLogCount.LogKeepCount > 0 { + _, task.Err = task.Cli.ConfigSet("log-keep-count", strconv.FormatInt(task.SSDLogCount.LogKeepCount, 10)) + if task.Err != nil { + return + } + mylog.Logger.Info(fmt.Sprintf("%s config set log-keep-count %d success", task.Addr(), + task.SSDLogCount.LogKeepCount)) + } else { + mylog.Logger.Info(fmt.Sprintf("%s skip config set log-keep-count(%d)...", task.Addr(), + task.SSDLogCount.LogKeepCount)) } if task.SSDLogCount.SlaveLogKeepCount > 0 { - _, task.Err = task.Cli.ConfigSet("slave-log-keep-count", strconv.FormatInt(task.SSDLogCount.LogCount, 10)) + _, task.Err = task.Cli.ConfigSet("slave-log-keep-count", strconv.FormatInt(task.SSDLogCount.SlaveLogKeepCount, 10)) if task.Err != nil { return } + mylog.Logger.Info(fmt.Sprintf("%s config set slave-log-keep-count %d success", task.Addr(), + task.SSDLogCount.SlaveLogKeepCount)) + } else { + mylog.Logger.Info(fmt.Sprintf("%s skip config set slave-log-keep-count(%d)...", task.Addr(), + task.SSDLogCount.SlaveLogKeepCount)) } + } // TransferToBackupSystem 备份文件上传到备份系统 func (task *BackupTask) TransferToBackupSystem() { var msg string - cliFileInfo, err := os.Stat(consts.BackupClient) + cliFileInfo, err := os.Stat(consts.IBSBackupClient) if err != nil { - err = fmt.Errorf("os.stat(%s) failed,err:%v", consts.BackupClient, err) + err = fmt.Errorf("os.stat(%s) failed,err:%v", consts.IBSBackupClient, err) mylog.Logger.Error(err.Error()) return } if !util.IsExecOther(cliFileInfo.Mode().Perm()) { - err = fmt.Errorf("%s is unable to execute by other", consts.BackupClient) + err = fmt.Errorf("%s is unable to execute by other", consts.IBSBackupClient) mylog.Logger.Error(err.Error()) return } - mylog.Logger.Info(fmt.Sprintf("redis(%s) backupFiles:%+v start upload backupSystem", task.Addr(), task.BackupFiles)) - bkTag := consts.RedisFullBackupTAG - if task.BackupType == consts.ForeverBackupType { - bkTag = consts.RedisForeverBackupTAG - } - uploader := backupsys.UploadTask{ - Files: task.BackupFiles, - Tag: bkTag, - } - task.Err = uploader.UploadFiles() + mylog.Logger.Info(fmt.Sprintf("redis(%s) backupFiles:%+v start upload backupSystem", task.Addr(), task.BackupFile)) + task.BackupTaskID, task.Err = task.backupClient.Upload(task.BackupFile) if task.Err != nil { return } - task.BackupTaskIDs = uploader.TaskIDs - // task.Err = uploader.WaitForUploadFinish() - // if task.Err != nil { - // return - // } msg = fmt.Sprintf("redis(%s) backupFiles%+v taskid(%+v) uploading to backupSystem", - task.Addr(), task.BackupFiles, task.BackupTaskIDs) + task.Addr(), task.BackupFile, task.BackupTaskID) mylog.Logger.Info(msg) return } @@ -702,13 +776,7 @@ func (task *BackupTask) BackupRecordReport() { // BackupRecordSaveToDoingFile 备份记录保存到本地 redis_backup_file_list_${port}_doing 文件中 func (task *BackupTask) BackupRecordSaveToDoingFile() { - var backupDir string - if len(task.BackupFiles) == 0 { - mylog.Logger.Warn(fmt.Sprintf("redis(%s) backupFiles:%+v empty", task.Addr(), task.BackupFiles)) - backupDir = task.BackupDir - } else { - backupDir = filepath.Dir(task.BackupFiles[0]) - } + backupDir := filepath.Dir(task.BackupFile) // 例如: /data/dbbak/backup/redis_backup_file_list_30000_doing backupDir = filepath.Join(backupDir, "backup") util.MkDirsIfNotExists([]string{}) diff --git a/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/redis_data_recovery.go b/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/redis_data_recovery.go new file mode 100644 index 0000000000..d595d4f7e9 --- /dev/null +++ b/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/redis_data_recovery.go @@ -0,0 +1,316 @@ +package atomredis + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + "strconv" + "sync" + "time" + + "dbm-services/redis/db-tools/dbactuator/models/myredis" + "dbm-services/redis/db-tools/dbactuator/pkg/backupsys" + "dbm-services/redis/db-tools/dbactuator/pkg/consts" + "dbm-services/redis/db-tools/dbactuator/pkg/jobruntime" + + "dbm-services/redis/db-tools/dbactuator/pkg/util" + + "github.com/go-playground/validator/v10" +) + +// RedisDataRecoverParams redis 数据构造参数 +type RedisDataRecoverParams struct { + SourceIP string `json:"source_ip" validate:"required"` + SourcePorts []int `json:"source_ports" validate:"required"` + NeWTempIP string `json:"new_temp_ip" validate:"required"` + NewTempPorts []int `json:"new_temp_ports" validate:"required" ` + RecoveryTimePoint string `json:"recovery_time_point" validate:"required"` + User string `json:"user" validate:"required"` + Password string `json:"password" validate:"required"` + IsIncludeSlave bool `json:"is_include_slave" ` + IsPrecheck bool `json:"is_precheck" ` + TendisType string `json:"tendis_type" validate:"required"` + BaseInfo backupsys.IBSBaseInfo `json:"base_info"` +} + +// RedisDataRecover redis 数据构造 +type RedisDataRecover struct { + runtime *jobruntime.JobGenericRuntime + params RedisDataRecoverParams + query backupsys.QueryReq + RecoverDir string + successPort []int + failPort []int + fileNames []string + password string + taskidList string + BackupSize uint64 + TendisType string `json:"tendis_type"` +} + +// 无实际作用,仅确保实现了 taskruntime.JobRunner 接口 +var _ jobruntime.JobRunner = (*RedisDataRecover)(nil) + +// NewRedisDataRecover new +func NewRedisDataRecover() jobruntime.JobRunner { + return &RedisDataRecover{} +} + +// Init 初始化 +func (task *RedisDataRecover) Init(m *jobruntime.JobGenericRuntime) error { + task.runtime = m + + err := json.Unmarshal([]byte(task.runtime.PayloadDecoded), &task.params) + if err != nil { + task.runtime.Logger.Error(fmt.Sprintf("json.Unmarshal failed,err:%+v", err)) + return err + } + // 参数有效性检查 + validate := validator.New() + err = validate.Struct(task.params) + if err != nil { + if _, ok := err.(*validator.InvalidValidationError); ok { + task.runtime.Logger.Error("RedisDataRecover Init params validate failed,err:%v,params:%+v", + err, task.params) + return err + } + for _, err := range err.(validator.ValidationErrors) { + task.runtime.Logger.Error("RedisDataRecover Init params validate failed,err:%v,params:%+v", + err, task.params) + return err + } + } + // 检查传入的端口不能为空 + if len(task.params.SourcePorts) == 0 || len(task.params.NewTempPorts) == 0 { + err = fmt.Errorf("RedisDataRecover SourcePorts(%d) or NewTempPorts(%d) =0 , is invalid ", + task.params.SourcePorts, task.params.NewTempPorts) + task.runtime.Logger.Error(err.Error()) + return err + } + + //传入的源端口数应该等于临时节点端口数 + if len(task.params.SourcePorts) != len(task.params.NewTempPorts) { + err = fmt.Errorf("RedisDataRecover SourcePorts(%d) != NewTempPorts(%d) , is invalid ", + task.params.SourcePorts, task.params.NewTempPorts) + task.runtime.Logger.Error(err.Error()) + return err + } + // 设置回档用户和密码 + backupsys.SetUserPassword(task.params.User, task.params.Password) + // 设置备份系统信息 + backupsys.SetIBSBaseInfo(task.params.BaseInfo.Url, task.params.BaseInfo.SysID, task.params.BaseInfo.Key) + return nil +} + +// Name 原子任务名 +func (task *RedisDataRecover) Name() string { + return "redis_data_structure" +} + +// Retry times +func (task *RedisDataRecover) Retry() uint { + return 2 +} + +// Rollback rollback +func (task *RedisDataRecover) Rollback() error { + return nil + +} + +// Run 执行 +// NOCC:golint/fnsize(设计如此) +func (task *RedisDataRecover) Run() (err error) { + // 构造目录初始化 + recoverDir := filepath.Join(consts.GetRedisBackupDir(), "dbbak/recover_redis") + task.RecoverDir = recoverDir + err = task.CheckRecoverDir() + if err != nil { + return err + } + + task.runtime.Logger.Info(task.params.RecoveryTimePoint) + // 恢复任务初始化 + recoverTasks := make([]*backupsys.RedisInsRecoverTask, 0, len(task.params.SourcePorts)) + for idx, sourceRort := range task.params.SourcePorts { + newTmpPort := task.params.NewTempPorts[idx] + task.TendisType = task.params.TendisType + // 前置检查备份信息时,因为还没安装节点,所以password信息还拿不到 + task.password = "" + // 提前检查备份信息 + if task.params.IsPrecheck { + // 恢复任务 + recoverTask, err := backupsys.NewRedisInsRecoverTask(task.params.SourceIP, sourceRort, + task.params.NeWTempIP, newTmpPort, task.password, + task.params.RecoveryTimePoint, recoverDir, task.TendisType, + task.params.IsIncludeSlave, task.params.IsPrecheck, task.runtime) + if err != nil { + return err + } + recoverTasks = append(recoverTasks, recoverTask) + + } else { + // 数据构造时从本地获取密码信息 + task.password, err = myredis.GetPasswordFromLocalConfFile(newTmpPort) + if err != nil { + return err + } + + // 获取回档类型 + redisAddr := fmt.Sprintf("%s:%s", task.params.NeWTempIP, strconv.Itoa(task.params.NewTempPorts[0])) + // 验证节点是否可连接 + redisCli, err := myredis.NewRedisClient(redisAddr, task.password, 0, consts.TendisTypeRedisInstance) + if err != nil { + return err + } + defer redisCli.Close() + // 获取节点类型 + task.TendisType, err = redisCli.GetTendisType() + if err != nil { + err = fmt.Errorf("GetTendisType Err:%v", err) + task.runtime.Logger.Error(err.Error()) + return err + } + task.runtime.Logger.Info("TendisType:%s", task.TendisType) + + // 恢复任务 + recoverTask, err := backupsys.NewRedisInsRecoverTask(task.params.SourceIP, sourceRort, + task.params.NeWTempIP, newTmpPort, task.password, + task.params.RecoveryTimePoint, recoverDir, task.TendisType, + task.params.IsIncludeSlave, task.params.IsPrecheck, task.runtime) + if err != nil { + return err + } + // 获取链接 + err = recoverTask.GetRedisCli() + if err != nil { + return err + } + + recoverTasks = append(recoverTasks, recoverTask) + } + + } + // 如果是构造数据时才停dbmon,前置检查备份信息时,还没安装dbmon + if !task.params.IsPrecheck { + // 停BkDbmon + err = task.stopBkDbmon() + if err != nil { + return err + } + } + + wg := sync.WaitGroup{} + genChan := make(chan *backupsys.RedisInsRecoverTask) + limit := 3 // 并发度 + for worker := 0; worker < limit; worker++ { + wg.Add(1) + go func() { + defer wg.Done() + for taskItem := range genChan { + // 执行回档任务 + taskItem.Run() + } + }() + } + go func() { + // 关闭genChan,以便让所有goroutine退出 + defer close(genChan) + for _, task := range recoverTasks { + recoverItem := task + genChan <- recoverItem + } + }() + wg.Wait() + for _, task := range recoverTasks { + recoverItem := task + if recoverItem.Err != nil { + return recoverItem.Err + } + } + // 如果是构造数据时才拉dbmon,前置检查备份信息时,还没dbmon相关文件 + if !task.params.IsPrecheck { + // 拉起BkDbmon + err = task.startBkDbmon() + if err != nil { + return err + } + + } + + return nil +} + +// CheckRecoverDir 数据构造本地数据目录 +func (task *RedisDataRecover) CheckRecoverDir() (err error) { + + // 检查构造目录是否存在 + _, err = os.Stat(task.RecoverDir) + if err != nil && os.IsNotExist(err) { + mkCmd := fmt.Sprintf("mkdir -p %s ", task.RecoverDir) + _, err = util.RunLocalCmd("bash", []string{"-c", mkCmd}, "", nil, 10*time.Minute) + if err != nil { + err = fmt.Errorf("创建目录:%s失败,err:%v", task.RecoverDir, err) + task.runtime.Logger.Error(err.Error()) + return err + } + util.LocalDirChownMysql(task.RecoverDir) + } else if err != nil { + err = fmt.Errorf("访问目录:%s 失败,err:%v", task.RecoverDir, err) + task.runtime.Logger.Error(err.Error()) + return err + + } + task.runtime.Logger.Info("CheckRecoverDir:%s success", task.RecoverDir) + return nil +} + +// // StopBkDbmon 停 bk-dbmon +func (task *RedisDataRecover) stopBkDbmon() (err error) { + + task.runtime.Logger.Info("stop dbmon start") + // 调用脚本,路径是固定的 + stopScript := filepath.Join(consts.BkDbmonPath, "stop.sh") + if !util.FileExists(stopScript) { + err = fmt.Errorf("%s stop.sh not exists", consts.BkDbmonPath) + task.runtime.Logger.Error(err.Error()) + return err + + } + + stopCmd := fmt.Sprintf("su %s -c '%s'", consts.MysqlAaccount, "sh "+stopScript) + task.runtime.Logger.Info(stopCmd) + _, err = util.RunLocalCmd("su", []string{consts.MysqlAaccount, "-c", "sh " + stopScript}, + "", nil, 1*time.Minute) + if err != nil { + err = fmt.Errorf("执行关闭dbmon命令:%s失败:%v", stopCmd, err) + task.runtime.Logger.Error(err.Error()) + return err + } + task.runtime.Logger.Info("stop dbmon success") + return nil +} + +// startBkDbmon 拉起 bk-dbmon +func (task *RedisDataRecover) startBkDbmon() (err error) { + task.runtime.Logger.Info("start dbmon ...") + startScript := filepath.Join(consts.BkDbmonPath, "start.sh") + if !util.FileExists(startScript) { + err = fmt.Errorf("%s start.sh not exists", consts.BkDbmonPath) + task.runtime.Logger.Error(err.Error()) + return err + } + //用mysql权限 + startCmd := fmt.Sprintf("su %s -c 'nohup %s &'", consts.MysqlAaccount, "sh "+startScript) + task.runtime.Logger.Info(startCmd) + _, err = util.RunLocalCmd("su", []string{consts.MysqlAaccount, "-c", "nohup sh " + startScript + " &"}, + "", nil, 1*time.Minute) + if err != nil { + err = fmt.Errorf("执行启动dbmon命令:%s失败:%v", startCmd, err) + task.runtime.Logger.Error(err.Error()) + return err + } + task.runtime.Logger.Info("start dbmon success") + return nil +} diff --git a/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/redis_dts_datacheck.go b/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/redis_dts_datacheck.go index 098529eaff..3a4a36174c 100644 --- a/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/redis_dts_datacheck.go +++ b/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/redis_dts_datacheck.go @@ -58,12 +58,12 @@ type RedisDtsDataCheckAndRpaireParams struct { // RedisDtsDataCheck dts 数据校验 type RedisDtsDataCheck struct { - atomJobName string - saveDir string - dataCheckTool string - dataRepaireTool string - params RedisDtsDataCheckAndRpaireParams - runtime *jobruntime.JobGenericRuntime + atomJobName string + saveDir string + dataCheckTool string + dataRepairTool string + params RedisDtsDataCheckAndRpaireParams + runtime *jobruntime.JobGenericRuntime } // 无实际作用,仅确保实现了 jobruntime.JobRunner 接口 @@ -107,7 +107,7 @@ func (job *RedisDtsDataCheck) Init(m *jobruntime.JobGenericRuntime) error { } // 为何这里 要用这种方式 返回Name()? -// 因为 RedisDtsDataRepaire 继承自 RedisDtsDataCheck, 两者Init()是相同的 +// 因为 RedisDtsDataRepair 继承自 RedisDtsDataCheck, 两者Init()是相同的 // 只有这样 Init()中 job.Name() 方法才会返回正确的名字 // Name 原子任务名 @@ -147,7 +147,7 @@ func (job *RedisDtsDataCheck) Run() (err error) { for _, portItem := range job.params.SrcRedisPortSegmentList { wg.Add(1) - task, err := NewRedisInsDtsDataCheckAndRepaireTask(job.params.SrcRedisIP, portItem, job) + task, err := NewRedisInsDtsDataCheckAndRepairTask(job.params.SrcRedisIP, portItem, job) if err != nil { continue } @@ -176,6 +176,8 @@ func (job *RedisDtsDataCheck) Run() (err error) { func (job *RedisDtsDataCheck) getSaveDir() { job.saveDir = filepath.Join(consts.GetRedisBackupDir(), "dbbak/get_keys_pattern") + util.MkDirsIfNotExists([]string{job.saveDir}) + util.LocalDirChownMysql(job.saveDir) } // TestConnectable 测试redis是否可连接 @@ -247,7 +249,7 @@ func (job *RedisDtsDataCheck) GetTools() (err error) { return } job.dataCheckTool = consts.TendisDataCheckBin - job.dataRepaireTool = consts.RedisDiffKeysRepairerBin + job.dataRepairTool = consts.RedisDiffKeysRepairerBin return nil } @@ -270,8 +272,8 @@ type RedisInsDtsDataCheckAndRepairTask struct { Err error `json:"err"` } -// NewRedisInsDtsDataCheckAndRepaireTask new -func NewRedisInsDtsDataCheckAndRepaireTask(ip string, portAndSeg PortAndSegment, job *RedisDtsDataCheck) ( +// NewRedisInsDtsDataCheckAndRepairTask new +func NewRedisInsDtsDataCheckAndRepairTask(ip string, portAndSeg PortAndSegment, job *RedisDtsDataCheck) ( task *RedisInsDtsDataCheckAndRepairTask, err error) { task = &RedisInsDtsDataCheckAndRepairTask{} task.datacheckJob = job @@ -320,8 +322,8 @@ func (task *RedisInsDtsDataCheckAndRepairTask) getDataCheckDiffKeysFile() string return filepath.Join(task.getSaveDir(), basename) } -func (task *RedisInsDtsDataCheckAndRepairTask) getRepaireHotKeysFile() string { - basename := fmt.Sprintf("dts_repaire_hot_keys_%s_%d", task.keyPatternTask.IP, task.keyPatternTask.Port) +func (task *RedisInsDtsDataCheckAndRepairTask) getRepairHotKeysFile() string { + basename := fmt.Sprintf("dts_repair_hot_keys_%s_%d", task.keyPatternTask.IP, task.keyPatternTask.Port) return filepath.Join(task.getSaveDir(), basename) } @@ -413,9 +415,9 @@ func (task *RedisInsDtsDataCheckAndRepairTask) getDataCheckRet() { task.getLogger().Info(predix + headLines) } -func (task *RedisInsDtsDataCheckAndRepairTask) getDataRepaireRet() { +func (task *RedisInsDtsDataCheckAndRepairTask) getDataRepairRet() { var msg string - hotFileStat, err := os.Stat(task.getRepaireHotKeysFile()) + hotFileStat, err := os.Stat(task.getRepairHotKeysFile()) if err != nil && os.IsNotExist(err) { // 没有hotKey msg = fmt.Sprintf("all keys repaired successfully,srcAddr:%s,dstAddr:%s", task.getSrcRedisAddr(), @@ -423,7 +425,7 @@ func (task *RedisInsDtsDataCheckAndRepairTask) getDataRepaireRet() { task.getLogger().Info(msg) return } else if err != nil { - task.Err = fmt.Errorf("hotKeysFile:%s os.stat fail,err:%v", task.getRepaireHotKeysFile(), err) + task.Err = fmt.Errorf("hotKeysFile:%s os.stat fail,err:%v", task.getRepairHotKeysFile(), err) task.getLogger().Info(msg) return } @@ -433,7 +435,7 @@ func (task *RedisInsDtsDataCheckAndRepairTask) getDataRepaireRet() { task.getLogger().Info(msg) return } - task.HotKeysCnt, err = util.FileLineCounter(task.getRepaireHotKeysFile()) + task.HotKeysCnt, err = util.FileLineCounter(task.getRepairHotKeysFile()) if err != nil { task.Err = err task.getLogger().Error(task.Err.Error()) @@ -571,8 +573,8 @@ func (task *RedisInsDtsDataCheckAndRepairTask) KeyPatternAndDataCheck() { return } -// RunDataRepaire 执行数据修复 -func (task *RedisInsDtsDataCheckAndRepairTask) RunDataRepaire() { +// RunDataRepair 执行数据修复 +func (task *RedisInsDtsDataCheckAndRepairTask) RunDataRepair() { var diffKeysCnt uint64 var locked bool var flockP *flock.Flock @@ -591,7 +593,7 @@ func (task *RedisInsDtsDataCheckAndRepairTask) RunDataRepaire() { } // 尝试获取文件锁,确保单个redis同一时间只有一个进程在进行数据修复 - lockFile := filepath.Join(task.getSaveDir(), fmt.Sprintf("lock_dtsdatarepaire.%s.%d", + lockFile := filepath.Join(task.getSaveDir(), fmt.Sprintf("lock_dtsdatarepair.%s.%d", task.keyPatternTask.IP, task.keyPatternTask.Port)) locked, flockP = task.tryFileLock(lockFile, 5*time.Hour) if task.Err != nil { @@ -614,17 +616,17 @@ func (task *RedisInsDtsDataCheckAndRepairTask) RunDataRepaire() { repairCmd := fmt.Sprintf( `cd %s && %s --src-addr=%s --src-password=%s \ --dest-addr=%s --dest-password=%s --diff-keys-file=%s --hot-keys-file=%s %s`, - task.getSaveDir(), task.datacheckJob.dataRepaireTool, + task.getSaveDir(), task.datacheckJob.dataRepairTool, task.getSrcRedisAddr(), task.getSrcRedisPassword(), task.getDstRedisAddr(), task.getDstRedisPassword(), - task.getDataCheckDiffKeysFile(), task.getRepaireHotKeysFile(), + task.getDataCheckDiffKeysFile(), task.getRepairHotKeysFile(), extraOptsBuilder.String()) logCmd := fmt.Sprintf( `cd %s && %s --src-addr=%s --src-password=xxxx \ --dest-addr=%s --dest-password=xxxx --diff-keys-file=%s --hot-keys-file=%s %s`, - task.getSaveDir(), task.datacheckJob.dataRepaireTool, + task.getSaveDir(), task.datacheckJob.dataRepairTool, task.getSrcRedisAddr(), task.getDstRedisAddr(), - task.getDataCheckDiffKeysFile(), task.getRepaireHotKeysFile(), + task.getDataCheckDiffKeysFile(), task.getRepairHotKeysFile(), extraOptsBuilder.String()) task.getLogger().Info(logCmd) @@ -632,6 +634,6 @@ func (task *RedisInsDtsDataCheckAndRepairTask) RunDataRepaire() { if task.Err != nil { return } - task.getDataRepaireRet() + task.getDataRepairRet() return } diff --git a/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/redis_dts_datarepair.go b/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/redis_dts_datarepair.go new file mode 100644 index 0000000000..bc68c7d4b4 --- /dev/null +++ b/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/redis_dts_datarepair.go @@ -0,0 +1,92 @@ +package atomredis + +import ( + "dbm-services/redis/db-tools/dbactuator/pkg/jobruntime" + "fmt" + "sync" + + "github.com/panjf2000/ants/v2" +) + +// RedisDtsDataRepair dts数据修复 +type RedisDtsDataRepair struct { + *RedisDtsDataCheck +} + +// 无实际作用,仅确保实现了 jobruntime.JobRunner 接口 +var _ jobruntime.JobRunner = (*RedisDtsDataRepair)(nil) + +// NewRedisDtsDataRepair 创建dts数据修复实例 +func NewRedisDtsDataRepair() jobruntime.JobRunner { + dataCheck := &RedisDtsDataCheck{} + return &RedisDtsDataRepair{dataCheck} +} + +// Init 初始化 +func (job *RedisDtsDataRepair) Init(m *jobruntime.JobGenericRuntime) error { + job.Name() // 这一步是必须的 + return job.RedisDtsDataCheck.Init(m) +} + +// Name 原子任务名 +func (job *RedisDtsDataRepair) Name() string { + if job.atomJobName == "" { + job.atomJobName = "redis_dts_datarepair" + } + return job.atomJobName +} + +// Run 运行 +func (job *RedisDtsDataRepair) Run() (err error) { + // 1. 测试redis是否可连接 + err = job.TestConnectable() + if err != nil { + return + } + // 2. 获取工具 + err = job.GetTools() + if err != nil { + return + } + // 3. 并发提取与校验,并发度5 + var wg sync.WaitGroup + taskList := make([]*RedisInsDtsDataCheckAndRepairTask, 0, len(job.params.SrcRedisPortSegmentList)) + pool, err := ants.NewPoolWithFunc(5, func(i interface{}) { + defer wg.Done() + task := i.(*RedisInsDtsDataCheckAndRepairTask) + task.RunDataRepair() + }) + if err != nil { + job.runtime.Logger.Error("RedisDtsDataRepair Run NewPoolWithFunc failed,err:%v", err) + return err + } + defer pool.Release() + + for _, portItem := range job.params.SrcRedisPortSegmentList { + wg.Add(1) + task, err := NewRedisInsDtsDataCheckAndRepairTask(job.params.SrcRedisIP, portItem, job.RedisDtsDataCheck) + if err != nil { + continue + } + taskList = append(taskList, task) + _ = pool.Invoke(task) + } + // 等待所有task执行完毕 + wg.Wait() + + var totalHotKeysCnt uint64 = 0 + for _, tmp := range taskList { + task := tmp + if task.Err != nil { + return task.Err + } + totalHotKeysCnt += task.HotKeysCnt + } + if totalHotKeysCnt > 0 { + err = fmt.Errorf("RedisDtsDataRepair totalHotKeysCnt:%d", totalHotKeysCnt) + job.runtime.Logger.Error(err.Error()) + return + } + job.runtime.Logger.Info("RedisDtsDataRepair success totalHotKeysCnt:%d", totalHotKeysCnt) + return +} diff --git a/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/redis_dts_datarepaire.go b/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/redis_dts_datarepaire.go deleted file mode 100644 index bd520a7fc5..0000000000 --- a/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/redis_dts_datarepaire.go +++ /dev/null @@ -1,93 +0,0 @@ -package atomredis - -import ( - "fmt" - "sync" - - "dbm-services/redis/db-tools/dbactuator/pkg/jobruntime" - - "github.com/panjf2000/ants/v2" -) - -// RedisDtsDataRepaire dts数据修复 -type RedisDtsDataRepaire struct { - *RedisDtsDataCheck -} - -// 无实际作用,仅确保实现了 jobruntime.JobRunner 接口 -var _ jobruntime.JobRunner = (*RedisDtsDataRepaire)(nil) - -// NewRedisDtsDataRepaire 创建dts数据修复实例 -func NewRedisDtsDataRepaire() jobruntime.JobRunner { - dataCheck := &RedisDtsDataCheck{} - return &RedisDtsDataRepaire{dataCheck} -} - -// Init 初始化 -func (job *RedisDtsDataRepaire) Init(m *jobruntime.JobGenericRuntime) error { - job.Name() // 这一步是必须的 - return job.RedisDtsDataCheck.Init(m) -} - -// Name 原子任务名 -func (job *RedisDtsDataRepaire) Name() string { - if job.atomJobName == "" { - job.atomJobName = "redis_dts_datarepaire" - } - return job.atomJobName -} - -// Run 运行 -func (job *RedisDtsDataRepaire) Run() (err error) { - // 1. 测试redis是否可连接 - err = job.TestConnectable() - if err != nil { - return - } - // 2. 获取工具 - err = job.GetTools() - if err != nil { - return - } - // 3. 并发提取与校验,并发度5 - var wg sync.WaitGroup - taskList := make([]*RedisInsDtsDataCheckAndRepairTask, 0, len(job.params.SrcRedisPortSegmentList)) - pool, err := ants.NewPoolWithFunc(5, func(i interface{}) { - defer wg.Done() - task := i.(*RedisInsDtsDataCheckAndRepairTask) - task.RunDataRepaire() - }) - if err != nil { - job.runtime.Logger.Error("RedisDtsDataRepaire Run NewPoolWithFunc failed,err:%v", err) - return err - } - defer pool.Release() - - for _, portItem := range job.params.SrcRedisPortSegmentList { - wg.Add(1) - task, err := NewRedisInsDtsDataCheckAndRepaireTask(job.params.SrcRedisIP, portItem, job.RedisDtsDataCheck) - if err != nil { - continue - } - taskList = append(taskList, task) - _ = pool.Invoke(task) - } - // 等待所有task执行完毕 - wg.Wait() - - var totalHotKeysCnt uint64 = 0 - for _, tmp := range taskList { - task := tmp - if task.Err != nil { - return task.Err - } - totalHotKeysCnt += task.HotKeysCnt - } - if totalHotKeysCnt > 0 { - err = fmt.Errorf("RedisDtsDataRepaire totalHotKeysCnt:%d", totalHotKeysCnt) - job.runtime.Logger.Error(err.Error()) - return - } - job.runtime.Logger.Info("RedisDtsDataRepaire success totalHotKeysCnt:%d", totalHotKeysCnt) - return -} diff --git a/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/redis_dts_online_switch.go b/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/redis_dts_online_switch.go new file mode 100644 index 0000000000..07e7cd6c13 --- /dev/null +++ b/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/redis_dts_online_switch.go @@ -0,0 +1,752 @@ +package atomredis + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + "regexp" + "strconv" + "strings" + "time" + + "github.com/go-playground/validator/v10" + + "dbm-services/redis/db-tools/dbactuator/models/myredis" + "dbm-services/redis/db-tools/dbactuator/pkg/common" + "dbm-services/redis/db-tools/dbactuator/pkg/consts" + "dbm-services/redis/db-tools/dbactuator/pkg/jobruntime" + "dbm-services/redis/db-tools/dbactuator/pkg/util" +) + +// RedisDtsOnlineSwitchParams 在线切换参数 +type RedisDtsOnlineSwitchParams struct { + DstProxyPkg common.MediaPkg `json:"dst_proxy_pkg" validate:"required"` + DtsBillID int64 `json:"dts_bill_id" validate:"required"` + SrcProxyIP string `json:"src_proxy_ip" validate:"required"` + SrcProxyPort int `json:"src_proxy_port" validate:"required"` + SrcProxyPassword string `json:"src_proxy_password" validate:"required"` + SrcClusterType string `json:"src_cluster_type" validate:"required"` + DstProxyIP string `json:"dst_proxy_ip" validate:"required"` + DstProxyPort int `json:"dst_proxy_port" validate:"required"` + DstProxyPassword string `json:"dst_proxy_password" validate:"required"` + DstClusterType string `json:"dst_cluster_type" validate:"required"` + DstRedisIP string `json:"dst_redis_ip" validate:"required"` + DstRedisPort int `json:"dst_redis_port" validate:"required"` + DstProxyConfigContent string `json:"dst_proxy_config_content" validate:"required"` +} + +// RedisDtsOnlineSwitch dts 在线切换 +type RedisDtsOnlineSwitch struct { + params RedisDtsOnlineSwitchParams + runtime *jobruntime.JobGenericRuntime + srcProxyConfigSaveDir string + srcProxyConfigFile string +} + +// 无实际作用,仅确保实现了 jobruntime.JobRunner 接口 +var _ jobruntime.JobRunner = (*RedisDtsOnlineSwitch)(nil) + +// NewRedisDtsOnlineSwitch new +func NewRedisDtsOnlineSwitch() jobruntime.JobRunner { + return &RedisDtsOnlineSwitch{} +} + +// Init 初始化 +func (job *RedisDtsOnlineSwitch) Init(m *jobruntime.JobGenericRuntime) error { + job.runtime = m + err := json.Unmarshal([]byte(job.runtime.PayloadDecoded), &job.params) + if err != nil { + job.runtime.Logger.Error(fmt.Sprintf("json.Unmarshal failed,err:%+v", err)) + return err + } + // 参数有效性检查 + validate := validator.New() + err = validate.Struct(job.params) + if err != nil { + if _, ok := err.(*validator.InvalidValidationError); ok { + job.runtime.Logger.Error("%s Init params validate failed,err:%v,params:%+v", + job.Name(), err, job.params) + return err + } + for _, err := range err.(validator.ValidationErrors) { + job.runtime.Logger.Error("%s Init params validate failed,err:%v,params:%+v", job.Name(), err, job.params) + return err + } + } + return nil +} + +// Name 原子任务名 +func (job *RedisDtsOnlineSwitch) Name() string { + return "redis_dts_online_switch" +} + +// Run 执行 +func (job *RedisDtsOnlineSwitch) Run() (err error) { + if !consts.IsTwemproxyClusterType(job.params.SrcClusterType) && + !consts.IsPredixyClusterType(job.params.SrcClusterType) { + err = fmt.Errorf("src cluster type %s is not supported", job.params.SrcClusterType) + job.runtime.Logger.Error(err.Error()) + return err + } + if !consts.IsTwemproxyClusterType(job.params.DstClusterType) && + !consts.IsPredixyClusterType(job.params.DstClusterType) { + err = fmt.Errorf("dst cluster type %s is not supported", job.params.DstClusterType) + job.runtime.Logger.Error(err.Error()) + return err + } + var sameType bool = false + if consts.IsTwemproxyClusterType(job.params.SrcClusterType) && + consts.IsTwemproxyClusterType(job.params.DstClusterType) { + sameType = true + } + if consts.IsPredixyClusterType(job.params.SrcClusterType) && + consts.IsPredixyClusterType(job.params.DstClusterType) { + sameType = true + } + err = job.IsSrcProxyConnected() + if err != nil { + return err + } + err = job.IsSrcProxyConfigOK() + if err != nil { + return err + } + if sameType { + err = job.BackupSrcProxyConfFile() + if err != nil { + return err + } + err = job.NewProxyConfigFileForSameType() + if err != nil { + return err + } + err = job.RestartProxyAndCheckBackends() + if err != nil { + return err + } + err = job.RestartDbMon() + if err != nil { + return err + } + return nil + } + err = job.UntarDstProxyMedia() + if err != nil { + return err + } + err = job.NewProxyConfigFileForDiffType() + if err != nil { + return err + } + err = job.RestartProxyAndCheckBackends() + if err != nil { + return err + } + err = job.RestartDbMon() + if err != nil { + return err + } + return nil +} + +// Retry times +func (job *RedisDtsOnlineSwitch) Retry() uint { + return 2 +} + +// Rollback rollback +func (job *RedisDtsOnlineSwitch) Rollback() error { + return nil +} + +func (job *RedisDtsOnlineSwitch) getSrcProxyAddr() string { + return fmt.Sprintf("%s:%d", job.params.SrcProxyIP, job.params.SrcProxyPort) +} + +func (job *RedisDtsOnlineSwitch) getDstProxyAddr() string { + return fmt.Sprintf("%s:%d", job.params.DstProxyIP, job.params.DstProxyPort) +} + +func (job *RedisDtsOnlineSwitch) getProxyConfigSaveDir(clusterType string, proxyPort int) string { + if consts.IsTwemproxyClusterType(clusterType) { + return fmt.Sprintf("%s/twemproxy-0.2.4/%d/", consts.DataPath, proxyPort) + } + if consts.IsPredixyClusterType(clusterType) { + return fmt.Sprintf("%s/predixy/%d/", consts.GetRedisDataDir(), proxyPort) + } + return "" +} + +func (job *RedisDtsOnlineSwitch) getSrcProxyConfigSaveDir() string { + if job.srcProxyConfigSaveDir == "" { + return job.srcProxyConfigSaveDir + } + return job.getProxyConfigSaveDir(job.params.SrcClusterType, job.params.SrcProxyPort) +} + +func (job *RedisDtsOnlineSwitch) getSrcConfigFileSuffix() string { + if consts.IsTwemproxyClusterType(job.params.SrcClusterType) { + return "yml" + } + if consts.IsPredixyClusterType(job.params.SrcClusterType) { + return "conf" + } + return "conf" +} + +func (job *RedisDtsOnlineSwitch) getProxyFile(clusterType string, proxyPort int) string { + if consts.IsTwemproxyClusterType(clusterType) { + return fmt.Sprintf("nutcracker.%d.yml", proxyPort) + } else if consts.IsPredixyClusterType(clusterType) { + return "predixy.conf" + } + return "" +} + +func (job *RedisDtsOnlineSwitch) getProxyBackends(clusterType, proxyIP string, proxyPort int, + proxyPassword string) (backends string, err error) { + if consts.IsTwemproxyClusterType(clusterType) { + return myredis.GetTwemproxyBackendsRaw(proxyIP, proxyPort) + } else if consts.IsPredixyClusterType(clusterType) { + return myredis.GetPredixyInfoServersRaw(proxyIP, proxyPort, proxyPassword) + } + return "", fmt.Errorf("invalid cluster type:%s", clusterType) +} + +// IsSrcProxyConnected 检查源proxy是否连接 +func (job *RedisDtsOnlineSwitch) IsSrcProxyConnected() (err error) { + localip, err := util.GetLocalIP() + if err != nil { + job.runtime.Logger.Error(err.Error()) + return err + } + ok, err := util.CheckIPBelongToLocalServer(job.params.SrcProxyIP) + if err != nil { + job.runtime.Logger.Error(fmt.Sprintf("CheckIPBelongToLocalServer failed,err:%+v", err)) + return err + } + if !ok { + job.runtime.Logger.Error(fmt.Sprintf("src proxy ip:%s not belong to local server(%s)", + job.params.SrcProxyIP, localip)) + return fmt.Errorf("src proxy ip:%s not belong to local server(%s)", job.params.SrcProxyIP, localip) + } + err = myredis.LocalRedisConnectTest(job.params.SrcProxyIP, []int{job.params.SrcProxyPort}, job.params.SrcProxyPassword) + if err != nil { + return err + } + return nil +} + +// IsSrcProxyConfigOK 检查源proxy配置是否ok +// - 配置文件是否存在 +// - 密码是否正确 +func (job *RedisDtsOnlineSwitch) IsSrcProxyConfigOK() (err error) { + var psCmd string + + if consts.IsTwemproxyClusterType(job.params.SrcClusterType) { + psCmd = fmt.Sprintf( + `ps aux|grep 'twemproxy'|grep -Ev 'grep|exporter'|grep %d|grep 'yml'|head -1|grep --only-match -P 'c .*.yml '|awk '{print $2}'`, + job.params.SrcProxyPort) + } else if consts.IsPredixyClusterType(job.params.SrcClusterType) { + psCmd = fmt.Sprintf(`ps aux|grep 'predixy'|grep -Ev 'grep|exporter'|grep %d|grep 'conf'|awk '{print $NF}'`, + job.params.SrcProxyPort) + } else { + err = fmt.Errorf("unknown src cluster type:%s", job.params.SrcClusterType) + job.runtime.Logger.Error(err.Error()) + return + } + job.runtime.Logger.Info(fmt.Sprintf("psCmd:%s", psCmd)) + job.srcProxyConfigFile, err = util.RunBashCmd(psCmd, "", nil, 30*time.Second) + if err != nil { + job.runtime.Logger.Error(fmt.Sprintf("psCmd(%s) fail,err:%+v", psCmd, err)) + return + } + if !util.FileExists(job.srcProxyConfigFile) { + err = fmt.Errorf("src proxy config file(%s) not exists", job.srcProxyConfigFile) + job.runtime.Logger.Error(err.Error()) + return + } + srcProxyBytes, err := os.ReadFile(job.srcProxyConfigFile) + if err != nil { + return err + } + srcProxyStr := string(srcProxyBytes) + if !strings.Contains(srcProxyStr, job.getSrcProxyAddr()) { + err = fmt.Errorf("src proxy config file(%s) not contains src proxy addr(%s)", job.srcProxyConfigFile, + job.getSrcProxyAddr()) + job.runtime.Logger.Error(err.Error()) + return + } + if !strings.Contains(srcProxyStr, job.params.SrcProxyPassword) { + err = fmt.Errorf("src proxy config file(%s) not contains correct password", job.srcProxyConfigFile) + job.runtime.Logger.Error(err.Error()) + return + } + job.srcProxyConfigSaveDir = filepath.Dir(job.srcProxyConfigFile) + return nil +} + +// BackupSrcProxyConfFile 备份源proxy配置文件 +func (job *RedisDtsOnlineSwitch) BackupSrcProxyConfFile() (err error) { + var msg string + bakFile := fmt.Sprintf("dts_bak_config.billid_%d.%s_%d.%s", + job.params.DtsBillID, job.params.SrcProxyIP, job.params.SrcProxyPort, + job.getSrcConfigFileSuffix()) + bakFile = filepath.Join(job.getSrcProxyConfigSaveDir(), bakFile) + if util.FileExists(bakFile) { + msg = fmt.Sprintf("bakFile(%s) already exists", bakFile) + job.runtime.Logger.Info(msg) + return + } + cpCmd := fmt.Sprintf("cp %s %s", job.srcProxyConfigFile, bakFile) + job.runtime.Logger.Info(fmt.Sprintf("cpCmd:%s", cpCmd)) + _, err = util.RunBashCmd(cpCmd, "", nil, 30*time.Second) + if err != nil { + job.runtime.Logger.Error(fmt.Sprintf("cpCmd(%s) fail,err:%+v", cpCmd, err)) + return + } + return nil +} + +// NewProxyConfigFileForSameType (类型不变时)生成新的proxy配置文件 +func (job *RedisDtsOnlineSwitch) NewProxyConfigFileForSameType() (err error) { + dstConfContent := job.params.DstProxyConfigContent + dstConfContent = strings.ReplaceAll(dstConfContent, job.getDstProxyAddr(), job.getSrcProxyAddr()) + job.runtime.Logger.Info(fmt.Sprintf("replace dstConfContent dstProxyAddr:%s => srcProxyAddr:%s", + job.getDstProxyAddr(), job.getDstProxyAddr())) + dstConfContent = strings.ReplaceAll(dstConfContent, "\\n", "\n") + if consts.IsTwemproxyClusterType(job.params.SrcClusterType) { + re := regexp.MustCompile(`\s\spassword\s*:\s*` + job.params.DstProxyPassword) + dstConfContent = re.ReplaceAllString(dstConfContent, " password: "+job.params.SrcProxyPassword) + dstConfContent = strings.ReplaceAll(dstConfContent, "hash_tag: {}", "hash_tag: '{}'") + } else if consts.IsPredixyClusterType(job.params.SrcClusterType) { + re := regexp.MustCompile(`Auth\s*"` + job.params.DstProxyPassword + `"`) + dstConfContent = re.ReplaceAllString(dstConfContent, `Auth "`+job.params.SrcProxyPassword+`"`) + } + + newFile := fmt.Sprintf("dts_new_config.billid_%d.%s_%d.%s", job.params.DtsBillID, job.params.SrcProxyIP, + job.params.SrcProxyPort, job.getSrcConfigFileSuffix()) + newFile = filepath.Join(job.getSrcProxyConfigSaveDir(), newFile) + err = os.WriteFile(newFile, []byte(dstConfContent), 0644) + if err != nil { + err = fmt.Errorf("write new proxy config file(%s) fail,err:%+v", newFile, err) + job.runtime.Logger.Error(err.Error()) + return + } + newFileMd5, err := util.GetFileMd5(newFile) + if err != nil { + job.runtime.Logger.Error(err.Error()) + return + } + currentMd5, err := util.GetFileMd5(job.srcProxyConfigFile) + if err != nil { + job.runtime.Logger.Error(err.Error()) + return + } + if newFileMd5 == currentMd5 { + job.runtime.Logger.Info(fmt.Sprintf("newFile(%s) md5(%s) equals currentFile(%s) md5(%s),no need to update", + newFile, newFileMd5, job.srcProxyConfigFile, currentMd5)) + return nil + } + mvCmd := fmt.Sprintf("mv %s %s", newFile, job.srcProxyConfigFile) + job.runtime.Logger.Info(fmt.Sprintf("mvCmd:%s", mvCmd)) + _, err = util.RunBashCmd(mvCmd, "", nil, 30*time.Second) + if err != nil { + return + } + return nil +} + +// NewProxyConfigFileForDiffType (类型变化时)生成新的proxy配置文件 +func (job *RedisDtsOnlineSwitch) NewProxyConfigFileForDiffType() (err error) { + saveDirForDstPort := job.getProxyConfigSaveDir(job.params.DstClusterType, job.params.DstProxyPort) + util.MkDirsIfNotExists([]string{saveDirForDstPort}) + dstConfContent := job.params.DstProxyConfigContent + // 先替换 ip,password,不替换 port + dstConfContent = strings.ReplaceAll(dstConfContent, job.params.DstProxyIP, job.params.SrcProxyIP) + dstConfContent = strings.ReplaceAll(dstConfContent, "\\n", "\n") + if consts.IsTwemproxyClusterType(job.params.DstClusterType) { + re := regexp.MustCompile(`\s\spassword\s*:\s*` + job.params.DstProxyPassword) + dstConfContent = re.ReplaceAllString(dstConfContent, " password: "+job.params.SrcProxyPassword) + dstConfContent = strings.ReplaceAll(dstConfContent, "hash_tag: {}", "hash_tag: '{}'") + } else if consts.IsPredixyClusterType(job.params.DstClusterType) { + re := regexp.MustCompile(`Auth\s*"` + job.params.DstProxyPassword + `"`) + dstConfContent = re.ReplaceAllString(dstConfContent, `Auth "`+job.params.SrcProxyPassword+`"`) + } + + proxyFileForDstPort := job.getProxyFile(job.params.DstClusterType, job.params.DstProxyPort) + proxyFileForDstPort = filepath.Join(saveDirForDstPort, proxyFileForDstPort) + err = os.WriteFile(proxyFileForDstPort, []byte(dstConfContent), 0644) + if err != nil { + err = fmt.Errorf("write new proxy config file(%s) fail,err:%+v", proxyFileForDstPort, err) + job.runtime.Logger.Error(err.Error()) + return + } + util.LocalDirChownMysql(saveDirForDstPort) + + // 重启 proxy(此时端口还是 dstProxyPort) + err = job.StopProxy(job.params.SrcProxyIP, job.params.DstProxyPort, + job.params.SrcProxyPassword, job.params.DstClusterType) + if err != nil { + return + } + err = job.StartProxy(job.params.SrcProxyIP, job.params.DstProxyPort, + job.params.SrcProxyPassword, job.params.DstClusterType) + if err != nil { + return + } + // 确定dstProxy backends包含 dstRedis + proxyBackends, err := job.getProxyBackends(job.params.DstClusterType, + job.params.SrcProxyIP, job.params.DstProxyPort, + job.params.SrcProxyPassword) + if err != nil { + return err + } + dstRedisAddr := fmt.Sprintf("%s:%d", job.params.DstRedisIP, job.params.DstRedisPort) + if !strings.Contains(proxyBackends, dstRedisAddr) { + err = fmt.Errorf("new proxy(%s:%d) backends(%s) not contains dst redis(%s)", + job.params.SrcProxyIP, job.params.DstProxyPort, proxyBackends, dstRedisAddr) + job.runtime.Logger.Error(err.Error()) + return err + } + job.runtime.Logger.Info(fmt.Sprintf("new proxy(%s:%d) backends contains dst redis(%s)", + job.params.SrcProxyIP, job.params.DstProxyPort, dstRedisAddr)) + + // 再次 stop proxy,修改端口 + err = job.StopProxy(job.params.SrcProxyIP, job.params.DstProxyPort, + job.params.SrcProxyPassword, job.params.DstClusterType) + if err != nil { + return + } + // 修改端口 + dstConfContent = strings.ReplaceAll(dstConfContent, + job.params.SrcProxyIP+":"+strconv.Itoa(job.params.DstProxyPort), + job.params.SrcProxyIP+":"+strconv.Itoa(job.params.SrcProxyPort), + ) + dstConfContent = strings.ReplaceAll(dstConfContent, + strconv.Itoa(job.params.DstProxyPort), + strconv.Itoa(job.params.SrcProxyPort)) + err = os.WriteFile(proxyFileForDstPort, []byte(dstConfContent), 0644) + if err != nil { + err = fmt.Errorf("write final proxy config file(%s) fail,err:%+v", proxyFileForDstPort, err) + job.runtime.Logger.Error(err.Error()) + return + } + // 下面执行逻辑,例如: + // mv /data/twemproxy-0.2.4/50100/ /data/twemproxy-0.2.4/50000/ + // cd /data/twemproxy-0.2.4/50000/ && mv nutcracker.50100.yml nutcracker.50000.yml + saveDirForSrcPort := job.getProxyConfigSaveDir(job.params.DstClusterType, job.params.SrcProxyPort) + if util.FileExists(saveDirForSrcPort) { + // 先删除 + rmCmd := fmt.Sprintf("rm -rf %s", saveDirForSrcPort) + job.runtime.Logger.Info(rmCmd) + util.RunBashCmd(rmCmd, "", nil, 30*time.Second) + } + mvCmd := fmt.Sprintf("mv %s %s", saveDirForDstPort, saveDirForSrcPort) + job.runtime.Logger.Info(mvCmd) + _, err = util.RunBashCmd(mvCmd, "", nil, 30*time.Second) + if err != nil { + err = fmt.Errorf("mv proxy config dir(%s) to dir(%s) fail,err:%+v", saveDirForDstPort, saveDirForSrcPort, err) + job.runtime.Logger.Error(err.Error()) + return + } + proxyFileForSrcPort := job.getProxyFile(job.params.DstClusterType, job.params.SrcProxyPort) + if filepath.Base(proxyFileForDstPort) != proxyFileForSrcPort { + mvCmd = fmt.Sprintf("cd %s && mv %s %s", saveDirForSrcPort, filepath.Base(proxyFileForDstPort), proxyFileForSrcPort) + job.runtime.Logger.Info(mvCmd) + _, err = util.RunBashCmd(mvCmd, "", nil, 30*time.Second) + if err != nil { + return + } + } + + util.LocalDirChownMysql(saveDirForSrcPort) + + return nil +} + +// IsProxyAlive 检查proxy是否存活 +func (job *RedisDtsOnlineSwitch) IsProxyAlive(proxyIP string, proxyPort int, proxyPassword string) (alive bool) { + var err error + alive, err = util.CheckPortIsInUse(proxyIP, strconv.Itoa(proxyPort)) + if !alive { + return alive + } + addr := fmt.Sprintf("%s:%d", proxyIP, proxyPort) + client, err := myredis.NewRedisClientWithTimeout(addr, proxyPassword, 0, consts.TendisTypeRedisInstance, 5*time.Second) + if err != nil { + return false + } + defer client.Close() + return true +} + +// StartProxy 启动proxy +func (job *RedisDtsOnlineSwitch) StartProxy(proxyIP string, proxyPort int, + proxyPassword string, clusterType string) (err error) { + if job.IsProxyAlive(proxyIP, proxyPort, proxyPassword) { + job.runtime.Logger.Info(fmt.Sprintf("proxy(%s:%d) already started,skip...", proxyIP, proxyPort)) + return nil + } + var cmd string + if consts.IsTwemproxyClusterType(clusterType) { + cmd = fmt.Sprintf("/usr/local/twemproxy/bin/start_nutcracker.sh %d", proxyPort) + } else if consts.IsPredixyClusterType(clusterType) { + cmd = fmt.Sprintf("/usr/local/predixy/bin/start_predixy.sh %d", proxyPort) + } + job.runtime.Logger.Info(fmt.Sprintf("start proxy(%s:%d) cmd:%s", proxyIP, proxyPort, cmd)) + startCmd := []string{"su", consts.MysqlAaccount, "-c", cmd} + maxRetryTimes := 5 + for maxRetryTimes >= 0 { + maxRetryTimes-- + err = nil + _, err = util.RunLocalCmd(startCmd[0], startCmd[1:], "", nil, 30*time.Second) + if err != nil { + job.runtime.Logger.Warn(fmt.Sprintf("start proxy(%s:%d) fail,err:%+v,retry...", proxyIP, proxyPort, err)) + time.Sleep(5 * time.Second) + continue + } + if !job.IsProxyAlive(proxyIP, proxyPort, proxyPassword) { + job.runtime.Logger.Warn(fmt.Sprintf("start proxy(%s:%d) fail,proxy not alive,retry...", proxyIP, proxyPort)) + time.Sleep(5 * time.Second) + continue + } + break + } + if err != nil { + job.runtime.Logger.Error(fmt.Sprintf("start proxy(%s:%d) fail,err:%+v, not retry", proxyIP, proxyPort, err)) + return + } + job.runtime.Logger.Info(fmt.Sprintf("start proxy(%s:%d) success", proxyIP, proxyPort)) + return nil +} + +// StopProxy 停止proxy +func (job *RedisDtsOnlineSwitch) StopProxy(proxyIP string, proxyPort int, + proxyPassword string, clusterType string) error { + var err error + if !job.IsProxyAlive(proxyIP, proxyPort, proxyPassword) { + job.runtime.Logger.Info(fmt.Sprintf("proxy(%s:%d) already stopped,skip...", proxyIP, proxyPort)) + return nil + } + var cmd1, killCmd string + if consts.IsTwemproxyClusterType(clusterType) { + cmd1 = fmt.Sprintf("/usr/local/twemproxy/bin/stop_nutcracker.sh %d", proxyPort) + killCmd = fmt.Sprintf( + `ps aux|grep 'twemproxy'|grep -Ev 'grep|exporter'|grep %d|head -1|awk '{print $2}'|xargs kill -9`, + proxyPort) + } else if consts.IsPredixyClusterType(clusterType) { + cmd1 = fmt.Sprintf("/usr/local/predixy/bin/stop_predixy.sh %d", proxyPort) + killCmd = fmt.Sprintf(`ps aux|grep 'predixy'|grep -Ev 'grep|exporter'|grep %d|awk '{print $2}'|xargs kill -9`, + proxyPort) + } + job.runtime.Logger.Info(fmt.Sprintf("stop proxy(%s:%d) cmd1:%s,cmd2:%s", proxyIP, proxyPort, cmd1, killCmd)) + stopCmd := []string{"su", consts.MysqlAaccount, "-c", cmd1} + util.RunLocalCmd(stopCmd[0], stopCmd[1:], "", nil, 30*time.Second) + if !job.IsProxyAlive(proxyIP, proxyPort, proxyPassword) { + job.runtime.Logger.Info(fmt.Sprintf("proxy(%s:%d) stop success", proxyIP, proxyPort)) + return nil + } + time.Sleep(2 * time.Second) + _, err = util.RunBashCmd(killCmd, "", nil, 30*time.Second) + if err != nil { + job.runtime.Logger.Error(fmt.Sprintf("proxy(%s:%d) stop fail,err:%+v", proxyIP, proxyPort, err)) + return err + } + if job.IsProxyAlive(proxyIP, proxyPort, proxyPassword) { + err = fmt.Errorf("proxy(%s:%d) stop fail,proxy still alive", proxyIP, proxyPort) + job.runtime.Logger.Error(err.Error()) + return err + } + job.runtime.Logger.Info(fmt.Sprintf("proxy(%s:%d) stop success", proxyIP, proxyPort)) + return nil +} + +// RestartProxyAndCheckBackends 重启proxy并检查后端是否正常 +func (job *RedisDtsOnlineSwitch) RestartProxyAndCheckBackends() (err error) { + err = job.StopProxy(job.params.SrcProxyIP, job.params.SrcProxyPort, + job.params.SrcProxyPassword, job.params.SrcClusterType) + if err != nil { + return + } + time.Sleep(2 * time.Second) + err = job.StartProxy(job.params.SrcProxyIP, job.params.SrcProxyPort, + job.params.SrcProxyPassword, job.params.DstClusterType) + if err != nil { + return err + } + proxyBackends, err := job.getProxyBackends(job.params.DstClusterType, + job.params.SrcProxyIP, job.params.SrcProxyPort, + job.params.SrcProxyPassword) + if err != nil { + return err + } + dstRedisAddr := fmt.Sprintf("%s:%d", job.params.DstRedisIP, job.params.DstRedisPort) + if !strings.Contains(proxyBackends, dstRedisAddr) { + err = fmt.Errorf("proxy(%s:%d) backends(%s) not contains dst redis(%s)", + job.params.SrcProxyIP, job.params.SrcProxyPort, proxyBackends, dstRedisAddr) + job.runtime.Logger.Error(err.Error()) + return err + } + job.runtime.Logger.Info(fmt.Sprintf("proxy(%s:%d) backends contains dst redis(%s)", + job.params.SrcProxyIP, job.params.SrcProxyPort, dstRedisAddr)) + return nil +} + +func (job *RedisDtsOnlineSwitch) getProxyRoleByClusterType(clusterType string) string { + if consts.IsTwemproxyClusterType(clusterType) { + return "twemproxy" + } else if consts.IsPredixyClusterType(clusterType) { + return "predixy" + } + return "" +} + +// RestartDbMon 如果集群类型有变化,修复dbmon 配置,重启 dbmon +func (job *RedisDtsOnlineSwitch) RestartDbMon() (err error) { + if job.params.SrcClusterType == job.params.DstClusterType { + job.runtime.Logger.Info("src cluster type equals dst cluster type,skip restart dbmon") + return nil + } + sedCmd := fmt.Sprintf(`sed -i 's/cluster_type: %s/cluster_type: %s/g' %s`, + job.params.SrcClusterType, job.params.DstClusterType, consts.BkDbmonConfFile) + job.runtime.Logger.Info(sedCmd) + _, err = util.RunBashCmd(sedCmd, "", nil, 30*time.Second) + if err != nil { + return + } + srcProxyRole := job.getProxyRoleByClusterType(job.params.SrcClusterType) + dstProxyRole := job.getProxyRoleByClusterType(job.params.DstClusterType) + if srcProxyRole != dstProxyRole { + sedCmd = fmt.Sprintf(`sed -i 's/meta_role: %s/meta_role: %s/g' %s`, + srcProxyRole, dstProxyRole, consts.BkDbmonConfFile) + job.runtime.Logger.Info(sedCmd) + _, err = util.RunBashCmd(sedCmd, "", nil, 30*time.Second) + if err != nil { + return + } + } + // 重启 dbmon + err = util.StopBkDbmon() + if err != nil { + return err + } + err = util.StartBkDbmon() + if err != nil { + return err + } + return nil +} + +// UntarDstProxyMedia 解压目标 proxy 介质 +func (job *RedisDtsOnlineSwitch) UntarDstProxyMedia() (err error) { + // 如果源集群、目标集群都是 twemproxy 类型 or 都是 predixy 类型,则不需要解压介质 s + if consts.IsTwemproxyClusterType(job.params.SrcClusterType) && + consts.IsTwemproxyClusterType(job.params.DstClusterType) { + job.runtime.Logger.Info("src cluster and dst cluster both twemproxy,skip untar media") + return + } + if consts.IsPredixyClusterType(job.params.SrcClusterType) && + consts.IsPredixyClusterType(job.params.DstClusterType) { + job.runtime.Logger.Info("src cluster and dst cluster both predixy,skip untar media") + return + } + err = job.params.DstProxyPkg.Check() + if err != nil { + job.runtime.Logger.Error(fmt.Sprintf("check dst proxy pkg fail,err:%+v", err)) + return err + } + pkgBaseName := job.params.DstProxyPkg.GePkgBaseName() + dstProxyBinDir := filepath.Join(consts.UsrLocal, pkgBaseName) + string(filepath.Separator) + _, err = os.Stat(dstProxyBinDir) + if err != nil && os.IsNotExist(err) { + // 如果包不存在,则解压到 /usr/local 下 + pkgAbsPath := job.params.DstProxyPkg.GetAbsolutePath() + tarCmd := fmt.Sprintf("tar -zxf %s -C %s", pkgAbsPath, consts.UsrLocal) + job.runtime.Logger.Info(tarCmd) + _, err = util.RunBashCmd(tarCmd, "", nil, 10*time.Second) + if err != nil { + return + } + util.LocalDirChownMysql(dstProxyBinDir) + } + var proxySoftLink string + if consts.IsTwemproxyClusterType(job.params.DstClusterType) { + proxySoftLink = filepath.Join(consts.UsrLocal, "twemproxy") + } else if consts.IsPredixyClusterType(job.params.DstClusterType) { + proxySoftLink = filepath.Join(consts.UsrLocal, "predixy") + } + var createSoftLink bool = false + var softLinkTarget string + _, err = os.Stat(proxySoftLink) + if err != nil && os.IsNotExist(err) { + // 如果软连接不存在,则创建软连接 + createSoftLink = true + } else { + // 如果软连接存在,则判断是否指向了正确的目录 + softLinkTarget, err = os.Readlink(proxySoftLink) + if err != nil { + job.runtime.Logger.Error(fmt.Sprintf("read soft link(%s) fail,err:%+v", proxySoftLink, err)) + return err + } + if softLinkTarget != dstProxyBinDir { + // 如果软连接指向的目录不正确,则删除软连接,重新创建 + createSoftLink = true + rmSoftLinkCmd := fmt.Sprintf("rm -f %s", proxySoftLink) + job.runtime.Logger.Info(rmSoftLinkCmd) + _, err = util.RunBashCmd(rmSoftLinkCmd, "", nil, 10*time.Second) + if err != nil { + return err + } + } + } + if createSoftLink { + // 创建软连接 + softLinkCmd := fmt.Sprintf("ln -s %s %s", dstProxyBinDir, proxySoftLink) + job.runtime.Logger.Info(softLinkCmd) + _, err = util.RunBashCmd(softLinkCmd, "", nil, 10*time.Second) + if err != nil { + return + } + job.runtime.Logger.Info(fmt.Sprintf("create soft link(%s) success", proxySoftLink)) + } + job.runtime.Logger.Info(fmt.Sprintf("soft link(%s) => %s", proxySoftLink, dstProxyBinDir)) + util.LocalDirChownMysql(proxySoftLink + string(filepath.Separator)) + + if consts.IsPredixyClusterType(job.params.DstClusterType) { + sedCmd := fmt.Sprintf("sed -i 's#/data#%s#g' %s", consts.GetRedisDataDir(), + filepath.Join(proxySoftLink, "bin", "start_predixy.sh")) + job.runtime.Logger.Info(sedCmd) + _, err = util.RunBashCmd(sedCmd, "", nil, 30*time.Second) + if err != nil { + return + } + } + return nil +} + +// MvSrcProxyCfgDirForDiffType move源 proxy 配置文件目录(如果源集群、目标集群类型不一致) +func (job *RedisDtsOnlineSwitch) MvSrcProxyCfgDirForDiffType() (err error) { + srcProxyConfDir := job.getSrcProxyConfigSaveDir() + parentDir := filepath.Dir(srcProxyConfDir) + + targetConfDir := fmt.Sprintf("dts_online_switch_bak.billid_%d.%d", job.params.DtsBillID, job.params.SrcProxyPort) + targetConfDir = filepath.Join(parentDir, targetConfDir) + _, err = os.Stat(targetConfDir) + if err == nil { + // 如果目标目录已经存在,则直接返回 + job.runtime.Logger.Info(fmt.Sprintf("target conf dir(%s) already exist,skip mv", targetConfDir)) + return nil + } + mvCmd := fmt.Sprintf("mv %s %s", srcProxyConfDir, targetConfDir) + job.runtime.Logger.Info(mvCmd) + _, err = util.RunBashCmd(mvCmd, "", nil, 10*time.Second) + if err != nil { + return + } + job.runtime.Logger.Info(fmt.Sprintf("mv src proxy conf dir(%s) to target dir(%s) success", + srcProxyConfDir, targetConfDir)) + return nil +} diff --git a/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/redis_remove_dts_server.go b/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/redis_remove_dts_server.go new file mode 100644 index 0000000000..dc70a79420 --- /dev/null +++ b/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/redis_remove_dts_server.go @@ -0,0 +1,147 @@ +package atomredis + +import ( + "encoding/json" + "fmt" + "path/filepath" + "time" + + "dbm-services/redis/db-tools/dbactuator/pkg/common" + "dbm-services/redis/db-tools/dbactuator/pkg/consts" + "dbm-services/redis/db-tools/dbactuator/pkg/jobruntime" + "dbm-services/redis/db-tools/dbactuator/pkg/util" + + "github.com/go-playground/validator/v10" +) + +// RedisRemoveDtsServerParams 删除dts_server参数 +type RedisRemoveDtsServerParams struct { + common.MediaPkg +} + +// RedisRemoveDtsServer 删除dts_server +type RedisRemoveDtsServer struct { + runtime *jobruntime.JobGenericRuntime + params *RedisRemoveDtsServerParams + DataDir string `json:"data_dir"` + RedisDtsDir string `json:"redis_dts_dir"` +} + +// 无实际作用,仅确保实现了 jobruntime.JobRunner 接口 +var _ jobruntime.JobRunner = (*RedisRemoveDtsServer)(nil) + +// NewRedisRemoveDtsServer new +func NewRedisRemoveDtsServer() jobruntime.JobRunner { + return &RedisRemoveDtsServer{} +} + +// Init 初始化 +func (job *RedisRemoveDtsServer) Init(m *jobruntime.JobGenericRuntime) error { + job.runtime = m + err := json.Unmarshal([]byte(job.runtime.PayloadDecoded), &job.params) + if err != nil { + job.runtime.Logger.Error(fmt.Sprintf("json.Unmarshal failed,err:%+v", err)) + return err + } + // 参数有效性检查 + validate := validator.New() + err = validate.Struct(job.params) + if err != nil { + if _, ok := err.(*validator.InvalidValidationError); ok { + job.runtime.Logger.Error("RedisRemoveDtsServer Init params validate failed,err:%v,params:%+v", + err, job.params) + return err + } + for _, err := range err.(validator.ValidationErrors) { + job.runtime.Logger.Error("RedisRemoveDtsServer Init params validate failed,err:%v,params:%+v", + err, job.params) + return err + } + } + return nil +} + +// Name 原子任务名 +func (job *RedisRemoveDtsServer) Name() string { + return "redis_remove_dts_server" +} + +// Run 执行 +func (job *RedisRemoveDtsServer) Run() (err error) { + err = job.getDataDir() + if err != nil { + return err + } + if !job.isDtsDirExists() { + job.runtime.Logger.Warn("dts_server dir(%s) not exists,skip", job.RedisDtsDir) + return nil + } + ok, err := job.ableToStopDtsServer() + if err != nil { + return err + } + if !ok { + err = fmt.Errorf("redis_dts_server other tasks are running,can not stop") + return + } + err = job.stopDtsServer() + return err +} + +func (job *RedisRemoveDtsServer) getDataDir() (err error) { + job.DataDir = filepath.Join(consts.GetRedisDataDir(), "dbbak") + util.MkDirsIfNotExists([]string{job.DataDir}) + util.LocalDirChownMysql(job.DataDir) + return nil +} + +func (job *RedisRemoveDtsServer) isDtsDirExists() bool { + pkgBaseName := job.params.GePkgBaseName() + job.RedisDtsDir = filepath.Join(job.DataDir, pkgBaseName) + return util.FileExists(job.RedisDtsDir) +} + +// ableToStopDtsServer 是否可以停止dts_server +func (job *RedisRemoveDtsServer) ableToStopDtsServer() (ok bool, err error) { + psCmd := + "ps aux|grep 'redis_dts'|grep -vE 'dbactuator|grep|./redis_dts_server|redis-sync|redis-shake' || { true; }" + job.runtime.Logger.Info(psCmd) + output, err := util.RunBashCmd(psCmd, "", nil, 10*time.Second) + if err != nil { + job.runtime.Logger.Error("check redis_dts_server process failed,err:%v", err) + return false, err + } + if output != "" { + job.runtime.Logger.Error("redis_dts other tasks are running,can not stop.details:\n%s\n", output) + return false, nil + } + return true, nil +} + +func (job *RedisRemoveDtsServer) stopDtsServer() (err error) { + job.runtime.Logger.Info("begin to stop redis_dts_server") + defer func() { + if err != nil { + job.runtime.Logger.Error("stop redis_dts_server fail") + } else { + job.runtime.Logger.Info("stop redis_dts_server success") + } + }() + stopCmd := fmt.Sprintf("cd %s && sh stop.sh", job.RedisDtsDir) + job.runtime.Logger.Info(stopCmd) + _, err = util.RunBashCmd(stopCmd, "", nil, 10*time.Second) + if err != nil { + return err + } + return nil +} + +// Retry times +func (job *RedisRemoveDtsServer) Retry() uint { + return 2 +} + +// Rollback rollback +func (job *RedisRemoveDtsServer) Rollback() error { + return nil +} diff --git a/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/redis_switch.go b/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/redis_switch.go index 87bbfb983b..3208a039b6 100644 --- a/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/redis_switch.go +++ b/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/redis_switch.go @@ -188,14 +188,17 @@ func (job *RedisSwitch) Run() (err error) { job.runtime.Logger.Info("redisswitch begin do storages switch .") // 执行切换, proxy并行,instance 窜行 for idx, storagePair := range job.params.SwitchRelation { - if job.params.SyncCondition.CanWriteBeforeSwitch { + if job.params.SyncCondition.CanWriteBeforeSwitch && + consts.IsTwemproxyClusterType(job.params.ClusterMeta.ClusterType) { if err := job.enableWrite4Slave(storagePair.SlaveInfo.IP, storagePair.SlaveInfo.Port, job.params.ClusterMeta.StoragePassword); err != nil { - job.runtime.Logger.Error("redisswitch slaveof no one failed when do %d:[%+v];with err:%+v", idx, storagePair, err) + job.runtime.Logger.Error("enable write before change 2 master %d:[%+v];with err:%+v", idx, storagePair, err) } } // 这里需要区分集群类型, 不同架构切换方式不一致 if consts.IsTwemproxyClusterType(job.params.ClusterMeta.ClusterType) { + job.runtime.Logger.Info("do switch of twemproxy arch %s:%s", + job.params.ClusterMeta.ImmuteDomain, job.params.ClusterMeta.ClusterType) if err := job.doTendisStorageSwitch4Twemproxy(storagePair); err != nil { job.runtime.Logger.Error("redisswitch switch failed when do %d:[%+v];with err:%+v", idx, storagePair, err) return err @@ -214,11 +217,6 @@ func (job *RedisSwitch) Run() (err error) { job.runtime.Logger.Error("redisswitch switch failed when do %d:[%+v];with err:%+v", idx, storagePair, err) return err } - if err := job.clusterForgetNode(storagePair); err != nil { - job.runtime.Logger.Error("redisswitch forget old node failed %d:[%+v];with err:%+v", idx, storagePair, err) - return err - } - // 验证切换成功 ? } else { job.runtime.Logger.Error("unsupported cluster type :%+v", job.params.ClusterMeta) } @@ -230,75 +228,10 @@ func (job *RedisSwitch) Run() (err error) { return nil } -// clusterForgetNode 为了将节点从群集中彻底删除,必须将 CLUSTER FORGET 命令发送到所有其余节点,无论他们是Master/Slave。 -// 不允许命令执行的特殊条件, 并在以下情况下返回错误: -// 1. 节点表中找不到指定的节点标识。 -// 2. 接收命令的节点是从属节点,并且指定的节点ID标识其当前主节点。 -// 3. 节点 ID 标识了我们发送命令的同一个节点。 - -// clusterForgetNode 返回值 -// 简单的字符串回复:OK如果命令执行成功,否则返回错误。 -// 我们有一个60秒的窗口来,所以这个函数必须在60s内执行完成 -func (job *RedisSwitch) clusterForgetNode(storagePair InstanceSwitchParam) error { - newMasterAddr := fmt.Sprintf("%s:%d", storagePair.SlaveInfo.IP, storagePair.SlaveInfo.Port) - newMasterConn, err := myredis.NewRedisClientWithTimeout(newMasterAddr, - job.params.ClusterMeta.StoragePassword, 1, job.params.ClusterMeta.ClusterType, time.Second) - if err != nil { - return err - } - defer newMasterConn.Close() - addrNodes, err := newMasterConn.GetAddrMapToNodes() - if err != nil { - return err - } - // 遍历集群节点信息, 拿到老的Master/Slave 节点ID - var ok bool - var willForgetNode *myredis.ClusterNodeData - var forgetNodeMasterID, forgetNodeSlaveID string - if willForgetNode, ok = addrNodes[fmt.Sprintf("%s:%d", - storagePair.MasterInfo.IP, storagePair.SlaveInfo.Port)]; !ok { - job.runtime.Logger.Warn("cluster old master not found by cluster %s:%d :%+v", - storagePair.MasterInfo.IP, storagePair.SlaveInfo.Port, addrNodes) - return nil - } - forgetNodeMasterID = willForgetNode.NodeID - for addr, nodeInfo := range addrNodes { - if nodeInfo.MasterID == forgetNodeMasterID { - forgetNodeSlaveID = nodeInfo.NodeID - job.runtime.Logger.Info("get myslave[%s] nodeID[%s]", addr, forgetNodeSlaveID) - break - } - job.runtime.Logger.Warn("got no slave for me [%s:%d]", storagePair.MasterInfo.IP, storagePair.SlaveInfo.Port) - } - - // 遍历集群中所有节点 - for addr := range addrNodes { - nodeConn, err := myredis.NewRedisClientWithTimeout(addr, - job.params.ClusterMeta.StoragePassword, 1, job.params.ClusterMeta.ClusterType, time.Second) - if err != nil { - return err - } - defer nodeConn.Close() - // var fgRst *redis.StatusCmd - // // (error) ERR:18,msg:forget node unkown 传了不存在的NodeID 1. 节点表中找不到指定的节点标识。 !!! 这里和官方版本返回错误不一致 !!! - // // (error) ERR:18,msg:I tried hard but I can't forget myself... 传了我自己进去 - // // (error) ERR:18,msg:Can't forget my master! 2. 接收命令的节点是从属节点,并且指定的节点ID标识其当前主节点。 - // if fgRst = nodeConn.InstanceClient.ClusterForget(context.TODO(), forgetNodeMasterID); fgRst.Err != nil { - - // } - - // if fgRst = nodeConn.InstanceClient.ClusterForget(context.TODO(), forgetNodeSlaveID); fgRst.Err != nil { - - // } - } - // GetClusterNodes - return nil -} - func (job *RedisSwitch) enableWrite4Slave(ip string, port int, pass string) error { newMasterAddr := fmt.Sprintf("%s:%d", ip, port) newMasterConn, err := myredis.NewRedisClientWithTimeout(newMasterAddr, - pass, 1, job.params.ClusterMeta.ClusterType, time.Second) + pass, 0, job.params.ClusterMeta.ClusterType, time.Second) if err != nil { return err } @@ -346,31 +279,40 @@ func (job *RedisSwitch) doTendisStorageSwitch4Cluster(storagePair InstanceSwitch return err } defer newMasterConn.Close() + // 该命令只能在群集slave节点执行,让slave节点进行一次人工故障切换。 if job.params.SyncCondition.SwitchOpt == "" { if rst := newMasterConn.InstanceClient.ClusterFailover(context.TODO()); rst.Err() != nil { - job.runtime.Logger.Error("exec cluster FAILOVER for %s failed:%+v", newMasterAddr, err) + job.runtime.Logger.Error("on [%s] exec %s failed:%+v", newMasterAddr, rst.String(), err) return err - } else if rst.String() != "OK" { - // 该命令已被接受并进行人工故障转移回复OK,切换操作无法执行(如发送命令的已经时master节点)时回复错误 - job.runtime.Logger.Error("exec cluster FAILOVER for %s failed:%s", newMasterAddr, rst.String()) - return fmt.Errorf("clusterfailover:%s", rst.String()) } } else { - if rst, err := newMasterConn.DoCommand([]string{"CLUSTER", "FAILOVER", + if _, err := newMasterConn.DoCommand([]string{"CLUSTER", "FAILOVER", job.params.SyncCondition.SwitchOpt}, 0); err != nil { job.runtime.Logger.Error("exec cluster FAILOVER %s for %s failed:%+v", newMasterAddr, job.params.SyncCondition.SwitchOpt, err) return err - } else if result, ok := rst.(string); ok { - if result != "OK" { - job.runtime.Logger.Error("exec cluster FAILOVER %s for %s failed:%s", - newMasterAddr, job.params.SyncCondition.SwitchOpt, result) - return fmt.Errorf("clusterfailover:%s", result) - } } } + time.Sleep(time.Second) // 预留足够的投票时间 + if infomap, err := newMasterConn.Info("replication"); err != nil { + job.runtime.Logger.Error("exec info replication for %s failed:%s", newMasterAddr, err) + return fmt.Errorf("ErrInfo:%s", err) + } else { + job.runtime.Logger.Info("on [%s] info replication : %+v", newMasterAddr, infomap) + if strings.ToUpper(infomap["role"]) == "SLAVE" { + job.runtime.Logger.Error("on [%s] exec cluster failover %s with no err, but role is ", + newMasterAddr, job.params.SyncCondition.SwitchOpt) + return fmt.Errorf("Err:SwitchFailed4Role") + } else { + job.runtime.Logger.Info("on [%s] exec cluster failover %s with no err, and role is ", + newMasterAddr, job.params.SyncCondition.SwitchOpt) + } + } + + clusterNodes, _ := newMasterConn.GetClusterNodes() + job.runtime.PipeContextData = clusterNodes job.runtime.Logger.Info("switch succ from:%s:%d to:%s:%d ^_^", storagePair.MasterInfo.IP, storagePair.MasterInfo.Port, storagePair.SlaveInfo.IP, storagePair.SlaveInfo.Port) @@ -526,7 +468,7 @@ func (job *RedisSwitch) precheckStorageSync() error { // oldMasterAddr := fmt.Sprintf("%s:%d", storagePair.MasterInfo.IP, storagePair.MasterInfo.Port) newMasterAddr := fmt.Sprintf("%s:%d", storagePair.SlaveInfo.IP, storagePair.SlaveInfo.Port) newMasterConn, err := myredis.NewRedisClientWithTimeout(newMasterAddr, - job.params.ClusterMeta.StoragePassword, 1, job.params.ClusterMeta.ClusterType, time.Second) + job.params.ClusterMeta.StoragePassword, 0, job.params.ClusterMeta.ClusterType, time.Second) if err != nil { job.errChan <- fmt.Errorf("[%s]new master node, err:%+v", newMasterAddr, err) return @@ -550,8 +492,10 @@ func (job *RedisSwitch) precheckStorageSync() error { job.errChan <- fmt.Errorf("[%s]new master node, bad role or link status", newMasterAddr) } - // 3. 检查监控写入心跳 master:PORT:time 时间差。 【重要!!!】 - job.errChan <- job.checkReplicationSync(newMasterConn, storagePair, replic) + if consts.IsTwemproxyClusterType(job.params.ClusterMeta.ClusterType) { + // 3. 检查监控写入心跳 master:PORT:time 时间差。 【重要!!!】 Twemproxy 架构才有,其他架构没有这个 + job.errChan <- job.checkReplicationSync(newMasterConn, storagePair, replic) + } // 4. 检查信息对等 ,slave 的master 是真实的master realMasterIP := replic["master_host"] @@ -575,7 +519,8 @@ func (job *RedisSwitch) precheckStorageSync() error { func (job *RedisSwitch) checkReplicationDetail( storagePair InstanceSwitchParam, realIP, realPort string) error { - if job.params.SyncCondition.InstanceSyncType == "mms" { + if job.params.SyncCondition.InstanceSyncType == "mms" || + job.params.SyncCondition.InstanceSyncType == "ms" { // check slave's master if storagePair.MasterInfo.IP != realIP && strconv.Itoa(storagePair.MasterInfo.Port) != realPort { return fmt.Errorf("err switch type [%s] new master's [%s:%d] real master [%s:%s] not eq inputed [%s:%d]", @@ -583,7 +528,7 @@ func (job *RedisSwitch) checkReplicationDetail( realIP, realPort, storagePair.MasterInfo.IP, storagePair.MasterInfo.Port) } // check master && slave version compactiable. - job.runtime.Logger.Info("[%s:%d] storage really had running confied master %s:%s in [mms] mode !", + job.runtime.Logger.Info("[%s:%d] storage really had running confied master %s:%s in [ms] mode !", storagePair.SlaveInfo.IP, storagePair.SlaveInfo.Port, realIP, realPort) } else if job.params.SyncCondition.InstanceSyncType == "msms" { oldSlaveConn, err := myredis.NewRedisClientWithTimeout(fmt.Sprintf("%s:%s", realIP, realPort), @@ -645,6 +590,9 @@ func (job *RedisSwitch) checkReplicationSync(newMasterConn *myredis.RedisClient, oldMasterAddr := fmt.Sprintf("%s:%d", storagePair.MasterInfo.IP, storagePair.MasterInfo.Port) newMasterAddr := fmt.Sprintf("%s:%d", storagePair.SlaveInfo.IP, storagePair.SlaveInfo.Port) + if err := newMasterConn.SelectDB(1); err != nil { + return fmt.Errorf("[%s] select db 1, exec cmd err:%+v", newMasterAddr, err) + } rst := newMasterConn.InstanceClient.Get(context.TODO(), fmt.Sprintf("%s:time", oldMasterAddr)) if rst.Err() != nil { return fmt.Errorf("[%s]new master node, exec cmd err:%+v", newMasterAddr, err) @@ -754,6 +702,10 @@ func (job *RedisSwitch) precheckLogin(addr, pass, clusterType string) error { defer rconn.Close() if _, err := rconn.DoCommand([]string{"TYPe", "key|for|dba|login|test"}, 0); err != nil { + if strings.Contains(fmt.Sprintf("%s", err), "MOVED") { + job.runtime.Logger.Warn("precheck for [redis login] got moved :%+v", err) + return nil + } return fmt.Errorf("do cmd failed %s:%+v", addr, err) } return nil diff --git a/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/tendisssd_dr_restore.go b/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/tendisssd_dr_restore.go index e8bcdbe52f..2bec394c00 100644 --- a/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/tendisssd_dr_restore.go +++ b/dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis/tendisssd_dr_restore.go @@ -357,15 +357,9 @@ func (task *TendisssdDrRestoreTask) Precheck() { // UnTarFullBackup 解压全备并检查 func (task *TendisssdDrRestoreTask) UnTarFullBackup() { - if len(task.BakTask.BackupFiles) != 1 { - task.Err = fmt.Errorf("master(%s) has %d backupFiles?? [%+v]", task.MasterAddr(), len(task.BakTask.BackupFiles), - task.BakTask.BackupFiles) - task.runtime.Logger.Error(task.Err.Error()) - return - } var localTarFile string var ret string - localTarFile, task.Err = util.UnionSplitFiles(task.TaskDir, task.BakTask.BackupFiles) + localTarFile, task.Err = util.UnionSplitFiles(task.TaskDir, []string{task.BakTask.BackupFile}) if task.Err != nil { task.runtime.Logger.Error(task.Err.Error()) return @@ -510,26 +504,6 @@ export LD_LIBRARY_PATH=LD_LIBRARY_PATH:%s return } - // 一些必要设置 - _, task.Err = task.SlaveCli.ConfigSet("masterauth", task.MasterAuth) - if task.Err != nil { - return - } - _, task.Err = task.SlaveCli.ConfigSet("is-master-snapshot", "1") - if task.Err != nil { - return - } - infoRet, task.Err = task.MasterCli.Info("server") - if task.Err != nil { - return - } - masterRunID := infoRet["run_id"] - task.runtime.Logger.Info("slave(%s) confxx set server-runid %s", task.SlaveAddr(), masterRunID) - _, task.Err = task.SlaveCli.ConfigSet("server-runid", masterRunID) - if task.Err != nil { - return - } - // 检查binlog范围ok slaveBinlogRange, task.Err = task.SlaveCli.TendisSSDBinlogSize() if task.Err != nil { @@ -555,6 +529,26 @@ export LD_LIBRARY_PATH=LD_LIBRARY_PATH:%s task.MasterAddr(), masterBinlogRange.String(), task.SlaveAddr(), slaveBinlogRange.String()) task.runtime.Logger.Info(msg) + // 一些必要设置 + _, task.Err = task.SlaveCli.ConfigSet("masterauth", task.MasterAuth) + if task.Err != nil { + return + } + _, task.Err = task.SlaveCli.ConfigSet("is-master-snapshot", "1") + if task.Err != nil { + return + } + infoRet, task.Err = task.MasterCli.Info("server") + if task.Err != nil { + return + } + masterRunID := infoRet["run_id"] + task.runtime.Logger.Info("slave(%s) confxx set server-runid %s", task.SlaveAddr(), masterRunID) + _, task.Err = task.SlaveCli.ConfigSet("server-runid", masterRunID) + if task.Err != nil { + return + } + // slaveof _, task.Err = task.SlaveCli.SlaveOf(task.MasterIP, strconv.Itoa(task.MasterPort)) if task.Err != nil { @@ -568,6 +562,11 @@ export LD_LIBRARY_PATH=LD_LIBRARY_PATH:%s return } + _, task.Err = task.SlaveCli.ConfigRewrite() + if task.Err != nil { + return + } + // 最多等待10分钟 maxRetryTimes := 120 var i int = 0 @@ -595,5 +594,6 @@ export LD_LIBRARY_PATH=LD_LIBRARY_PATH:%s // TendisSSDSetLougCount tendisSSD恢复log-count参数 func (task *TendisssdDrRestoreTask) TendisSSDSetLougCount() { task.MasterCli.ConfigSet("log-count", "200000") + task.MasterCli.ConfigSet("log-keep-count", "20000000") task.MasterCli.ConfigSet("slave-log-keep-count", "0") } diff --git a/dbm-services/redis/db-tools/dbactuator/pkg/backupsys/backupclient.go b/dbm-services/redis/db-tools/dbactuator/pkg/backupsys/backupclient.go new file mode 100644 index 0000000000..e2fc2db7bc --- /dev/null +++ b/dbm-services/redis/db-tools/dbactuator/pkg/backupsys/backupclient.go @@ -0,0 +1,203 @@ +package backupsys + +import ( + "bufio" + "encoding/json" + "fmt" + "strconv" + "strings" + "time" + + "dbm-services/common/go-pubpkg/backupclient" + "dbm-services/redis/db-tools/dbactuator/mylog" + "dbm-services/redis/db-tools/dbactuator/pkg/consts" + "dbm-services/redis/db-tools/dbactuator/pkg/util" +) + +// BackupClient TODO +type BackupClient interface { + Upload(fileName string) (string, error) + TaskStatus(taskId string) (int, string, error) +} + +// COSBackupClient 新备份系统 +type COSBackupClient struct { + backupClient *backupclient.BackupClient +} + +// NewCosBackupClient new +func NewCosBackupClient(toolPath string, authFile string, fileTag string) (*COSBackupClient, error) { + if backupClient, err := backupclient.New(toolPath, authFile, fileTag); err != nil { + mylog.Logger.Error(fmt.Sprintf("NewCosBackupClient failed,err:%v", err)) + return nil, err + } else { + return &COSBackupClient{backupClient: backupClient}, nil + } +} + +// Upload 上传文件 +func (o *COSBackupClient) Upload(fileName string) (taskId string, err error) { + taskId, err = o.backupClient.Upload(fileName) + if err != nil { + mylog.Logger.Error(fmt.Sprintf("CosBackupClient upload failed,err:%v", err)) + return + } + return +} + +// TaskStatus 备份任务状态 +func (o *COSBackupClient) TaskStatus(taskId string) (status int, statusMsg string, err error) { + status, statusMsg, err = o.backupClient.Query2(taskId) + if err != nil { + mylog.Logger.Error(fmt.Sprintf("CosBackupClient Query2 failed,err:%v", err)) + return + } + return +} + +// IBSBackupClient 旧备份系统 +type IBSBackupClient struct { + ToolPath string `json:"tool_path"` + FileTag string `json:"file_tag"` +} + +// NewIBSBackupClient new +func NewIBSBackupClient(toolPath, fileTag string) *IBSBackupClient { + return &IBSBackupClient{ToolPath: toolPath, FileTag: fileTag} +} + +// Upload 上传文件 +func (o *IBSBackupClient) Upload(fileName string) (taskId string, err error) { + bkCmd := fmt.Sprintf("%s -n -f %s --with-md5 -t %s 2>/dev/null|grep 'taskid'|awk -F: '{print $2}'", + o.ToolPath, fileName, o.FileTag) + mylog.Logger.Info(bkCmd) + taskId, err = util.RunBashCmd(bkCmd, "", nil, 10*time.Minute) + if err != nil { + return "", err + } + if taskId == "" { + err = fmt.Errorf("%s failed,taskId is empty", bkCmd) + mylog.Logger.Error(err.Error()) + return "", err + } + return taskId, nil +} + +// TaskStatus 备份任务状态 +func (o *IBSBackupClient) TaskStatus(taskId string) (status int, statusMsg string, err error) { + ret, err := o.GetTaskStatus(taskId) + if err != nil { + return + } + return ret.Status, ret.StatusInfo, nil +} + +// IBSTaskStatus backup_client -q --taskid=xxxx 命令的结果 +type IBSTaskStatus struct { + File string `json:"file"` + Host string `json:"host"` + SednupDateTime time.Time `json:"sendup_datetime"` + Status int `json:"status"` + StatusInfo string `json:"status_info"` + StartTime time.Time `json:"start_time"` + CompleteTime time.Time `json:"complete_time"` + ExpireTime time.Time `json:"expire_time"` +} + +// String 用于打印 +func (status *IBSTaskStatus) String() string { + statusBytes, _ := json.Marshal(status) + return string(statusBytes) +} + +// GetTaskStatus 获取备份任务状态 +func (o *IBSBackupClient) GetTaskStatus(taskId string) (status IBSTaskStatus, err error) { + var cmdRet string + bkCmd := fmt.Sprintf("%s -q --taskid=%s", o.ToolPath, taskId) + cmdRet, err = util.RunBashCmd(bkCmd, "", nil, 30*time.Second) + if err != nil { + return + } + scanner := bufio.NewScanner(strings.NewReader(cmdRet)) + scanner.Split(bufio.ScanLines) + for scanner.Scan() { + line := scanner.Text() + line = strings.TrimSpace(line) + if line == "" { + continue + } + l01 := strings.SplitN(line, ":", 2) + if len(l01) != 2 { + err = fmt.Errorf("len()!=2,cmd:%s,result format not correct:%s", bkCmd, cmdRet) + mylog.Logger.Error(err.Error()) + return + } + first := strings.TrimSpace(l01[0]) + second := strings.TrimSpace(l01[1]) + switch first { + case "file": + status.File = second + case "host": + status.Host = second + case "sendup datetime": + if second == "0000-00-00 00:00:00" { + status.SednupDateTime = time.Time{} // "0000-01-00 00:00:00" + break + } + status.SednupDateTime, err = time.ParseInLocation(consts.UnixtimeLayout, second, time.Local) + if err != nil { + err = fmt.Errorf("time.Parse 'sendup datetime' failed,err:%v,value:%s,cmd:%s", err, second, bkCmd) + mylog.Logger.Error(err.Error()) + return + } + case "status": + status.Status, err = strconv.Atoi(second) + if err != nil { + err = fmt.Errorf("strconv.Atoi failed,err:%v,value:%s,cmd:%s", err, second, bkCmd) + mylog.Logger.Error(err.Error()) + return + } + case "status info": + status.StatusInfo = second + case "start_time": + if second == "0000-00-00 00:00:00" { + status.StartTime = time.Time{} // "0000-01-00 00:00:00" + break + } + status.StartTime, err = time.ParseInLocation(consts.UnixtimeLayout, second, time.Local) + if err != nil { + err = fmt.Errorf("time.Parse start_time failed,err:%v,value:%s,cmd:%s", err, second, bkCmd) + mylog.Logger.Error(err.Error()) + return + } + case "complete_time": + if second == "0000-00-00 00:00:00" { + status.CompleteTime = time.Time{} // "0000-01-00 00:00:00" + break + } + status.CompleteTime, err = time.ParseInLocation(consts.UnixtimeLayout, second, time.Local) + if err != nil { + err = fmt.Errorf("time.Parse complete_time failed,err:%v,value:%s,cmd:%s", err, second, bkCmd) + mylog.Logger.Error(err.Error()) + return + } + case "expire_time": + if second == "0000-00-00 00:00:00" { + status.ExpireTime = time.Time{} // "0000-01-00 00:00:00" + break + } + status.ExpireTime, err = time.ParseInLocation(consts.UnixtimeLayout, second, time.Local) + if err != nil { + err = fmt.Errorf("time.Parse expire_time failed,err:%v,value:%s,cmd:%s", err, second, bkCmd) + mylog.Logger.Error(err.Error()) + return + } + } + } + if err = scanner.Err(); err != nil { + err = fmt.Errorf("scanner.Scan failed,err:%v,cmd:%s", err, cmdRet) + mylog.Logger.Error(err.Error()) + return + } + return +} diff --git a/dbm-services/redis/db-tools/dbactuator/pkg/backupsys/backupsys.go b/dbm-services/redis/db-tools/dbactuator/pkg/backupsys/backupsys.go index 73dfd43776..30a12f0e1b 100644 --- a/dbm-services/redis/db-tools/dbactuator/pkg/backupsys/backupsys.go +++ b/dbm-services/redis/db-tools/dbactuator/pkg/backupsys/backupsys.go @@ -1,231 +1,42 @@ // Package backupsys 备份系统 package backupsys -import ( - "bufio" - "encoding/json" - "fmt" - "strconv" - "strings" - "time" - - "dbm-services/redis/db-tools/dbactuator/mylog" - "dbm-services/redis/db-tools/dbactuator/pkg/consts" - "dbm-services/redis/db-tools/dbactuator/pkg/util" -) - -// UploadTask 操作备份系统 -type UploadTask struct { - Files []string `json:"files"` // 全路径 - TaskIDs []uint64 `json:"taskids"` - Tag string `json:"tag"` -} - -// UploadFiles 上传文件 -func (task *UploadTask) UploadFiles() (err error) { - var taskIDStr string - var taskIDNum uint64 - if len(task.Files) == 0 { - return - } - if task.Tag == "" { - err = fmt.Errorf("BackupSystem uploadFiles tag(%s) cannot be empty", task.Tag) - mylog.Logger.Error(err.Error()) - return - } - for _, file := range task.Files { - if !util.FileExists(file) { - err = fmt.Errorf("BackupSystem uploadFiles %s not exists", file) - mylog.Logger.Error(err.Error()) - return - } - } - for _, bkfile := range task.Files { - bkCmd := fmt.Sprintf("%s -n -f %s --with-md5 -t %s|grep 'taskid'|awk -F: '{print $2}'", - consts.BackupClient, bkfile, task.Tag) - mylog.Logger.Info(bkCmd) - taskIDStr, err = util.RunBashCmd(bkCmd, "", nil, 10*time.Minute) - if err != nil { - return - } - taskIDNum, err = strconv.ParseUint(taskIDStr, 10, 64) - if err != nil { - err = fmt.Errorf("%s ParseUint failed,err:%v", taskIDStr, err) - mylog.Logger.Error(err.Error()) - return - } - task.TaskIDs = append(task.TaskIDs, taskIDNum) - } - return -} - -// CheckTasksStatus 检查tasks状态 -func (task *UploadTask) CheckTasksStatus() (runningTaskIDs, failTaskIDs, succTaskIDs []uint64, - runningFiles, failedFiles, succFiles []string, failMsgs []string, err error) { - var status TaskStatus - for idx, taskID := range task.TaskIDs { - status, err = GetTaskStatus(taskID) - if err != nil { - return - } - if status.Status > 4 { - // err = fmt.Errorf("ToBackupSystem %s failed,err:%s,taskid:%d", - // status.File, status.StatusInfo, taskID) - // mylog.Logger.Error(err.Error()) - failMsgs = append(failMsgs, fmt.Sprintf("taskid:%d,failMsg:%s", taskID, status.StatusInfo)) - failedFiles = append(failedFiles, task.Files[idx]) - failTaskIDs = append(failTaskIDs, task.TaskIDs[idx]) - } else if status.Status == 4 { - succFiles = append(succFiles, task.Files[idx]) - succTaskIDs = append(succTaskIDs, task.TaskIDs[idx]) - } else if status.Status < 4 { - runningFiles = append(runningFiles, task.Files[idx]) - runningTaskIDs = append(runningTaskIDs, task.TaskIDs[idx]) - } - } - return -} - -// WaitForUploadFinish 等待所有files上传成功 -func (task *UploadTask) WaitForUploadFinish() (err error) { - var times int64 - var msg string - var runningFiles, failFiles, succFiles, failMsgs []string - for { - times++ - _, _, _, runningFiles, failFiles, succFiles, failMsgs, err = task.CheckTasksStatus() - if err != nil { - return - } - // 只要有running的task,则继续等待 - if len(runningFiles) > 0 { - if times%6 == 0 { - // 每分钟打印一次日志 - msg = fmt.Sprintf("files[%+v] cnt:%d upload to backupSystem still running", runningFiles, len(runningFiles)) - mylog.Logger.Info(msg) - } - time.Sleep(10 * time.Second) - continue - } - if len(failMsgs) > 0 { - err = fmt.Errorf("failCnt:%d,failFiles:[%+v],err:%s", len(failFiles), failFiles, strings.Join(failFiles, ",")) - mylog.Logger.Error(err.Error()) - return - } - if len(succFiles) == len(task.Files) { - return nil - } - break - } - return -} - -// TaskStatus backup_client -q --taskid=xxxx 命令的结果 -type TaskStatus struct { - File string `json:"file"` - Host string `json:"host"` - SednupDateTime time.Time `json:"sendup_datetime"` - Status int `json:"status"` - StatusInfo string `json:"status_info"` - StartTime time.Time `json:"start_time"` - CompleteTime time.Time `json:"complete_time"` - ExpireTime time.Time `json:"expire_time"` +// DstIPBackupUser TODO +var DstIPBackupUser string + +// DstIPBackupPassword TODO +var DstIPBackupPassword string + +// BackupURL TODO +var BackupURL string + +// BackupSysID TODO +var BackupSysID string + +// BackupKey TODO +var BackupKey string + +// IBSBaseInfo godoc +type IBSBaseInfo struct { + // 备份系统 api url 地址,会在后面拼接 /query /recover 后缀进行请求 + Url string `json:"url" validate:"required" example:"http://127.0.0.x/backupApi" env:"IBS_INFO_url" envDefault:"http://127.0.0.x/backupApi"` + // application标识,亦即哪个系统需要访问本接口,可从环境变量获取 IBS_INFO_sys_id + SysID string `json:"sys_id" validate:"required" env:"IBS_INFO_sys_id"` + // 16位字串,由备份系统分配,可从环境变量获取 IBS_INFO__key + Key string `json:"key" validate:"required" env:"IBS_INFO_key,unset"` + // OA验证的ticket,一个长串,通常附加在访问内网应用的URL上,主要用来验证用户身份,可以留空 + Ticket string `json:"ticket"` } -// String 用于打印 -func (status *TaskStatus) String() string { - statusBytes, _ := json.Marshal(status) - return string(statusBytes) +// SetUserPassword for rollback +func SetUserPassword(user, password string) { + DstIPBackupUser = user + DstIPBackupPassword = password } -// GetTaskStatus 执行backup_client -q --taskid=xxxx 命令的结果并解析 -func GetTaskStatus(taskid uint64) (status TaskStatus, err error) { - var cmdRet string - bkCmd := fmt.Sprintf("%s -q --taskid=%d", consts.BackupClient, taskid) - cmdRet, err = util.RunBashCmd(bkCmd, "", nil, 30*time.Second) - if err != nil { - return - } - scanner := bufio.NewScanner(strings.NewReader(cmdRet)) - scanner.Split(bufio.ScanLines) - for scanner.Scan() { - line := scanner.Text() - line = strings.TrimSpace(line) - if line == "" { - continue - } - l01 := strings.SplitN(line, ":", 2) - if len(l01) != 2 { - err = fmt.Errorf("len()!=2,cmd:%s,result format not correct:%s", bkCmd, cmdRet) - mylog.Logger.Error(err.Error()) - return - } - first := strings.TrimSpace(l01[0]) - second := strings.TrimSpace(l01[1]) - switch first { - case "file": - status.File = second - case "host": - status.Host = second - case "sendup datetime": - if second == "0000-00-00 00:00:00" { - status.SednupDateTime = time.Time{} // "0000-01-00 00:00:00" - break - } - status.SednupDateTime, err = time.ParseInLocation(consts.UnixtimeLayout, second, time.Local) - if err != nil { - err = fmt.Errorf("time.Parse 'sendup datetime' failed,err:%v,value:%s,cmd:%s", err, second, bkCmd) - mylog.Logger.Error(err.Error()) - return - } - case "status": - status.Status, err = strconv.Atoi(second) - if err != nil { - err = fmt.Errorf("strconv.Atoi failed,err:%v,value:%s,cmd:%s", err, second, bkCmd) - mylog.Logger.Error(err.Error()) - return - } - case "status info": - status.StatusInfo = second - case "start_time": - if second == "0000-00-00 00:00:00" { - status.StartTime = time.Time{} // "0000-01-00 00:00:00" - break - } - status.StartTime, err = time.ParseInLocation(consts.UnixtimeLayout, second, time.Local) - if err != nil { - err = fmt.Errorf("time.Parse start_time failed,err:%v,value:%s,cmd:%s", err, second, bkCmd) - mylog.Logger.Error(err.Error()) - return - } - case "complete_time": - if second == "0000-00-00 00:00:00" { - status.CompleteTime = time.Time{} // "0000-01-00 00:00:00" - break - } - status.CompleteTime, err = time.ParseInLocation(consts.UnixtimeLayout, second, time.Local) - if err != nil { - err = fmt.Errorf("time.Parse complete_time failed,err:%v,value:%s,cmd:%s", err, second, bkCmd) - mylog.Logger.Error(err.Error()) - return - } - case "expire_time": - if second == "0000-00-00 00:00:00" { - status.ExpireTime = time.Time{} // "0000-01-00 00:00:00" - break - } - status.ExpireTime, err = time.ParseInLocation(consts.UnixtimeLayout, second, time.Local) - if err != nil { - err = fmt.Errorf("time.Parse expire_time failed,err:%v,value:%s,cmd:%s", err, second, bkCmd) - mylog.Logger.Error(err.Error()) - return - } - } - } - if err = scanner.Err(); err != nil { - err = fmt.Errorf("scanner.Scan failed,err:%v,cmd:%s", err, cmdRet) - mylog.Logger.Error(err.Error()) - return - } - return +// SetIBSBaseInfo for backupsys +func SetIBSBaseInfo(url, sysId, key string) { + BackupURL = url + BackupSysID = sysId + BackupKey = key } diff --git a/dbm-services/redis/db-tools/dbactuator/pkg/backupsys/backupsys_query.go b/dbm-services/redis/db-tools/dbactuator/pkg/backupsys/backupsys_query.go new file mode 100644 index 0000000000..bce29cfa70 --- /dev/null +++ b/dbm-services/redis/db-tools/dbactuator/pkg/backupsys/backupsys_query.go @@ -0,0 +1,169 @@ +package backupsys + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + + "dbm-services/redis/db-tools/dbactuator/pkg/util" + + "dbm-services/redis/db-tools/dbactuator/mylog" + + "dbm-services/redis/db-tools/dbactuator/pkg/consts" +) + +// QueryReq 备份系统查询请求体 +type QueryReq struct { + Params `json:"params"` +} + +// Params 备份系统查询请求参数格式 +type Params struct { + Version string `json:"version"` + RequestInfo `json:"request_info"` +} + +// RequestInfo 备份系统查询请求信息 +type RequestInfo struct { + BaseInfo `json:"base_info"` + DetailInfo `json:"detail_info"` +} + +// BaseInfo 密钥信息 +type BaseInfo struct { + SysID string `json:"sys_id" validate:"required"` + Key string `json:"key" validate:"required"` + Ticket string `json:"ticket"` +} + +// DetailInfo 查询备份文件api的请求参数 +type DetailInfo struct { + // 备份源IP即提交备份任务的机器IP + SourceIP string `json:"source_ip" validate:"required"` + // 提交备份开始时间 如:"2022-12-05 00:00:01" + BeginDate string `json:"begin_date" validate:"required"` + //备份结束时间 如:"2022-12-06 00:00:53" 与begin_date形成一个时间范围,建议begin_date与end_date形成的时间范围不要超过3天 + EndDate string `json:"end_date" validate:"required"` + // 文件名 可为空 + FileName string `json:"filename"` + // 文件名是否支持通配符 默认"0" + // FileSpWildchar string `json:"file_sp_wildchar"` +} + +// QueryResult 备份系统查询结果 单条记录信息 备份系统查询ip特定时间段的备份文件结果记录详情 +type QueryResult struct { + TaskID string `json:"task_id"` //任务ID,用于拉取备份文件 + Uptime string `json:"uptime"` //备份任务上报时间 + FileLastMtime string `json:"file_last_mtime"` // 文件最后修改时间 + SourceIP string `json:"source_ip"` //备份源IP + Md5 string `json:"md5"` + Size string `json:"size"` + FileTag string `json:"file_tag"` //文件类型 REDIS_FULL、REDIS_BINLOG + Status string `json:"status"` + FileName string `json:"file_name"` + Pod string `json:"pod"` + Bkstif string `json:"bkstif"` // 备份状态信息 'done, success', 'Fail: bad md5' 等 + ExpireTime string `json:"expire_time"` + Expired string `json:"expired"` +} + +// QueryResp 查询备份文件的响应内容 +type QueryResp struct { + Code string `json:"code"` + Msg string `json:"msg"` + Detail []QueryResult `json:"detail"` + Num int `json:"num"` +} + +// QueryRespAbnormal 备份系统 api: 有结果时 detail 是一个 struct,无结果时 detail="" +// QueryResp 的补充 +type QueryRespAbnormal struct { + Code string `json:"code"` + Msg string `json:"msg"` + Detail string `json:"detail"` +} + +// QueryBackupFileResult 查询备份文件结果是否成功 +func (task *QueryResult) QueryBackupFileResult() bool { + return task.Status == consts.BackupTaskSuccess && task.Expired == consts.FileNotExpired +} + +// QueryFile 查询备份文件 +func (task *QueryReq) QueryFile(params QueryReq) (*QueryResp, error) { + err := util.ValidateStruct(params) + if err != nil { + mylog.Logger.Error("QueryFile:validate struct failed:%v", err) + return nil, err + } + respBody, err := task.Get(params) + if err != nil { + mylog.Logger.Error("QueryFile:query backup sys :%v", err) + return nil, err + } + mylog.Logger.Debug("QueryFile get respBody:%v", respBody) + + var resp QueryResp + err = json.Unmarshal(respBody, &resp) + if err != nil { + respAbn := QueryRespAbnormal{} + err := json.Unmarshal(respBody, &respAbn) + if err != nil { + mylog.Logger.Error("QueryFile:Unmarshal failed:%v", err) + return nil, err + } + resp.Code = respAbn.Code + resp.Msg = respAbn.Msg + + } + if resp.Code != "0" && resp.Code == "5" { + mylog.Logger.Warn("QueryFile: backup sys return resp code: %s,resp msg:%s", resp.Code, resp.Msg) + mylog.Logger.Warn("params:%v", params) + return nil, nil + } + if resp.Code != "0" { + mylog.Logger.Error("QueryFile: backup sys return resp code !=0") + return nil, err + } + + if resp.Detail != nil { + resp.Num = len(resp.Detail) + } + mylog.Logger.Debug("QueryFile:%v", resp) + return &resp, nil + +} + +// Get backuppai get file +func (task *QueryReq) Get(params QueryReq) ([]byte, error) { + + client := &http.Client{} + url := fmt.Sprintf(BackupURL + "/query") + jsonData, err := json.Marshal(params) + if err != nil { + mylog.Logger.Error("param marshal fail:%v", err) + } + req, err := http.NewRequest("GET", url, bytes.NewBuffer(jsonData)) + if err != nil { + mylog.Logger.Error("http request get fail:%v", err) + } + mylog.Logger.Info("http request get success") + req.Header.Set("Content-Type", "application/json") + + resp, err := client.Do(req) + if err != nil { + mylog.Logger.Error("http do request fail:%v", err) + } + defer resp.Body.Close() + respBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + mylog.Logger.Error("read resp body fail:%v", err) + } + + if !(resp.StatusCode >= 200 && resp.StatusCode < 300) { + mylog.Logger.Error("resp code %d", resp.StatusCode) + } + mylog.Logger.Info("GetFile from api success") + return respBody, nil +} diff --git a/dbm-services/redis/db-tools/dbactuator/pkg/backupsys/backupsys_recover.go b/dbm-services/redis/db-tools/dbactuator/pkg/backupsys/backupsys_recover.go new file mode 100644 index 0000000000..d05c096067 --- /dev/null +++ b/dbm-services/redis/db-tools/dbactuator/pkg/backupsys/backupsys_recover.go @@ -0,0 +1,175 @@ +package backupsys + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "time" + + "dbm-services/redis/db-tools/dbactuator/mylog" + "dbm-services/redis/db-tools/dbactuator/pkg/consts" + "dbm-services/redis/db-tools/dbactuator/pkg/util" +) + +// RecoverReq 备份系统下载请求体 +type RecoverReq struct { + RecoverParams `json:"params"` +} + +// RecoverParams 备份系统下载请求参数格式 +type RecoverParams struct { + Version string `json:"version"` + RecoverRequestInfo `json:"request_info"` +} + +// RecoverRequestInfo 备份系统查询请求信息 +type RecoverRequestInfo struct { + BaseInfo `json:"base_info"` + RecoverDetailInfo `json:"detail_info"` +} + +// RecoverDetailInfo 请求下载文件的参数 +type RecoverDetailInfo struct { + // taskid列表,该列表由根据备份查询接口返回的数据自由组合 + TaskidList string `json:"taskid_list,omitempty" example:"10000,100001"` + // 目标IP,文件恢复到哪一台机器上的,就是这台机器的IP + DestIP string `json:"dest_ip" validate:"required,ip" example:"x.x.x.x"` + // 登录 dest_ip 的用户名,下载后的文件属组是该用户 + LoginUser string `json:"login_user" validate:"required"` + // 登录 dest_ip 的用户名的密码, ieg 传统scp 方式下载才需要。如果是 cos 下载则不需要 + LoginPasswd string `json:"login_passwd,omitempty"` + // 文件恢复到哪个目录 + Directory string `json:"diretory" validate:"required" example:"/data/dbbak"` // diretory 是备份系统参数错误拼写 + // 恢复原因(备注用途) + Reason string `json:"reason"` +} + +// RecoverResp 拉取备份响应内容 +type RecoverResp struct { + Code string `json:"code"` + Msg string `json:"msg"` + // 恢复任务ID 用于查询拉取进度 + RecoverID string `json:"recoverid"` +} + +// WaitForRecoverFinish 根据 backup task_id 异步下载文件并等待完成 +func (task *RecoverReq) WaitForRecoverFinish(params RecoverReq, destIP string) (err error) { + // 异步下载备份文件 + recoverID, err := task.RecoverFile(params) + if err != nil { + mylog.Logger.Error("WaitForRecoverFinish:RecoverFile failed") + return err + } + mylog.Logger.Info("recoverID:%s", recoverID) + recoverQueryParams := RecoverQueryReq{ + RecoverQueryParams: RecoverQueryParams{ + Version: consts.BackupVersion, + RecoverQueryRequestInfo: RecoverQueryRequestInfo{ + BaseInfo: BaseInfo{ + SysID: BackupSysID, + Key: BackupKey, + Ticket: "", + }, + RecoverQueryDetailInfo: RecoverQueryDetailInfo{ + RecoverID: recoverID, + DestIP: destIP, + }, + }, + }, + } + retryTimeLimit := 180 + // 获取下载状态 + for { + if retryTimeLimit == 0 { + break + } + result, err := task.CheckRecoverTasksStatus(recoverQueryParams) + if err != nil { + if retryTimeLimit > 0 { + mylog.Logger.Warn("WaitForRecoverFinish:CheckRecoverTasksStatus info:%v", err) + retryTimeLimit-- + time.Sleep(10 * time.Second) + continue + } + mylog.Logger.Error("CheckRecoverTasksStatus fail recoverId:%s,retry times:%d,queryParams:%v,err:%v", + recoverID, retryTimeLimit, recoverQueryParams, err) + return err + } + mylog.Logger.Info("CheckRecoverTasksStatus finish") + if result.Fail > 0 { + err = fmt.Errorf("WaitForRecoverFinish:download result %s", result.Detail) + mylog.Logger.Error(err.Error()) + return err + } + mylog.Logger.Debug("WaitForRecoverFinish result:%v", result) + mylog.Logger.Info("WaitForRecoverFinish result") + break + } + return nil + +} + +// RecoverFile 拉取备份文件 根据 backup task_id 异步下载文件 +func (task *RecoverReq) RecoverFile(params RecoverReq) (string, error) { + err := util.ValidateStruct(params) + if err != nil { + mylog.Logger.Error("RecoverFile:validate struct failed:%v", err) + return "", err + } + var recoverID string = "0" + respBody, err := task.Post(params) + if err != nil { + mylog.Logger.Error("RecoverFile:recover backup sys :%v", err) + return recoverID, err + } + + var resp RecoverResp + err = json.Unmarshal(respBody, &resp) + if err != nil { + mylog.Logger.Error("RecoverFile:Unmarshal failed:%v", err) + return recoverID, err + } + if resp.Code != "0" { + mylog.Logger.Error("RecoverFile: backup sys return resp code !=0") + return recoverID, err + } + mylog.Logger.Info("RecoverFile return result:%v", resp) + recoverID = resp.RecoverID + return recoverID, nil + +} + +// Post backup api post +func (task *RecoverReq) Post(params RecoverReq) ([]byte, error) { + + client := &http.Client{} + url := fmt.Sprintf(BackupURL + "/recover") + jsonData, err := json.Marshal(params) + if err != nil { + mylog.Logger.Error("param marshal fail:%v", err) + } + req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData)) + if err != nil { + mylog.Logger.Error("http request get fail:%v", err) + } + mylog.Logger.Info("http request get success") + req.Header.Set("Content-Type", "application/json") + + resp, err := client.Do(req) + if err != nil { + mylog.Logger.Error("http do request fail:%v", err) + } + defer resp.Body.Close() + respBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + mylog.Logger.Error("read resp body fail:%v", err) + } + + if !(resp.StatusCode >= 200 && resp.StatusCode < 300) { + mylog.Logger.Error("resp code %d", resp.StatusCode) + } + mylog.Logger.Info("request api success") + return respBody, nil +} diff --git a/dbm-services/redis/db-tools/dbactuator/pkg/backupsys/backupsys_recover_query.go b/dbm-services/redis/db-tools/dbactuator/pkg/backupsys/backupsys_recover_query.go new file mode 100644 index 0000000000..5ff2d96cde --- /dev/null +++ b/dbm-services/redis/db-tools/dbactuator/pkg/backupsys/backupsys_recover_query.go @@ -0,0 +1,122 @@ +package backupsys + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "net/http" + + "dbm-services/redis/db-tools/dbactuator/mylog" + "dbm-services/redis/db-tools/dbactuator/pkg/util" +) + +// RecoverQueryReq 备份系统下载结果请求体 +type RecoverQueryReq struct { + RecoverQueryParams `json:"params"` +} + +// RecoverQueryParams 备份系统下载结果请求参数格式 +type RecoverQueryParams struct { + Version string `json:"version"` + RecoverQueryRequestInfo `json:"request_info"` +} + +// RecoverQueryRequestInfo 备份系统查询请求信息 +type RecoverQueryRequestInfo struct { + BaseInfo `json:"base_info"` + RecoverQueryDetailInfo `json:"detail_info"` +} + +// RecoverQueryDetailInfo 查询拉取进度的参数 +type RecoverQueryDetailInfo struct { + // 恢复任务ID 用于查询拉取进度 + RecoverID string `json:"recoverid"` + //目标IP,文件恢复到哪一台机器上的,就是这台机器的IP + DestIP string `json:"dest_ip"` +} + +// RecoverQueryResp 恢复文件下载任务的响应参数 +type RecoverQueryResp struct { + Code string `json:"code"` + Msg string `json:"msg"` + Todo int `json:"todo"` + Doing int `json:"doing"` + Success int `json:"success"` + Fail int `json:"fail"` + Detail []RecoverQueryDetail `json:"detail"` +} + +// RecoverQueryDetail 备份文件信息 +type RecoverQueryDetail struct { + TaskID string `json:"task_id"` + FileName string `json:"filename"` + Status string `json:"status"` + DestPath string `json:"dest_path"` + StatusEn string `json:"status_en"` +} + +// RecoverQueryPost backup api post +func (task *RecoverReq) RecoverQueryPost(params RecoverQueryReq) ([]byte, error) { + + client := &http.Client{} + url := fmt.Sprintf(BackupURL + "/get_recover_result") + jsonData, err := json.Marshal(params) + if err != nil { + mylog.Logger.Error("param marshal fail:%v", err) + } + req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData)) + if err != nil { + mylog.Logger.Error("http request get fail:%v", err) + } + mylog.Logger.Info("http request get success") + + req.Header.Set("Content-Type", "application/json") + resp, err := client.Do(req) + if err != nil { + mylog.Logger.Error("http do request fail:%v", err) + } + mylog.Logger.Debug("RecoverQueryPost:resp:%v", resp) + defer resp.Body.Close() + respBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + mylog.Logger.Error("read resp body fail:%v", err) + } + + if !(resp.StatusCode >= 200 && resp.StatusCode < 300) { + mylog.Logger.Error("resp code %d", resp.StatusCode) + } + mylog.Logger.Debug("RecoverQueryPost:respBody:%s", respBody) + mylog.Logger.Info("request api success") + return respBody, nil +} + +// CheckRecoverTasksStatus 查询备份拉取状态 +func (task *RecoverReq) CheckRecoverTasksStatus(params RecoverQueryReq) (*RecoverQueryResp, error) { + err := util.ValidateStruct(params) + if err != nil { + mylog.Logger.Error("CheckRecoverTasksStatus:validate struct failed:%v", err) + return nil, err + } + result, err := task.RecoverQueryPost(params) + if err != nil { + mylog.Logger.Error("CheckRecoverTasksStatus:RecoverQueryPost failed:%v", err) + return nil, err + } + mylog.Logger.Debug("CheckRecoverTasksStatus:result:%s", result) + var resp RecoverQueryResp + err = json.Unmarshal(result, &resp) + if err != nil { + mylog.Logger.Error("CheckRecoverTasksStatus:json Unmarshal failed:%v", err) + return nil, err + } + if resp.Code != "0" { + mylog.Logger.Warn("CheckRecoverTasksStatus: resp return code != 0") + return nil, errors.New(resp.Msg) + } + mylog.Logger.Debug("CheckRecoverTasksStatus success resp:%v", resp) + mylog.Logger.Info("CheckRecoverTasksStatus success ") + return &resp, nil + +} diff --git a/dbm-services/redis/db-tools/dbactuator/pkg/backupsys/fullback.go b/dbm-services/redis/db-tools/dbactuator/pkg/backupsys/fullback.go new file mode 100644 index 0000000000..7f459d893c --- /dev/null +++ b/dbm-services/redis/db-tools/dbactuator/pkg/backupsys/fullback.go @@ -0,0 +1,1695 @@ +package backupsys + +import ( + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "sort" + "strconv" + "strings" + "time" + + "dbm-services/redis/db-tools/dbactuator/models/myredis" + "dbm-services/redis/db-tools/dbactuator/mylog" + "dbm-services/redis/db-tools/dbactuator/pkg/consts" + "dbm-services/redis/db-tools/dbactuator/pkg/customtime" + "dbm-services/redis/db-tools/dbactuator/pkg/util" +) + +// TplusFullBackItem 全备信息项 +type TplusFullBackItem struct { + Incr int `json:"incr"` + NodeIP string `json:"node_ip"` + FileName string `json:"filename"` + BackupFile string `json:"backup_file"` + //DecpDir: 全备解压目录 + //如全备名是 3-TENDISPLUS-FULL-slave-127.0.0.x-30002-20230810-050140.tar + //则值为 3-TENDISPLUS-FULL-slave-127.0.0.x-30002-20230810-050140 + DecpDir string `json:"decpDir"` + ClusterMeataDir string `json:"decompressedFile"` //解压后,全备clusterMeta所在目录 + BackupTaskid int64 `json:"backup_taskid"` + BackupSize int64 `json:"backup_size"` + SplitFileIdx int `json:"split_file_idx"` + BackupStart customtime.CustomTime `json:"backup_start"` //备份开始时间,从备份文件名中获取 + BackupEnd customtime.CustomTime `json:"backup_end"` //备份结束时间 + StartPos uint64 `json:"startPos"` //ssd 回档导入增备时,--start-position + +} + +// TplusFullBackPull 拉取全备 +type TplusFullBackPull struct { + FileHead string `json:"fileHead"` // 文件正则匹配过滤查询 + SourceIP string `json:"sourceIp"` + + //保存备份文件的本地目录 :传入的 task.RecoverDir + SaveDir string `json:"saveDir"` + SaveHost string `json:"saveHost"` + RollbackDstTime time.Time `json:"rollbackDstTime"` //回档目标时间,拉取离这个时间点最近的备份 + //2005000194-TENDISPLUS-FULL-slave-127.0.0.x-30000-20230311-214752.split.000 + //2005000194-TENDISPLUS-FULL-slave-127.0.0.x-30000-20230311-214752.split.001 + //2005000194-TENDISPLUS-FULL-slave-127.0.0.x-30000-20230311-214752.split.002 + //key: 0 1 2 ... 文件分割后缀 + //value: 文件对应的fullbackup项 + ResultFullbackup []*TplusFullBackItem `json:"resultFullbackup"` + //key: fullbackup file name -> 去重 + //value: 文件对应的fullbackup项 + ResultFullbackMap map[string]*TplusFullBackItem `json:"resultFullbackMap"` + + //全备文件去掉后缀 + LocalFullBackupDir string `json:"local_full_backup_dir"` + Err error `json:"-"` //错误信息 + query QueryReq //备份系统查询 + recover RecoverReq //备份系统恢复 + KvstoreNums int + TendisType string `json:"tendis_type"` +} + +// NewFullbackPull 新建全备拉取任务 +func NewFullbackPull(sourceIP, filehead, rollbackTime, + saveHost, saveDir string, kvstoreNums int, tendisType string) (ret *TplusFullBackPull) { + ret = &TplusFullBackPull{ + SourceIP: sourceIP, + FileHead: filehead, + SaveHost: saveHost, + SaveDir: saveDir, + KvstoreNums: kvstoreNums, + TendisType: tendisType, + } + layout := "2006-01-02 15:04:05" + ret.RollbackDstTime, ret.Err = time.ParseInLocation(layout, rollbackTime, time.Local) + if ret.Err != nil { + ret.Err = fmt.Errorf("rollbackTime:%s time.parese fail,err:%s,layout:%s", rollbackTime, ret.Err, layout) + mylog.Logger.Error(ret.Err.Error()) + return ret + } + if ret.RollbackDstTime.After(time.Now()) == true { + //未来时间 + ret.Err = fmt.Errorf("rollbackTime:%s > time.Now()", ret.RollbackDstTime) + mylog.Logger.Error(ret.Err.Error()) + return ret + } + ret.ResultFullbackup = []*TplusFullBackItem{} + ret.ResultFullbackMap = make(map[string]*TplusFullBackItem) + mylog.Logger.Info("FileHead:%s", ret.FileHead) + return +} + +// LastNDaysFullBack 需要最近N天的全备 +func LastNDaysFullBack() int { + lastNDays := 2 + return lastNDays +} + +// GetFullFilesSpecTimeRange 获取某个正则指定时间范围内的所有全备文件 +// NOCC:golint/fnsize(设计如此) +func (full *TplusFullBackPull) GetFullFilesSpecTimeRange() (backs []*TplusFullBackItem) { + mylog.Logger.Info("GetFullFilesSpecTimeRange start ... ") + // layout := "2006-01-02 15:04:05" + // 查询范围:回档时间<->回档时间往前48小时 + h, _ := time.ParseDuration("-48h") + beginData := full.RollbackDstTime.Add(h).Format(consts.UnixtimeLayout) + endDate := full.RollbackDstTime.Format(consts.UnixtimeLayout) + mylog.Logger.Info("fileName 正则匹配:%s", full.FileHead) + + params := QueryReq{ + Params: Params{ + Version: "1.0", + RequestInfo: RequestInfo{ + BaseInfo: BaseInfo{ + SysID: BackupSysID, + Key: BackupKey, + Ticket: "", + }, + DetailInfo: DetailInfo{ + SourceIP: full.SourceIP, + BeginDate: beginData, + EndDate: endDate, + FileName: full.FileHead, + // FileSpWildchar: "0", + }, + }, + }, + } + resp, err := full.query.QueryFile(params) + if err != nil { + full.Err = fmt.Errorf("QueryFile fail,err:%v,params:%s", err, params) + mylog.Logger.Error(full.Err.Error()) + return + } + if resp == nil { + return + } + + mylog.Logger.Debug("resp.Detail:%v", resp.Detail) + mylog.Logger.Info("resp.Detail:%v", resp.Detail) + mylog.Logger.Info("GetFullFilesSpecTimeRange query resp num :%v ,query info :ip:%v,beginDate:%v,endDate:%v", + resp.Num, full.SourceIP, beginData, endDate) + mylog.Logger.Debug("query info :ip:%v,beginDate:%v,endDate:%v", full.SourceIP, beginData, endDate) + + mylog.Logger.Info("正则匹配:%s 共获取到%d个全备信息", full.FileHead, resp.Num) + + // 如没有切割的备份文件: + // 2005000194-TENDISPLUS-FULL-slave-127.0.0.x-30008-20230323-214618.tar + // 2005000191-TENDISSSD-FULL-slave-127.0.0.x-30000-20230418-050000-227999.tar + // 有切割的备份文件: + // 2005000194-TENDISPLUS-FULL-slave-127.0.0.x-30008-20230326-131129.split.000 + // 2005000194-TENDISPLUS-FULL-slave-127.0.0.x-30008-20230326-131129.split.001 + // 2005000194-TENDISPLUS-FULL-slave-127.0.0.x-30003-20230629-130151.tar + // 获得最后的: 20230326-131129 + // tendisplus + var lastDateReg *regexp.Regexp + var lastDateReg1 *regexp.Regexp + if full.TendisType == consts.TendisTypeTendisplusInsance { + lastDateReg = regexp.MustCompile(`^.*?(\d+-\d+).tar`) + lastDateReg1 = regexp.MustCompile(`^.*?(\d+-\d+).split.(\d+)`) + + } else if full.TendisType == consts.TendisTypeTendisSSDInsance { + // tendis ssd + lastDateReg = regexp.MustCompile(`^.*?(\d+-\d+)-(\d+).tar`) + lastDateReg1 = regexp.MustCompile(`^.*?(\d+-\d+-\d+).split.(\d+)`) + } else { + // tendis cache (cache 这里没有做文件分割) + // 2005000194-redis-master-127.0.0.x-30000-20230426-210004.rdb + // 2005000194-redis-slave-127.0.0.x-30000-20230508-130108.aof.zst + // rdb 文件不会压缩,因为redis本身有做压缩 + // aof 文件会压缩,需要解压 + lastDateReg = regexp.MustCompile(`^.*?(\d+-\d+).aof.zst`) + lastDateReg1 = regexp.MustCompile(`^.*?(\d+-\d+).rdb`) + } + + layout1 := "20060102-150405" + for _, str01 := range resp.Detail { + back01 := &TplusFullBackItem{} + taskID, _ := strconv.Atoi(str01.TaskID) + size, _ := strconv.Atoi(str01.Size) + if taskID < 0 || size < 0 { + //backup_taskid 小于0 或backup_size 小于0的备份,是无效备份 + msg := fmt.Sprintf("filename:%s fullbackup:%s backupTaskid:%s<0 backupSize:%s<0 is invalid,skip...", + full.FileHead, str01.FileName, str01.TaskID, str01.Size) + mylog.Logger.Info(msg) + continue + } + + back01.BackupTaskid, _ = strconv.ParseInt(str01.TaskID, 10, 64) + back01.BackupSize, _ = strconv.ParseInt(str01.Size, 10, 64) + mylog.Logger.Info("back01.BackupSize:%d", back01.BackupSize) + back01.BackupFile = str01.FileName + back01.NodeIP = str01.SourceIP + mylog.Logger.Debug("str01.FileName:%s", str01.FileName) + match01 := lastDateReg.FindStringSubmatch(str01.FileName) + mylog.Logger.Debug("match01:%s", match01) + + //备份文件有切割 + if match01 == nil { + match01 = lastDateReg1.FindStringSubmatch(str01.FileName) + if full.TendisType == consts.TendisTypeTendisSSDInsance || full.TendisType == consts.TendisTypeTendisplusInsance { + back01.SplitFileIdx, _ = strconv.Atoi(match01[2]) + } + } + + if len(match01) < 2 { + err = fmt.Errorf("filename:%s backup:%s format not correct,cann't find createTime", full.FileHead, str01) + mylog.Logger.Error(err.Error()) + full.Err = err + return + } + bkCreateTime, err01 := time.ParseInLocation(layout1, match01[1], time.Local) + + // 3-TENDISPLUS-FULL-slave-127.0.0.x-30002-20230810-050140.tar + // 3-TENDISSSD-FULL-slave-127.0.0.x-30000-20230809-105510-52447.tar + if full.TendisType == consts.TendisTypeTendisSSDInsance { + back01.StartPos, _ = strconv.ParseUint(match01[2], 10, 64) + mylog.Logger.Info("全备文件名中获取的 StartPos:%d,文件:%s", back01.StartPos, back01.BackupFile) + } + + if err01 != nil { + full.Err = fmt.Errorf("backup file createTime:%s time.parese fail,err:%s,layout1:%s", match01[1], err01, layout1) + mylog.Logger.Error(full.Err.Error()) + return + } + back01.BackupStart.Time = bkCreateTime + back01.FileName = str01.FileName + backs = append(backs, back01) + } + mylog.Logger.Info("GetFullFilesSpecTimeRange backs[0]:%v", backs[0]) + return +} + +// GetTplusFullbackNearestRkTime 获取Trendis最靠近 回档目的时间 的全备 +func (full *TplusFullBackPull) GetTplusFullbackNearestRkTime() { + mylog.Logger.Info("GetTplusFullbackNearestRkTime start ... ") + //从备份列表中选择最靠近 回档目标时间 的备份 + var nearestFullbk *TplusFullBackItem = nil + + backs := full.GetFullFilesSpecTimeRange() + if full.Err != nil { + err := fmt.Errorf("GetFullFilesSpecTimeRange failed :%v", full.Err) + mylog.Logger.Error(err.Error()) + full.Err = err + return + } + mylog.Logger.Info("GetFullFilesSpecTimeRange finish ") + // 1、先找到一个最靠近 回档目标时间 的备份文件 + for _, bk01 := range backs { + bkItem := bk01 + //只需要那些BackupStart 小于等于 回档目标时间 的备份 + if bkItem.BackupStart.Before(full.RollbackDstTime) == true || + bkItem.BackupStart.Time == full.RollbackDstTime { + if nearestFullbk == nil { + //第一次找到 开始时间 小于 回档目标时间的备份 + nearestFullbk = bkItem + } else { + // nearestFullbk.BackupStart < 该备份BackupStart <= full.RollbackDstTime + if bkItem.BackupStart.After(nearestFullbk.BackupStart.Time) == true { + nearestFullbk = bkItem + } + } + } + } + if nearestFullbk != nil { + //已经找到 + mylog.Logger.Info("GetTplusFullbackNearestRkTime filename 正则:%s nearestFullbk:%v", full.FileHead, nearestFullbk) + // 2、再查找同时段的其他分割文件,怎么判断分割文件是否完备呢? -》 isGetAllFileInfo + for _, bk01 := range backs { + bkItem := bk01 + //找到同一时间的分割文件 + if bkItem.BackupStart.Time == nearestFullbk.BackupStart.Time { + if _, ok := full.ResultFullbackMap[bkItem.BackupFile]; ok == true { + //去重 + return + } + full.ResultFullbackMap[bkItem.BackupFile] = bkItem + full.ResultFullbackup = append(full.ResultFullbackup, bkItem) + + } + } + layout := "2006-01-02 15:04:05" + if nearestFullbk == nil { + full.Err = fmt.Errorf("filename 正则:%s 最近%d天内没找到小于回档目标时间[%s]的全备", + full.FileHead, LastNDaysFullBack(), full.RollbackDstTime.Local().Format(layout)) + mylog.Logger.Error(full.Err.Error()) + return + } + msg := fmt.Sprintf("找到距离回档目标时间:%s最近的全备:%s", + full.RollbackDstTime, nearestFullbk.BackupFile) + mylog.Logger.Info(msg) + + //按照splitfile index排序 + sort.Slice(full.ResultFullbackup, func(i, j int) bool { + return full.ResultFullbackup[i].SplitFileIdx < full.ResultFullbackup[j].SplitFileIdx + }) + // 校验获取到的分割/tar文件信息:检查是否完整 + full.isGetAllFileInfo() + if full.Err != nil { + mylog.Logger.Error("isGetAllFileInfo failed,err:%v", full.Err.Error()) + return + } + return + } + layout := "2006-01-02 15:04:05" + full.Err = fmt.Errorf("GetTplusFullbackNearestRkTime: filename 正则:%s 最近%d天内没找到小于回档目标时间[%s]的全备", + full.FileHead, LastNDaysFullBack(), full.RollbackDstTime.Local().Format(layout)) + mylog.Logger.Error(full.Err.Error()) + + return +} + +/* +判断split文件是否连续,是否重复; +- 重复则报错; +- 不连续则返回缺失的split index,如 2,3,5,8 则返回缺失的4,6,7 +*/ +func getNotReadySplitFile(resultBackItem []*TplusFullBackItem) ([]string, error) { + var ret []string + var err error + prelistIdx := 000 + preSplitFileIdx := resultBackItem[0].SplitFileIdx + for idx, item := range resultBackItem { + if idx == prelistIdx { + // 第一个元素忽略 + continue + } + if item.SplitFileIdx <= preSplitFileIdx { + //如果后面的split index小于等于前一个 index + err = fmt.Errorf("当前split index:%d <= 前一个split index:%d", item.SplitFileIdx, preSplitFileIdx) + mylog.Logger.Error(err.Error()) + mylog.Logger.Error(err.Error()) + return ret, err + } + if item.SplitFileIdx == preSplitFileIdx+1 { + // 递增符合预期 + preSplitFileIdx = item.SplitFileIdx + continue + } + preSplitFileIdx++ + for preSplitFileIdx < item.SplitFileIdx { + ret = append(ret, fmt.Sprintf("%d", preSplitFileIdx)) + preSplitFileIdx++ + } + + } + return ret, nil +} + +/* +判断所需split/tar file文件信息是否已全部获取到; +- ResultFullbackup 第二个split文件序号 必须 只比 第一个split 文件序号大1 +- ResultFullbackup 倒数第一个split文件序号 必须 只比 倒数第二个split文件序号大 1 +- 最后一个文件序号 减去 第一个文件序号 等于 len(ResultFullbackup)+1 +- ResultFullbackup split.BackupStart都相等 +*/ +func (full *TplusFullBackPull) isGetAllFileInfo() (ret bool) { + mylog.Logger.Info("isGetAllFileInfo start ...") + cnt := len(full.ResultFullbackup) + mylog.Logger.Info("ResultFullbackup len:%d", cnt) + // 没有分割,文件小于8G + if cnt == 1 { + for _, bk01 := range full.ResultFullbackup { + mylog.Logger.Info(bk01.BackupFile) + msg := fmt.Sprintf(`filename:%s 找到所有[%s~%s]时间段的tar文件,共%d个:%s`, + full.FileHead, + full.ResultFullbackup[0].BackupStart, + full.ResultFullbackup[0].BackupEnd, + cnt, + bk01.BackupFile, + ) + mylog.Logger.Info(msg) + return + + } + } + firstSplit := full.ResultFullbackup[0] + secondSplit := full.ResultFullbackup[1] + + lastSplit := full.ResultFullbackup[cnt-1] + beforeLastSplit := full.ResultFullbackup[cnt-2] + + //第二个split文件序号 必须 只比 第一个split 文件序号大1 + if secondSplit.SplitFileIdx-firstSplit.SplitFileIdx != 1 { + full.Err = fmt.Errorf("filename:%s 拉取[%s ~ %s] 时间段的split file,第一个分割文件:%s 和 第二个分割文件:%s 不连续", + full.FileHead, full.ResultFullbackup[0].BackupStart, full.ResultFullbackup[0].BackupEnd, + firstSplit.BackupFile, secondSplit.BackupFile) + mylog.Logger.Error(full.Err.Error()) + return + } + + //倒数第一个split文件序号 必须 只比 倒数第二个split文件序号大 1 + if lastSplit.SplitFileIdx-beforeLastSplit.SplitFileIdx != 1 { + full.Err = fmt.Errorf("filename:%s 拉取[%s ~ %s] 时间段的split file,倒数第一个分割文件:%s 和 倒数第二个分割文件:%s 不连续", + full.FileHead, full.ResultFullbackup[0].BackupStart, full.ResultFullbackup[0].BackupEnd, + lastSplit.BackupFile, beforeLastSplit.BackupFile) + mylog.Logger.Error(full.Err.Error()) + return + } + //全部是否连续 + splitIndexList, err := getNotReadySplitFile(full.ResultFullbackup) + if err != nil { + full.Err = err + return false + } + + if len(splitIndexList) > 0 { + full.Err = fmt.Errorf("缺失的split共%d个,缺失的split index是:%s", + len(splitIndexList), strings.Join(splitIndexList, ",")) + mylog.Logger.Error(full.Err.Error()) + return false + } + // 判断时间相等 + baseTime := full.ResultFullbackup[0].BackupStart + if len(full.ResultFullbackup) > 1 { + for _, bk01 := range full.ResultFullbackup { + if bk01.BackupStart != baseTime { + full.Err = fmt.Errorf("filename:%s 拉取[%s ~ %s] 时间段的split file 的时间不一致,第一个文件是:%s,不一致的文件是:%s", + full.FileHead, full.ResultFullbackup[0].BackupStart, full.ResultFullbackup[0].BackupEnd, + full.ResultFullbackup[0].BackupFile, bk01.BackupFile) + } + } + + } + msg := fmt.Sprintf(`filename:%s 找到所有[%s~%s]时间段的splitFile,共%d个,第一个splitFile:%s,最后一个splitFile:%s`, + full.FileHead, + full.ResultFullbackup[0].BackupStart, + full.ResultFullbackup[0].BackupEnd, + cnt, + firstSplit.BackupFile, + lastSplit.BackupFile, + ) + mylog.Logger.Info(msg) + return + +} + +// PullFiles 拉取文件到对应位置(saveHost,saveRemoteDir),检验文件完整性 +func (full *TplusFullBackPull) PullFiles() { + mylog.Logger.Info("PullFiles start ... ") + if len(full.ResultFullbackup) == 0 { + full.Err = fmt.Errorf( + " ResultFullbackup 信息为空(len:%d),无法拉取,请先获取binlog信息再拉取", len(full.ResultFullbackup)) + mylog.Logger.Error(full.Err.Error()) + return + } + var taskIds string + for _, bk01 := range full.ResultFullbackup { + bkItem := bk01 + taskIds = fmt.Sprintf("%s,%s", taskIds, strconv.FormatInt(bkItem.BackupTaskid, 10)) + } + mylog.Logger.Info("PullFiles FromBS TaskidList:%v", taskIds) + + recoverParams := RecoverReq{ + RecoverParams: RecoverParams{ + Version: consts.BackupVersion, + RecoverRequestInfo: RecoverRequestInfo{ + BaseInfo: BaseInfo{ + SysID: BackupSysID, + Key: BackupKey, + Ticket: "", + }, + RecoverDetailInfo: RecoverDetailInfo{ + TaskidList: taskIds, + DestIP: full.SaveHost, + LoginUser: DstIPBackupUser, + LoginPasswd: DstIPBackupPassword, + Directory: full.SaveDir, + Reason: "data recover", + }, + }, + }, + } + + msg := fmt.Sprintf("taskIds:%s", taskIds) + mylog.Logger.Info(msg) + + // 因为并行度是由备份系统调度的,所以这里直接全部请求下载 + // 如果按 task_id 逐个请求备份系统,就需要客户端控制并发 + err := full.recover.WaitForRecoverFinish(recoverParams, full.SaveHost) + if err != nil { + full.Err = fmt.Errorf("等待备份文件下载失败:%v", err) + return + } + +} + +// TotalSize 全备文件的大小 +func (full *TplusFullBackPull) TotalSize() int64 { + var ret int64 = 0 + for _, bk01 := range full.ResultFullbackup { + bkItem := bk01 + ret = ret + bkItem.BackupSize + } + return ret +} + +// findDstFileInDir 在指定文件夹下找到目标文件 +func findDstFileInDir(dir string, dstFile string) (dstFilePos string, err error) { + err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.Name() == dstFile { + dstFilePos = path + return nil + } + return nil + }) + if err != nil { + err = fmt.Errorf("findDstFileInDir filepath.Walk fail,err:%v,dir:%s dstFile:%s", err, dir, dstFile) + mylog.Logger.Error(err.Error()) + return "", err + } + if dstFilePos == "" { + err = fmt.Errorf("the destination file was not found in dir,dstFile:%s,dir:%s", + dstFile, dir) + mylog.Logger.Error(err.Error()) + return "", util.NewNotFoundErr() + } + return +} + +// GetBackupFileExt 获取全备文件后缀 +func (full *TplusFullBackPull) GetBackupFileExt() (fileExt string) { + + if len(full.ResultFullbackup) != 0 { + fileExt = filepath.Ext(full.ResultFullbackup[0].BackupFile) + } else { + full.Err = fmt.Errorf("结果文件为空,无法获取文件后缀,请检查") + mylog.Logger.Error(full.Err.Error()) + return + } + + if fileExt == ".tar" || fileExt == ".tgz" { + return fileExt + } else if strings.HasSuffix(full.ResultFullbackup[0].BackupFile, ".tar.gz") { + fileExt = ".tar.gz" + } else if strings.HasSuffix(full.ResultFullbackup[0].BackupFile, ".zst") { + fileExt = ".zst" + } else if strings.HasSuffix(full.ResultFullbackup[0].BackupFile, ".rdb") { + fileExt = ".rdb" + } else if strings.Contains(full.ResultFullbackup[0].BackupFile, ".split.") { + fileExt = "split" + } else { + full.Err = fmt.Errorf("不支持解压的全备文件类型:%s", full.ResultFullbackup[0].BackupFile) + mylog.Logger.Error(full.Err.Error()) + return + } + mylog.Logger.Info("GetBackupFileExt: fileExt is %s", fileExt) + return +} + +// SetDecompressedDir 设置解压目录 +func (full *TplusFullBackPull) SetDecompressedDir() { + bkFileExt := full.GetBackupFileExt() + if full.Err != nil { + return + } + mylog.Logger.Info("SetDecompressedDir bkFileExt:%s", bkFileExt) + var prefix, cmd01 string + if bkFileExt == "split" { + // 2005000194-TENDISPLUS-FULL-slave-127.0.0.x-30000-20230311-214752.split.004 + // 需要的:2005000194-TENDISPLUS-FULL-slave-127.0.0.x-30000-20230311-214752 + prefix = strings.Split(full.ResultFullbackup[0].BackupFile, ".split")[0] + cmd01 := fmt.Sprintf("cd %s && mkdir -p %s ", full.SaveDir, prefix) + mylog.Logger.Info("SetDecompressedDir bkFileExt:%s, cmd01:%s", bkFileExt, cmd01) + _, full.Err = util.RunLocalCmd("bash", []string{"-c", cmd01}, "", nil, 1800*time.Second) + if full.Err != nil { + return + } + } else if bkFileExt == ".tar" { + prefix = strings.TrimSuffix(full.ResultFullbackup[0].BackupFile, bkFileExt) + cmd01 := fmt.Sprintf("cd %s && mkdir -p %s ", full.SaveDir, prefix) + mylog.Logger.Info("SetDecompressedDir bkFileExt:%s, cmd01:%s", bkFileExt, cmd01) + _, full.Err = util.RunLocalCmd("bash", []string{"-c", cmd01}, "", nil, 1800*time.Second) + if full.Err != nil { + return + } + } else if bkFileExt == ".zst" { + // xx-xx-xx.aof.zst + prefix = strings.TrimSuffix(full.ResultFullbackup[0].BackupFile, ".aof.zst") + mylog.Logger.Info("prefix:%s", prefix) + cmd01 := fmt.Sprintf("cd %s && mkdir -p %s ", full.SaveDir, prefix) + mylog.Logger.Info("SetDecompressedDir bkFileExt:%s, cmd01:%s", bkFileExt, cmd01) + _, full.Err = util.RunLocalCmd("bash", []string{"-c", cmd01}, "", nil, 1800*time.Second) + if full.Err != nil { + return + } + } else if bkFileExt == ".rdb" { + // xx-xx.rdb + prefix = strings.TrimSuffix(full.ResultFullbackup[0].BackupFile, ".rdb") + mylog.Logger.Info("prefix:%s", prefix) + cmd01 := fmt.Sprintf("cd %s && mkdir -p %s ", full.SaveDir, prefix) + mylog.Logger.Info("SetDecompressedDir bkFileExt:%s, cmd01:%s", bkFileExt, cmd01) + _, full.Err = util.RunLocalCmd("bash", []string{"-c", cmd01}, "", nil, 1800*time.Second) + if full.Err != nil { + return + } + } + mylog.Logger.Info("SetDecompressedDir cmd01:%s", cmd01) + full.LocalFullBackupDir = prefix + mylog.Logger.Info("GetDecompressedDir: Decompressed Dir is:%s", full.LocalFullBackupDir) +} + +// GetDecompressedDir 获取 DecpDir 的值,全备解压目录 +// 如全备名是 3-TENDISPLUS-FULL-slave-127.0.0.x-30002-20230810-050140.tar +// 则值为 3-TENDISPLUS-FULL-slave-127.0.0.x-30002-20230810-050140 +func (full *TplusFullBackPull) GetDecompressedDir() (decpDir string) { + if full.LocalFullBackupDir != "" { + return full.LocalFullBackupDir + } + full.SetDecompressedDir() + if full.Err != nil { + return + } + return full.LocalFullBackupDir +} + +// CheckDecompressedDirIsOK 检查全备解压文件夹 是否存在,数据是否完整 +// 数据是否完整:通过判断是否有clustermeta.txt、${rocksdbIdx}/backup_meta文件来确认 +func (full *TplusFullBackPull) CheckDecompressedDirIsOK() (isExists, isCompelete bool, msg string) { + + mylog.Logger.Info("开始检查全备(已解压)目录是否存在,是否完整") + isExists = false + isCompelete = false + + decpDir := full.GetDecompressedDir() + if full.Err != nil { + return + } + decpFullPath := filepath.Join(full.SaveDir, decpDir) + if _, err := os.Stat(decpFullPath); os.IsNotExist(err) { + msg = fmt.Sprintf("全备解压目录:%s 不存在", decpFullPath) + mylog.Logger.Info(msg) + return + } + isExists = true //解压目录存在 + if full.TendisType == consts.TendisTypeTendisplusInsance { + var metaFile string + metaFile, _ = findDstFileInDir(decpFullPath, "clustermeta.txt") + + if _, err := os.Stat(metaFile); os.IsNotExist(err) { + //clustermeta.txt 不存在 + msg = fmt.Sprintf("全备解压目录:%s 中找不到 clustermeta.txt 文件", metaFile) + mylog.Logger.Info(msg) + isCompelete = false + return + } + full.ResultFullbackup[0].ClusterMeataDir = filepath.Dir(metaFile) + for i := 0; i < full.KvstoreNums; i++ { + rocksdbBackupMeta := filepath.Join(full.ResultFullbackup[0].ClusterMeataDir, fmt.Sprintf("%d/backup_meta", i)) + if _, err := os.Stat(rocksdbBackupMeta); os.IsNotExist(err) { + msg = fmt.Sprintf("全备解压目录:%s/%d/backup_meta 找不到", rocksdbBackupMeta, i) + mylog.Logger.Info(msg) + isCompelete = false + return + } + } + + } else if full.TendisType == consts.TendisTypeTendisSSDInsance { + //todo 这里不存在会有errlog ,预期内的情况? 看看怎么优化 + mylog.Logger.Info("检查tendis ssd 的全备解压是否有效") + err := full.tendisSSDBackupVerify(decpFullPath) + if err != nil { + msg = fmt.Sprintf("全备解压目录Verify 无效:%s", decpFullPath) + isCompelete = false + return + } + } else if full.TendisType == consts.TendisTypeRedisInstance { + // todo 怎样判断是否完整? + // 1、存在 + // 2、大小接近 + var file, backupFile string + mylog.Logger.Info("decpFullPath:%s", decpFullPath) + bkFileExt := full.GetBackupFileExt() + if full.Err != nil { + return + } + if bkFileExt == ".zst" { + backupFile = strings.TrimSuffix(full.ResultFullbackup[0].BackupFile, ".zst") + file, _ = findDstFileInDir(decpFullPath, backupFile) + + } else if bkFileExt == ".rdb" { + backupFile = full.ResultFullbackup[0].BackupFile + file, _ = findDstFileInDir(decpFullPath, backupFile) + } + + if _, err := os.Stat(file); os.IsNotExist(err) { + msg = fmt.Sprintf("全备解压目录:%s 中找不到 %s 文件", decpFullPath, backupFile) + mylog.Logger.Info(msg) + isCompelete = false + return + } + + } + + msg = fmt.Sprintf("解压目录存在且完整:%s", decpFullPath) + mylog.Logger.Info(msg) + //解压文件存在且是完整的 + return true, true, msg +} + +// tendisSSDBackupVerify 确定tendissd备份是否是有效的 +func (task *TplusFullBackPull) tendisSSDBackupVerify(backupFullDir string) (err error) { + + verifyBin := consts.TredisverifyBin + if !util.FileExists(verifyBin) { + task.Err = fmt.Errorf("%s not exists", verifyBin) + mylog.Logger.Error(task.Err.Error()) + return + } + cmd := fmt.Sprintf(` +export LD_PRELOAD=/usr/local/redis/bin/deps/libjemalloc.so; +export LD_LIBRARY_PATH=LD_LIBRARY_PATH:/usr/local/redis/bin/deps; +%s %s 1 2>/dev/null + `, verifyBin, backupFullDir) + mylog.Logger.Info(cmd) + _, err = util.RunBashCmd(cmd, "", nil, 1*time.Hour) + if err != nil { + err = fmt.Errorf("backupData(%s) verify failed", backupFullDir) + mylog.Logger.Error(err.Error()) + return + } + return +} + +// CheckLocalBackupFileIsOK 检查本地全备(未解压) 是否存在,是否完整 +func (full *TplusFullBackPull) CheckLocalBackupFileIsOK() (isExists, isCompelete bool, msg string) { + mylog.Logger.Info("开始检查本地全备文件(未解压)是否存在,是否完整") + + mylog.Logger.Info("full.LocalFullBackupDir:%s", full.LocalFullBackupDir) + //TODO 通过gse检查saveRemoteDidr备份文件是否存在,大小如何 + // 这里当前只是 简单检查下saveMyyDir中备份文件是否存在,大小是否ok + full.SetDecompressedDir() + if full.Err != nil { + msg = fmt.Sprintf("本地全备(未解压):%s 不存在", full.LocalFullBackupDir) + mylog.Logger.Info(msg) + return false, false, msg + } + if full.SaveDir != "" { + bkFileFullPath := filepath.Join(full.SaveDir, full.LocalFullBackupDir) + mylog.Logger.Info("bkFileFullPath:%s", bkFileFullPath) + var bkFileFullPathFile string + if full.TendisType == consts.TendisTypeTendisplusInsance || full.TendisType == consts.TendisTypeTendisSSDInsance { + bkFileFullPathFile = fmt.Sprintf("%s%s", bkFileFullPath, ".tar") + } else if full.TendisType == consts.TendisTypeRedisInstance { + bkFileExt := full.GetBackupFileExt() + if full.Err != nil { + return + } + if bkFileExt == ".zst" { + bkFileFullPathFile = fmt.Sprintf("%s%s", bkFileFullPath, ".aof.zst") + } else if bkFileExt == ".rdb" { + bkFileFullPathFile = fmt.Sprintf("%s%s", bkFileFullPath, ".rdb") + } + + } + + mylog.Logger.Info("bkFileFullPathFile:%s", bkFileFullPathFile) + bkFileOsInfo, err := os.Stat(bkFileFullPathFile) + if os.IsNotExist(err) { + // 解压后就会删除,基本是不存在的 + msg = fmt.Sprintf("本地全备(未解压):%s 不存在", bkFileFullPathFile) + mylog.Logger.Info(msg) + return false, false, msg + } + + isExists = true + + msg = fmt.Sprintf("本地全备(未解压):%s 大小%dGB, 目标大小:%dGB", + full.LocalFullBackupDir, + bkFileOsInfo.Size()/1024/1024/1024, + full.TotalSize()/1024/1024/1024) + mylog.Logger.Info(msg) + mylog.Logger.Info("size:%d,full.TotalSize():%d", bkFileOsInfo.Size(), full.TotalSize()) + // 这里获取的文件夹大小和单个文件大小,数据会不一致,有很少的差异,所以比较单位换算为GB + if bkFileOsInfo.Size()/1024/1024/1024 != full.TotalSize()/1024/1024/1024 { + msg = fmt.Sprintf("本地全备(未解压):%s 大小%dGB != 目标大小:%dGB", + full.LocalFullBackupDir, + bkFileOsInfo.Size()/1024/1024/1024, + full.TotalSize()/1024/1024/1024) + mylog.Logger.Info(msg) + isCompelete = false + return + } + mylog.Logger.Info("CheckLocalBackupFileIsOK: result is ok") + return true, true, "" + } + msg = fmt.Sprintf("SaveDir:%s is empty", full.SaveDir) + return false, false, msg +} + +// DirSize 获取文件夹的大小 +func (full *TplusFullBackPull) DirSize(path string) (int64, error) { + var size int64 + err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error { + if !info.IsDir() { + size += info.Size() + } + return err + }) + return size, err +} + +// RmDecompressedDir 删除本地全备解压文件夹 +func (full *TplusFullBackPull) RmDecompressedDir() { + decpDir := full.GetDecompressedDir() + if full.Err != nil { + return + } + decpFullPath := filepath.Join(full.SaveDir, decpDir) + if _, err := os.Stat(decpFullPath); os.IsNotExist(err) { + //本地全备解压文件夹不存在 + return + } + rmCmd := fmt.Sprintf("cd %s && rm -rf %s 2>/dev/null", full.SaveDir, decpDir) + _, full.Err = util.RunLocalCmd("bash", []string{"-c", rmCmd}, "", nil, 30*time.Minute) + if full.Err != nil { + return + } + msg := fmt.Sprintf("全备文件夹(已解压):%s 删除成功", decpDir) + mylog.Logger.Info(msg) + return +} + +// RmLocalBakcupFile 删除本地全备(未解压)文件 +func (full *TplusFullBackPull) RmLocalBakcupFile() { + bkFileFullPath := filepath.Join(full.SaveDir, full.LocalFullBackupDir) + if _, err := os.Stat(bkFileFullPath); os.IsNotExist(err) { + return + } + bkFileExt := full.GetBackupFileExt() + if full.Err != nil { + return + } + var backupFiles string + // rdb 没压缩,不用处理 + if bkFileExt == "split" { + // 2005000194-TENDISPLUS-FULL-slave-127.0.0.x-30000-20230311-214752.split.004 + // 需要的:2005000194-TENDISPLUS-FULL-slave-127.0.0.x-30000-20230311-214752 + backupFiles = fmt.Sprintf("%s.split.*", full.GetDecompressedDir()) + } else if bkFileExt == ".tar" { + backupFiles = fmt.Sprintf("%s.tar", full.GetDecompressedDir()) + } else if bkFileExt == ".zst" { + backupFiles = fmt.Sprintf("%s.aof.zst", full.GetDecompressedDir()) + DecompressDir := filepath.Join(full.SaveDir, full.GetDecompressedDir()) + rmCmd := fmt.Sprintf("cd %s && rm -rf %s 2>/dev/null", DecompressDir, backupFiles) + _, full.Err = util.RunLocalCmd("bash", []string{"-c", rmCmd}, "", nil, 30*time.Minute) + if full.Err != nil { + return + } + msg := fmt.Sprintf("本地全备(未解压):%s 删除成功", backupFiles) + mylog.Logger.Info(msg) + return + } + rmCmd := fmt.Sprintf("cd %s && rm -rf %s 2>/dev/null", full.SaveDir, backupFiles) + _, full.Err = util.RunLocalCmd("bash", []string{"-c", rmCmd}, "", nil, 30*time.Minute) + if full.Err != nil { + return + } + msg := fmt.Sprintf("本地全备(未解压):%s 删除成功", backupFiles) + mylog.Logger.Info(msg) + return +} + +// Decompressed ... 解压文件 +// NOCC:golint/fnsize(设计如此) +func (full *TplusFullBackPull) Decompressed() { + mylog.Logger.Info("Decompressed start ...") + var cmd01 string + bkFileExt := full.GetBackupFileExt() + if full.Err != nil { + return + } + + if bkFileExt == ".tar" { + // 只有一个备份文件,并且后缀为.tar + if len(full.ResultFullbackup) == 1 { + cmd01 = fmt.Sprintf("tar -xf %s", full.ResultFullbackup[0].BackupFile) + } + + } else if bkFileExt == ".tar.gz" || bkFileExt == ".tgz" { + if len(full.ResultFullbackup) == 1 { + cmd01 = fmt.Sprintf("tar -xf %s", full.ResultFullbackup[0].BackupFile) + } + } else if bkFileExt == ".zst" { + if len(full.ResultFullbackup) == 1 { + // 检查 zst 是否存在 + _, err := os.Stat(consts.ZstdBin) + if err != nil && os.IsNotExist(err) { + + err = fmt.Errorf("Decompress: 解压工具 zst 不存在,"+ + "请检查 %s是否存在 err:%v", consts.ZstdBin, err) + mylog.Logger.Error(err.Error()) + full.Err = err + return + + } + // 未解压文件 + cmd01 = fmt.Sprintf(" cd %s && %s -d %s ", full.GetDecompressedDir(), + consts.ZstdBin, full.ResultFullbackup[0].BackupFile) + } + prefix := strings.TrimSuffix(full.ResultFullbackup[0].BackupFile, ".aof.zst") + cmd01 := fmt.Sprintf("cd %s && mkdir -p %s ", full.SaveDir, prefix) + mylog.Logger.Info("SetDecompressedDir bkFileExt:%s, cmd01:%s", bkFileExt, cmd01) + _, full.Err = util.RunLocalCmd("bash", []string{"-c", cmd01}, "", nil, 1800*time.Second) + if full.Err != nil { + return + } + // 将备份文件mv到解压目录下 + DecompressDir := filepath.Join(full.SaveDir, full.GetDecompressedDir()) + mvCmd := fmt.Sprintf("cd %s && mv %s %s ", full.SaveDir, full.ResultFullbackup[0].BackupFile, DecompressDir) + mylog.Logger.Info("将备份文件mv到解压目录下 mvCmd:%s", mvCmd) + _, full.Err = util.RunLocalCmd("bash", []string{"-c", mvCmd}, "", nil, 1800*time.Second) + if full.Err != nil { + return + } + + } else if bkFileExt == ".rdb" { + // 将备份文件mv到目录下,rdb不用解压 + DecompressDir := filepath.Join(full.SaveDir, full.GetDecompressedDir()) + cmd01 := fmt.Sprintf("cd %s && mkdir -p %s ", full.SaveDir, DecompressDir) + mylog.Logger.Info("SetDecompressedDir bkFileExt:%s, cmd01:%s", bkFileExt, cmd01) + _, full.Err = util.RunLocalCmd("bash", []string{"-c", cmd01}, "", nil, 1800*time.Second) + if full.Err != nil { + return + } + + mvCmd := fmt.Sprintf("cd %s && mv %s %s ", full.SaveDir, full.ResultFullbackup[0].BackupFile, DecompressDir) + mylog.Logger.Info("将备份文件mv到解压目录下 mvCmd:%s", mvCmd) + _, full.Err = util.RunLocalCmd("bash", []string{"-c", mvCmd}, "", nil, 1800*time.Second) + if full.Err != nil { + return + } + } else if bkFileExt == "split" { + // 2005000194-TENDISPLUS-FULL-slave-127.0.0.x-30000-20230311-214752.split.004 + backupFilesPrefix := strings.Split(full.ResultFullbackup[0].BackupFile, ".split")[0] + cmd01 = fmt.Sprintf("cat %s.* |tar x ", backupFilesPrefix) + mylog.Logger.Info("Decompressed cmd01:%s", cmd01) + } + if cmd01 != "" { + decDir01 := full.GetDecompressedDir() + mylog.Logger.Info("Decompressed: decDir01 is:%s", decDir01) + //将全备文件解压到指定目录下 + cmd01 = fmt.Sprintf("cd %s && %s", full.SaveDir, cmd01) + msg := fmt.Sprintf("解压命令:%s", cmd01) + mylog.Logger.Info(msg) + _, full.Err = util.RunLocalCmd("bash", []string{"-c", cmd01}, "", nil, 1800*time.Second) + if full.Err != nil { + return + } + isExists, isCompelete, msg := full.CheckDecompressedDirIsOK() + if full.Err != nil { + return + } + if isExists == false || isCompelete == false { + //如果不存在或不完整 + full.Err = errors.New(msg) + mylog.Logger.Error(full.Err.Error()) + return + } + //rm 源文件 + full.RmLocalBakcupFile() + } + return +} + +// PullFullbackDecompressed 拉取全备文件并解压,可重试 +// 1: 本地全备.tar, 全备(已解压)文件夹 均不存在 =>拉取、解压; +// 2. 本地全备.tar不存在、全备(已解压)文件夹 存在: +// - 全备(已解压)文件夹 完整 => 直接返回; +// - 全备(已解压)文件夹 不完整 => 删除全备(已解压)文件夹,重新拉取、解压; +// 3. 本地全备.tar存在、全备(已解压)文件夹 不存在: +// - 本地全备.tar 完整, => 解压; +// - 本地全备.tar 不完整 => 删除 本地全备.tar,重新拉取、解压; +// 4. 本地全备.tar存在、全备(已解压)文件夹 存在 +// - 本地全备.tar 完整 => 删除 全备(已解压)文件夹,重新解压(有理由怀疑上一次解压失败了,本地全备.tar 来不及删除) +// - 本地全备.tar 不完整 => 删除本地全备.tar + 删除 全备(已解压)文件夹, 重新拉取、解压; +// NOCC:golint/fnsize(设计如此) +func (full *TplusFullBackPull) PullFullbackDecompressed() { + + // 备份文件 (未解压) + localBkIsExists, localBkIsCompelete, _ := full.CheckLocalBackupFileIsOK() + if full.Err != nil { + return + } + mylog.Logger.Info("localBkIsExists:%v,localBkIsCompelete:%v ", localBkIsExists, localBkIsCompelete) + // 已解压的备份文件 + decpDirIsExists, decpIsCompelete, _ := full.CheckDecompressedDirIsOK() + if full.Err != nil { + return + } + mylog.Logger.Info("decpDirIsExists:%v,decpIsCompelete:%v", decpDirIsExists, decpIsCompelete) + + //1: 本地全备.tar, 全备(已解压)文件夹 均不存在 =>拉取、解压; + if localBkIsExists == false && decpDirIsExists == false { + full.PullFiles() + if full.Err != nil { + return + } + full.Decompressed() + if full.Err != nil { + return + + } + + } else if localBkIsExists == false && decpDirIsExists == true { + //2. 本地全备.tar不存在、全备(已解压)文件夹 存在: + //- 全备(已解压)文件夹 完整 => 直接返回; + //- 全备(已解压)文件夹 不完整 => 删除全备(已解压)文件夹,重新拉取、解压; + if decpIsCompelete == true { + return + } + full.RmDecompressedDir() + if full.Err != nil { + return + } + full.PullFiles() + if full.Err != nil { + return + } + full.Decompressed() + if full.Err != nil { + return + } + } else if localBkIsExists == true && decpDirIsExists == false { + //3. 本地全备.tar存在、全备(已解压)文件夹 不存在: + //- 本地全备.tar 完整, => 解压; + //- 本地全备.tar 不完整 => 删除 本地全备.tar,重新拉取、解压; + if localBkIsCompelete == true { + full.Decompressed() + return + } + full.RmLocalBakcupFile() + if full.Err != nil { + return + } + full.PullFiles() + if full.Err != nil { + return + } + full.Decompressed() + if full.Err != nil { + return + } + } else if localBkIsExists == true && decpDirIsExists == true { + //4. 本地全备.tar存在、全备(已解压)文件夹 存在 + //- 本地全备.tar 完整 => 删除 全备(已解压)文件夹,重新解压(有理由怀疑上一次解压失败了,本地全备.tar 来不及删除) + //- 本地全备.tar 不完整 => 删除本地全备.tar + 删除 全备(已解压)文件夹, 重新拉取、解压; + if localBkIsCompelete == true { + full.RmDecompressedDir() + if full.Err != nil { + return + } + full.Decompressed() + return + } + full.RmDecompressedDir() + if full.Err != nil { + return + } + full.RmLocalBakcupFile() + if full.Err != nil { + return + } + full.PullFiles() + if full.Err != nil { + return + } + full.Decompressed() + return + } + +} + +// RestoreBackup 在目标tendisplus上执行restorebackup命令 +// 参数backupDir: 代表从目标tendisplus来看,全备的位置 +// (其实 参数backupDir 和 full.SaveLocalDir full.SaveRemoteDir是同一个文件夹, 但是是不同视角) +func (full *TplusFullBackPull) RestoreBackup(dstTplusIP string, dstTplusPort int, dstTplusPasswd string) error { + + redisAddr := fmt.Sprintf("%s:%s", dstTplusIP, strconv.Itoa(dstTplusPort)) + msg := fmt.Sprintf("master:%s开始导入全备", redisAddr) + mylog.Logger.Info(msg) + //再次探测tendisplus连接性 + redisCli, err := myredis.NewRedisClient(redisAddr, dstTplusPasswd, 0, consts.TendisTypeTendisplusInsance) + if err != nil { + return err + } + defer redisCli.Close() + + if full.ResultFullbackup[0].ClusterMeataDir == "" { + err = fmt.Errorf("全备文件夹不存在,请检查:%s", full.ResultFullbackup[0].ClusterMeataDir) + mylog.Logger.Error(err.Error()) + return err + + } + mylog.Logger.Info("full.ResultFullbackup[0].ClusterMeataDir:%s", full.ResultFullbackup[0].ClusterMeataDir) + //这里会强制(force)恢复全备,检查tendisplus是否为空需要自己完成 + restoreCmd := fmt.Sprintf("redis-cli -h %s -p %d -a %s restorebackup all %s force", + dstTplusIP, dstTplusPort, dstTplusPasswd, full.ResultFullbackup[0].ClusterMeataDir) + logCmd := fmt.Sprintf("redis-cli -h %s -p %d -a xxxx restorebackup all %s force", + dstTplusIP, dstTplusPort, full.ResultFullbackup[0].ClusterMeataDir) + mylog.Logger.Info("开始恢复全备,恢复命令:%s", logCmd) + + ret01, err := util.RunLocalCmd("bash", []string{"-c", restoreCmd}, "", nil, 600*time.Second) + mylog.Logger.Info("恢复全备执行结果:%v", ret01) + if err != nil { + mylog.Logger.Error(fmt.Sprintf("恢复全备失败,详情:%v", err)) + return err + } + ret01 = strings.TrimSpace(ret01) + if strings.Contains(ret01, "ERR:") == true { + mylog.Logger.Error(fmt.Sprintf("恢复全备失败,err:%v,cmd:%s", err, logCmd)) + + return err + } + msg = fmt.Sprintf("%s:%d 恢复全备成功", dstTplusIP, dstTplusPort) + mylog.Logger.Info(msg) + + return nil +} + +// GetTendisplusHearbeatKey 根据tendisplus ip port 获取心跳key +func (full *TplusFullBackPull) GetTendisplusHearbeatKey(masterIP string, masterPort int) string { + Heartbeat := fmt.Sprintf("%s_%s:heartbeat", masterIP, strconv.Itoa(masterPort)) + return Heartbeat +} + +// ClusterMeta 全备的meta信息 +type ClusterMeta struct { + Slot string `json:"slot"` + Flag string `json:"flag"` + ConfigEpoch int `json:"configEpoch"` + CurrentEpoch int `json:"currentEpoch"` + LastVoteEpoch int `json:"lastVoteEpoch"` +} + +// GetClusterMeata 从解压的目录中读取clustermeta.txt文件信息 +func (task *RedisInsRecoverTask) GetClusterMeata() (meta *ClusterMeta, err error) { + if task.BackupFileDir == "" { + err = fmt.Errorf("task.BackupFileDir:%s 为空,请检查RestoreBackup功能处的解压和赋值情况", task.BackupFileDir) + task.runtime.Logger.Error(err.Error()) + return nil, err + } + metaFile := filepath.Join(task.RecoverDir, task.BackupFileDir, "clustermeta.txt") + _, err = os.Stat(metaFile) + if err != nil { + err = fmt.Errorf("%s os.Stat fail,err:%v", metaFile, err) + task.runtime.Logger.Error(err.Error()) + return + } + fileData, err := ioutil.ReadFile(metaFile) + if err != nil { + err = fmt.Errorf("读取clusterMeta文件:%s失败,err:%v", metaFile, err) + task.runtime.Logger.Error(err.Error()) + return + } + task.runtime.Logger.Info("clustermeta:%s,fileData:%s", metaFile, fileData) + meta = &ClusterMeta{} + fileLines := strings.Split(string(fileData), "\n") + for _, line := range fileLines { + line = strings.TrimSpace(line) + list01 := strings.Split(line, ":") + if len(list01) == 0 { + continue + } + if len(list01) < 2 { + continue + } + if list01[0] == "slot" { + meta.Slot = list01[1] + } else if list01[0] == "flag" { + meta.Flag = list01[1] + } else if list01[0] == "configEpoch" { + meta.ConfigEpoch, _ = strconv.Atoi(list01[1]) + } else if list01[0] == "currentEpoch" { + meta.CurrentEpoch, _ = strconv.Atoi(list01[1]) + } else if list01[0] == "lastVoteEpoch" { + meta.LastVoteEpoch, _ = strconv.Atoi(list01[1]) + } + } + if meta.Slot == "" { + err = fmt.Errorf("读取clusterMeta文件:%s失败,meta.Slot(%s)信息为空", metaFile, meta.Slot) + task.runtime.Logger.Error(err.Error()) + return + } + return +} + +// RocksdbBackupMeta Rocksdb 备份元数据 +type RocksdbBackupMeta struct { + BackupType int `json:"backupType"` + BinlogPos uint64 `json:"binlogpos"` + StartTimeSec int64 `json:"startTimeSec"` + StartTime time.Time `json:"startTime"` + EndTimeSec int64 `json:"endTimeSec"` + EndTime time.Time `json:"endTime"` + UseTimeSec int `json:"useTimeSec"` + BinlogVersion int `json:"binlogVersion"` +} + +// GetRocksdbBackupMeta 获取全备中某个kvstore(rocksdb)的元数据信息 +func (task *RedisInsRecoverTask) GetRocksdbBackupMeta(rocksIdx int) (meta *RocksdbBackupMeta, err error) { + task.runtime.Logger.Info("GetRocksdbBackupMeta start ... ") + // 解压后的文件和文件名有关,在RestoreBackup 中确定 + // 直接赋值测试 + // task.BackupFileDir = "/data/dbbak/recover_redis/2005000194-TENDISPLUS-FULL-slave-127.0.0.x-30010-20230311-214730" + task.BackupFileDir = task.FullBackup.LocalFullBackupDir + mylog.Logger.Info("task BackupFileDir:%s", task.BackupFileDir) + if task.BackupFileDir == "" { + err = fmt.Errorf("task.BackupFileDir:%s 为空,请检查RestoreBackup功能处的解压和赋值情况", task.BackupFileDir) + task.runtime.Logger.Error(err.Error()) + return + } + metaFile := filepath.Join(task.RecoverDir, task.BackupFileDir, strconv.Itoa(rocksIdx), "backup_meta") + _, err = os.Stat(metaFile) + if err != nil { + err = fmt.Errorf("%s os.Stat fail,err:%v", metaFile, err) + task.runtime.Logger.Error(err.Error()) + return + } + task.runtime.Logger.Info("backup_meta metaFile:%s", metaFile) + fileData, err := ioutil.ReadFile(metaFile) + if err != nil { + err = fmt.Errorf("读取backup_meta文件:%s失败,err:%v", metaFile, err) + task.runtime.Logger.Error(err.Error()) + return + } + meta = &RocksdbBackupMeta{} + err = json.Unmarshal(fileData, meta) + if err != nil { + err = fmt.Errorf("GetRocksdbBackupMeta json.Unmarshal fail,err:%v,fileData:%s", err, string(fileData)) + task.runtime.Logger.Error(err.Error()) + return + } + if meta.StartTimeSec <= 0 { + err = fmt.Errorf("读取backup_meta文件:%s失败,meta.StartTimeSec(%d)<=0", metaFile, meta.StartTimeSec) + task.runtime.Logger.Error(err.Error()) + return + } + meta.StartTime = time.Unix(meta.StartTimeSec, 0) + meta.EndTime = time.Unix(meta.EndTimeSec, 0) + return + +} + +// RecoverTredisFromRocksdb 恢复目标tendis ssd +// (其实 参数backupDir 和 full.SaveLocalDir full.SaveRemoteDir是同一个文件夹, 但是是不同视角) +// NOCC:golint/fnsize(设计如此) +func (full *TplusFullBackPull) RecoverTredisFromRocksdb(dstTplusIP string, + dstTplusPort int, dstTplusPasswd string) error { + + redisAddr := fmt.Sprintf("%s:%s", dstTplusIP, strconv.Itoa(dstTplusPort)) + msg := fmt.Sprintf("master:%s开始导入全备", redisAddr) + mylog.Logger.Info(msg) + //再次探测tendisplus连接性 + redisCli, err := myredis.NewRedisClient(redisAddr, dstTplusPasswd, 0, consts.TendisTypeTendisSSDInsance) + if err != nil { + return err + } + defer redisCli.Close() + + var infoRet map[string]string + infoRet, full.Err = redisCli.Info("server") + if full.Err != nil { + return full.Err + } + masterVersion := infoRet["redis_version"] + mylog.Logger.Info("Get redis_version success redis_version:%s", masterVersion) + + var ssdDataDir string + ssdDataDir, full.Err = redisCli.GetDir() + if full.Err != nil { + return full.Err + } + // "Get SsdDataDir success SsdDataDir:/data1/redis/15000/data" + mylog.Logger.Info("Get SsdDataDir success SsdDataDir:%s", ssdDataDir) + + //1、shutdown + err = redisCli.Shutdown() + if err != nil { + return err + } + mylog.Logger.Info("master(%s) shutdown success", redisAddr) + + if full.LocalFullBackupDir == "" { + err = fmt.Errorf("全备文件夹不存在,请检查:%s", full.LocalFullBackupDir) + mylog.Logger.Error(err.Error()) + return err + + } + mylog.Logger.Info("full.LocalFullBackupDir:%s", full.LocalFullBackupDir) + + fullFilePath := fmt.Sprintf("%v/%v", full.SaveDir, full.LocalFullBackupDir) + if _, err := os.Stat(fullFilePath); os.IsNotExist(err) { + err = fmt.Errorf("全备文件夹不存在,请检查:%s", full.LocalFullBackupDir) + mylog.Logger.Error(err.Error()) + return err + } + + DepsDir := "/usr/local/redis/bin/deps" + if _, err := os.Stat(DepsDir); os.IsNotExist(err) { + err = fmt.Errorf("%s:不存在,请检查:%s", DepsDir, DepsDir) + mylog.Logger.Error(err.Error()) + return err + } + + nowtime := time.Now().Local().Format(consts.FilenameTimeLayout) + rockdbDir := filepath.Join(ssdDataDir, "rocksdb") + bakDir := filepath.Join(filepath.Dir(ssdDataDir), "backup_rocksdb."+nowtime) + // 2、mv 掉本地的数据,mv 到备份目录 + mvCmd := fmt.Sprintf("mv %s %s", rockdbDir, bakDir) + mylog.Logger.Info(mvCmd) + util.RunBashCmd(mvCmd, "", nil, 2*time.Hour) + util.LocalDirChownMysql(bakDir) + + // 3、备份文件恢复 + var extraOpt string + if strings.Contains(masterVersion, "v1.2") { + extraOpt = " 1" + } else if strings.Contains(masterVersion, "v1.3") { + extraOpt = "" + } else { + full.Err = fmt.Errorf("unsupported tendis version:%s,exit.", masterVersion) + mylog.Logger.Error(full.Err.Error()) + return full.Err + } + restoreTool := full.getRestoreTool(dstTplusIP, masterVersion, dstTplusPort) + if full.Err != nil { + return full.Err + } + + restoreCmd := fmt.Sprintf(` + export LD_PRELOAD=%s/libjemalloc.so + export LD_LIBRARY_PATH=LD_LIBRARY_PATH:%s + %s %s %s %s + `, DepsDir, DepsDir, restoreTool, fullFilePath, rockdbDir, extraOpt) + mylog.Logger.Info(restoreCmd) + + var ret string + ret, full.Err = util.RunLocalCmd("bash", []string{"-c", restoreCmd}, "", nil, 2*time.Hour) + mylog.Logger.Info("restore command result:" + ret) + if full.Err != nil { + mylog.Logger.Error(fmt.Sprintf("恢复全备失败,详情:%v", err)) + return full.Err + } + + if util.FileExists(rockdbDir) { + mylog.Logger.Info("restore ok, %s generated", rockdbDir) + } else { + full.Err = fmt.Errorf("restore command failed, %s not generated", rockdbDir) + mylog.Logger.Error(full.Err.Error()) + return full.Err + } + util.LocalDirChownMysql(rockdbDir) + + ret01 := strings.TrimSpace(ret) + if strings.Contains(ret01, "ERR:") == true { + mylog.Logger.Error(fmt.Sprintf("恢复全备失败,err:%v,cmd:%s", err, restoreCmd)) + return full.Err + } + + // 4、拉起节点 + startScript := filepath.Join("/usr/local/redis/bin", "start-redis.sh") + _, full.Err = util.RunLocalCmd("su", []string{consts.MysqlAaccount, "-c", startScript + " " + strconv.Itoa( + dstTplusPort)}, "", nil, 30*time.Second) + if full.Err != nil { + return full.Err + } + mylog.Logger.Info(fmt.Sprintf("su %s -c \"%s\"", consts.MysqlAaccount, + startScript+" "+strconv.Itoa(dstTplusPort))) + time.Sleep(2 * time.Second) + + //再次探测tendisplus连接性->拉起是否成功 + redisCli, err = myredis.NewRedisClient(redisAddr, dstTplusPasswd, 0, consts.TendisTypeTendisplusInsance) + if err != nil { + return err + } + defer redisCli.Close() + + msg = fmt.Sprintf("%s:%d 恢复全备成功", dstTplusIP, dstTplusPort) + mylog.Logger.Info(msg) + + return nil +} + +func (full *TplusFullBackPull) getRestoreTool(dstTplusIP, masterVersion string, dstTplusPort int) (restoreTool string) { + + if strings.Contains(masterVersion, "v1.2.") { + restoreTool = "/usr/local/redis/bin/rr_restore_backup" + } else if strings.Contains(masterVersion, "v1.3.") { + restoreTool = "/usr/local/redis/bin/tredisrestore" + } else { + full.Err = fmt.Errorf("redisMaster(%s:%d) version:%s cannot find restore-tool", + dstTplusIP, dstTplusPort, masterVersion) + mylog.Logger.Error(full.Err.Error()) + return + } + if !util.FileExists(restoreTool) { + full.Err = fmt.Errorf("redis(%s) restore_tool:%s not exists", dstTplusIP, restoreTool) + mylog.Logger.Error(full.Err.Error()) + return + } + return +} + +// RecoverCacheRedisFromBackupFile 恢复目标tendis cache +// NOCC:golint/fnsize(其他) +func (full *TplusFullBackPull) RecoverCacheRedisFromBackupFile(sourceIP string, + sourcePort int, dstTplusIP string, dstTplusPort int, dstTplusPasswd string) { + + newRedisAddr := fmt.Sprintf("%s:%s", dstTplusIP, strconv.Itoa(dstTplusPort)) + oldnewRedisAddr := fmt.Sprintf("%s:%s", sourceIP, strconv.Itoa(sourcePort)) + msg := fmt.Sprintf("master:%s开始导入全备", newRedisAddr) + mylog.Logger.Info("RecoverCacheRedisFromBackupFile: start recover"+ + "redis:%s from redis:%s aof or rdb ... ", newRedisAddr, oldnewRedisAddr) + mylog.Logger.Info(msg) + //再次探测tendisplus连接性 + redisCli, err := myredis.NewRedisClient(newRedisAddr, dstTplusPasswd, 0, consts.TendisTypeRedisInstance) + if err != nil { + mylog.Logger.Error(err.Error()) + full.Err = err + return + } + defer redisCli.Close() + + //1、检查全备文件是否存在 + if full.LocalFullBackupDir == "" { + err = fmt.Errorf("全备文件夹不存在,请检查:%s", full.LocalFullBackupDir) + mylog.Logger.Error(err.Error()) + full.Err = err + return + } + mylog.Logger.Info("full.LocalFullBackupDir:%s", full.LocalFullBackupDir) + + //全备解压完整目录 + fullFilePath := fmt.Sprintf("%v/%v", full.SaveDir, full.LocalFullBackupDir) + if _, err := os.Stat(fullFilePath); os.IsNotExist(err) { + err = fmt.Errorf("全备文件夹不存在,请检查:%s", full.LocalFullBackupDir) + mylog.Logger.Error(err.Error()) + full.Err = err + return + } + + // 2、停dbmon 作为整体在所有任务开始前操作(前置任务已完成) + + //3、shutdown 关闭节点 + mylog.Logger.Info("redis:%s begin to shutdown", newRedisAddr) + err = redisCli.Shutdown() + if err != nil { + mylog.Logger.Error("执行shutdown失败:err:%v", err) + full.Err = err + return + } + mylog.Logger.Info("master(%s) shutdown success", newRedisAddr) + + //4、 修改 配置文件appendonly 可以采用命令模式 先config set appendonly yes,在config rewrite 写入配置文件, + // 只是这样兼容不了以前的instance.conf 配置模式 + redisConfDir := filepath.Join(consts.GetRedisDataDir(), "redis", strconv.Itoa(dstTplusPort), "redis.conf") + _, err = os.Stat(redisConfDir) + if err != nil && os.IsNotExist(err) { + redisConfDir = filepath.Join(consts.GetRedisDataDir(), "redis", strconv.Itoa(dstTplusPort), "instance.conf") + _, err = os.Stat(redisConfDir) + if err != nil && os.IsNotExist(err) { + err := fmt.Errorf("没有找到redis.conf配置文件和instance.conf配置文件") + mylog.Logger.Error("recoverRedisFromAof failed 没有找到redis.conf配置文件和instance.conf配置文件") + full.Err = err + return + } + + } + mylog.Logger.Info("配置文件为:%s", redisConfDir) + + // 获取 appendonly 值 + getAppendonlyCmd := fmt.Sprintf(`grep -E '^appendonly' %s|awk '{print $2}'|head -1`, redisConfDir) + appendonly, err := util.RunBashCmd(getAppendonlyCmd, "", nil, 10*time.Minute) + if err != nil { + mylog.Logger.Error("get appendonly failed: %v", err) + full.Err = err + return + } + mylog.Logger.Info("appendonly:%s", appendonly) + var dataPath, backupFile, backupFilePath string + + if strings.Contains(full.ResultFullbackup[0].BackupFile, ".aof.zst") { + // 解压后文件 + backupFile = strings.Trim(full.ResultFullbackup[0].BackupFile, ".zst") + backupFilePath = filepath.Join(fullFilePath, backupFile) + dataPath = filepath.Join(consts.GetRedisDataDir(), "redis", strconv.Itoa(dstTplusPort), "data", "appendonly.aof") + if appendonly != "yes" { + // 修改appendonly 值为yes ,用aof 文件拉起redis + modifyAppendonlyCmd := fmt.Sprintf("sed -i 's/appendonly no/appendonly yes/' %s", redisConfDir) + mylog.Logger.Info(modifyAppendonlyCmd) + _, err = util.RunLocalCmd("bash", []string{"-c", modifyAppendonlyCmd}, "", nil, 10*time.Minute) + if err != nil { + err = fmt.Errorf("modify appendonly 失败,err:%v", err) + mylog.Logger.Error(err.Error()) + full.Err = err + return + } + + } + // 如果存在原来的aof文件,则删除,一般是新部署的节点不会存在 + oldAofdir := filepath.Join(consts.GetRedisDataDir(), "redis", strconv.Itoa(dstTplusPort), + "data", "appendonly.aof") + rmAofCmd := fmt.Sprintf("rm %s", oldAofdir) + _, err = os.Stat(oldAofdir) + if err == nil && !os.IsNotExist(err) { + mylog.Logger.Info("删除原来存在的aof文件:%s", rmAofCmd) + _, err = util.RunLocalCmd("bash", []string{"-c", rmAofCmd}, "", nil, 10*time.Minute) + if err != nil { + err = fmt.Errorf("rm appendonly 失败,err:%v", err) + mylog.Logger.Error(err.Error()) + full.Err = err + return + } + } + + } else if strings.Contains(full.ResultFullbackup[0].BackupFile, ".rdb") { + backupFile = full.ResultFullbackup[0].BackupFile + backupFilePath = filepath.Join(fullFilePath, backupFile) + dataPath = filepath.Join(consts.GetRedisDataDir(), "redis", strconv.Itoa(dstTplusPort), "data", "dump.rdb") + if appendonly == "yes" { + mylog.Logger.Info("appendonly value is:%s,need modify appendonly=no to use rdb", appendonly) + // 修改appendonly 值为no ,用rdb 文件拉起redis + modifyAppendonlyCmd := fmt.Sprintf("sed -i 's/appendonly yes/appendonly no/' %s", redisConfDir) + mylog.Logger.Info(modifyAppendonlyCmd) + _, err = util.RunLocalCmd("bash", []string{"-c", modifyAppendonlyCmd}, "", nil, 10*time.Minute) + if err != nil { + err = fmt.Errorf("modify appendonly 失败,err:%v", err) + mylog.Logger.Error(err.Error()) + full.Err = err + return + } + } + + // 如果存在原来的rdb文件,则删除,用最新的rdb拉起 + oldRdbdir := filepath.Join(consts.GetRedisDataDir(), "redis", strconv.Itoa(dstTplusPort), "data", "dump.rdb") + rmRdbCmd := fmt.Sprintf("rm %s", oldRdbdir) + _, err = os.Stat(oldRdbdir) + if err == nil && !os.IsNotExist(err) { + mylog.Logger.Info("删除原来存在的dump.rdb文件:%s", rmRdbCmd) + _, err = util.RunLocalCmd("bash", []string{"-c", rmRdbCmd}, "", nil, 10*time.Minute) + if err != nil { + err = fmt.Errorf("rm dump.rdb 失败,err:%v", err) + mylog.Logger.Error(err.Error()) + full.Err = err + return + } + } + + } + mylog.Logger.Info("dataPath:%s", dataPath) + + // 确保data 目录不为空 + if dataPath == "" { + err = fmt.Errorf("get dataPath failed,dataPath:%s", dataPath) + mylog.Logger.Error(err.Error()) + return + } + // 5、移动和重命名 备份文件 + util.LocalDirChownMysql(backupFilePath) + mvCmd := fmt.Sprintf("mv -f %s %s ", backupFilePath, dataPath) + mylog.Logger.Info("RecoverCacheRedisFromBackupFile: mvCmd:%s", mvCmd) + _, err = util.RunLocalCmd("bash", []string{"-c", mvCmd}, "", nil, 10*time.Minute) + if err != nil { + err = fmt.Errorf("mv aof file failed,err:%v", err) + full.Err = err + mylog.Logger.Error(err.Error()) + return + } + + // 6、加载备份文件,拉起节点 + err = full.WaitForStartRedis(dstTplusIP, dstTplusPort, dstTplusPasswd) + if err != nil { + mylog.Logger.Error("WaitForStartRedis failed :%v", err) + full.Err = err + return + } + + //再次探测tendisplus连接性->拉起是否成功 + redisCli, err = myredis.NewRedisClient(newRedisAddr, dstTplusPasswd, 0, consts.TendisTypeTendisplusInsance) + if err != nil { + mylog.Logger.Error("NewRedisClient failed :%v", err) + full.Err = err + return + } + defer redisCli.Close() + // 7、拉起Dbmon 作为整体在所有任务结束后操作 (前置任务来完成) + + msg = fmt.Sprintf("%s:%d 恢复全备成功", dstTplusIP, dstTplusPort) + mylog.Logger.Info(msg) + + return +} + +// WaitForStartRedis 加载redis +func (full *TplusFullBackPull) WaitForStartRedis(dstTplusIP string, dstTplusPort int, dstTplusPasswd string) error { + + mylog.Logger.Info("WaitForStartRedis:start-redis.sh,addr:%s:%d", dstTplusIP, dstTplusPort) + // 加载备份文件,拉起节点 + startScript := filepath.Join(consts.UsrLocal, "redis", "bin", "start-redis.sh") + mylog.Logger.Info(fmt.Sprintf("su %s -c \"%s\"", + consts.MysqlAaccount, startScript+" "+strconv.Itoa(dstTplusPort))) + _, err := util.RunLocalCmd("su", []string{consts.MysqlAaccount, "-c", + startScript + " " + strconv.Itoa(dstTplusPort)}, "", nil, 20*time.Minute) + if err != nil { + err = fmt.Errorf("WaitForStartRedis failed:%v", err) + return err + } + time.Sleep(30 * time.Second) + + newRedisAddr := fmt.Sprintf("%s:%s", dstTplusIP, strconv.Itoa(dstTplusPort)) + retryTimeLimit := 6 + // 等待加载redis + for { + if retryTimeLimit == 0 { + break + } + // NewRedisClient 这里也会去ping + _, err = myredis.NewRedisClient(newRedisAddr, dstTplusPasswd, 0, consts.TendisTypeRedisInstance) + if err != nil { + if retryTimeLimit > 0 { + mylog.Logger.Warn("WaitForStartRedis info:%v", err) + retryTimeLimit-- + time.Sleep(10 * time.Second) + continue + } + mylog.Logger.Error("WaitForStartRedis fail newRedisAddr:%s,retry times:%d,err:%v", newRedisAddr, retryTimeLimit, err) + return err + } + mylog.Logger.Info("WaitForStartRedis finish success") + break + } + return nil + +} + +// startBkDbmon start bk-dbmon +func (full *TplusFullBackPull) startBkDbmon() (err error) { + mylog.Logger.Info("start dbmon ...") + startScript := filepath.Join(consts.BkDbmonPath, "start.sh") + if !util.FileExists(startScript) { + err = fmt.Errorf("%sstart.sh not exists", consts.BkDbmonPath) + mylog.Logger.Error(err.Error()) + return + } + + startCmd := fmt.Sprintf("su %s -c 'nohup %s &'", consts.MysqlAaccount, "sh "+startScript) + mylog.Logger.Info(startCmd) + _, err = util.RunLocalCmd("su", []string{consts.MysqlAaccount, "-c", "nohup sh " + startScript + " &"}, + "", nil, 1*time.Minute) + if err != nil { + err = fmt.Errorf("执行启动dbmon命令:%s失败:%v", startCmd, err) + mylog.Logger.Error(err.Error()) + } + mylog.Logger.Info("start dbmon success") + + return +} + +// StopBkDbmon stop bk-dbmon +func (full *TplusFullBackPull) stopBkDbmon() (err error) { + + mylog.Logger.Info("stop dbmon start") + stopScript := filepath.Join(consts.BkDbmonPath, "stop.sh") + if !util.FileExists(stopScript) { + err = fmt.Errorf("%s stop.sh not exists", consts.BkDbmonPath) + mylog.Logger.Error(err.Error()) + return + + } + + stopCmd := fmt.Sprintf("su %s -c '%s'", consts.MysqlAaccount, "sh "+stopScript) + mylog.Logger.Info(stopCmd) + _, err = util.RunLocalCmd("su", []string{consts.MysqlAaccount, "-c", "sh " + stopScript}, + "", nil, 1*time.Minute) + if err != nil { + err = fmt.Errorf("执行关闭dbmon命令:%s失败:%v", stopCmd, err) + mylog.Logger.Error(err.Error()) + return + } + mylog.Logger.Info("stop dbmon success") + + return +} diff --git a/dbm-services/redis/db-tools/dbactuator/pkg/backupsys/rollback.go b/dbm-services/redis/db-tools/dbactuator/pkg/backupsys/rollback.go new file mode 100644 index 0000000000..8cba772bbe --- /dev/null +++ b/dbm-services/redis/db-tools/dbactuator/pkg/backupsys/rollback.go @@ -0,0 +1,1475 @@ +package backupsys + +import ( + "errors" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strconv" + "strings" + "time" + + "dbm-services/redis/db-tools/dbactuator/models/myredis" + "dbm-services/redis/db-tools/dbactuator/mylog" + "dbm-services/redis/db-tools/dbactuator/pkg/consts" + "dbm-services/redis/db-tools/dbactuator/pkg/jobruntime" + "dbm-services/redis/db-tools/dbactuator/pkg/util" +) + +// RedisInsRecoverTask 节点数据构造任务 +type RedisInsRecoverTask struct { + SourceIP string `json:"source_ip"` + SourcePort int `json:"source_ports" ` + NeWTempIP string `json:"new_temp_ip" ` + NewTmpPort int `json:"new_temp_ports"` + NewTmpPassword string `json:"new_tmp_password"` + RecoveryTimePoint string `json:"recovery_time_point"` + BackupFileDir string `json:"backup_file_dir"` // 解压后文件 + RecoverDir string `json:"recoverDir"` // 备份目录 + BackupFile string `json:"backup_file"` // 备份文件名 + FullBackup *TplusFullBackPull `json:"fullBackup"` + IncrBackup *TplusIncrBackPull `json:"incrBackup"` + SSDIncrBackup *TredisRocksDBIncrBack `json:"ssdIncrBackup"` + MyRollbackDir string `json:"myRollbackDir"` //从'我'的视角,回档目录 + NodeRollbackDir string `json:"NodeRollbackDir"` //从'Node'的视角,回档目录 + TendisType string `json:"tendis_type"` + IsIncludeSlave bool `json:"is_include_slave"` + IsPrecheck bool `json:"in_precheck"` + KvstoreNums int `json:"kvstore_nums"` + MasterVersion string `json:"master_version"` // 确定ssd 加载全备工具版本 + RestoreTool string `json:"restore_tool"` // ssd 加载全备工具 + SsdDataDir string `json:"ssd_data_dir"` // ssd 数据目录 + DepsDir string `json:"deps_dir"` // /usr/local/redis/bin/deps + redisCli *myredis.RedisClient + runtime *jobruntime.JobGenericRuntime + Err error `json:"-"` +} + +// NewRedisInsRecoverTask 新建数据构建任务 +func NewRedisInsRecoverTask(sourceIP string, sourcePort int, neWTempIP string, newTmpPort int, + newTmpPasswordsword, recoveryTimePoint, recoverDir, tendisType string, isIncludeSlave bool, isPrecheck bool, + runtime *jobruntime.JobGenericRuntime) (task *RedisInsRecoverTask, err error) { + return &RedisInsRecoverTask{ + SourceIP: sourceIP, + SourcePort: sourcePort, + NeWTempIP: neWTempIP, + NewTmpPort: newTmpPort, + NewTmpPassword: newTmpPasswordsword, + RecoveryTimePoint: recoveryTimePoint, + RecoverDir: recoverDir, + TendisType: tendisType, + IsIncludeSlave: isIncludeSlave, + IsPrecheck: isPrecheck, + runtime: runtime, + }, nil +} + +// GetRedisCli 获取redis连接 +func (task *RedisInsRecoverTask) GetRedisCli() error { + redisAddr := fmt.Sprintf("%s:%s", task.NeWTempIP, strconv.Itoa(task.NewTmpPort)) + msg := fmt.Sprintf("开始获取master:%s的连接...", redisAddr) + task.runtime.Logger.Info(msg) + redisCli, err := myredis.NewRedisClient(redisAddr, task.NewTmpPassword, 0, consts.TendisTypeTendisplusInsance) + if err != nil { + err = fmt.Errorf("获取连接失败:%v", err) + task.runtime.Logger.Error(err.Error()) + return err + } + task.runtime.Logger.Info("获取master:%s的连接成功", redisAddr) + task.redisCli = redisCli + return nil +} + +// GetRocksdbNum 获取tendisplus kvstore 个数 +func (task *RedisInsRecoverTask) GetRocksdbNum() (kvstorecounts int, err error) { + task.runtime.Logger.Info("GetRocksdbNum start ...") + + // 获取kvstore个数 + var kvstorecount string + kvstorecount, err = task.redisCli.GetKvstoreCount() + if err != nil { + err = fmt.Errorf("GetRocksdbNum GetKvstoreCount Err:%v", err) + task.runtime.Logger.Error(err.Error()) + return + } + task.runtime.Logger.Info("kvstorecount:%s", kvstorecount) + kvstorecounts, err = strconv.Atoi(kvstorecount) + if err != nil { + errMsg := fmt.Sprintf(" kvstorecount string to int failed err:%v", err) + task.runtime.Logger.Error(errMsg) + } + task.KvstoreNums = kvstorecounts + return +} + +// PrecheckTendis tendis 前置检查 +func (task *RedisInsRecoverTask) PrecheckTendis() error { + + task.runtime.Logger.Info("Precheck:检查链接是否可用") + // 检查tendis 是否可连接,是否在使用中 + redisAddr := fmt.Sprintf("%s:%s", task.NeWTempIP, strconv.Itoa(task.NewTmpPort)) + replInfo, err := task.redisCli.Info("replication") + if err != nil { + return err + } + role, _ := replInfo["role"] + + // 校验角色是否是master + if role != consts.RedisMasterRole { + err = fmt.Errorf("Precheck failed: target tendis:%s role:%s !=master", redisAddr, role) + task.runtime.Logger.Error(err.Error()) + return err + } + task.runtime.Logger.Info("%s: role:%s", redisAddr, role) + + //校验集群是否有业务在使用 + isUsing, cmds, err := task.redisCli.IsRedisUsing("redis-cli ", 10*time.Second) + if err != nil { + err = fmt.Errorf("Precheck failed: target tendis:%s isUsing:%v", redisAddr, err) + return err + } + if isUsing == true { + err = fmt.Errorf("Precheck failed: target tendis:%s is in use
cmds:%s", redisAddr, strings.Join(cmds, "
")) + return err + } + task.runtime.Logger.Info("target tendis:%s no using", redisAddr) + + return nil + +} + +// Precheck tendisplus && tendis ssd && tendis cache 前置检查 +// - 检查源tendis全备+binlog信息是否能获取; +// - 检查目的tendis磁盘空间是否足够; +// NOCC:golint/fnsize(设计如此) +func (task *RedisInsRecoverTask) Precheck() error { + + task.runtime.Logger.Info("Precheck:检查全备和binlog信息是否能获取,磁盘空间是否够") + task.runtime.Logger.Info("task.TendisType is:%s", task.TendisType) + // 节点维度的 + //尝试获取源tendis全备信息 + // 备份系统查询时过滤正则 + fullBack := &TplusFullBackPull{} + var binlogSize int64 + if task.TendisType == consts.TendisTypeTendisplusInsance { + incrBack := &TplusIncrBackPull{} + filename := fmt.Sprintf("TENDISPLUS-FULL-slave-%s-%d", task.SourceIP, task.SourcePort) + task.runtime.Logger.Info("需要匹配 filename:%s", filename) + + // 前置检查的时候还没部署节点,拿不到kvstoreNums信息,检查的是给固定值 10 + kvstoreNums := 10 + fullBack = NewFullbackPull(task.SourceIP, filename, task.RecoveryTimePoint, + task.NeWTempIP, task.RecoverDir, kvstoreNums, task.TendisType) + if fullBack.Err != nil { + return fullBack.Err + } + task.FullBackup = fullBack + // 后加 + task.FullBackup.GetTplusFullbackNearestRkTime() + if task.FullBackup.Err != nil { + return task.FullBackup.Err + } + task.runtime.Logger.Info("查询全备份信息结束") + // 后加 + + // 尝试获取源tendis binlog全备信息 + fileName := fmt.Sprintf("binlog-%s-%d", task.SourceIP, task.SourcePort) + task.runtime.Logger.Info("fileName:%s", fileName) + // 节点维度增备信息:fileName 过滤,task.SourceIP 备份的源IP + incrBack = NewTplusIncrBackPull(fileName, task.SourceIP) + if incrBack.Err != nil { + return incrBack.Err + } + layout := "2006-01-02 15:04:05" + rbDstTime, _ := time.ParseInLocation(layout, task.RecoveryTimePoint, time.Local) + //回档目标时间 比 用户填写的时间多1秒 + //(因为binlog_tool的--end-datetime参数,--end-datetime这个时间点的binlog是不会被应用的) + rbDstTime = rbDstTime.Add(1 * time.Second) + task.runtime.Logger.Info("回档目标时间 rbDstTime:%v", rbDstTime) + + for i := 0; i < kvstoreNums; i++ { + //每个rocksdb全备的startTimeSec是不一样的(所以其拉取的增备范围也是不一样的) + //其对应的startTimeSec可以从全备文件中获取到 + //其对应的startPos也是不同的,从全备中获取这里只是查询,所以startPos传入100也是可以的 + + //这里粗略地 以全备的创建时间 作为 binlog拉取的startTime + //后面真实的 binlog拉取startTime 需要从全备文件中获取 + // task.runtime.Logger.Info("kvstore:%d ,backupMeta:%v", i, backupMeta) + // kvstore 维度的 的拉取备份文件任务,每个kvstore都是一个任务,因为kvstore的开始时间不一样 + + incrBack.NewRocksDBIncrBack(i, 100, task.FullBackup.ResultFullbackup[0].BackupStart.Local().Format(layout), + rbDstTime.Local().Format(layout), task.NeWTempIP, task.RecoverDir, task.RecoverDir) + if incrBack.Err != nil { + return incrBack.Err + } + } + task.IncrBackup = incrBack + task.runtime.Logger.Info("IncrBackup,特定节点的增备信息:%v", task.IncrBackup) + // 获取节点维度的备份信息 + task.IncrBackup.GetAllIncrBacksInfo() + if task.IncrBackup.Err != nil { + return task.IncrBackup.Err + } + // binlog 文件大小 + binlogSize = incrBack.TotalSize() + + } else if task.TendisType == consts.TendisTypeTendisSSDInsance { + + filename := fmt.Sprintf("TENDISSSD-FULL-slave-%s-%d", task.SourceIP, task.SourcePort) + task.runtime.Logger.Info("需要匹配 filename:%s", filename) + fullBack = NewFullbackPull(task.SourceIP, filename, task.RecoveryTimePoint, + task.NeWTempIP, task.RecoverDir, 0, task.TendisType) + if fullBack.Err != nil { + return fullBack.Err + } + task.FullBackup = fullBack + + task.FullBackup.GetTplusFullbackNearestRkTime() + if task.FullBackup.Err != nil { + return task.FullBackup.Err + } + task.runtime.Logger.Info("查询全备份信息结束") + + //新加 binlog 维度 + // 尝试获取源tendis ssd binlog全备信息 + // 节点维度增备信息:fileName 过滤,task.SourceIP 备份的源IP + fileName := fmt.Sprintf("binlog-%s-%d", task.SourceIP, task.SourcePort) + task.runtime.Logger.Info("fileName:%s", fileName) + + layout := "2006-01-02 15:04:05" + rbDstTime, _ := time.ParseInLocation(layout, task.RecoveryTimePoint, time.Local) + //回档目标时间 比 用户填写的时间多1秒 + //(因为binlog_tool的--end-datetime参数,--end-datetime这个时间点的binlog是不会被应用的) + rbDstTime = rbDstTime.Add(1 * time.Second) + task.runtime.Logger.Info("回档目标时间 rbDstTime:%v", rbDstTime) + + //传入全备份开始时间和回档时间 + // startTime 拉取增备的开始时间 -> 全备份的开始时间 + // endTime 拉取增备份的结束时间 -> 回档时间 + ssdIncrBackup := NewTredisRocksDBIncrBack(fileName, task.SourceIP, 100, + task.FullBackup.ResultFullbackup[0].BackupStart.Local().Format(layout), + rbDstTime.Local().Format(layout), task.NeWTempIP, task.RecoverDir, task.RecoverDir, task.RecoveryTimePoint) + if ssdIncrBackup.Err != nil { + task.Err = ssdIncrBackup.Err + return task.Err + } + + task.SSDIncrBackup = ssdIncrBackup + task.runtime.Logger.Info("ssdIncrBackup,特定节点的增备信息:%v", task.SSDIncrBackup) + // 获取节点维度的备份信息 + task.SSDIncrBackup.GetTredisIncrbacksSpecRocks() + if task.SSDIncrBackup.Err != nil { + task.Err = ssdIncrBackup.Err + return task.Err + } + // binlog 文件大小 + binlogSize = task.SSDIncrBackup.TotalSize() + + } else if task.TendisType == consts.TendisTypeRedisInstance { + + filename := fmt.Sprintf("%s-%d", task.SourceIP, task.SourcePort) + task.runtime.Logger.Info("需要匹配 filename:%s", filename) + fullBack = NewFullbackPull(task.SourceIP, filename, task.RecoveryTimePoint, + task.NeWTempIP, task.RecoverDir, 0, task.TendisType) + if fullBack.Err != nil { + return fullBack.Err + } + task.FullBackup = fullBack + task.FullBackup.GetTplusFullbackNearestRkTime() + if task.FullBackup.Err != nil { + return task.FullBackup.Err + } + } + + task.runtime.Logger.Info("binlogSize:%d", binlogSize) + + //确认磁盘空间足够 + // - 可用空间必须大于 全备大小+binlog大小 + // - 总空间必须大于 全备大小+binlog大小 的两倍 + backupSize := fullBack.TotalSize() + binlogSize + needSize := 1.2 * float64(backupSize) + // 检查下载磁盘空间是否足够 + // 备份盘 + bakDiskUsg, err := util.GetLocalDirDiskUsg(task.RecoverDir) + if err != nil { + task.runtime.Logger.Error(err.Error()) + return err + } + // 数据盘 + dataDiskUsg, err := util.GetLocalDirDiskUsg(consts.GetRedisDataDir()) + if err != nil { + task.runtime.Logger.Error(err.Error()) + return err + } + + //检查备份空间是否大于 全备大小+binlog大小 + if int64(bakDiskUsg.AvailSize) < backupSize { + err = fmt.Errorf("Precheck failed: ip:%s 备份目录磁盘可用空间:%d MB 小于备份文件所需磁盘空间:%d MB", + task.NeWTempIP, bakDiskUsg.AvailSize/1024/1024.0, backupSize/1024/1024.0) + task.runtime.Logger.Error(err.Error()) + return err + + } + task.runtime.Logger.Info("bakDiskUsg.AvailSize:%d MB backupSize:%d MB", + bakDiskUsg.AvailSize/1024/1024.0, backupSize/1024/1024.0) + + //检查数据盘是否大于 1.2*(全备大小+binlog)大小 + if float64(dataDiskUsg.AvailSize) < needSize { + err = fmt.Errorf("Precheck failed: ip:%s 数据目录磁盘可用空间:%d MB 小于备份文件所需磁盘空间:%d MB", + task.NeWTempIP, dataDiskUsg.AvailSize/1024/1024.0, backupSize/1024/1024.0) + task.runtime.Logger.Error(err.Error()) + return err + + } + + task.runtime.Logger.Info("dataDiskUsg.AvailSize:%d MB(%d GB),backupSize:%d MB(%d GB)(%d KB)", + dataDiskUsg.AvailSize/1024/1024.0, dataDiskUsg.AvailSize/1024/1024/1024.0, + backupSize/1024/1024.0, backupSize/1024/1024/1024.0, backupSize/1024.0) + + // 磁盘空间使用已有85%,则报错 + if bakDiskUsg.UsageRatio > 85 || dataDiskUsg.UsageRatio > 85 { + err = fmt.Errorf("Precheck failed: %s disk Used%d%% > 85%% or %s disk Used(%d%%) >85%%", + task.RecoverDir, bakDiskUsg.UsageRatio, + consts.GetRedisDataDir(), dataDiskUsg.UsageRatio) + task.runtime.Logger.Error(err.Error()) + return err + } + + return nil + +} + +// ClearAllData 清理目标集群数据 +func (task *RedisInsRecoverTask) ClearAllData() error { + redisAddr := fmt.Sprintf("%s:%s", task.NeWTempIP, strconv.Itoa(task.NewTmpPort)) + msg := fmt.Sprintf("开始清理master:%s数据(fluashall)", redisAddr) + task.runtime.Logger.Info(msg) + + cmd := []string{consts.TendisPlusFlushAllRename} + result, err := task.redisCli.DoCommand(cmd, 0) + if err != nil { + return err + } + + if !strings.Contains(result.(string), "OK") { + err = fmt.Errorf("flush all master[%s]", redisAddr) + task.runtime.Logger.Error(err.Error()) + return err + } + + msg = fmt.Sprintf("清理master:%s数据(fluashall)完成", redisAddr) + task.runtime.Logger.Info(msg) + return nil +} + +// ClusterResetMaster 断开目的回档节点和集群的联系(cluster reset) +func (task *RedisInsRecoverTask) ClusterResetMaster() error { + redisAddr := fmt.Sprintf("%s:%s", task.NeWTempIP, strconv.Itoa(task.NewTmpPort)) + msg := fmt.Sprintf("开始断开master:%s与集群的联系(cluster reset)...", redisAddr) + task.runtime.Logger.Info(msg) + + err := task.redisCli.ClusterReset() + if err != nil { + return err + } + time.Sleep(5 * time.Second) //sleep 2 seconds + //检查确实与集群断开联系 + runningMasters, err := task.redisCli.GetRunningMasters() + if err != nil { + return err + } + if len(runningMasters) != 1 { + err = fmt.Errorf("master:%s cluster reset fail,running master count:%d > 1", + redisAddr, len(runningMasters)) + str, _ := task.redisCli.GetClusterNodesStr() + task.runtime.Logger.Error("cluster nodes%v,err:%v", str, err) + + } + msg = fmt.Sprintf("断开master:%s与集群的联系(cluster reset)成功", redisAddr) + task.runtime.Logger.Info(msg) + + return nil + +} + +// StopSlave 断开slave到master的同步关系 +func (task *RedisInsRecoverTask) StopSlave() error { + redisAddr := fmt.Sprintf("%s:%s", task.NeWTempIP, strconv.Itoa(task.NewTmpPort)) + msg := fmt.Sprintf("开始断开slave到master:%s的同步关系...", redisAddr) + task.runtime.Logger.Info(msg) + + //找到目标tendisplus 的slave + slaveNodeList, err := task.redisCli.GetAllSlaveNodesByMasterAddr(redisAddr) + if err != nil && util.IsNotFoundErr(err) == false { + return err + } + if err != nil && util.IsNotFoundErr(err) == true { + msg = fmt.Sprintf("master:%s 没有发现连接的slave,无需断开slave连接", redisAddr) + task.runtime.Logger.Error(msg) + return err + } + for _, slaveNodeTmp := range slaveNodeList { + slaveNode01 := slaveNodeTmp + + msg = fmt.Sprintf("master:%s开始断开slave:%s同步关系", redisAddr, slaveNode01.Addr) + task.runtime.Logger.Info(msg) + + //断开同步关系 + newCli01, err := myredis.NewRedisClient(slaveNode01.Addr, task.NewTmpPassword, 0, consts.TendisTypeTendisplusInsance) + if err != nil { + return err + } + err = newCli01.ClusterReset() + if err != nil { + newCli01.Close() + return err + } + time.Sleep(5 * time.Second) + + //检查同步关系确实已断开 + infoData, err := newCli01.Info("replication") + if err != nil { + newCli01.Close() + return err + } + newCli01.Close() + role, _ := infoData["role"] + if role != "master" { + err = fmt.Errorf("%s 执行'cluster reset'失败,info replication结果role:%s,而不是master", slaveNode01.Addr, role) + task.runtime.Logger.Error(err.Error()) + return err + } + msg = fmt.Sprintf("master:%s 断开slave:%s同步成功,slave Role:%s", redisAddr, slaveNode01.Addr, role) + task.runtime.Logger.Info(msg) + } + + msg = fmt.Sprintf("master:%s 共断开%d个slave同步关系", redisAddr, len(slaveNodeList)) + task.runtime.Logger.Info(msg) + return nil +} + +// GetBackupFileExt 获取全备文件后缀 +func (task *RedisInsRecoverTask) GetBackupFileExt(backupFilePath string) (fileExt string, err error) { + fileExt = filepath.Ext(backupFilePath) + if fileExt == ".tar" || fileExt == ".tgz" { + return fileExt, nil + } else if strings.HasSuffix(backupFilePath, ".tar.gz") { + fileExt = ".tar.gz" + } else { + err = fmt.Errorf("无法解压全备文件:%s", backupFilePath) + task.runtime.Logger.Error(err.Error()) + return "", err + } + return fileExt, nil +} + +// GetDecompressedDir 获取 recoverDir 的值,全备解压目录 +// 如全备名是 3-TENDISPLUS-FULL-slave-127.0.0.x-30002-20230810-050140.tar +// 则值为 3-TENDISPLUS-FULL-slave-127.0.0.x-30002-20230810-050140 +func (task *RedisInsRecoverTask) GetDecompressedDir() (decpDir string, err error) { + if task.BackupFileDir != "" { + return task.BackupFileDir, nil + } + err = fmt.Errorf("BackupFileDir:%s 没有复赋值", task.BackupFileDir) + task.runtime.Logger.Error(err.Error()) + return decpDir, err + +} + +// FindDstFileInDir 在指定文件夹下找到目标文件 +func (task *RedisInsRecoverTask) FindDstFileInDir(dir string, dstFile string) (dstFilePos string, err error) { + err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.Name() == dstFile { + dstFilePos = path + return nil + } + return nil + }) + if err != nil { + err = fmt.Errorf("findDstFileInDir filepath.Walk fail,err:%v,dir:%s dstFile:%s", err, dir, dstFile) + task.runtime.Logger.Error(err.Error()) + return "", err + } + if dstFilePos == "" { + info := fmt.Sprintf("the destination file was not found in dir,dstFile:%s,dir:%s", dstFile, dir) + task.runtime.Logger.Info(info) + return "", util.NewNotFoundErr() + } + return +} + +// CheckDecompressedDirIsOK 检查全备解压文件夹 是否存在,数据是否完整 +// 数据是否完整:通过判断是否有clustermeta.txt、${rocksdbIdx}/backup_meta文件来确认 +func (task *RedisInsRecoverTask) CheckDecompressedDirIsOK() (isExists, isCompelete bool, msg string) { + + redisAddr := fmt.Sprintf("%s:%s", task.NeWTempIP, strconv.Itoa(task.NewTmpPort)) + msg = fmt.Sprintf("开始检查全备(已解压)目录是否存在,是否完整") + task.runtime.Logger.Info(msg) + //测试tendisplus连接性 + redisCli, err := myredis.NewRedisClient(redisAddr, task.NewTmpPassword, 0, consts.TendisTypeTendisplusInsance) + if err != nil { + return false, false, msg + } + defer redisCli.Close() + + isExists = false + isCompelete = false + + decpDir, err := task.GetDecompressedDir() + if err != nil { + return + } + + if _, err := os.Stat(decpDir); os.IsNotExist(err) { + msg = fmt.Sprintf("全备解压目录:%s 不存在", decpDir) + task.runtime.Logger.Info(msg) + return + } + isExists = true //解压目录存在 + var metaFile string + metaFile, err = task.FindDstFileInDir(decpDir, "clustermeta.txt") + if err != nil { + return + } + if _, err := os.Stat(metaFile); os.IsNotExist(err) { + //clustermeta.txt 不存在 + msg = fmt.Sprintf("全备解压目录:%s 中找不到 clustermeta.txt 文件", decpDir) + task.runtime.Logger.Info(msg) + isCompelete = false + return + } + ClusterMeataDir := filepath.Dir(metaFile) + + // 获取kvstore个数 + var kvstorecount string + kvstorecount, err = redisCli.GetKvstoreCount() + if err != nil { + err = fmt.Errorf("CheckDecompressedDirIsOK GetKvstoreCount Err:%v", err) + task.runtime.Logger.Error(err.Error()) + return + } + task.runtime.Logger.Info("kvstorecount:%s", kvstorecount) + kvstorecounts, err := strconv.Atoi(kvstorecount) + if err != nil { + errMsg := fmt.Sprintf("%s kvstorecount string to int failed err:%v", redisAddr, task.Err) + task.runtime.Logger.Error(errMsg) + } + + for i := 0; i < kvstorecounts; i++ { + rocksdbBackupMeta := filepath.Join(ClusterMeataDir, fmt.Sprintf("%d/backup_meta", i)) + if _, err := os.Stat(rocksdbBackupMeta); os.IsNotExist(err) { + msg = fmt.Sprintf("全备解压目录:%s/%d/backup_meta 找不到", decpDir, i) + task.runtime.Logger.Info(msg) + isCompelete = false + return + } + } + msg = fmt.Sprintf("解压目录存在且完整:%s", decpDir) + task.runtime.Logger.Info(msg) + //解压文件存在且是完整的 + return true, true, msg +} + +// Decompress 解压文件 +func (task *RedisInsRecoverTask) Decompress(fileName string) error { + + // 解压 .lzo 类型文件 + if strings.HasSuffix(fileName, ".lzo") { + // 检查 lzop 是否存在 + lzopBin := consts.LzopBin + _, err := os.Stat(lzopBin) + if err != nil && os.IsNotExist(err) { + task.runtime.Logger.Error("Decompress: 解压工具 lzop 不存在,"+ + "请检查 %s 是否存在 err:%v", consts.LzopBin, err) + task.runtime.Logger.Error(err.Error()) + return err + + } + // 解压 + DecompressCmd := fmt.Sprintf("%s -d %s", lzopBin, fileName) + task.runtime.Logger.Info("DecompressCmd:%s", DecompressCmd) + _, err = util.RunLocalCmd("bash", []string{"-c", DecompressCmd}, "", nil, 10*time.Minute) + if err != nil { + task.runtime.Logger.Error("Decompress: 解压失败,请检查 %s 是否异常 err:%v", fileName, err) + return err + } + return nil + } + + var DecompressCmd string + bkFileExt, err := task.GetBackupFileExt(fileName) + if err != nil { + return err + } + if bkFileExt == ".tar" { + DecompressCmd = fmt.Sprintf("tar -xf %s", fileName) + } else if bkFileExt == ".tar.gz" || bkFileExt == ".tgz" { + DecompressCmd = fmt.Sprintf("tar -zxf %s", fileName) + } + + if DecompressCmd != "" { + + //将全备文件解压到指定目录下 + DecompressCmd = fmt.Sprintf("cd %s && %s -C %s", task.RecoverDir, DecompressCmd, task.RecoverDir) + msg := fmt.Sprintf("解压命令:%s", DecompressCmd) + task.runtime.Logger.Info(msg) + _, err = util.RunLocalCmd("bash", []string{"-c", DecompressCmd}, "", nil, 10*time.Minute) + if err != nil { + return err + } + if task.TendisType == consts.TendisTypeTendisplusInsance { + isExists, isCompelete, msg := task.CheckDecompressedDirIsOK() + if err != nil { + return err + } + if isExists == false || isCompelete == false { + //如果不存在或不完整 + err = errors.New(msg) + task.runtime.Logger.Error(err.Error()) + return err + } + + } else if task.TendisType == consts.TendisTypeTendisSSDInsance { + // todo + task.runtime.Logger.Info("检查tendis ssd 的全备解压文件是否有效") + decpDir, err := task.GetDecompressedDir() + if err != nil { + return err + } + task.FullBackup.tendisSSDBackupVerify(decpDir) + } + + // (测试先不删)删除 源文件 + err = task.RmLocalBakcupFile() + if err != nil { + return err + } + } + + return nil +} + +// RmLocalBakcupFile 删除本地全备(未解压)文件 +func (task *RedisInsRecoverTask) RmLocalBakcupFile() error { + bkFileFullPath := filepath.Join(task.RecoverDir, task.BackupFile) + if _, err := os.Stat(bkFileFullPath); os.IsNotExist(err) { + return err + } + rmCmd := fmt.Sprintf("cd %s && rm -rf %s 2>/dev/null", task.RecoverDir, task.BackupFile) + _, err := util.RunLocalCmd("bash", []string{"-c", rmCmd}, "", nil, 30*time.Minute) + if err != nil { + return err + } + msg := fmt.Sprintf("本地全备(未解压):%s 删除成功", task.BackupFile) + task.runtime.Logger.Info(msg) + return nil +} + +// RecoverClusterSlots 恢复tendisplus slots信息 +// 依据全备clustermeta.txt文件中slot:xxx-xxxx信息,cluster addslot +func (task *RedisInsRecoverTask) RecoverClusterSlots() error { + redisAddr := fmt.Sprintf("%s:%s", task.NeWTempIP, strconv.Itoa(task.NewTmpPort)) + msg := fmt.Sprintf("开始恢复master:%s slots信息", redisAddr) + task.runtime.Logger.Info(msg) + + fullClusterMeta, err := task.GetClusterMeata() + if err != nil { + return err + } + msg = fmt.Sprintf("redisAddr:%s全备中slots:%s", redisAddr, fullClusterMeta.Slot) + task.runtime.Logger.Info(msg) + + slotList, _, _, _, err := myredis.DecodeSlotsFromStr(fullClusterMeta.Slot, "") + if err != nil { + return err + } + _, err = task.redisCli.ClusterAddSlots(slotList) + if err != nil { + return err + } + msg = fmt.Sprintf("恢复master:%s slots信息:%s 完成", redisAddr, fullClusterMeta.Slot) + task.runtime.Logger.Info(msg) + return nil +} + +// RecoverSlave 恢复slave同步关系 +// NOCC:golint/fnsize(设计如此) +func (task *RedisInsRecoverTask) RecoverSlave() error { + redisAddr := fmt.Sprintf("%s:%s", task.NeWTempIP, strconv.Itoa(task.NewTmpPort)) + msg := fmt.Sprintf("开始恢复slave到master:%s的同步关系...", redisAddr) + task.runtime.Logger.Info(msg) + + //获取master的nodeiD + var rbTenplusNodeData *myredis.ClusterNodeData = nil + _, err := task.redisCli.GetClusterNodes() + if err != nil { + return err + } + clusterNodes, err := task.redisCli.GetAddrMapToNodes() + if err != nil { + return err + } + rbTenplusNodeData, ok := clusterNodes[redisAddr] + if ok == false { + err = fmt.Errorf("获取%s cluster nodes信息失败,结果为空", redisAddr) + str01, _ := task.redisCli.GetClusterNodesStr() + task.runtime.Logger.Info("cluster nodes:%v", str01) + task.runtime.Logger.Error(err.Error()) + return err + } + msg = fmt.Sprintf("master:%s 对应NodeID:%s", redisAddr, rbTenplusNodeData.NodeID) + task.runtime.Logger.Info(msg) + + // 从恢复目录的cluster_nodes.txt获取slave信息,cluster_nodes.txt是任务开始时生成的 + slaveNodes, err := task.GetTplusSlaveNodes(redisAddr) + if err != nil { + err = fmt.Errorf("master:%s 从cluster_nodes.txt中没有找到任何slave", redisAddr) + task.runtime.Logger.Error(err.Error()) + return err + } + + msg = fmt.Sprintf("master:%s 从cluster nodes中共找到%d个slave", redisAddr, len(slaveNodes)) + task.runtime.Logger.Info(msg) + maxRetryTimes := 10 //最多重试10次 + isOK := false + for _, slaveNode01 := range slaveNodes { + slaveNodeItem := slaveNode01 + slaveAddr := slaveNodeItem.Addr + slaveCli02, err := myredis.NewRedisClient(slaveAddr, task.NewTmpPassword, 0, consts.TendisTypeTendisplusInsance) + if err != nil { + return err + } + msg = fmt.Sprintf("master:%s 'cluster meet' slave:%s", redisAddr, slaveAddr) + task.runtime.Logger.Info(msg) + + list01 := strings.Split(slaveAddr, ":") + //cluster meet slave + _, err = task.redisCli.ClusterMeet(list01[0], list01[1]) + if err != nil { + slaveCli02.Close() + return err + } + //确保slave 和 master已connected + idx := 0 + for ; idx < maxRetryTimes; idx++ { + time.Sleep(5 * time.Second) + _, err = slaveCli02.GetClusterNodes() + if err != nil { + return err + } + clusterNodes, err = slaveCli02.GetAddrMapToNodes() + if err != nil { + return err + } + _, isOK = clusterNodes[redisAddr] + if isOK == true { + msg = fmt.Sprintf("get master info success in slave after 'cluster meet',master:%s,slave:%s", + redisAddr, slaveAddr) + task.runtime.Logger.Info(msg) + break + } + msg = fmt.Sprintf("slave:%s still not connected master:%s", slaveAddr, redisAddr) + task.runtime.Logger.Info(msg) + str01, _ := slaveCli02.GetClusterNodesStr() + task.runtime.Logger.Info("print slave cluster nodes:%v", str01) + } + if isOK == false { + err = fmt.Errorf("slave cannot connect master after 'cluster meet',slave:%s,master:%s", + slaveAddr, redisAddr) + task.runtime.Logger.Error(err.Error()) + return err + } + msg = fmt.Sprintf("slave:%s 'cluster replicate' master nodeID:%s", slaveAddr, rbTenplusNodeData.NodeID) + task.runtime.Logger.Info(msg) + //cluster replicate + _, err = slaveCli02.ClusterReplicate(rbTenplusNodeData.NodeID) + if err != nil { + slaveCli02.Close() + return err + } + time.Sleep(5 * time.Second) + //检查同步确实恢复 + infoData, err := slaveCli02.Info("replication") + if err != nil { + slaveCli02.Close() + return err + } + slaveCli02.Close() + role, _ := infoData["role"] + if role != consts.RedisSlaveRole { + err = fmt.Errorf("%s 执行'cluster replicate %s'失败,info replication结果role:%s,而不是slave", + slaveAddr, rbTenplusNodeData.NodeID, role) + task.runtime.Logger.Error(err.Error()) + return err + } + msg = fmt.Sprintf("slave:%s 'info replication'=>role:%s", slaveAddr, role) + task.runtime.Logger.Info(msg) + } + msg = fmt.Sprintf("master:%s成功恢复%d个slave同步关系", redisAddr, len(slaveNodes)) + task.runtime.Logger.Info(msg) + return nil +} + +// GetClusterNodes 从备份文件目录获取clusternode信息,从而获取master的slaves信息 +func (task *RedisInsRecoverTask) GetClusterNodes() (fileData []byte, err error) { + if task.RecoverDir == "" { + err = fmt.Errorf("task.RecoverDir:%s 为空,请检查RestoreBackup功能处的解压和赋值情况", task.RecoverDir) + task.runtime.Logger.Error(err.Error()) + return nil, err + } + metaFile := filepath.Join(task.RecoverDir, "cluster_nodes.txt") + _, err = os.Stat(metaFile) + if err != nil { + err = fmt.Errorf("%s os.Stat fail,err:%v", metaFile, err) + task.runtime.Logger.Error(err.Error()) + return nil, err + } + fileData, err = ioutil.ReadFile(metaFile) + if err != nil { + err = fmt.Errorf("读取cluster_nodes文件:%s失败,err:%v", metaFile, err) + task.runtime.Logger.Error(err.Error()) + return nil, err + } + task.runtime.Logger.Info("cluster_nodes:%s,fileData:%s", metaFile, fileData) + + return fileData, nil +} + +// GetTplusSlaveNodes 从本地记录的cluster nodes中获得master的slave +func (task *RedisInsRecoverTask) GetTplusSlaveNodes(masterAddr string) ( + + slaveNodes []*myredis.ClusterNodeData, err error) { + fileData, err := task.GetClusterNodes() + if err != nil { + return nil, err + } + + nodesData, err := myredis.DecodeClusterNodes(string(fileData)) + if err != nil { + return nil, err + } + task.runtime.Logger.Info("get nodesData from cluster_nodes.txt success:%v", nodesData) + m01 := make(map[string]*myredis.ClusterNodeData) + for _, tmpItem := range nodesData { + infoItem := tmpItem + m01[infoItem.Addr] = infoItem + } + masterNode, ok := m01[masterAddr] + if ok == false { + err = fmt.Errorf("not found master node:%s", masterAddr) + task.runtime.Logger.Error(err.Error()) + return nil, err + } + if masterNode.Role != consts.RedisMasterRole { + err = fmt.Errorf("node:%s not a master(role:%s)", masterAddr, masterNode.Role) + task.runtime.Logger.Error(err.Error()) + return nil, err + } + for _, info01 := range m01 { + infoItem := info01 + // NOCC:tosa/linelength(其他) + if infoItem.Role == consts.RedisSlaveRole && infoItem.LinkState == consts.RedisLinkStateConnected && infoItem.MasterID == masterNode.NodeID { + msg := fmt.Sprintf("master:%s 找到一个slave:%s ", masterAddr, infoItem.Addr) + task.runtime.Logger.Info(msg) + slaveNodes = append(slaveNodes, infoItem) + } + } + if len(slaveNodes) == 0 { + msg := fmt.Sprintf("master:%s 没有找到任何slave信息", masterAddr) + task.runtime.Logger.Info(msg) + err = util.NewNotFoundErr() + task.runtime.Logger.Error(err.Error()) + return nil, err + } + task.runtime.Logger.Info("master:%s, get slaveNodes from cluster_nodes.txt success,slaveNodes:%v", + masterAddr, slaveNodes) + + return slaveNodes, nil + +} + +// PullFullbackup 拉取全备 +func (task *RedisInsRecoverTask) PullFullbackup() error { + fullBack := &TplusFullBackPull{} + if task.TendisType == consts.TendisTypeTendisplusInsance { + // 节点维度的 + // 备份系统查询时过滤正则 + filename := fmt.Sprintf("TENDISPLUS-FULL-slave-%s-%d", task.SourceIP, task.SourcePort) + task.runtime.Logger.Info("filename:%s", filename) + kvstoreNums, err := task.GetRocksdbNum() + if err != nil { + return err + } + fullBack = NewFullbackPull(task.SourceIP, filename, task.RecoveryTimePoint, + task.NeWTempIP, task.RecoverDir, kvstoreNums, task.TendisType) + if fullBack.Err != nil { + return fullBack.Err + } + // + task.runtime.Logger.Info("PullFullbackup fullBack.FileHead :%s", fullBack.FileHead) + + } else if task.TendisType == consts.TendisTypeTendisSSDInsance { + filename := fmt.Sprintf("TENDISSSD-FULL-slave-%s-%d", task.SourceIP, task.SourcePort) + task.runtime.Logger.Info("filename:%s", filename) + fullBack = NewFullbackPull(task.SourceIP, filename, task.RecoveryTimePoint, + task.NeWTempIP, task.RecoverDir, 0, task.TendisType) + if fullBack.Err != nil { + return fullBack.Err + } + + } else if task.TendisType == consts.TendisTypeRedisInstance { + + filename := fmt.Sprintf("%s-%d", task.SourceIP, task.SourcePort) + task.runtime.Logger.Info("需要匹配 filename:%s", filename) + fullBack = NewFullbackPull(task.SourceIP, filename, task.RecoveryTimePoint, + task.NeWTempIP, task.RecoverDir, 0, task.TendisType) + if fullBack.Err != nil { + return fullBack.Err + } + task.FullBackup = fullBack + } + + task.FullBackup = fullBack + // + task.runtime.Logger.Info("PullFullbackup task.FullBackup.FileHead :%s", task.FullBackup.FileHead) + + // 获取节点维度的所有文件信息 + task.FullBackup.GetTplusFullbackNearestRkTime() + if task.FullBackup.Err != nil { + return task.FullBackup.Err + } + // 备份文件解压 + task.FullBackup.PullFullbackDecompressed() + if task.FullBackup.Err != nil { + return task.FullBackup.Err + } + return nil +} + +// RestoreFullbackup 导入全备 +func (task *RedisInsRecoverTask) RestoreFullbackup() error { + redisAddr := fmt.Sprintf("%s:%s", task.NeWTempIP, strconv.Itoa(task.NewTmpPort)) + msg := fmt.Sprintf("master:%s开始导入全备", redisAddr) + task.runtime.Logger.Info(msg) + //再次探测tendisplus连接性 + redisCli, err := myredis.NewRedisClient(redisAddr, task.NewTmpPassword, 0, consts.TendisTypeTendisplusInsance) + if err != nil { + return err + } + defer redisCli.Close() + + task.FullBackup.RestoreBackup(task.NeWTempIP, task.NewTmpPort, task.NewTmpPassword) + if task.FullBackup.Err != nil { + task.Err = task.FullBackup.Err + return err + } + msg = fmt.Sprintf("master:%s导入全备完成", redisAddr) + task.runtime.Logger.Info(msg) + return nil +} + +// PullIncrbackup 拉取增备 +func (task *RedisInsRecoverTask) PullIncrbackup() { + task.runtime.Logger.Info("PullIncrbackup start...") + + // 节点维度的 + redisAddr := fmt.Sprintf("%s:%s", task.NeWTempIP, strconv.Itoa(task.NewTmpPort)) + fileName := fmt.Sprintf("binlog-%s-%d", task.SourceIP, task.SourcePort) + task.runtime.Logger.Info("fileName:%s", fileName) + // 节点维度增备信息:fileName 过滤,task.SourceIP 备份的源IP + incrBack := NewTplusIncrBackPull(fileName, task.SourceIP) + if incrBack.Err != nil { + task.Err = incrBack.Err + return + } + layout := "2006-01-02 15:04:05" + rbDstTime, _ := time.ParseInLocation(layout, task.RecoveryTimePoint, time.Local) + //回档目标时间 比 用户填写的时间多1秒 + //(因为binlog_tool的--end-datetime参数,--end-datetime这个时间点的binlog是不会被应用的) + rbDstTime = rbDstTime.Add(1 * time.Second) + task.runtime.Logger.Info("回档目标时间 rbDstTime:%v", rbDstTime) + // 获取kvstore个数 + var kvstorecount string + kvstorecount, err := task.redisCli.GetKvstoreCount() + if err != nil { + err = fmt.Errorf("PullIncrbackup GetKvstoreCount Err:%v", err) + task.runtime.Logger.Error(err.Error()) + return + } + task.runtime.Logger.Info("kvstorecount:%s", kvstorecount) + kvstorecounts, err := strconv.Atoi(kvstorecount) + if err != nil { + errMsg := fmt.Sprintf("%s kvstorecount string to int failed err:%v", redisAddr, task.Err) + task.runtime.Logger.Error(errMsg) + } + for i := 0; i < kvstorecounts; i++ { + //每个rocksdb全备的startTimeSec是不一样的(所以其拉取的增备范围也是不一样的) + //其对应的startTimeSec可以从全备文件中获取到 + //其对应的startPos也是不同的,从全备中获取 + backupMeta, err := task.GetRocksdbBackupMeta(i) + if err != nil { + task.Err = err + return + } + // task.runtime.Logger.Info("kvstore:%d ,backupMeta:%v", i, backupMeta) + // kvstore 维度的 的拉取备份文件任务,每个kvstore都是一个任务,因为kvstore的开始时间不一样 + incrBack.NewRocksDBIncrBack(i, backupMeta.BinlogPos+1, backupMeta.StartTime.Local().Format(layout), + rbDstTime.Local().Format(layout), task.NeWTempIP, task.RecoverDir, task.RecoverDir) + if incrBack.Err != nil { + task.Err = incrBack.Err + return + } + } + task.IncrBackup = incrBack + task.runtime.Logger.Info("IncrBackup,特定节点的增备信息:%v", task.IncrBackup) + // 获取节点维度的备份信息 + task.IncrBackup.GetAllIncrBacksInfo() + if task.IncrBackup.Err != nil { + task.Err = incrBack.Err + return + } + // 拉取节点维度的所有文件 + task.IncrBackup.PullAllFiles() + if task.IncrBackup.Err != nil { + task.Err = task.IncrBackup.Err + return + } + // 解压所有备份文件 + task.IncrBackup.Decompressed() + if task.IncrBackup.Err != nil { + task.Err = task.IncrBackup.Err + return + } + return +} + +// ImportIncrBackup 导入binlog +func (task *RedisInsRecoverTask) ImportIncrBackup() error { + + redisAddr := fmt.Sprintf("%s:%s", task.NeWTempIP, strconv.Itoa(task.NewTmpPort)) + msg := fmt.Sprintf("master:%s开始导入增备(binlog)", redisAddr) + task.runtime.Logger.Info(msg) + //再次探测tendisplus连接性 + redisCli, err := myredis.NewRedisClient(redisAddr, task.NewTmpPassword, 0, consts.TendisTypeTendisplusInsance) + if err != nil { + task.Err = err + return task.Err + } + defer redisCli.Close() + + task.IncrBackup.ImportBinlogsToTplus(task.NeWTempIP, task.NewTmpPort, task.NewTmpPassword) + if task.Err != nil { + task.Err = task.IncrBackup.Err + return task.Err + } + msg = fmt.Sprintf("master:%s导入增备(binlog)完成", redisAddr) + task.runtime.Logger.Info(msg) + return nil +} + +// GetTendisplusHearbeatKey 根据tendisplus 节点信息获取心跳key +func (task *RedisInsRecoverTask) GetTendisplusHearbeatKey(masterIP string, masterPort int) string { + Heartbeat := fmt.Sprintf("%s_%s:heartbeat", masterIP, strconv.Itoa(masterPort)) + return Heartbeat +} + +// CheckRollbackResult check rollback result is ok +func (task *RedisInsRecoverTask) CheckRollbackResult() error { + + redisAddr := fmt.Sprintf("%s:%s", task.NeWTempIP, strconv.Itoa(task.NewTmpPort)) + msg := fmt.Sprintf("CheckRollbackResult: master:%s开始检查回档结果是否正确", redisAddr) + task.runtime.Logger.Info(msg) + // 获取redis连接 + redisCli, err := myredis.NewRedisClient(redisAddr, task.NewTmpPassword, 0, consts.TendisTypeTendisplusInsance) + if err != nil { + task.Err = err + return task.Err + } + defer redisCli.Close() + // 检查目的集群是否有源集群的心跳数据 + srcHearbeatKey := task.GetTendisplusHearbeatKey(task.SourceIP, task.SourcePort) + srcNodeHearbeat, err := redisCli.GetTendisplusHeartbeat(srcHearbeatKey) + if err != nil { + task.Err = err + return task.Err + } + + srcRedisAddr := fmt.Sprintf("%s:%s", task.SourceIP, strconv.Itoa(task.SourcePort)) + if len(srcNodeHearbeat) == 0 { + msg = fmt.Sprintf("源tendisplus:%s 没有心跳写入,跳过回档结果校验", srcRedisAddr) + mylog.Logger.Info(msg) + return task.Err + } + kvstoreNums, err := task.GetRocksdbNum() + if err != nil { + task.Err = err + return task.Err + } + + rollbackDstTime, _ := time.ParseInLocation(consts.UnixtimeLayout, task.RecoveryTimePoint, time.Local) + var hearbeatVal time.Time + var ok bool + var errList []string + + for i := 0; i < kvstoreNums; i++ { + if hearbeatVal, ok = srcNodeHearbeat[i]; ok == false { + msg = fmt.Sprintf("源tendisplus:%s rocksdbid:%d 没有心跳写入", srcRedisAddr, i) + mylog.Logger.Warn(msg) + return task.Err + } + symbol := "" + if rollbackDstTime.Sub(hearbeatVal).Minutes() > 10 { + symbol = "<" + } else if rollbackDstTime.Sub(hearbeatVal).Minutes() < -10 { + symbol = ">" + } + if symbol != "" { + msg = fmt.Sprintf("目的tendisplus:%s 源tendisplus:%s rocksdbid:%d 回档到时间:%s %s 目的时间:%s", + redisAddr, srcRedisAddr, i, symbol, + hearbeatVal.Local().Format(consts.UnixtimeLayout), + rollbackDstTime.Local().Format(consts.UnixtimeLayout)) + errList = append(errList) + mylog.Logger.Error(msg) + continue + } + msg = fmt.Sprintf("目的tendisplus:%s 源tendisplus:%s rocksdbid:%d 回档到时间:%s =~ 目的时间:%s", + redisAddr, srcRedisAddr, i, + hearbeatVal.Local().Format(consts.UnixtimeLayout), + rollbackDstTime.Local().Format(consts.UnixtimeLayout)) + mylog.Logger.Info(msg) + } + if len(errList) > 0 { + task.Err = fmt.Errorf("回档失败") + return task.Err + } + return nil +} + +// SSDPullIncrbackup ssd拉取增备 +func (task *RedisInsRecoverTask) SSDPullIncrbackup() { + task.runtime.Logger.Info("SSDPullIncrbackup start...") + + redisAddr := fmt.Sprintf("%s:%s", task.NeWTempIP, strconv.Itoa(task.NewTmpPort)) + fileName := fmt.Sprintf("binlog-%s-%d", task.SourceIP, task.SourcePort) + task.runtime.Logger.Info("Source fileName:%s,DstAddr:%s", fileName, redisAddr) + // 节点维度增备信息:fileName 过滤,task.SourceIP 备份的源IP + + layout := "2006-01-02 15:04:05" + rbDstTime, _ := time.ParseInLocation(layout, task.RecoveryTimePoint, time.Local) + //回档目标时间 比 用户填写的时间多1秒 + //(因为binlog_tool的--end-datetime参数,--end-datetime这个时间点的binlog是不会被应用的) + rbDstTime = rbDstTime.Add(1 * time.Second) + task.runtime.Logger.Info("回档目标时间 rbDstTime:%v", rbDstTime) + + //传入全备份开始时间和回档时间 + // startTime 拉取增备的开始时间 -> 全备份的开始时间 + // endTime 拉取增备份的结束时间 -> 回档时间 + ssdIncrBackup := NewTredisRocksDBIncrBack(fileName, task.SourceIP, task.FullBackup.ResultFullbackup[0].StartPos, + task.FullBackup.ResultFullbackup[0].BackupStart.Local().Format(layout), + rbDstTime.Local().Format(layout), task.NeWTempIP, task.RecoverDir, task.RecoverDir, task.RecoveryTimePoint) + if ssdIncrBackup.Err != nil { + task.Err = ssdIncrBackup.Err + return + } + + task.SSDIncrBackup = ssdIncrBackup + task.runtime.Logger.Info("ssdIncrBackup,特定节点的增备信息:%v", task.SSDIncrBackup) + // 获取节点维度的备份信息 + task.SSDIncrBackup.GetTredisIncrbacksSpecRocks() + if task.SSDIncrBackup.Err != nil { + task.Err = ssdIncrBackup.Err + return + } + // 拉取节点维度的所有文件 + task.SSDIncrBackup.PullAllFiles() + if task.SSDIncrBackup.Err != nil { + task.Err = task.SSDIncrBackup.Err + return + } + // 解压所有备份文件 + task.SSDIncrBackup.Decompressed() + if task.SSDIncrBackup.Err != nil { + task.Err = task.SSDIncrBackup.Err + return + } + return +} + +// SSDRestoreFullbackup 导入全备 +func (task *RedisInsRecoverTask) SSDRestoreFullbackup() error { + redisAddr := fmt.Sprintf("%s:%s", task.NeWTempIP, strconv.Itoa(task.NewTmpPort)) + msg := fmt.Sprintf("master:%s start recover_tredis_from_rocksdb ...", redisAddr) + task.runtime.Logger.Info(msg) + //获取tendis ssd连接 + redisCli, err := myredis.NewRedisClient(redisAddr, task.NewTmpPassword, 0, consts.TendisTypeTendisSSDInsance) + if err != nil { + return err + } + + defer redisCli.Close() + + task.FullBackup.RecoverTredisFromRocksdb(task.NeWTempIP, task.NewTmpPort, task.NewTmpPassword) + if task.FullBackup.Err != nil { + task.Err = task.FullBackup.Err + return err + } + msg = fmt.Sprintf("master:%s导入全备完成", redisAddr) + task.runtime.Logger.Info(msg) + return nil +} + +// SSDImportIncrBackup 导入binlog +func (task *RedisInsRecoverTask) SSDImportIncrBackup() error { + + redisAddr := fmt.Sprintf("%s:%s", task.NeWTempIP, strconv.Itoa(task.NewTmpPort)) + msg := fmt.Sprintf("master:%s开始导入增备(binlog)", redisAddr) + task.runtime.Logger.Info(msg) + //再次探测tendisplus连接性 + redisCli, err := myredis.NewRedisClient(redisAddr, task.NewTmpPassword, 0, consts.TendisTypeTendisSSDInsance) + if err != nil { + task.Err = err + return task.Err + } + defer redisCli.Close() + + task.SSDIncrBackup.ImportAllBinlogToTredis(task.NeWTempIP, task.NewTmpPort, task.NewTmpPassword) + if task.Err != nil { + task.Err = task.IncrBackup.Err + return task.Err + } + msg = fmt.Sprintf("master:%s导入增备(binlog)完成", redisAddr) + task.runtime.Logger.Info(msg) + return nil +} + +// CacheRestoreFullbackup 导入全备 +func (task *RedisInsRecoverTask) CacheRestoreFullbackup() error { + redisAddr := fmt.Sprintf("%s:%s", task.NeWTempIP, strconv.Itoa(task.NewTmpPort)) + msg := fmt.Sprintf("master:%s start recover redis from aof/rdb ...", redisAddr) + task.runtime.Logger.Info(msg) + //获取tendis ssd连接 + redisCli, err := myredis.NewRedisClient(redisAddr, task.NewTmpPassword, 0, consts.TendisTypeRedisInstance) + if err != nil { + return err + } + + defer redisCli.Close() + // NOCC:tosa/linelength(其他) + task.FullBackup.RecoverCacheRedisFromBackupFile(task.SourceIP, task.SourcePort, task.NeWTempIP, task.NewTmpPort, task.NewTmpPassword) + if task.FullBackup.Err != nil { + task.Err = task.FullBackup.Err + return task.Err + } + msg = fmt.Sprintf("master:%s导入全备完成", redisAddr) + task.runtime.Logger.Info(msg) + return nil +} + +// getNeWTempIPClusterNodes 生成 cluster_nodes.txt 信息 +func (task *RedisInsRecoverTask) getNeWTempIPClusterNodes() error { + + redisAddr := fmt.Sprintf("%s:%s", task.NeWTempIP, strconv.Itoa(task.NewTmpPort)) + msg := fmt.Sprintf("开始获取master:%s的连接...", redisAddr) + task.runtime.Logger.Info(msg) + password, err := myredis.GetPasswordFromLocalConfFile(task.NewTmpPort) + if err != nil { + return err + } + // 验证节点是否可连接 + redisCli, err := myredis.NewRedisClient(redisAddr, password, 0, consts.TendisTypeTendisplusInsance) + if err != nil { + return err + } + defer redisCli.Close() + if task.RecoverDir == "" { + err := fmt.Errorf("task.RecoverDir为空,请赋值") + task.runtime.Logger.Error(err.Error()) + return err + } + // 获取前先检查是否存在 + clusterNodeInfoFile := filepath.Join(task.RecoverDir, "cluster_nodes.txt") + _, err = os.Stat(clusterNodeInfoFile) + //如存在先删除 + if err == nil { + mvCmd := fmt.Sprintf("cd %s && mv cluster_nodes.txt cluster_nodes_bak.txt", task.RecoverDir) + task.runtime.Logger.Info("mv cluster_nodes.txt文件:%s", mvCmd) + _, err = util.RunLocalCmd("bash", []string{"-c", mvCmd}, "", nil, 600*time.Second) + if err != nil { + task.runtime.Logger.Error(fmt.Sprintf("mv cluster_nodes.txt文件失败,详情:%v", err)) + return err + } + } + + cmd := fmt.Sprintf("cd %s && redis-cli -h %s -p %d -a %s cluster nodes > cluster_nodes.txt", + task.RecoverDir, task.NeWTempIP, task.NewTmpPort, password) + logCmd := fmt.Sprintf("cd %s && redis-cli -h %s -p %d -a xxxx cluster nodes > cluster_nodes.txt", + task.RecoverDir, task.NeWTempIP, task.NewTmpPort) + task.runtime.Logger.Info("获取cluster nodes信息:%s", logCmd) + ret01, err := util.RunLocalCmd("bash", []string{"-c", cmd}, "", nil, 600*time.Second) + if err != nil { + task.runtime.Logger.Error(fmt.Sprintf("获取cluster nodes信息失败,详情:%v", err)) + return err + } + ret01 = strings.TrimSpace(ret01) + if strings.Contains(ret01, "ERR:") == true { + task.runtime.Logger.Error(fmt.Sprintf("获取cluster nodes信息失败,err:%v,cmd:%s", err, logCmd)) + + return err + } + msg = fmt.Sprintf("redisAddr:%s获取cluster nodes信息成功", redisAddr) + task.runtime.Logger.Info(msg) + return nil +} + +// Run 回档逻辑 +// NOCC:golint/fnsize(设计如此) +func (task *RedisInsRecoverTask) Run() { + + // flow 先下发一个actuator 来检查备份文件是否存在,备份空间是够足够 + if task.IsPrecheck { + // 前置检查:备份是否存在等信息、磁盘空间是否够 + err := task.Precheck() + if err != nil { + task.Err = err + return + } + return + } + // tendis 前置检查:连接,是否在使用 + err := task.PrecheckTendis() + if err != nil { + task.Err = err + return + } + + if task.TendisType == consts.TendisTypeTendisplusInsance { + task.runtime.Logger.Info("开始Tendisplus 回档流程") + // 如果是集群包含slave得情况,获取临时集群的cluster nodes 信息 + if task.IsIncludeSlave { + err := task.getNeWTempIPClusterNodes() + if err != nil { + task.Err = err + return + } + } + + // 全备下载 + err := task.PullFullbackup() + if err != nil { + task.Err = err + return + } + // 增备下载 + task.PullIncrbackup() + if task.Err != nil { + task.Err = err + return + } + //清理数据,如果已经有部分数据,则会加载失败 + err = task.ClearAllData() + if err != nil { + task.Err = err + return + } + //停slave 断开同步,restorebackup的时候,用于恢复的目标实例不能是从属实例,同时用于恢复的目标实例不能有从属实例,否则会报错 + if task.IsIncludeSlave { + err = task.StopSlave() + if err != nil { + task.Err = err + return + } + + } + //重置集群,去掉集群和slots信息 + err = task.ClusterResetMaster() + if err != nil { + task.Err = err + return + } + // 加载全备 + err = task.RestoreFullbackup() + if err != nil { + task.Err = err + return + } + // 加载binlog + err = task.ImportIncrBackup() + if err != nil { + task.Err = err + return + } + // 回档结果校验 + err = task.CheckRollbackResult() + if err != nil { + task.Err = err + return + } + // 恢复master节点 slots信息(add slot) + err = task.RecoverClusterSlots() + if err != nil { + task.Err = err + return + } + //恢复slave关系 + if task.IsIncludeSlave { + err = task.RecoverSlave() + if err != nil { + task.Err = err + return + } + } + // 恢复集群关系,因为不知道哪些节点回档成功,所以在下一个actuator 做:由flow传入所以参数 + + } else if task.TendisType == consts.TendisTypeTendisSSDInsance { + task.runtime.Logger.Info("开始Tendis SSD 回档流程") + // 全备下载 + err := task.PullFullbackup() + if err != nil { + task.Err = err + return + } + + // 增备下载 + task.SSDPullIncrbackup() + if task.Err != nil { + task.Err = err + return + } + // 加载全备 + err = task.SSDRestoreFullbackup() + if err != nil { + task.Err = err + return + } + // 加载binlog + err = task.SSDImportIncrBackup() + if err != nil { + task.Err = err + return + } + + } else if task.TendisType == consts.TendisTypeRedisInstance { + task.runtime.Logger.Info("开始Tendis Cache 回档流程") + + // 备份文件下载 + err := task.PullFullbackup() + if err != nil { + task.Err = err + return + } + + // 加载备份文件 + err = task.CacheRestoreFullbackup() + if err != nil { + task.Err = err + return + } + + } + + return +} diff --git a/dbm-services/redis/db-tools/dbactuator/pkg/backupsys/tendisSSD_incrback.go b/dbm-services/redis/db-tools/dbactuator/pkg/backupsys/tendisSSD_incrback.go new file mode 100644 index 0000000000..dd535952a6 --- /dev/null +++ b/dbm-services/redis/db-tools/dbactuator/pkg/backupsys/tendisSSD_incrback.go @@ -0,0 +1,1220 @@ +package backupsys + +import ( + "bufio" + "fmt" + "os" + "path/filepath" + "regexp" + "sort" + "strconv" + "strings" + "time" + + "dbm-services/redis/db-tools/dbactuator/mylog" + "dbm-services/redis/db-tools/dbactuator/pkg/consts" + "dbm-services/redis/db-tools/dbactuator/pkg/customtime" + "dbm-services/redis/db-tools/dbactuator/pkg/util" +) + +// TredisRocksDBIncrBackItem tendisSSD 指定rocksdb的增备信息项 +type TredisRocksDBIncrBackItem struct { + Incr int `json:"incr"` + NodeIP string `json:"node_ip"` + FileName string `json:"filename"` + BackupFile string `json:"backup_file"` + DecompressedFile string `json:"decompressedFile"` //解压后的文件名 + BinlogIdx int64 `json:"binlogIdx"` + BackupTaskid int64 `json:"backup_taskid"` // 任务ID + BackupSize int64 `json:"backup_size"` // 文件大小 + BinlogStartPos uint64 `json:"binlogStartPos"` + BinlogEndPos uint64 `json:"binlogEndPos"` + BackupStart customtime.CustomTime `json:"backup_start"` //binlog文件中获取,代表 上一个binlog文件最后一条binlog的时间 + BackupEnd customtime.CustomTime `json:"backup_end"` //备份文件上传备份系统成功时间 + +} + +// TredisRocksDBIncrBack tendisSSD 节点拉取增备 +type TredisRocksDBIncrBack struct { + FileName string `json:"filename"` + SourceIP string `json:"sourceIp"` + //保存文件的目录(如果是在k8s中,则代表node 上的dir) + SaveNodeDir string `json:"saveRemoteDir"` + //保存备份文件的本地目录 + + SaveMyDir string `json:"saveLocalDir"` + //保存文件的服务器(如果是k8s中,则代表node ip) + SaveHost string `json:"saveHost"` + StartTime time.Time `json:"startTime"` //拉取增备的起始时间 + EndTime time.Time `json:"endTime"` //拉取增备的结束时间 + // #./tredisbinlog logfile --start-datetime=1111 --stop-datetime=22222 --start-position=333333 + // --stop-position=55555 --keys=1,2,4,5,6,7,8,9 + + FullStartPos uint64 `json:"fullStartPos"` //回档导入增备时,--start-position 这里是全备文件传入的position + RecoveryTimePoint string `json:"recovery_time_point"` //回档导入增备时,--end-datetime + //每个rocksdb 最靠近(小于) startTime 的binlog, + //binlog的BackupStart时间小于startTime + PerRocksNearestStart *TredisRocksDBIncrBackItem + //每个rocksdb 最靠近(大于) endTime 的binlog + //binlog的BackupStart时间大于endTime + PerRocksNearestEnd *TredisRocksDBIncrBackItem + + //value: 每个rocksdb对应的binlog列表,按照文件index从小到大排序 + ResultSortBinlog []*TredisRocksDBIncrBackItem `json:"resultSortBinlog"` + //key: binlog file name + //value: 文件对应的binlog项 + ResultBinlogMap map[string]*TredisRocksDBIncrBackItem `json:"resultBinlogMap"` + + Err error `json:"-"` //错误信息 + query QueryReq //备份系统查询 + recover RecoverReq +} + +// NewTredisRocksDBIncrBack 新建tendisssd rocksdb的binlog拉取任务 +// FileName=fileName 过滤信息 +// sourceIP 查询 IP 源 +// startTime 拉取增备的开始时间 -> 全备份的开始时间 +// endTime 拉取增备份的结束时间 -> 回档时间 +func NewTredisRocksDBIncrBack(filename, sourceIP string, fullStartPos uint64, startTime, endTime, + saveHost, saveNodeDir, saveMyDir, recoveryTimePoint string) (ret *TredisRocksDBIncrBack) { + mylog.Logger.Info("NewTredisRocksDBIncrBack start ...") + + ret = &TredisRocksDBIncrBack{ + FileName: filename, + SourceIP: sourceIP, + SaveHost: saveHost, + SaveNodeDir: saveNodeDir, + SaveMyDir: saveMyDir, + FullStartPos: fullStartPos, + RecoveryTimePoint: recoveryTimePoint, + } + layout := "2006-01-02 15:04:05" + var err error + ret.StartTime, err = time.ParseInLocation(layout, startTime, time.Local) + if err != nil { + ret.Err = fmt.Errorf("startTime:%s time.parse fail,err:%s,layout:%s", startTime, err, layout) + mylog.Logger.Error(ret.Err.Error()) + return ret + } + ret.EndTime, err = time.ParseInLocation(layout, endTime, time.Local) + if err != nil { + ret.Err = fmt.Errorf("endTime:%s time.parse fail,err:%s,layout:%s", endTime, err, layout) + mylog.Logger.Error(ret.Err.Error()) + return ret + } + mylog.Logger.Info("StartTime:%s,EndTime:%s", ret.StartTime, ret.EndTime) + if ret.EndTime.Before(ret.StartTime) == true || ret.EndTime.Equal(ret.StartTime) == true { + ret.Err = fmt.Errorf("tendisssd binlog拉取,endtime:%s 小于等于 startTime:%s", + endTime, startTime) + mylog.Logger.Error(ret.Err.Error()) + return ret + } + if ret.StartTime.After(time.Now()) == true { + //未来时间 + ret.Err = fmt.Errorf("binlogPull startTime:%s > time.Now()", startTime) + mylog.Logger.Error(ret.Err.Error()) + return ret + } + if ret.EndTime.After(time.Now()) == true { + //未来时间 + ret.Err = fmt.Errorf("binlogPull endTime:%s > time.Now()", endTime) + mylog.Logger.Error(ret.Err.Error()) + return ret + } + ret.ResultSortBinlog = []*TredisRocksDBIncrBackItem{} + ret.ResultBinlogMap = make(map[string]*TredisRocksDBIncrBackItem) + return +} + +// getSeqFromFile,获取文件中的序列号 +func (incr *TredisRocksDBIncrBack) getSeqFromFile(fpath string) uint64 { + mylog.Logger.Info("getSeqFromFile start ...") + DepsDir := "/usr/local/redis/bin/deps" + //获取文件第一行(包含序列号) + // firstline, err := exec.Command(filepath.Join(binPath, "tredisbinlog"), + // "--with-timestamp", "--with-seq", fpath).Output() + getSeqCmd := fmt.Sprintf(` + export LD_PRELOAD=%s/libjemalloc.so + export LD_LIBRARY_PATH=LD_LIBRARY_PATH:%s + %s --with-timestamp --with-seq %s + `, DepsDir, DepsDir, consts.TredisBinlogBin, fpath) + mylog.Logger.Info("获取 binlog 获取文件第一行(包含序列号),命令:%v", getSeqCmd) + firstline, err := util.RunLocalCmd("bash", []string{"-c", getSeqCmd}, "", nil, 1*time.Hour) + + if err != nil { + mylog.Logger.Error(fmt.Sprintf("解析binlog失败,详情:%v", err)) + incr.Err = err + return 0 + } + mylog.Logger.Info("解析binlog成功") + mylog.Logger.Debug("解析binlog成功,firstline:%v", firstline) + //如果获取到第一行 + if len(firstline) > 0 { + //将第一行按空格分割为三部分 + f := strings.Fields(string(firstline)) + //返回第二部分(序列号) + seq, err := strconv.Atoi(f[1]) + if err != nil { + mylog.Logger.Error(fmt.Sprintf("解析binlog,分割firstline失败,详情:%v", err)) + } + mylog.Logger.Info("getSeqFromFile file:%s seq:%d", fpath, seq) + return uint64(seq) + } + //如果未获取到第一行,返回-1 + return 0 +} + +// AddBinlogToMap 添加binlog项到 ResultBinlogMap,ResultSortBinlog中 +func (incr *TredisRocksDBIncrBack) AddBinlogToMap(item01 *TredisRocksDBIncrBackItem) { + if item01 == nil { + return + } + if _, ok := incr.ResultBinlogMap[item01.BackupFile]; ok == true { + //去重 + return + } + incr.ResultBinlogMap[item01.BackupFile] = item01 + incr.ResultSortBinlog = append(incr.ResultSortBinlog, item01) +} + +// GetTredisIncrbacks 查询特定端口的binlog备份文件 +// NOCC:golint/fnsize(设计如此) +func (incr *TredisRocksDBIncrBack) GetTredisIncrbacks() (backs []*TredisRocksDBIncrBackItem) { + mylog.Logger.Info("GetTredisIncrbacks start ...") + layout := "2006-01-02 15:04:05" + // incr.StartTime 往前24小时 + h, _ := time.ParseDuration("-24h") + startTime := incr.StartTime.Add(h) + beginData := startTime.Add(h).Format(layout) + // // incr.EndTime 往后8小时 + h1, _ := time.ParseDuration("24h") + endTime := incr.EndTime.Add(h1) + endDate := endTime.Format(layout) + mylog.Logger.Info("fileName:%s", incr.FileName) + + params := QueryReq{ + Params: Params{ + Version: "1.0", + RequestInfo: RequestInfo{ + BaseInfo: BaseInfo{ + SysID: BackupSysID, + Key: BackupKey, + Ticket: "", + }, + DetailInfo: DetailInfo{ + SourceIP: incr.SourceIP, + BeginDate: beginData, + EndDate: endDate, + FileName: incr.FileName, + // FileSpWildchar: "0", + }, + }, + }, + } + resp, err := incr.query.QueryFile(params) + if err != nil { + incr.Err = fmt.Errorf("QueryFile fail,err:%v,params:%s", err, params) + mylog.Logger.Error(incr.Err.Error()) + return + } + if resp == nil { + return + } + + mylog.Logger.Debug("resp.Detail:%v", resp.Detail) + mylog.Logger.Info("resp num :%v", resp.Num) + mylog.Logger.Info("ip:%v,beginDate:%v,endDate:%v", incr.SourceIP, beginData, endDate) + + // tredis示例: binlog-127.0.0.x-30000-7-0003612-20230326232536.log.zst + // ssd示例: binlog-127.0.0.x-30000-0000386-20230420021655.log.zst + binlogReg := regexp.MustCompile(`^*?-(\d+)-(\d+)-(\d+).log.zst`) + layout1 := "20060102150405" + for _, str01 := range resp.Detail { + back01 := &TredisRocksDBIncrBackItem{} + taskID, _ := strconv.Atoi(str01.TaskID) + size, _ := strconv.Atoi(str01.Size) + if taskID < 0 || size < 0 { + //backup_taskid 小于0 或backup_size 小于0的备份,是无效备份 + msg := fmt.Sprintf("filename:%s incrBackup:%s backupTaskid:%s<0 backupSize:%s<0 is invalid,skip...", + incr.FileName, str01.FileName, str01.TaskID, str01.Size) + mylog.Logger.Info(msg) + continue + } + back01.BackupTaskid, _ = strconv.ParseInt(str01.TaskID, 10, 64) + back01.BackupSize, _ = strconv.ParseInt(str01.Size, 10, 64) + back01.BackupFile = str01.FileName + back01.NodeIP = str01.SourceIP + mylog.Logger.Debug("BackupSize:%d", back01.BackupSize) + mylog.Logger.Debug("BackupTaskid:%d", back01.BackupTaskid) + match01 := binlogReg.FindStringSubmatch(str01.FileName) + + if len(match01) != 4 { + incr.Err = fmt.Errorf( + "filename:%s backup:%v format not correct,backupFile:%s cann't find rocksdbIdx/binlogIdx/createTime", + incr.FileName, back01, str01.FileName) + mylog.Logger.Error(incr.Err.Error()) + return + } + bkCreateTime, err01 := time.ParseInLocation(layout1, match01[3], time.Local) + if err01 != nil { + incr.Err = fmt.Errorf( + "backup file createTime:%s time.parese fail,err:%s,layout1:%s", + match01[3], err01, layout1) + mylog.Logger.Error(incr.Err.Error()) + return + } + + back01.BinlogIdx, _ = strconv.ParseInt(match01[2], 10, 64) + back01.BackupStart.Time = bkCreateTime //不要用backupStart值 + back01.BackupEnd.Time, err01 = time.ParseInLocation(layout, str01.FileLastMtime, time.Local) //文件最后修改时间 + if err01 != nil { + incr.Err = fmt.Errorf( + "backup file lastTime:%s time.parese fail,err:%s,layout:%s", + str01.FileLastMtime, err01, layout) + mylog.Logger.Error(incr.Err.Error()) + return + } + backs = append(backs, back01) + } + mylog.Logger.Info("TredisRocksDBIncrBackItem:%v", backs[0]) + return +} + +// GetTredisIncrbacksSpecRocks 获取startTime~endTime时间段内的binlog +// NOCC:golint/fnsize(设计如此) +func (incr *TredisRocksDBIncrBack) GetTredisIncrbacksSpecRocks() { + + // 获取startTime~endTime时间段内的binlog + // layout := "20060102" + layout02 := "2006-01-02 15:04:05" + //从备份列表中选择最靠近 startTime 的文件, + // nearestStartBk.BackupStart 小于等于 StartTime + var nearestStartBk *TredisRocksDBIncrBackItem = nil + //从备份列表中选择最靠近 endTime 的文件, + // nearestEndBk.BackupStart 大于等于 EndTime + var nearestEndBk *TredisRocksDBIncrBackItem = nil + //这里查询范围广一些,再过滤 + backs := incr.GetTredisIncrbacks() + for _, bk01 := range backs { + bkItem := bk01 + //只需要那些BackupStart <= incr.StartTime 的binlog + if bkItem.BackupStart.Before(incr.StartTime) == true || + bkItem.BackupEnd.Equal(incr.StartTime) == true { + if nearestStartBk == nil { + // 第一次找到 BackupStart 小于 startTime 的备份 + nearestStartBk = bkItem + } else { + // nearestStartBk.BackupStart < 该备份BackupStart <= incr.StartTime + // 或者 + // nearestStartBk.BackupStart == 该备份BackupStart 同时 该备份BinlogIdx < nearestStartBk.BinlogIdx + // (同一时间大量写入时可能一秒钟生成多个binlog文件,此时同一秒生成的所有binlog都保留,所以 nearestStartBk.BinlogIdx是最小的) + + // 如binlog-127.0.0.x-30010-0-0002495-20230311124436.log.zst: BinlogIdx是0002495 + if bkItem.BackupStart.After(nearestStartBk.BackupStart.Time) == true || + (bkItem.BackupStart.Equal(nearestStartBk.BackupStart.Time) == true && + bkItem.BinlogIdx < nearestStartBk.BinlogIdx) { + nearestStartBk = bkItem + } + + } + } + + //只需要那些BackupStart >= incr.EndTime 的binlog + if bkItem.BackupStart.After(incr.EndTime) == true || + bkItem.BackupStart.Equal(incr.EndTime) == true { + if nearestEndBk == nil { + //第一次找到 BackupStart 大于等于 endTIme的备份 + nearestEndBk = bkItem + } else { + // incr.EndTime <= 该备份BackupStart < nearestEndBk.BackupStart. + // 或者 + // nearestEndBk.BackupStart == 该备份BackupStart 同时 该备份BinlogIdx > nearestStartBk.BinlogIdx + // (同一时间大量写入时可能一秒钟生成多个binlog文件,此时同一秒生成的所有binlog都保留,所以 nearestEndBk.BinlogIdx是最大的) + if bkItem.BackupStart.Before(nearestEndBk.BackupStart.Time) == true || + (bkItem.BackupStart.Equal(nearestEndBk.BackupStart.Time) == true && + bkItem.BinlogIdx > nearestEndBk.BinlogIdx) { + nearestEndBk = bkItem + } + } + } + + } + + if nearestStartBk == nil { + incr.Err = fmt.Errorf("filename:%s 向前%d天,没有找到时间 小于 startTime:%s的binlog", + incr.FileName, + + LastNDaysIncrBack(), + incr.StartTime.Local().Format(layout02)) + mylog.Logger.Error(incr.Err.Error()) + return + } + + msg := fmt.Sprintf("filename:%s 找到距离startTime:%s最近(小于)的binlog:%s,binlogStart:%s", + incr.FileName, + + incr.StartTime.Local().Format(layout02), + nearestStartBk.BackupFile, + nearestStartBk.BackupStart.Local().Format(layout02)) + mylog.Logger.Info(msg) + + if nearestEndBk == nil { + incr.Err = fmt.Errorf("filename:%s 向后%d天,没有找到时间大于endTime:%s的binlog", + incr.FileName, + + LastNDaysIncrBack(), + incr.EndTime.Local().Format(layout02)) + mylog.Logger.Error(incr.Err.Error()) + return + } + + msg = fmt.Sprintf("filename:%s 找到距离endTime:%s最近(大于)的binlog:%s,binglogEnd:%s", + incr.FileName, + + incr.EndTime.Local().Format(layout02), + nearestEndBk.BackupFile, + nearestEndBk.BackupStart.Local().Format(layout02)) + mylog.Logger.Info(msg) + + //成功获取到 nearestStartBk nearestEndBk,继续获取两者之间的binlog + start01 := nearestStartBk.BackupStart.Time + // end01 := nearestEndBk.BackupStart.Time + // 使用 最后一个binlog文件上传备份系统成功时间, 而不是 binlog 文件名中的时间(也就是binlog文件生成时间) + // 因为6月1日生成的binlog,有可能6月2日才上传成功 + // 而在dba redis中 或者 备份系统中 只有 6月2号的记录里面才能查询到该 binlog文件 + end01 := nearestEndBk.BackupEnd.Time + //start02 对应nearestStartBk.BackupStart 当天0点0分0秒 + //end02 对应nearestEndBk.BackupStart 当天0点0分0秒 + //我们需要将 start02 end02之间所有day的binlog都筛选到 + start02 := time.Date(start01.Year(), start01.Month(), start01.Day(), 0, 0, 0, 0, start01.Location()) + end02 := time.Date(end01.Year(), end01.Month(), end01.Day(), 0, 0, 0, 0, end01.Location()) + incr.AddBinlogToMap(nearestStartBk) + incr.AddBinlogToMap(nearestEndBk) + + incr.PerRocksNearestStart = nearestStartBk + incr.PerRocksNearestEnd = nearestEndBk + + for start02.Before(end02) == true || start02.Equal(end02) == true { + + backs := incr.GetTredisIncrbacks() + + for _, bk01 := range backs { + bkItem := bk01 + if (bkItem.BackupStart.After(nearestStartBk.BackupStart.Time) == true || + bkItem.BackupStart.Equal(nearestStartBk.BackupStart.Time) == true) && + (bkItem.BackupStart.Before(nearestEndBk.BackupStart.Time) == true || + bkItem.BackupStart.Equal(nearestEndBk.BackupStart.Time) == true) { + incr.AddBinlogToMap(bkItem) + } + } + start02 = start02.Add(1 * 24 * time.Hour) + } + //按照binlog index排序 + sort.Slice(incr.ResultSortBinlog, func(i, j int) bool { + return incr.ResultSortBinlog[i].BinlogIdx < incr.ResultSortBinlog[j].BinlogIdx + }) + + incr.isGetAllBinlogInfo() + if incr.Err != nil { + return + } + return +} + +/* +判断binlog文件是否连续,是否重复; +- 重复则报错; +- 不连续则返回缺失的binlog index,如 2,3,5,8 则返回缺失的4,6,7 +*/ +func getTredisNotReadyBinlogs(sortBinlogs []*TredisRocksDBIncrBackItem) ([]string, error) { + var ret []string + var err error + preListIdx := 0 + preBinlogIdx := sortBinlogs[0].BinlogIdx + for idx, bin01 := range sortBinlogs { + binItem := bin01 + if idx == preListIdx { + //第一个元素忽略 + continue + } + if binItem.BinlogIdx <= preBinlogIdx { + //如果后面的binlog index小于等于前一个binlog index + err = fmt.Errorf("当前binlog index:%d <= 前一个binlog index:%d", binItem.BinlogIdx, preBinlogIdx) + mylog.Logger.Error(err.Error()) + mylog.Logger.Error(err.Error()) + return ret, err + } + if binItem.BinlogIdx == preBinlogIdx+1 { + //符合预期 + preBinlogIdx = binItem.BinlogIdx + continue + } + preBinlogIdx++ + for preBinlogIdx < bin01.BinlogIdx { + ret = append(ret, fmt.Sprintf("%d", preBinlogIdx)) + preBinlogIdx++ + } + } + return ret, nil +} + +/* +判断所需binlog文件信息是否已全部获取到; +- ResultSortBinlog 第二个binlog文件序号 必须 只比 第一个binlog 文件序号大1 +- ResultSortBinlog 倒数第一个binlog文件序号 必须 只比 倒数第二个binlog文件序号大 1 +- 最后一个文件序号 减去 第一个文件序号 等于 len(ResultSortBinlog)+1 +- ResultSortBinlog 第一个binlog.BackupStart必须小于 startTime, 第二个binlog.BackupStart必须大于 startTime +- ResultSortBinlog 最后一个binlog.BackupStart必须大于 endTime, 倒数第二个binlog.BackupStart必须小于 endTime +*/ +// NOCC:golint/fnsize(设计如此) +func (incr *TredisRocksDBIncrBack) isGetAllBinlogInfo() (ret bool) { + mylog.Logger.Info("isGetAllBinlogInfo start ...") + cnt := len(incr.ResultSortBinlog) + mylog.Logger.Info("ResultSortBinlog len:%d", cnt) + layout := "2006-01-02 15:04:05" + + if cnt < 2 { + //至少会包含两个binlog文件,第一个BackupStart小于 startTime, 第二个BackupStart 大于 endTime + str01 := "" + for _, bk01 := range incr.ResultSortBinlog { + str01 = fmt.Sprintf("%s,%s", str01, bk01.BackupFile) + } + incr.Err = fmt.Errorf( + "filename:%s 拉取[%s ~ %s]时间段的binlog,至少包含2个binglo,当前%d个binlog,详情:%s", + incr.FileName, + incr.StartTime.Local().Format(layout), + incr.EndTime.Local().Format(layout), + cnt, str01) + mylog.Logger.Error(incr.Err.Error()) + mylog.Logger.Error(incr.Err.Error()) + return false + } + firstBinlog := incr.ResultSortBinlog[0] + secondBinlog := incr.ResultSortBinlog[1] + + lastBinlog := incr.ResultSortBinlog[cnt-1] + beforeLastBinlog := incr.ResultSortBinlog[cnt-2] + + if secondBinlog.BinlogIdx-firstBinlog.BinlogIdx != 1 { + incr.Err = fmt.Errorf( + "filename:%s 拉取[%s ~ %s]时间段的binlog,第一binlog:%s 和 第二binlog:%s 不连续", + incr.FileName, + incr.StartTime.Local().Format(layout), + incr.EndTime.Local().Format(layout), + firstBinlog.BackupFile, secondBinlog.BackupFile, + ) + + mylog.Logger.Error(incr.Err.Error()) + return + } + if lastBinlog.BinlogIdx-beforeLastBinlog.BinlogIdx != 1 { + incr.Err = fmt.Errorf( + "filename:%s 拉取[%s ~ %s]时间段的binlog,倒数第二binlog:%s 和 倒数第一binlog:%s 不连续", + incr.FileName, + incr.StartTime.Local().Format(layout), + incr.EndTime.Local().Format(layout), + beforeLastBinlog.BackupFile, lastBinlog.BackupFile, + ) + mylog.Logger.Error(incr.Err.Error()) + + return + } + //是否连续 + binIndexList, err := getTredisNotReadyBinlogs(incr.ResultSortBinlog) + if err != nil { + incr.Err = err + return false + } + if len(binIndexList) > 0 { + incr.Err = fmt.Errorf("缺失的binlog共%d个,缺失的binlog index是:%s", + len(binIndexList), strings.Join(binIndexList, ",")) + + mylog.Logger.Error(incr.Err.Error()) + return false + } + //第一个binlog.BackupStart必须小于等于 startTime,大于则报错 + if firstBinlog.BackupStart.After(incr.StartTime) == true { + incr.Err = fmt.Errorf( + "filename:%s ,第一个binog:%s,binlogStart:%s 大于startTime(全备时间):%s", + incr.FileName, + firstBinlog.BackupFile, + firstBinlog.BackupStart.Local().Format(layout), + incr.StartTime.Local().Format(layout)) + mylog.Logger.Error(incr.Err.Error()) + + return false + } + //第二个binlog.BackupStart必须大于(等于) startTime,小于则报错 + for idx01 := 1; secondBinlog.BackupStart.Equal(firstBinlog.BackupStart.Time) == true; idx01++ { + //因为相同时间可能有多个binlog,所以跳过与 firstBinlog.BinlogEnd 相等的 + secondBinlog = incr.ResultSortBinlog[idx01] + } + if secondBinlog.BackupStart.Before(incr.StartTime) { + err = fmt.Errorf( + "filename:%s ,第二个binlog:%s,binlogStart:%s 时间小于startTime(全备时间):%s", + incr.FileName, + secondBinlog.BackupFile, + secondBinlog.BackupStart.Local().Format(layout), + incr.StartTime.Local().Format(layout)) + mylog.Logger.Error(err.Error()) + + return false + } + //倒数第二个binlog.BackupStart必须小于等于 endTime, 大于则报错 + for idx02 := cnt - 2; beforeLastBinlog.BackupStart.Equal(lastBinlog.BackupStart.Time) == true; idx02-- { + //因为相同时间可能有多个binlog,所以跳过与 lastBinlog.BinlogEnd 相等的 + beforeLastBinlog = incr.ResultSortBinlog[idx02] + } + if beforeLastBinlog.BackupStart.After(incr.EndTime) == true { + incr.Err = fmt.Errorf( + "filename:%s ,倒数第二个binlog:%s,binlogStart:%s 时间大于endTime(回档目标时间):%s", + incr.FileName, + beforeLastBinlog.BackupFile, + beforeLastBinlog.BackupStart.Local().Format(layout), + incr.EndTime.Local().Format(layout)) + mylog.Logger.Error(incr.Err.Error()) + + return false + } + //最后一个binlog.BackupStart必须大于等于 endTime,小于则报错 + if lastBinlog.BackupStart.Before(incr.EndTime) == true { + incr.Err = fmt.Errorf( + "filename:%s ,最后一个binlog:%s,binlogStart:%s 时间小于endTime(回档目标时间):%s", + incr.FileName, + lastBinlog.BackupFile, + lastBinlog.BackupStart.Local().Format(layout), + incr.EndTime.Local().Format(layout)) + mylog.Logger.Error(incr.Err.Error()) + return false + } + msg := fmt.Sprintf(`filename:%s找到所有[%s~%s]时间段的binlog, + 共%d个,第一个binlog:%s binlogStart:%s,最后一个binlog:%s binlogStart:%s`, + incr.FileName, + incr.StartTime.Local().Format(layout), + incr.EndTime.Local().Format(layout), + cnt, + firstBinlog.BackupFile, + firstBinlog.BackupStart.Local().Format(layout), + lastBinlog.BackupFile, + lastBinlog.BackupStart.Local().Format(layout), + ) + mylog.Logger.Info(msg) + return +} + +// TotalSize 所有binlog所需磁盘空间大小 +func (incr *TredisRocksDBIncrBack) TotalSize() int64 { + var ret int64 = 0 + for _, bk01 := range incr.ResultSortBinlog { + bkItem := bk01 + ret = ret + bkItem.BackupSize + } + return ret +} + +// GetBackupFileExt 获取备份文件后缀(如果后缀无法解压,及时报错) +func (incr *TredisRocksDBIncrBack) GetBackupFileExt( + item *TredisRocksDBIncrBackItem) (bkFileExt string) { + bkFileExt = filepath.Ext(item.BackupFile) + + okExt := map[string]bool{ + ".tar": true, + ".tgz": true, + ".gz": true, + ".zip": true, + ".lzo": true, + ".zst": true, + } + + if strings.HasSuffix(item.BackupFile, ".tar.gz") { + bkFileExt = ".tar.gz" + return + } else if _, ok := okExt[bkFileExt]; ok == true { + return bkFileExt + } else { + incr.Err = fmt.Errorf("无法解压的binlog文件:%s", item.BackupFile) + mylog.Logger.Error(incr.Err.Error()) + return + } +} + +// getDecompressedFile 获取解压文件,已经解压文件完整路径 +func (incr *TredisRocksDBIncrBack) getDecompressedFile( + item *TredisRocksDBIncrBackItem) (decpFile, decpFileFullPath string) { + var bkFileExt string = incr.GetBackupFileExt(item) + if incr.Err != nil { + return + } + item.DecompressedFile = strings.TrimSuffix(item.BackupFile, bkFileExt) + decpFile = item.DecompressedFile + decpFileFullPath = filepath.Join(incr.SaveMyDir, decpFile) + return +} + +// CheckLocalDecompressedFilesIsOk 检查本地 增备解压文件 是否ok +// 返回值: +// totalCnt: 全部增备(解压)文件个数 +// existsCnt: 本地存在的增备(解压)文件个数, existsList: 本地存在的增备(解压)文件详情 +func (incr *TredisRocksDBIncrBack) CheckLocalDecompressedFilesIsOk() ( + totalCnt, existsCnt int, existsList []*TredisRocksDBIncrBackItem, +) { + mylog.Logger.Info("CheckLocalDecompressedFilesIsOk start ...") + totalCnt = 0 + existsCnt = 0 + for _, bk01 := range incr.ResultSortBinlog { + bkItem := bk01 + totalCnt++ + _, decpFileFullPath := incr.getDecompressedFile(bkItem) + if incr.Err != nil { + return + } + _, err := os.Stat(decpFileFullPath) + if os.IsNotExist(err) == true { + continue + } + existsCnt++ + existsList = append(existsList, bkItem) + } + return +} + +// rmLocalDecompressedFiles 删除一些本地 增备(已解压)文件 +func (incr *TredisRocksDBIncrBack) rmLocalDecompressedFiles(bkList []*TredisRocksDBIncrBackItem) { + mylog.Logger.Info("rmLocalDecompressedFiles start ... ") + rmCnt := 0 + for _, bk01 := range bkList { + bkItem := bk01 + if bkItem.DecompressedFile == "" { + continue + } + decpFileFullPath := filepath.Join(incr.SaveMyDir, bkItem.DecompressedFile) + if _, err := os.Stat(decpFileFullPath); os.IsNotExist(err) { + continue + } + rmCmd := fmt.Sprintf("cd %s && rm -f %s 2>/dev/null", incr.SaveMyDir, bkItem.DecompressedFile) + _, incr.Err = util.RunLocalCmd("bash", []string{"-c", rmCmd}, "", nil, 30*time.Minute) + if incr.Err != nil { + return + } + rmCnt++ + } + msg := fmt.Sprintf("共删除%d个本地binlog(已解压)文件", rmCnt) + mylog.Logger.Info(msg) + return +} + +// CheckLocalBackupfilesIsOk 检查本地增备文件是否全部ok +// 返回值: +// totalCnt: 所需的全部增备文件个数 +// existsCnt: 本地存在的增备文件个数, existsList: 本地存在的增备文件详情 +// sizeOKCnt: 本地存在的增备文件 大小ok 的个数 +func (incr *TredisRocksDBIncrBack) CheckLocalBackupfilesIsOk() ( + totalCnt, existsCnt, sizeOKCnt int, existsList []*TredisRocksDBIncrBackItem, +) { + totalCnt = 0 + existsCnt = 0 + for _, bk01 := range incr.ResultSortBinlog { + bkItem := bk01 + totalCnt++ + incrBkFile := filepath.Join(incr.SaveMyDir, bkItem.BackupFile) + incrBkInfo, err := os.Stat(incrBkFile) + if os.IsNotExist(err) == true { + continue + } + existsCnt++ + existsList = append(existsList, bkItem) + if incrBkInfo.Size() == bkItem.BackupSize { + sizeOKCnt++ + } + } + return +} + +// rmLocalBackupFiles 删除一些本地 增备文件 +func (incr *TredisRocksDBIncrBack) rmLocalBackupFiles(bkList []*TredisRocksDBIncrBackItem) { + rmCnt := 0 + for _, bk01 := range bkList { + bkItem := bk01 + bkFileFullPath := filepath.Join(incr.SaveMyDir, bkItem.BackupFile) + if _, err := os.Stat(bkFileFullPath); os.IsNotExist(err) { + continue + } + rmCmd := fmt.Sprintf("cd %s && rm -f %s 2>/dev/null", incr.SaveMyDir, bkItem.BackupFile) + _, incr.Err = util.RunLocalCmd("bash", []string{"-c", rmCmd}, "", nil, 30*time.Minute) + if incr.Err != nil { + return + } + rmCnt++ + } + msg := fmt.Sprintf("共删除%d个本地binlog(未解压)文件", rmCnt) + mylog.Logger.Info(msg) + return +} + +// checkPulledFileOK 检查文件下载是否成功 +func (incr *TredisRocksDBIncrBack) checkPulledFileOK(item *TredisRocksDBIncrBackItem) (err error) { + + if incr.SaveMyDir != "" { + incrBkFile := filepath.Join(incr.SaveMyDir, item.BackupFile) + bkFileInfo, err := os.Stat(incrBkFile) + if err != nil { + err = fmt.Errorf("pod:%s 本地binlog文件:%s 信息获取失败,err:%v", + incr.FileName, incrBkFile, err) + mylog.Logger.Error(err.Error()) + return err + } + bkFileSize := bkFileInfo.Size() + if item.BackupSize != bkFileSize { + err = fmt.Errorf("本地binlog文件:%s 大小(%d) 不等于 redis备份记录大小:%d", + incrBkFile, bkFileSize, item.BackupSize) + mylog.Logger.Error(err.Error()) + return err + } + msg := fmt.Sprintf(" 本地binlog文件:%s 确认ok", incrBkFile) + mylog.Logger.Info(msg) + } + return nil +} + +// CheckAllPulledFilesOK 是否所有本地binlog都拉取ok +func (incr *TredisRocksDBIncrBack) CheckAllPulledFilesOK() { + mylog.Logger.Info("CheckAllPulledFilesOK start ... ") + errList := []string{} + successCnt := 0 + failCnt := 0 + totalCnt := 0 + for _, bk01 := range incr.ResultSortBinlog { + bkItem := bk01 + totalCnt++ + err := incr.checkPulledFileOK(bkItem) + if err != nil { + errList = append(errList, err.Error()) + } else { + successCnt++ + } + } + failCnt = len(errList) + if failCnt > 0 { + list01 := []string{} + //只打印前5条信息 + if failCnt > 5 { + list01 = errList[:5] + } else { + list01 = errList + } + incr.Err = fmt.Errorf( + " 检查本地binlog文件失败,成功%d个,失败%d个,共%d个,失败示例:%s", + successCnt, failCnt, totalCnt, strings.Join(list01, "\n"), + ) + mylog.Logger.Error(incr.Err.Error()) + return + } + msg := fmt.Sprintf( + " 检查本地binlog文件全部成功,成功%d个,失败%d个,共%d个", + successCnt, failCnt, totalCnt) + mylog.Logger.Info(msg) + + return +} + +// PullFilesFromBS 从备份系统拉取文件到对应位置(saveHost,saveRemoteDir) +func (incr *TredisRocksDBIncrBack) PullFilesFromBS() { + if len(incr.ResultSortBinlog) == 0 { + incr.Err = fmt.Errorf( + "filename:%s binlog信息为空(len:%d),无法拉取,请先获取binlog信息再拉取", + incr.FileName, len(incr.ResultSortBinlog)) + mylog.Logger.Error(incr.Err.Error()) + return + } + + var taskIds string + for _, bk01 := range incr.ResultSortBinlog { + bkItem := bk01 + taskIds = fmt.Sprintf("%s,%s", taskIds, strconv.FormatInt(bkItem.BackupTaskid, 10)) + } + mylog.Logger.Info("PullFilesFromBS TaskidList:%v", taskIds) + + recoverParams := RecoverReq{ + RecoverParams: RecoverParams{ + Version: consts.BackupVersion, + RecoverRequestInfo: RecoverRequestInfo{ + BaseInfo: BaseInfo{ + SysID: BackupSysID, + Key: BackupKey, + Ticket: "", + }, + RecoverDetailInfo: RecoverDetailInfo{ + TaskidList: taskIds, + DestIP: incr.SaveHost, + LoginUser: DstIPBackupUser, + LoginPasswd: DstIPBackupPassword, + Directory: incr.SaveNodeDir, + Reason: "data recover", + }, + }, + }, + } + // 因为并行度是由备份系统调度的,所以这里直接全部请求下载 + // 如果按 task_id 逐个请求备份系统,就需要客户端控制并发 + err := incr.recover.WaitForRecoverFinish(recoverParams, incr.SaveHost) + if err != nil { + incr.Err = fmt.Errorf("等待备份文件下载失败:%v", err) + return + } + + //拉取成功,检查拉取的文件是否ok + incr.CheckAllPulledFilesOK() + +} + +// PullAllFiles .. +// - 如果所有 增备(已解压)文件 全部存在,则直接return; +// - 如果所有 增备(未解压)文件 全部存在,且大小校验ok,则直接 return; +// - 否则删除 本地存在的增备(已解压)文件、增备(未解压)文件; +// - 继续拉取文件; +func (incr *TredisRocksDBIncrBack) PullAllFiles() { + mylog.Logger.Info("PullAllFiles start ... ") + var msg string + decpTotalCnt, decpExistsCnt, decpExistsList := incr.CheckLocalDecompressedFilesIsOk() + if incr.Err != nil { + return + } + if decpTotalCnt == decpExistsCnt { + msg = fmt.Sprintf("rollbackPod:%s 本地已存在%d个 增备(已解压文件),无需重新拉取增备文件...", + incr.FileName, decpTotalCnt) + mylog.Logger.Info(msg) + return + } + incr.rmLocalDecompressedFiles(decpExistsList) + if incr.Err != nil { + return + } + + localTotalCnt, localExistsCnt, sizeOkCnt, localExistsList := incr.CheckLocalBackupfilesIsOk() + if incr.Err != nil { + return + } + if localTotalCnt == localExistsCnt && localTotalCnt == sizeOkCnt { + msg = fmt.Sprintf("rollbackPod:%s 本地已存在%d个 增备(未解压文件),无需重新拉取增备文件...", + incr.FileName, localTotalCnt) + mylog.Logger.Info(msg) + return + } + incr.rmLocalBackupFiles(localExistsList) + if incr.Err != nil { + return + } + + incr.PullFilesFromBS() +} + +// DecompressedOne 解压一个binlog文件 +func (incr *TredisRocksDBIncrBack) DecompressedOne(item *TredisRocksDBIncrBackItem) { + var cmd string + var bkFileExt string = incr.GetBackupFileExt(item) + if incr.Err != nil { + return + } + if bkFileExt == ".tar" { + cmd = fmt.Sprintf("cd %s && tar -xf %s", + incr.SaveMyDir, item.BackupFile) + } else if bkFileExt == ".tar.gz" { + cmd = fmt.Sprintf("cd %s && tar -zxf %s", + incr.SaveMyDir, item.BackupFile) + } else if bkFileExt == ".tgz" { + cmd = fmt.Sprintf("cd %s && tar -zxf %s", + incr.SaveMyDir, item.BackupFile) + } else if bkFileExt == ".gz" { + cmd = fmt.Sprintf("cd %s && gunzip %s", + incr.SaveMyDir, item.BackupFile) + } else if bkFileExt == ".zip" { + cmd = fmt.Sprintf("cd %s && unzip %s", + incr.SaveMyDir, item.BackupFile) + } else if bkFileExt == ".lzo" { + cmd = fmt.Sprintf("cd %s && lzop -d %s", + incr.SaveMyDir, item.BackupFile) + } else if bkFileExt == ".zst" { + cmd = fmt.Sprintf("cd %s && %s -d %s", + incr.SaveMyDir, consts.ZstdBin, item.BackupFile) + } + if cmd != "" { + var decpFileFullPath string + item.DecompressedFile, decpFileFullPath = incr.getDecompressedFile(item) + msg := fmt.Sprintf("binlog解压命令:%s", cmd) + mylog.Logger.Info(msg) + + _, incr.Err = util.RunLocalCmd("bash", []string{"-c", cmd}, "", nil, 600*time.Second) + if incr.Err != nil { + return + } + _, err := os.Stat(decpFileFullPath) + if err != nil { + incr.Err = fmt.Errorf("解压binlog:%s => %s 失败,err:%v", + item.BackupFile, decpFileFullPath, err) + mylog.Logger.Error(incr.Err.Error()) + + return + } + msg = fmt.Sprintf("解压binlog:%s成功", item.BackupFile) + mylog.Logger.Info(msg) + + //rm 源文件 + rmCmd := fmt.Sprintf("cd %s && rm -f %s 2>/dev/null", incr.SaveMyDir, item.BackupFile) + _, incr.Err = util.RunLocalCmd("bash", []string{"-c", rmCmd}, "", nil, 30*time.Minute) + if incr.Err != nil { + return + } + msg = fmt.Sprintf("删除binlog源文件:%s完成", item.BackupFile) + mylog.Logger.Info(msg) + } +} + +// DecompressedAll 解压全部binlog文件 +func (incr *TredisRocksDBIncrBack) DecompressedAll() { + mylog.Logger.Info("DecompressedAll start ...") + if len(incr.ResultSortBinlog) == 0 { + incr.Err = fmt.Errorf( + "filename:%s binlog信息为空(len:%d),无法解压,请先获取binlog信息", + incr.FileName, len(incr.ResultSortBinlog)) + mylog.Logger.Error(incr.Err.Error()) + return + } + errList := []string{} + successCnt := 0 + failCnt := 0 + totalCnt := 0 + for _, bk01 := range incr.ResultSortBinlog { + bkItem := bk01 + totalCnt++ + incr.DecompressedOne(bkItem) + if incr.Err != nil { + errList = append(errList, incr.Err.Error()) + } else { + successCnt++ + } + } + failCnt = len(errList) + if failCnt > 0 { + list01 := []string{} + //只打印前5条信息 + if failCnt > 5 { + list01 = errList[:5] + } else { + list01 = errList + } + incr.Err = fmt.Errorf( + "解压pod:%s binlog文件失败,成功%d个,失败%d个,共%d个,失败示例:%s", + incr.FileName, successCnt, failCnt, totalCnt, strings.Join(list01, "\n"), + ) + mylog.Logger.Error(incr.Err.Error()) + return + } + msg := fmt.Sprintf("解压pod:%s binlog文件全部成功,成功%d个,失败%d个,共%d个", + incr.FileName, successCnt, failCnt, totalCnt) + mylog.Logger.Info(msg) + return +} + +// Decompressed .. +// 如果所有 增备(已解压)文件 全部存在,则直接return; +// 否则删除 本地存在的增备(已解压)文件; +// 继续执行解压; +func (incr *TredisRocksDBIncrBack) Decompressed() { + mylog.Logger.Info("DecompressedAllIfNotExists start .. ") + var msg string + decpTotalCnt, decpExistsCnt, decpExistsList := incr.CheckLocalDecompressedFilesIsOk() + if incr.Err != nil { + return + } + if decpTotalCnt == decpExistsCnt { + msg = fmt.Sprintf("rollbackPod:%s 本地已存在%d个 增备(已解压文件),无需执行解压...", + incr.FileName, decpTotalCnt) + mylog.Logger.Info(msg) + return + } + incr.rmLocalDecompressedFiles(decpExistsList) + if incr.Err != nil { + return + } + incr.DecompressedAll() +} + +// getNewResultSortBinlog 导入全部binlog +func (incr *TredisRocksDBIncrBack) getNewResultSortBinlog() { + mylog.Logger.Info("getNewResultSortBinlog start ...") + var first_incr *TredisRocksDBIncrBackItem + if len(incr.ResultSortBinlog) == 0 || len(incr.ResultSortBinlog) == 1 { + return + } else if len(incr.ResultSortBinlog) > 1 { + for _, bk01 := range incr.ResultSortBinlog { + bkItem := bk01 + incrBackFile := filepath.Join(incr.SaveMyDir, bkItem.DecompressedFile) + // # binlog 的start_seq 小于全备份的seq 并且 binlog的end_seq 大于全备份seq :这样就找到了所有需要加载的binlog + // if ( $incr_list[$i]->{start_seq} <= $full_seq and $incr_list[$i]->{end_seq} >= $full_seq ) + // 查询下载的范围更大些 ,再通过bingseq 来决定加载的文件 ?(new_incr_list) 这里获取这个值,必须文件已经下载了才能获取得到 + seq := incr.getSeqFromFile(incrBackFile) + if seq <= 0 { + err := fmt.Errorf("获取到的seq%d<=0,不符合预期", seq) + mylog.Logger.Error(err.Error()) + + } + mylog.Logger.Info("getNewResultSortBinlog filename:%s seq:%d", incrBackFile, seq) + bkItem.BinlogStartPos = seq + + } + // 遍历ResultSortBinlog ,对end_seq赋值,前面已经校验过文件index的连续性和对此排序(GetTredisIncrbacksSpecRocks 函数里) + for i := 0; i < len(incr.ResultSortBinlog)-1; i++ { + incr.ResultSortBinlog[i].BinlogEndPos = incr.ResultSortBinlog[i+1].BinlogStartPos + // binlog 的start_seq 小于全备份的seq 并且 binlog的end_seq 大于全备份seq :这样就找到了第一个binlog文件 + // NOCC:tosa/linelength(设计如此) + if incr.ResultSortBinlog[i].BinlogStartPos <= incr.FullStartPos && incr.ResultSortBinlog[i].BinlogEndPos >= incr.FullStartPos { + first_incr = incr.ResultSortBinlog[i] + } + + } + var newResultSortBinlog []*TredisRocksDBIncrBackItem + // binlog 的start_seq 小于全备份的seq 并且 binlog的end_seq 大于全备份seq -》 第一个binlog文件 + if first_incr != nil { + for i := 0; i < len(incr.ResultSortBinlog); i++ { + //找到第一个文件后面的所有文件 + if incr.ResultSortBinlog[i].BinlogStartPos >= first_incr.BinlogStartPos { + newResultSortBinlog = append(newResultSortBinlog, incr.ResultSortBinlog[i]) + } + } + } + // 找到最后需要加载的所有binlog文件 + incr.ResultSortBinlog = newResultSortBinlog + + } + +} + +// ImportAllBinlogToTredis 导入全部binlog +func (incr *TredisRocksDBIncrBack) ImportAllBinlogToTredis(tplusIP string, tplusPort int, tplusPasswd string) { + mylog.Logger.Info("ImportAllBinlogToTredis start ...") + // 获取需要加载的binlog文件,之前的ResultSortBinlog是范围更大点的 + incr.getNewResultSortBinlog() + for _, bk01 := range incr.ResultSortBinlog { + bkItem := bk01 + incr.ImportOneBinlogToTredis(tplusIP, tplusPort, tplusPasswd, bkItem) + if incr.Err != nil { + return + } + } + msg := fmt.Sprintf("filename:%s 共成功导入%d个binlog", + incr.FileName, len(incr.ResultSortBinlog)) + mylog.Logger.Info(msg) + return +} + +// ImportOneBinlogToTredis 导入一个binlog文件到tredis中 +// NOCC:golint/fnsize(设计如此) +func (incr *TredisRocksDBIncrBack) ImportOneBinlogToTredis(tplusIP string, tplusPort int, + tplusPasswd string, bkItem *TredisRocksDBIncrBackItem) { + mylog.Logger.Info("ImportOneBinlogToTredis start ... ") + DepsDir := "/usr/local/redis/bin/deps" + if _, err := os.Stat(DepsDir); os.IsNotExist(err) { + err = fmt.Errorf("目录不存在,请检查:%s", DepsDir) + mylog.Logger.Error(err.Error()) + incr.Err = err + return + } + + incrBackFile := filepath.Join(incr.SaveMyDir, bkItem.DecompressedFile) + incrBackFilePath := strings.TrimSuffix(incrBackFile, filepath.Ext(incrBackFile)) + + cmdfile := fmt.Sprintf("%s.cmd", incrBackFile) + outfile := fmt.Sprintf("%s.out", incrBackFile) + // incr.RecoveryTimePoint := "2022-01-01 00:00:00" // 待转换的字符串时间 + layout := "2006-01-02 15:04:05" // 字符串时间格式 + t, err := time.Parse(layout, incr.RecoveryTimePoint) + if err != nil { + err = fmt.Errorf("%s:时间转换失败,请检查:%v", incr.RecoveryTimePoint, err) + mylog.Logger.Error(err.Error()) + return + } + endtime := t.Unix() * 1000 // 转换为 Unix 时间戳,*1000 转为毫秒 + + restoreCmd := fmt.Sprintf(` + export LD_PRELOAD=%s/libjemalloc.so + export LD_LIBRARY_PATH=LD_LIBRARY_PATH:%s + %s --start-position=%d --end-datetime=%d %s >/%s + `, DepsDir, DepsDir, consts.TredisBinlogBin, incr.FullStartPos, endtime, incrBackFile, cmdfile) + + mylog.Logger.Info("解析binlog,命令:%v", restoreCmd) + ret01, err := util.RunLocalCmd("bash", []string{"-c", restoreCmd}, "", nil, 1*time.Hour) + if err != nil { + mylog.Logger.Error(fmt.Sprintf("解析binlog失败,详情:%v", err)) + incr.Err = err + return + } + ret01 = strings.TrimSpace(ret01) + if strings.Contains(ret01, "ERR:") == true { + mylog.Logger.Error(fmt.Sprintf("解析binlog失败,cmd:%s,err:%s", restoreCmd, ret01)) + incr.Err = fmt.Errorf("解析binlog失败") + return + } + //You can force human readable output when writing to a file or in pipe to other commands by using --no-raw. + // NOCC:tosa/linelength(设计如此) + importCmd := fmt.Sprintf("%s --no-raw --no-auth-warning -h %s -p %d -a %s < %s > %s", consts.RedisCliBin, tplusIP, tplusPort, + tplusPasswd, cmdfile, outfile) + mylog.Logger.Info("binlog 写入命令:%v", importCmd) + _, err = util.RunLocalCmd("bash", []string{"-c", importCmd}, "", nil, 1*time.Hour) + if err != nil { + mylog.Logger.Error(fmt.Sprintf("导入binlog 命令失败,详情:%v", err)) + incr.Err = err + return + } + ok_ret, err_ret, all_ret := 0, 0, 0 + ret := make(map[string]int) + + outF, _ := os.Open(filepath.Join(incrBackFilePath, outfile)) + scanner := bufio.NewScanner(outF) + for scanner.Scan() { + line := scanner.Text() + fields := strings.Split(line, " ") + f1 := fields[0] + + all_ret++ + + var reply_type string + first_char := string(f1[0]) + + if f1 == "OK" { + ok_ret++ + } else if f1 == "(error)" { + err_ret++ + } else if first_char == "\"" { + reply_type = "(string)" + ok_ret++ + } else if first_char == "(" { + reply_type = f1 + ok_ret++ + } else { + reply_type = "unknown" + err_ret++ + } + + ret[reply_type]++ + } + outF.Close() + + msg := "" + for k, v := range ret { + msg += fmt.Sprintf("'%s':%d,", k, v) + } + msg = strings.TrimSuffix(msg, ",") + + msg = (fmt.Sprintf("LOAD BINLOG %s, binlog count: %d, succ: %d, err: %d. detail: %s\n", + incrBackFilePath, all_ret, ok_ret, err_ret, msg)) + mylog.Logger.Info(msg) + if err_ret == 0 && all_ret > 0 { + mylog.Logger.Info("err_ret == 0 && all_ret > 0") + // 先注释测试,后续这些中间文件需要删除 + os.Remove(filepath.Join(incrBackFilePath, cmdfile)) + os.Remove(filepath.Join(incrBackFilePath, outfile)) + } + + mylog.Logger.Info("binlog:%s 导入 %s:%d成功", bkItem.BackupFile, tplusIP, tplusPort) + return +} diff --git a/dbm-services/redis/db-tools/dbactuator/pkg/backupsys/tendisplus_incrback.go b/dbm-services/redis/db-tools/dbactuator/pkg/backupsys/tendisplus_incrback.go new file mode 100644 index 0000000000..aece6635f8 --- /dev/null +++ b/dbm-services/redis/db-tools/dbactuator/pkg/backupsys/tendisplus_incrback.go @@ -0,0 +1,1236 @@ +package backupsys + +import ( + "fmt" + "os" + "path/filepath" + "regexp" + "sort" + "strconv" + "strings" + "sync" + + "time" + + "dbm-services/redis/db-tools/dbactuator/mylog" + + "dbm-services/redis/db-tools/dbactuator/pkg/consts" + "dbm-services/redis/db-tools/dbactuator/pkg/customtime" + "dbm-services/redis/db-tools/dbactuator/pkg/util" +) + +// TplusRocksDBIncrBackItem 指定tendisplus 指定rocksdb的增备信息项 +type TplusRocksDBIncrBackItem struct { + Incr int `json:"incr"` + NodeIP string `json:"node_ip"` + FileName string `json:"filename"` + BackupFile string `json:"backup_file"` + DecompressedFile string `json:"decompressedFile"` //解压后的文件名 + RocksdbIdx int `json:"rocksdbIdx"` //kvstore + BinlogIdx int64 `json:"binlogIdx"` + BackupTaskid int64 `json:"backup_taskid"` // 任务ID + BackupSize int64 `json:"backup_size"` // 文件大小 + BackupStart customtime.CustomTime `json:"backup_start"` //binlog文件中获取,代表 上一个binlog文件最后一条binlog的时间 + BackupEnd customtime.CustomTime `json:"backup_end"` //备份文件上传备份系统成功时间 + +} + +// TplusRocksDBIncrBack 指定tendisplus 指定rocksdb拉取增备 +type TplusRocksDBIncrBack struct { + FileName string `json:"filename"` + SourceIP string `json:"sourceIp"` + RocksDBIndex int `json:"rocksDbIndex"` + //保存文件的目录(如果是在k8s中,则代表node 上的dir) + SaveNodeDir string `json:"saveRemoteDir"` + //保存备份文件的本地目录 + + SaveMyDir string `json:"saveLocalDir"` + //保存文件的服务器(如果是k8s中,则代表node ip) + SaveHost string `json:"saveHost"` + StartTime time.Time `json:"startTime"` //拉取增备的起始时间 + EndTime time.Time `json:"endTime"` //拉取增备的结束时间 + StartPos uint64 `json:"startPos"` //回档导入增备时,--start-position + //每个rocksdb 最靠近(小于) startTime 的binlog, + //binlog的BackupStart时间小于startTime + PerRocksNearestStart *TplusRocksDBIncrBackItem + //每个rocksdb 最靠近(大于) endTime 的binlog + //binlog的BackupStart时间大于endTime + PerRocksNearestEnd *TplusRocksDBIncrBackItem + //key: 0 1 2 ..,, 每个tendisplus具有10个rocksdb,每个rocksdb都有自己的binlog + //value: 每个rocksdb对应的binlog列表,按照文件index从小到大排序 + ResultSortBinlog []*TplusRocksDBIncrBackItem `json:"resultSortBinlog"` + //key: binlog file name + //value: 文件对应的binlog项 + ResultBinlogMap map[string]*TplusRocksDBIncrBackItem `json:"resultBinlogMap"` + // redisCli *myredis.RedisWorker `json:"-"` //保存备份信息的redis + Err error `json:"-"` //错误信息 + query QueryReq //备份系统查询 + recover RecoverReq + // recoverQuery RecoverQueryReq +} + +// NewTplusRocksDBIncrBack 新建tendisplus rocksdb的binlog拉取任务 +// fileName 过滤信息 +// sourceIP 查询 IP 源 +// startTime 拉取增备的开始时间 -> 全备份的开始时间 +// endTime 拉取增备份的结束时间 -> 回档时间 +func NewTplusRocksDBIncrBack(filename, sourceIP string, rocksdbIdx int, startPos uint64, startTime, endTime, + saveHost, saveNodeDir, saveMyDir string) (ret *TplusRocksDBIncrBack) { + mylog.Logger.Info("NewTplusRocksDBIncrBack start ...") + if rocksdbIdx < 0 || rocksdbIdx > 1000 { + ret.Err = fmt.Errorf(" rocksdbIdx:%d cann't <0 or > 1000", rocksdbIdx) + mylog.Logger.Error(ret.Err.Error()) + return + } + ret = &TplusRocksDBIncrBack{ + FileName: filename, + SourceIP: sourceIP, + SaveHost: saveHost, + SaveNodeDir: saveNodeDir, + SaveMyDir: saveMyDir, + RocksDBIndex: rocksdbIdx, + StartPos: startPos, + } + layout := "2006-01-02 15:04:05" + var err error + ret.StartTime, err = time.ParseInLocation(layout, startTime, time.Local) + if err != nil { + ret.Err = fmt.Errorf("startTime:%s time.parse fail,err:%s,layout:%s", startTime, err, layout) + mylog.Logger.Error(ret.Err.Error()) + return ret + } + ret.EndTime, err = time.ParseInLocation(layout, endTime, time.Local) + if err != nil { + ret.Err = fmt.Errorf("endTime:%s time.parse fail,err:%s,layout:%s", endTime, err, layout) + mylog.Logger.Error(ret.Err.Error()) + return ret + } + mylog.Logger.Info("StartTime:%s,EndTime:%s", ret.StartTime, ret.EndTime) + if ret.EndTime.Before(ret.StartTime) == true || ret.EndTime.Equal(ret.StartTime) == true { + ret.Err = fmt.Errorf("tendisplus binlog拉取,endtime:%s 小于等于 startTime:%s", + endTime, startTime) + mylog.Logger.Error(ret.Err.Error()) + return ret + } + if ret.StartTime.After(time.Now()) == true { + //未来时间 + ret.Err = fmt.Errorf("binlogPull startTime:%s > time.Now()", startTime) + mylog.Logger.Error(ret.Err.Error()) + return ret + } + if ret.EndTime.After(time.Now()) == true { + //未来时间 + ret.Err = fmt.Errorf("binlogPull endTime:%s > time.Now()", endTime) + mylog.Logger.Error(ret.Err.Error()) + return ret + } + ret.ResultSortBinlog = []*TplusRocksDBIncrBackItem{} + ret.ResultBinlogMap = make(map[string]*TplusRocksDBIncrBackItem) + return +} + +// LastNDaysIncrBack 需要最近N天的增备 +func LastNDaysIncrBack() int { + lastNDays := 1 + return lastNDays +} + +// AddBinlogToMap 添加binlog项到 ResultBinlogMap,ResultSortBinlog中 +func (incr *TplusRocksDBIncrBack) AddBinlogToMap(item01 *TplusRocksDBIncrBackItem) { + if item01 == nil { + return + } + if _, ok := incr.ResultBinlogMap[item01.BackupFile]; ok == true { + //去重 + return + } + incr.ResultBinlogMap[item01.BackupFile] = item01 + incr.ResultSortBinlog = append(incr.ResultSortBinlog, item01) +} + +// GetTplusIncrbacks 查询特定端口的binlog备份文件 +// NOCC:golint/fnsize(设计如此) +func (incr *TplusRocksDBIncrBack) GetTplusIncrbacks() (backs []*TplusRocksDBIncrBackItem) { + layout := "2006-01-02 15:04:05" + // incr.StartTime 往前24小时 + h, _ := time.ParseDuration("-24h") + // incr.StartTime = incr.StartTime.Add(h) + beginData := incr.StartTime.Add(h).Format(layout) + // incr.EndTime 往后8小时 + h1, _ := time.ParseDuration("24h") + endDate := incr.EndTime.Add(h1).Format(layout) + mylog.Logger.Info("fileName:%s", incr.FileName) + + params := QueryReq{ + Params: Params{ + Version: "1.0", + RequestInfo: RequestInfo{ + BaseInfo: BaseInfo{ + SysID: BackupSysID, + Key: BackupKey, + Ticket: "", + }, + DetailInfo: DetailInfo{ + SourceIP: incr.SourceIP, + BeginDate: beginData, + EndDate: endDate, + FileName: incr.FileName, + // FileSpWildchar: "0", + }, + }, + }, + } + resp, err := incr.query.QueryFile(params) + if err != nil { + incr.Err = fmt.Errorf("QueryFile fail,err:%v,params:%s", err, params) + mylog.Logger.Error(incr.Err.Error()) + return + } + if resp == nil { + return + } + + mylog.Logger.Debug("resp.Detail:%v", resp.Detail) + mylog.Logger.Info("resp num :%v", resp.Num) + mylog.Logger.Info("ip:%v,beginDate:%v,endDate:%v", incr.SourceIP, beginData, endDate) + + // 示例: binlog-127.0.0.x-30000-7-0003612-20230326232536.log.zst + binlogReg := regexp.MustCompile(`^*?-(\d+)-(\d+)-(\d+)-(\d+).log.zst`) + layout1 := "20060102150405" + for _, str01 := range resp.Detail { + back01 := &TplusRocksDBIncrBackItem{} + taskID, _ := strconv.Atoi(str01.TaskID) + size, _ := strconv.Atoi(str01.Size) + if taskID < 0 || size < 0 { + //backup_taskid 小于0 或backup_size 小于0的备份,是无效备份 + msg := fmt.Sprintf("filename:%s incrBackup:%s backupTaskid:%s<0 backupSize:%s<0 is invalid,skip...", + incr.FileName, str01.FileName, str01.TaskID, str01.Size) + mylog.Logger.Info(msg) + continue + } + back01.BackupTaskid, _ = strconv.ParseInt(str01.TaskID, 10, 64) + back01.BackupSize, _ = strconv.ParseInt(str01.Size, 10, 64) + back01.BackupFile = str01.FileName + back01.NodeIP = str01.SourceIP + match01 := binlogReg.FindStringSubmatch(str01.FileName) + + if len(match01) != 5 { + incr.Err = fmt.Errorf( + "filename:%s backup:%v format not correct,backupFile:%s cann't find rocksdbIdx/binlogIdx/createTime", + incr.FileName, back01, str01.FileName) + mylog.Logger.Error(incr.Err.Error()) + return + } + bkCreateTime, err01 := time.ParseInLocation(layout1, match01[4], time.Local) + if err01 != nil { + incr.Err = fmt.Errorf( + "backup file createTime:%s time.parese fail,err:%s,layout1:%s", + match01[3], err01, layout1) + mylog.Logger.Error(incr.Err.Error()) + return + } + // binlog-127.0.0.x-30010-0-0002495-20230311124436.log.zst + back01.RocksdbIdx, _ = strconv.Atoi(match01[2]) + back01.BinlogIdx, _ = strconv.ParseInt(match01[3], 10, 64) + back01.BackupStart.Time = bkCreateTime //不要用backupStart值 + back01.BackupEnd.Time, err01 = time.ParseInLocation(layout, str01.FileLastMtime, time.Local) //文件最后修改时间 + if err01 != nil { + incr.Err = fmt.Errorf( + "backup file lastTime:%s time.parese fail,err:%s,layout:%s", + str01.FileLastMtime, err01, layout) + mylog.Logger.Error(incr.Err.Error()) + return + } + + backs = append(backs, back01) + } + + mylog.Logger.Info("TplusRocksDBIncrBackItem:%v", backs[0]) + return +} + +// GetTplusIncrbacksSpecRocks 获取startTime~endTime时间段内的binlog +// NOCC:golint/fnsize(设计如此) +func (incr *TplusRocksDBIncrBack) GetTplusIncrbacksSpecRocks() { + + // 获取startTime~endTime时间段内的binlog + // layout := "20060102" + layout02 := "2006-01-02 15:04:05" + //从备份列表中选择最靠近 startTime 的文件, + // nearestStartBk.BackupStart 小于等于 StartTime + var nearestStartBk *TplusRocksDBIncrBackItem = nil + //从备份列表中选择最靠近 endTime 的文件, + // nearestEndBk.BackupStart 大于等于 EndTime + var nearestEndBk *TplusRocksDBIncrBackItem = nil + + backs := incr.GetTplusIncrbacks() + for _, bk01 := range backs { + bkItem := bk01 + + if bkItem.RocksdbIdx != incr.RocksDBIndex { + // 不是目标rocksdb的binlog,忽略 + continue + } + //只需要那些BackupStart <= incr.StartTime 的binlog + if bkItem.BackupStart.Before(incr.StartTime) == true || + bkItem.BackupEnd.Equal(incr.StartTime) == true { + if nearestStartBk == nil { + // 第一次找到 BackupStart 小于 startTime 的备份 + nearestStartBk = bkItem + } else { + // nearestStartBk.BackupStart < 该备份BackupStart <= incr.StartTime + // 或者 + // nearestStartBk.BackupStart == 该备份BackupStart 同时 该备份BinlogIdx < nearestStartBk.BinlogIdx + // (同一时间大量写入时可能一秒钟生成多个binlog文件,此时同一秒生成的所有binlog都保留,所以 nearestStartBk.BinlogIdx是最小的) + + // 如binlog-127.0.0.x-30010-0-0002495-20230311124436.log.zst: BinlogIdx是0002495 + if bkItem.BackupStart.After(nearestStartBk.BackupStart.Time) == true || + (bkItem.BackupStart.Equal(nearestStartBk.BackupStart.Time) == true && + bkItem.BinlogIdx < nearestStartBk.BinlogIdx) { + nearestStartBk = bkItem + } + + } + } + + //只需要那些BackupStart >= incr.EndTime 的binlog + if bkItem.BackupStart.After(incr.EndTime) == true || + bkItem.BackupStart.Equal(incr.EndTime) == true { + if nearestEndBk == nil { + //第一次找到 BackupStart 大于等于 endTIme的备份 + nearestEndBk = bkItem + } else { + // incr.EndTime <= 该备份BackupStart < nearestEndBk.BackupStart. + // 或者 + // nearestEndBk.BackupStart == 该备份BackupStart 同时 该备份BinlogIdx > nearestStartBk.BinlogIdx + // (同一时间大量写入时可能一秒钟生成多个binlog文件,此时同一秒生成的所有binlog都保留,所以 nearestEndBk.BinlogIdx是最大的) + if bkItem.BackupStart.Before(nearestEndBk.BackupStart.Time) == true || + (bkItem.BackupStart.Equal(nearestEndBk.BackupStart.Time) == true && + bkItem.BinlogIdx > nearestEndBk.BinlogIdx) { + nearestEndBk = bkItem + } + } + } + + } + + if nearestStartBk == nil { + incr.Err = fmt.Errorf("filename:%s rocksdbIdx:%d 向前%d天,没有找到时间 小于 startTime:%s的binlog", + incr.FileName, + incr.RocksDBIndex, + LastNDaysIncrBack(), + incr.StartTime.Local().Format(layout02)) + mylog.Logger.Error(incr.Err.Error()) + return + } + + msg := fmt.Sprintf("filename:%s rocksdbIdx:%d 找到距离startTime:%s最近(小于)的binlog:%s,binlogStart:%s", + incr.FileName, + incr.RocksDBIndex, + incr.StartTime.Local().Format(layout02), + nearestStartBk.BackupFile, + nearestStartBk.BackupStart.Local().Format(layout02)) + mylog.Logger.Info(msg) + + if nearestEndBk == nil { + incr.Err = fmt.Errorf("filename:%s rocksdbIdx:%d 向后%d天,没有找到时间大于endTime:%s的binlog", + incr.FileName, + incr.RocksDBIndex, + LastNDaysIncrBack(), + incr.EndTime.Local().Format(layout02)) + mylog.Logger.Error(incr.Err.Error()) + return + } + + msg = fmt.Sprintf("filename:%s rocksdbIdx:%d 找到距离endTime:%s最近(大于)的binlog:%s,binglogEnd:%s", + incr.FileName, + incr.RocksDBIndex, + incr.EndTime.Local().Format(layout02), + nearestEndBk.BackupFile, + nearestEndBk.BackupStart.Local().Format(layout02)) + mylog.Logger.Info(msg) + + //成功获取到 nearestStartBk nearestEndBk,继续获取两者之间的binlog + start01 := nearestStartBk.BackupStart.Time + // end01 := nearestEndBk.BackupStart.Time + // 使用 最后一个binlog文件上传备份系统成功时间, 而不是 binlog 文件名中的时间(也就是binlog文件生成时间) + // 因为6月1日生成的binlog,有可能6月2日才上传成功 + // 而在dba redis中 或者 备份系统中 只有 6月2号的记录里面才能查询到该 binlog文件 + end01 := nearestEndBk.BackupEnd.Time + //start02 对应nearestStartBk.BackupStart 当天0点0分0秒 + //end02 对应nearestEndBk.BackupStart 当天0点0分0秒 + //我们需要将 start02 end02之间所有day的binlog都筛选到 + start02 := time.Date(start01.Year(), start01.Month(), start01.Day(), 0, 0, 0, 0, start01.Location()) + end02 := time.Date(end01.Year(), end01.Month(), end01.Day(), 0, 0, 0, 0, end01.Location()) + incr.AddBinlogToMap(nearestStartBk) + incr.AddBinlogToMap(nearestEndBk) + + incr.PerRocksNearestStart = nearestStartBk + incr.PerRocksNearestEnd = nearestEndBk + + for start02.Before(end02) == true || start02.Equal(end02) == true { + + backs := incr.GetTplusIncrbacks() + + for _, bk01 := range backs { + bkItem := bk01 + if bkItem.RocksdbIdx != incr.RocksDBIndex { + //不是目标rocksdb的binlog,直接忽略 + continue + } + if (bkItem.BackupStart.After(nearestStartBk.BackupStart.Time) == true || + bkItem.BackupStart.Equal(nearestStartBk.BackupStart.Time) == true) && + (bkItem.BackupStart.Before(nearestEndBk.BackupStart.Time) == true || + bkItem.BackupStart.Equal(nearestEndBk.BackupStart.Time) == true) { + incr.AddBinlogToMap(bkItem) + } + } + start02 = start02.Add(1 * 24 * time.Hour) + } + //按照binlog index排序 + sort.Slice(incr.ResultSortBinlog, func(i, j int) bool { + return incr.ResultSortBinlog[i].BinlogIdx < incr.ResultSortBinlog[j].BinlogIdx + }) + + incr.isGetAllBinlogInfo() + if incr.Err != nil { + return + } + return +} + +/* +判断binlog文件是否连续,是否重复; +- 重复则报错; +- 不连续则返回缺失的binlog index,如 2,3,5,8 则返回缺失的4,6,7 +*/ +func getNotReadyBinlogs(sortBinlogs []*TplusRocksDBIncrBackItem) ([]string, error) { + var ret []string + var err error + preListIdx := 0 + preBinlogIdx := sortBinlogs[0].BinlogIdx + for idx, bin01 := range sortBinlogs { + binItem := bin01 + if idx == preListIdx { + //第一个元素忽略 + continue + } + if binItem.BinlogIdx <= preBinlogIdx { + //如果后面的binlog index小于等于前一个binlog index + err = fmt.Errorf("当前binlog index:%d <= 前一个binlog index:%d", binItem.BinlogIdx, preBinlogIdx) + mylog.Logger.Error(err.Error()) + mylog.Logger.Error(err.Error()) + return ret, err + } + if binItem.BinlogIdx == preBinlogIdx+1 { + //符合预期 + preBinlogIdx = binItem.BinlogIdx + continue + } + preBinlogIdx++ + for preBinlogIdx < bin01.BinlogIdx { + ret = append(ret, fmt.Sprintf("%d", preBinlogIdx)) + preBinlogIdx++ + } + } + return ret, nil +} + +/* +判断所需binlog文件信息是否已全部获取到; +- ResultSortBinlog 第二个binlog文件序号 必须 只比 第一个binlog 文件序号大1 +- ResultSortBinlog 倒数第一个binlog文件序号 必须 只比 倒数第二个binlog文件序号大 1 +- 最后一个文件序号 减去 第一个文件序号 等于 len(ResultSortBinlog)+1 +- ResultSortBinlog 第一个binlog.BackupStart必须小于 startTime, 第二个binlog.BackupStart必须大于 startTime +- ResultSortBinlog 最后一个binlog.BackupStart必须大于 endTime, 倒数第二个binlog.BackupStart必须小于 endTime +*/ +// NOCC:golint/fnsize(设计如此) +func (incr *TplusRocksDBIncrBack) isGetAllBinlogInfo() (ret bool) { + mylog.Logger.Info("isGetAllBinlogInfo start ...") + cnt := len(incr.ResultSortBinlog) + mylog.Logger.Info("ResultSortBinlog len:%d", cnt) + layout := "2006-01-02 15:04:05" + + if cnt < 2 { + //至少会包含两个binlog文件,第一个BackupStart小于 startTime, 第二个BackupStart 大于 endTime + str01 := "" + for _, bk01 := range incr.ResultSortBinlog { + str01 = fmt.Sprintf("%s,%s", str01, bk01.BackupFile) + } + incr.Err = fmt.Errorf( + "filename:%s rocksdbIdx:%d拉取[%s ~ %s]时间段的binlog,至少包含2个binglo,当前%d个binlog,详情:%s", + incr.FileName, incr.RocksDBIndex, + incr.StartTime.Local().Format(layout), + incr.EndTime.Local().Format(layout), + cnt, str01) + mylog.Logger.Error(incr.Err.Error()) + mylog.Logger.Error(incr.Err.Error()) + return false + } + firstBinlog := incr.ResultSortBinlog[0] + secondBinlog := incr.ResultSortBinlog[1] + + lastBinlog := incr.ResultSortBinlog[cnt-1] + beforeLastBinlog := incr.ResultSortBinlog[cnt-2] + + if secondBinlog.BinlogIdx-firstBinlog.BinlogIdx != 1 { + incr.Err = fmt.Errorf( + "filename:%s rocksdbIdx:%d拉取[%s ~ %s]时间段的binlog,第一binlog:%s 和 第二binlog:%s 不连续", + incr.FileName, incr.RocksDBIndex, + incr.StartTime.Local().Format(layout), + incr.EndTime.Local().Format(layout), + firstBinlog.BackupFile, secondBinlog.BackupFile, + ) + + mylog.Logger.Error(incr.Err.Error()) + return + } + if lastBinlog.BinlogIdx-beforeLastBinlog.BinlogIdx != 1 { + incr.Err = fmt.Errorf( + "filename:%s rocksdbIdx:%d拉取[%s ~ %s]时间段的binlog,倒数第二binlog:%s 和 倒数第一binlog:%s 不连续", + incr.FileName, incr.RocksDBIndex, + incr.StartTime.Local().Format(layout), + incr.EndTime.Local().Format(layout), + beforeLastBinlog.BackupFile, lastBinlog.BackupFile, + ) + mylog.Logger.Error(incr.Err.Error()) + + return + } + //是否连续 + binIndexList, err := getNotReadyBinlogs(incr.ResultSortBinlog) + if err != nil { + incr.Err = err + return false + } + if len(binIndexList) > 0 { + incr.Err = fmt.Errorf("缺失的binlog共%d个,缺失的binlog index是:%s", + len(binIndexList), strings.Join(binIndexList, ",")) + + mylog.Logger.Error(incr.Err.Error()) + return false + } + //第一个binlog.BackupStart必须小于等于 startTime,大于则报错 + if firstBinlog.BackupStart.After(incr.StartTime) == true { + incr.Err = fmt.Errorf( + "filename:%s rocksdbIdx:%d,第一个binog:%s,binlogStart:%s 大于startTime(全备时间):%s", + incr.FileName, incr.RocksDBIndex, + firstBinlog.BackupFile, + firstBinlog.BackupStart.Local().Format(layout), + incr.StartTime.Local().Format(layout)) + mylog.Logger.Error(incr.Err.Error()) + + return false + } + //第二个binlog.BackupStart必须大于(等于) startTime,小于则报错 + for idx01 := 1; secondBinlog.BackupStart.Equal(firstBinlog.BackupStart.Time) == true; idx01++ { + //因为相同时间可能有多个binlog,所以跳过与 firstBinlog.BinlogEnd 相等的 + secondBinlog = incr.ResultSortBinlog[idx01] + } + if secondBinlog.BackupStart.Before(incr.StartTime) { + err = fmt.Errorf( + "filename:%s RocksdbIdx:%d,第二个binlog:%s,binlogStart:%s 时间小于startTime(全备时间):%s", + incr.FileName, incr.RocksDBIndex, + secondBinlog.BackupFile, + secondBinlog.BackupStart.Local().Format(layout), + incr.StartTime.Local().Format(layout)) + mylog.Logger.Error(err.Error()) + + return false + } + //倒数第二个binlog.BackupStart必须小于等于 endTime, 大于则报错 + for idx02 := cnt - 2; beforeLastBinlog.BackupStart.Equal(lastBinlog.BackupStart.Time) == true; idx02-- { + //因为相同时间可能有多个binlog,所以跳过与 lastBinlog.BinlogEnd 相等的 + beforeLastBinlog = incr.ResultSortBinlog[idx02] + } + if beforeLastBinlog.BackupStart.After(incr.EndTime) == true { + incr.Err = fmt.Errorf( + "filename:%s RocksdbIdx:%d,倒数第二个binlog:%s,binlogStart:%s 时间大于endTime(回档目标时间):%s", + incr.FileName, incr.RocksDBIndex, + beforeLastBinlog.BackupFile, + beforeLastBinlog.BackupStart.Local().Format(layout), + incr.EndTime.Local().Format(layout)) + mylog.Logger.Error(incr.Err.Error()) + + return false + } + //最后一个binlog.BackupStart必须大于等于 endTime,小于则报错 + if lastBinlog.BackupStart.Before(incr.EndTime) == true { + incr.Err = fmt.Errorf( + "filename:%s RocksdbIdx:%d,最后一个binlog:%s,binlogStart:%s 时间小于endTime(回档目标时间):%s", + incr.FileName, incr.RocksDBIndex, + lastBinlog.BackupFile, + lastBinlog.BackupStart.Local().Format(layout), + incr.EndTime.Local().Format(layout)) + mylog.Logger.Error(incr.Err.Error()) + return false + } + msg := fmt.Sprintf(`filename:%s rocksdbIdx:%d 找到所有[%s~%s]时间段的binlog, + 共%d个,第一个binlog:%s binlogStart:%s,最后一个binlog:%s binlogStart:%s`, + incr.FileName, incr.RocksDBIndex, + incr.StartTime.Local().Format(layout), + incr.EndTime.Local().Format(layout), + cnt, + firstBinlog.BackupFile, + firstBinlog.BackupStart.Local().Format(layout), + lastBinlog.BackupFile, + lastBinlog.BackupStart.Local().Format(layout), + ) + mylog.Logger.Info(msg) + return +} + +// TplusIncrBackPull 指定tendisplus增备拉取 +type TplusIncrBackPull struct { + FileName string `json:"filename"` + SourceIP string `json:"sourceIP"` + RocksMap map[int]*TplusRocksDBIncrBack `json:"rocksMap"` //多个rocksdb 拉取增备 + Err error `json:"-"` +} + +// NewTplusIncrBackPull 新建tendisplus拉取任务 +func NewTplusIncrBackPull(filename, sourceIP string) *TplusIncrBackPull { + return &TplusIncrBackPull{ + FileName: filename, + SourceIP: sourceIP, + RocksMap: make(map[int]*TplusRocksDBIncrBack), + } +} + +// NewRocksDBIncrBack 新增rocksdb增备拉取任务 +func (incr *TplusIncrBackPull) NewRocksDBIncrBack(rocksdbIdx int, startPos uint64, + startTime, endTime, saveHost, saveNodeDir, saveMyDir string) { + mylog.Logger.Info("NewRocksDBIncrBack start ...") + + task01 := NewTplusRocksDBIncrBack(incr.FileName, incr.SourceIP, rocksdbIdx, + startPos, startTime, endTime, + saveHost, saveNodeDir, saveMyDir) + if task01.Err != nil { + incr.Err = task01.Err + return + } + // fmt.Printf("rocksdbIdx====>%d task01==>%+v\n", rocksdbIdx, task01) + incr.RocksMap[rocksdbIdx] = task01 + return +} + +// GetAllIncrBacksInfo 获取所有rocksdb的binlog信息 +func (incr *TplusIncrBackPull) GetAllIncrBacksInfo() { + mylog.Logger.Info("GetAllIncrBacksInfo start ...") + if len(incr.RocksMap) == 0 { + incr.Err = fmt.Errorf("filename:%s 请先添加rocksdb信息再获取备份信息", incr.FileName) + mylog.Logger.Error(incr.Err.Error()) + return + } + //所有rocksdb并发执行 + mylog.Logger.Info("GetAllIncrBacksInfo 并发查询所有kvstore的信息 ...") + wg := sync.WaitGroup{} + for _, rock01 := range incr.RocksMap { + rockItem := rock01 + wg.Add(1) + go func(rock001 *TplusRocksDBIncrBack) { + defer wg.Done() + rock001.GetTplusIncrbacksSpecRocks() + }(rockItem) + } + wg.Wait() + + //检测是否有失败 + for _, rock01 := range incr.RocksMap { + rockItem := rock01 + if rockItem.Err != nil { + incr.Err = rockItem.Err + return + } + } + return +} + +// TotalSize 所有binlog所需磁盘空间大小 +func (incr *TplusRocksDBIncrBack) TotalSize() int64 { + var ret int64 = 0 + for _, bk01 := range incr.ResultSortBinlog { + bkItem := bk01 + ret = ret + bkItem.BackupSize + } + return ret +} + +// TotalSize 获取tendisplus所有binlog所需的总size +func (incr *TplusIncrBackPull) TotalSize() int64 { + var ret int64 = 0 + for _, rock01 := range incr.RocksMap { + rockItem := rock01 + ret = ret + rockItem.TotalSize() + } + return ret +} + +// GetBackupFileExt 获取备份文件后缀(如果后缀无法解压,及时报错) +func (incr *TplusRocksDBIncrBack) GetBackupFileExt( + item *TplusRocksDBIncrBackItem) (bkFileExt string) { + bkFileExt = filepath.Ext(item.BackupFile) + + okExt := map[string]bool{ + ".tar": true, + ".tgz": true, + ".gz": true, + ".zip": true, + ".lzo": true, + ".zst": true, + } + + if strings.HasSuffix(item.BackupFile, ".tar.gz") { + bkFileExt = ".tar.gz" + return + } else if _, ok := okExt[bkFileExt]; ok == true { + return bkFileExt + } else { + incr.Err = fmt.Errorf("无法解压的binlog文件:%s", item.BackupFile) + mylog.Logger.Error(incr.Err.Error()) + return + } +} + +// getDecompressedFile 获取解压文件,已经解压文件完整路径 +func (incr *TplusRocksDBIncrBack) getDecompressedFile( + item *TplusRocksDBIncrBackItem) (decpFile, decpFileFullPath string) { + var bkFileExt string = incr.GetBackupFileExt(item) + if incr.Err != nil { + return + } + item.DecompressedFile = strings.TrimSuffix(item.BackupFile, bkFileExt) + decpFile = item.DecompressedFile + decpFileFullPath = filepath.Join(incr.SaveMyDir, decpFile) + return +} + +// CheckLocalBackupfilesIsOk 检查本地增备文件是否全部ok +// 返回值: +// totalCnt: 所需的全部增备文件个数 +// existsCnt: 本地存在的增备文件个数, existsList: 本地存在的增备文件详情 +// sizeOKCnt: 本地存在的增备文件 大小ok 的个数 +func (incr *TplusRocksDBIncrBack) CheckLocalBackupfilesIsOk() ( + totalCnt, existsCnt, sizeOKCnt int, existsList []*TplusRocksDBIncrBackItem, +) { + totalCnt = 0 + existsCnt = 0 + for _, bk01 := range incr.ResultSortBinlog { + bkItem := bk01 + totalCnt++ + incrBkFile := filepath.Join(incr.SaveMyDir, bkItem.BackupFile) + incrBkInfo, err := os.Stat(incrBkFile) + if os.IsNotExist(err) == true { + continue + } + existsCnt++ + existsList = append(existsList, bkItem) + if incrBkInfo.Size() == bkItem.BackupSize { + sizeOKCnt++ + } + } + return +} + +// CheckLocalDecompressedFilesIsOk 检查本地 增备解压文件 是否ok +// 返回值: +// totalCnt: 全部增备(解压)文件个数 +// existsCnt: 本地存在的增备(解压)文件个数, existsList: 本地存在的增备(解压)文件详情 +func (incr *TplusRocksDBIncrBack) CheckLocalDecompressedFilesIsOk() ( + totalCnt, existsCnt int, existsList []*TplusRocksDBIncrBackItem, +) { + mylog.Logger.Info("CheckLocalDecompressedFilesIsOk start ...") + totalCnt = 0 + existsCnt = 0 + for _, bk01 := range incr.ResultSortBinlog { + bkItem := bk01 + totalCnt++ + _, decpFileFullPath := incr.getDecompressedFile(bkItem) + if incr.Err != nil { + return + } + _, err := os.Stat(decpFileFullPath) + if os.IsNotExist(err) == true { + continue + } + existsCnt++ + existsList = append(existsList, bkItem) + } + return +} + +// rmLocalBackupFiles 删除一些本地 增备文件 +func (incr *TplusRocksDBIncrBack) rmLocalBackupFiles(bkList []*TplusRocksDBIncrBackItem) { + rmCnt := 0 + for _, bk01 := range bkList { + bkItem := bk01 + bkFileFullPath := filepath.Join(incr.SaveMyDir, bkItem.BackupFile) + if _, err := os.Stat(bkFileFullPath); os.IsNotExist(err) { + continue + } + rmCmd := fmt.Sprintf("cd %s && rm -f %s 2>/dev/null", incr.SaveMyDir, bkItem.BackupFile) + _, incr.Err = util.RunLocalCmd("bash", []string{"-c", rmCmd}, "", nil, 30*time.Minute) + if incr.Err != nil { + return + } + rmCnt++ + } + msg := fmt.Sprintf("共删除%d个本地binlog(未解压)文件", rmCnt) + mylog.Logger.Info(msg) + return +} + +// rmLocalDecompressedFiles 删除一些本地 增备(已解压)文件 +func (incr *TplusRocksDBIncrBack) rmLocalDecompressedFiles(bkList []*TplusRocksDBIncrBackItem) { + mylog.Logger.Info("rmLocalDecompressedFiles start ... ") + rmCnt := 0 + for _, bk01 := range bkList { + bkItem := bk01 + if bkItem.DecompressedFile == "" { + continue + } + decpFileFullPath := filepath.Join(incr.SaveMyDir, bkItem.DecompressedFile) + if _, err := os.Stat(decpFileFullPath); os.IsNotExist(err) { + continue + } + rmCmd := fmt.Sprintf("cd %s && rm -f %s 2>/dev/null", incr.SaveMyDir, bkItem.DecompressedFile) + _, incr.Err = util.RunLocalCmd("bash", []string{"-c", rmCmd}, "", nil, 30*time.Minute) + if incr.Err != nil { + return + } + rmCnt++ + } + msg := fmt.Sprintf("共删除%d个本地binlog(已解压)文件", rmCnt) + mylog.Logger.Info(msg) + return +} + +// PullFilesFromBS 从备份系统拉取文件到对应位置(saveHost,saveRemoteDir) +func (incr *TplusRocksDBIncrBack) PullFilesFromBS() { + if len(incr.ResultSortBinlog) == 0 { + incr.Err = fmt.Errorf( + "filename:%s rocksdbIdx:%d binlog信息为空(len:%d),无法拉取,请先获取binlog信息再拉取", + incr.FileName, incr.RocksDBIndex, len(incr.ResultSortBinlog)) + mylog.Logger.Error(incr.Err.Error()) + return + } + + var taskIds string + for _, bk01 := range incr.ResultSortBinlog { + bkItem := bk01 + taskIds = fmt.Sprintf("%s,%s", taskIds, strconv.FormatInt(bkItem.BackupTaskid, 10)) + } + mylog.Logger.Info("PullFilesFromBS TaskidList:%v", taskIds) + + recoverParams := RecoverReq{ + RecoverParams: RecoverParams{ + Version: consts.BackupVersion, + RecoverRequestInfo: RecoverRequestInfo{ + BaseInfo: BaseInfo{ + SysID: BackupSysID, + Key: BackupKey, + Ticket: "", + }, + RecoverDetailInfo: RecoverDetailInfo{ + TaskidList: taskIds, + DestIP: incr.SaveHost, + LoginUser: DstIPBackupUser, + LoginPasswd: DstIPBackupPassword, + Directory: incr.SaveNodeDir, + Reason: "data recover", + }, + }, + }, + } + // 因为并行度是由备份系统调度的,所以这里直接全部请求下载 + // 如果按 task_id 逐个请求备份系统,就需要客户端控制并发 + err := incr.recover.WaitForRecoverFinish(recoverParams, incr.SaveHost) + if err != nil { + incr.Err = fmt.Errorf("等待备份文件下载失败:%v", err) + return + } + + //拉取成功,检查拉取的文件是否ok + incr.CheckAllPulledFilesOK() + +} + +// checkPulledFileOK 检查文件下载是否成功 +func (incr *TplusRocksDBIncrBack) checkPulledFileOK(item *TplusRocksDBIncrBackItem) (err error) { + + if incr.SaveMyDir != "" { + incrBkFile := filepath.Join(incr.SaveMyDir, item.BackupFile) + bkFileInfo, err := os.Stat(incrBkFile) + if err != nil { + err = fmt.Errorf("pod:%s 本地binlog文件:%s 信息获取失败,err:%v", + incr.FileName, incrBkFile, err) + mylog.Logger.Error(err.Error()) + return err + } + bkFileSize := bkFileInfo.Size() + if item.BackupSize != bkFileSize { + err = fmt.Errorf("rocksdbIdx:%d 本地binlog文件:%s 大小(%d) 不等于 redis备份记录大小:%d", + incr.RocksDBIndex, incrBkFile, bkFileSize, item.BackupSize) + mylog.Logger.Error(err.Error()) + return err + } + msg := fmt.Sprintf("rocksdbIdx:%d 本地binlog文件:%s 确认ok", incr.RocksDBIndex, incrBkFile) + mylog.Logger.Info(msg) + } + return nil +} + +// CheckAllPulledFilesOK 是否所有本地binlog都拉取ok +func (incr *TplusRocksDBIncrBack) CheckAllPulledFilesOK() { + mylog.Logger.Info("CheckAllPulledFilesOK start ... ") + errList := []string{} + successCnt := 0 + failCnt := 0 + totalCnt := 0 + for _, bk01 := range incr.ResultSortBinlog { + bkItem := bk01 + totalCnt++ + err := incr.checkPulledFileOK(bkItem) + if err != nil { + errList = append(errList, err.Error()) + } else { + successCnt++ + } + } + failCnt = len(errList) + if failCnt > 0 { + list01 := []string{} + //只打印前5条信息 + if failCnt > 5 { + list01 = errList[:5] + } else { + list01 = errList + } + incr.Err = fmt.Errorf( + "rocksdbIdx:%d 检查本地binlog文件失败,成功%d个,失败%d个,共%d个,失败示例:%s", + incr.RocksDBIndex, successCnt, failCnt, totalCnt, strings.Join(list01, "\n"), + ) + mylog.Logger.Error(incr.Err.Error()) + return + } + msg := fmt.Sprintf( + "rocksdbIdx:%d 检查本地binlog文件全部成功,成功%d个,失败%d个,共%d个", + incr.RocksDBIndex, successCnt, failCnt, totalCnt) + mylog.Logger.Info(msg) + + return +} + +// PullFilesIfNotExists .. +// - 如果所有 增备(已解压)文件 全部存在,则直接return; +// - 如果所有 增备(未解压)文件 全部存在,且大小校验ok,则直接 return; +// - 否则删除 本地存在的增备(已解压)文件、增备(未解压)文件; +// - 继续拉取文件; +func (incr *TplusRocksDBIncrBack) PullFilesIfNotExists() { + mylog.Logger.Info("PullFilesIfNotExists start ... ") + var msg string + decpTotalCnt, decpExistsCnt, decpExistsList := incr.CheckLocalDecompressedFilesIsOk() + if incr.Err != nil { + return + } + if decpTotalCnt == decpExistsCnt { + msg = fmt.Sprintf("rollbackPod:%s rocksdbIdx:%d 本地已存在%d个 增备(已解压文件),无需重新拉取增备文件...", + incr.FileName, incr.RocksDBIndex, decpTotalCnt) + mylog.Logger.Info(msg) + return + } + incr.rmLocalDecompressedFiles(decpExistsList) + if incr.Err != nil { + return + } + + localTotalCnt, localExistsCnt, sizeOkCnt, localExistsList := incr.CheckLocalBackupfilesIsOk() + if incr.Err != nil { + return + } + if localTotalCnt == localExistsCnt && localTotalCnt == sizeOkCnt { + msg = fmt.Sprintf("rollbackPod:%s rocksdbIdx:%d 本地已存在%d个 增备(未解压文件),无需重新拉取增备文件...", + incr.FileName, incr.RocksDBIndex, localTotalCnt) + mylog.Logger.Info(msg) + return + } + incr.rmLocalBackupFiles(localExistsList) + if incr.Err != nil { + return + } + + incr.PullFilesFromBS() +} + +// PullAllFiles 拉取所有rocksb增备信息 +func (pull *TplusIncrBackPull) PullAllFiles() { + mylog.Logger.Info("PullAllFiles start ... ") + if len(pull.RocksMap) == 0 { + pull.Err = fmt.Errorf(" filename:%s rocksdb信息为空,无法拉取binlog文件", pull.FileName) + mylog.Logger.Error(pull.Err.Error()) + return + } + //所有rocksdb并发执行 + wg := sync.WaitGroup{} + for _, rock01 := range pull.RocksMap { + rockItem := rock01 + wg.Add(1) + go func(rock001 *TplusRocksDBIncrBack) { + defer wg.Done() + rock001.PullFilesIfNotExists() + if rock001.Err != nil { + //拉取备份失败 + return + } + }(rockItem) + } + wg.Wait() + + //检测是否有失败 + for _, rock01 := range pull.RocksMap { + rockItem := rock01 + if rockItem.Err != nil { + pull.Err = rockItem.Err + return + } + } + return +} + +// DecompressedOne 解压一个binlog文件 +func (incr *TplusRocksDBIncrBack) DecompressedOne(item *TplusRocksDBIncrBackItem) { + var cmd string + var bkFileExt string = incr.GetBackupFileExt(item) + if incr.Err != nil { + return + } + if bkFileExt == ".tar" { + cmd = fmt.Sprintf("cd %s && tar -xf %s", + incr.SaveMyDir, item.BackupFile) + } else if bkFileExt == ".tar.gz" { + cmd = fmt.Sprintf("cd %s && tar -zxf %s", + incr.SaveMyDir, item.BackupFile) + } else if bkFileExt == ".tgz" { + cmd = fmt.Sprintf("cd %s && tar -zxf %s", + incr.SaveMyDir, item.BackupFile) + } else if bkFileExt == ".gz" { + cmd = fmt.Sprintf("cd %s && gunzip %s", + incr.SaveMyDir, item.BackupFile) + } else if bkFileExt == ".zip" { + cmd = fmt.Sprintf("cd %s && unzip %s", + incr.SaveMyDir, item.BackupFile) + } else if bkFileExt == ".lzo" { + cmd = fmt.Sprintf("cd %s && lzop -d %s", + incr.SaveMyDir, item.BackupFile) + } else if bkFileExt == ".zst" { + cmd = fmt.Sprintf("cd %s && %s -d %s", + incr.SaveMyDir, consts.ZstdBin, item.BackupFile) + } + if cmd != "" { + var decpFileFullPath string + item.DecompressedFile, decpFileFullPath = incr.getDecompressedFile(item) + msg := fmt.Sprintf("binlog解压命令:%s", cmd) + mylog.Logger.Info(msg) + + _, incr.Err = util.RunLocalCmd("bash", []string{"-c", cmd}, "", nil, 600*time.Second) + if incr.Err != nil { + return + } + _, err := os.Stat(decpFileFullPath) + if err != nil { + incr.Err = fmt.Errorf("解压binlog:%s => %s 失败,err:%v", + item.BackupFile, decpFileFullPath, err) + mylog.Logger.Error(incr.Err.Error()) + + return + } + msg = fmt.Sprintf("解压binlog:%s成功", item.BackupFile) + mylog.Logger.Info(msg) + + //rm 源文件 + rmCmd := fmt.Sprintf("cd %s && rm -f %s 2>/dev/null", incr.SaveMyDir, item.BackupFile) + _, incr.Err = util.RunLocalCmd("bash", []string{"-c", rmCmd}, "", nil, 30*time.Minute) + if incr.Err != nil { + return + } + msg = fmt.Sprintf("删除binlog源文件:%s完成", item.BackupFile) + mylog.Logger.Info(msg) + } +} + +// DecompressedAll 解压全部binlog文件 +func (incr *TplusRocksDBIncrBack) DecompressedAll() { + mylog.Logger.Info("DecompressedAll start ...") + if len(incr.ResultSortBinlog) == 0 { + incr.Err = fmt.Errorf( + "filename:%s rocksdbIdx:%d binlog信息为空(len:%d),无法解压,请先获取binlog信息", + incr.FileName, incr.RocksDBIndex, len(incr.ResultSortBinlog)) + mylog.Logger.Error(incr.Err.Error()) + return + } + errList := []string{} + successCnt := 0 + failCnt := 0 + totalCnt := 0 + + for _, bk01 := range incr.ResultSortBinlog { + bkItem := bk01 + totalCnt++ + incr.DecompressedOne(bkItem) + if incr.Err != nil { + errList = append(errList, incr.Err.Error()) + } else { + successCnt++ + } + } + failCnt = len(errList) + if failCnt > 0 { + list01 := []string{} + //只打印前5条信息 + if failCnt > 5 { + list01 = errList[:5] + } else { + list01 = errList + } + incr.Err = fmt.Errorf( + "解压pod:%s rocksdbIdx:%d binlog文件失败,成功%d个,失败%d个,共%d个,失败示例:%s", + incr.FileName, incr.RocksDBIndex, successCnt, failCnt, totalCnt, strings.Join(list01, "\n"), + ) + mylog.Logger.Error(incr.Err.Error()) + return + } + msg := fmt.Sprintf("解压pod:%s rocksdbIdx:%d binlog文件全部成功,成功%d个,失败%d个,共%d个", + incr.FileName, incr.RocksDBIndex, successCnt, failCnt, totalCnt) + mylog.Logger.Info(msg) + return +} + +// Decompressed 解压binlog +func (pull *TplusIncrBackPull) Decompressed() { + mylog.Logger.Info("Decompressed start ... ") + if len(pull.RocksMap) == 0 { + pull.Err = fmt.Errorf("filename:%s rocksdb信息为空,无法解压本地binlog", pull.FileName) + mylog.Logger.Error(pull.Err.Error()) + return + } + //所有rocksdb并发解压 + wg := sync.WaitGroup{} + for _, rock01 := range pull.RocksMap { + rockItem := rock01 + wg.Add(1) + go func(rock001 *TplusRocksDBIncrBack) { + defer wg.Done() + rock001.DecompressedAllIfNotExists() + }(rockItem) + } + wg.Wait() + + //检测是否有失败 + for _, rock01 := range pull.RocksMap { + rockItem := rock01 + if rockItem.Err != nil { + pull.Err = rockItem.Err + return + } + } + return +} + +// DecompressedAllIfNotExists .. +// 如果所有 增备(已解压)文件 全部存在,则直接return; +// 否则删除 本地存在的增备(已解压)文件; +// 继续执行解压; +func (incr *TplusRocksDBIncrBack) DecompressedAllIfNotExists() { + mylog.Logger.Info("DecompressedAllIfNotExists start .. ") + var msg string + decpTotalCnt, decpExistsCnt, decpExistsList := incr.CheckLocalDecompressedFilesIsOk() + if incr.Err != nil { + return + } + if decpTotalCnt == decpExistsCnt { + msg = fmt.Sprintf("rollbackPod:%s rocksdbIdx:%d 本地已存在%d个 增备(已解压文件),无需执行解压...", + incr.FileName, incr.RocksDBIndex, decpTotalCnt) + mylog.Logger.Info(msg) + return + } + incr.rmLocalDecompressedFiles(decpExistsList) + if incr.Err != nil { + return + } + incr.DecompressedAll() +} + +// ImportOneBinlogToTplus 导入一个binlog文件到tendisplus中 +func (incr *TplusRocksDBIncrBack) ImportOneBinlogToTplus(tplusIP string, tplusPort int, + tplusPasswd string, bkItem *TplusRocksDBIncrBackItem) { + mylog.Logger.Info("ImportOneBinlogToTplus start ... ") + incrBackFile := filepath.Join(incr.SaveMyDir, bkItem.DecompressedFile) + importCmd := fmt.Sprintf( + "binlog_tool --logfile=%s --mode=base64 --start-position=%d --end-datetime=%d|redis-cli -h %s -p %d -a %s", + incrBackFile, incr.StartPos, incr.EndTime.Unix()*1000, tplusIP, tplusPort, tplusPasswd) + logCmd := fmt.Sprintf( + "binlog_tool --logfile=%s --mode=base64 --start-position=%d --end-datetime=%d|redis-cli -h %s -p %d -a xxxx", + incrBackFile, incr.StartPos, incr.EndTime.Unix()*1000, tplusIP, tplusPort) + + mylog.Logger.Info("导入binlog,命令:%v", logCmd) + ret01, err := util.RunLocalCmd("bash", []string{"-c", importCmd}, "", nil, 1*time.Hour) + if err != nil { + mylog.Logger.Error(fmt.Sprintf("导入binlog失败,详情:%v", err)) + incr.Err = err + return + } + ret01 = strings.TrimSpace(ret01) + if strings.Contains(ret01, "ERR:") == true { + mylog.Logger.Error(fmt.Sprintf("导入binlog失败,cmd:%s,err:%s", logCmd, ret01)) + incr.Err = fmt.Errorf("导入binlog失败") + return + } + mylog.Logger.Info("binlog:%s 导入 %s:%d成功", bkItem.BackupFile, tplusIP, tplusPort) + return +} + +// ImportAllBinlogToTplus 导入全部binlog +func (incr *TplusRocksDBIncrBack) ImportAllBinlogToTplus(tplusIP string, tplusPort int, tplusPasswd string) { + for _, bk01 := range incr.ResultSortBinlog { + bkItem := bk01 + incr.ImportOneBinlogToTplus(tplusIP, tplusPort, tplusPasswd, bkItem) + if incr.Err != nil { + return + } + } + msg := fmt.Sprintf("filename:%s rocksdbIdx:%d 共成功导入%d个binlog", + incr.FileName, incr.RocksDBIndex, len(incr.ResultSortBinlog)) + mylog.Logger.Info(msg) + return +} + +// ImportBinlogsToTplus 将binlog导入到tendisplus +func (pull *TplusIncrBackPull) ImportBinlogsToTplus( + tplusIP string, tplusPort int, tplusPasswd string) { + if len(pull.RocksMap) == 0 { + pull.Err = fmt.Errorf("filename:%s rocksdb信息为空,无法导入binlog", pull.FileName) + mylog.Logger.Error(pull.Err.Error()) + return + } + //所有rocksdb并发导入binlog,单个rocksdb串行导入 + wg := sync.WaitGroup{} + for _, rock01 := range pull.RocksMap { + rockItem := rock01 + wg.Add(1) + go func(rock001 *TplusRocksDBIncrBack) { + defer wg.Done() + rock001.ImportAllBinlogToTplus(tplusIP, tplusPort, tplusPasswd) + }(rockItem) + } + wg.Wait() + + //检测是否有失败 + for _, rock01 := range pull.RocksMap { + rockItem := rock01 + if rockItem.Err != nil { + pull.Err = rockItem.Err + return + } + } + return +} diff --git a/dbm-services/redis/db-tools/dbactuator/pkg/consts/consts.go b/dbm-services/redis/db-tools/dbactuator/pkg/consts/consts.go index 8f1e4f0b98..02b11b418f 100644 --- a/dbm-services/redis/db-tools/dbactuator/pkg/consts/consts.go +++ b/dbm-services/redis/db-tools/dbactuator/pkg/consts/consts.go @@ -155,7 +155,8 @@ const ( const ( NormalBackupType = "normal_backup" ForeverBackupType = "forever_backup" - BackupClient = "/usr/local/bin/backup_client" + IBSBackupClient = "/usr/local/bin/backup_client" + COSBackupClient = "/usr/local/backup_client/bin/backup_client" BackupTarSplitSize = "8G" RedisFullBackupTAG = "REDIS_FULL" RedisBinlogTAG = "REDIS_BINLOG" @@ -177,6 +178,15 @@ const ( BackupStatusLocalSuccess = "local_success" ) +// BackupSystem +const ( + BackupTaskSuccess string = "4" + FileExpired string = "1" + FileNotExpired string = "0" + BackupVersion string = "1.0" + BackupMaxQueryRetryTimes int = 60 +) + // meta role const ( MetaRoleRedisMaster = "redis_master" @@ -202,6 +212,8 @@ const ( KeysRename = "mykeys" // ConfigRename .. ConfigRename = "confxx" + // TendisPlusFlushAllRename .. + TendisPlusFlushAllRename = "cleanall" ) // IsClusterDbType 存储端是否是cluster类型 @@ -236,6 +248,15 @@ func IsTwemproxyClusterType(dbType string) bool { return false } +// IsPredixyClusterType 检查proxy是否为Predixy +func IsPredixyClusterType(dbType string) bool { + if dbType == TendisTypePredixyRedisCluster || + dbType == TendisTypePredixyTendisplusCluster { + return true + } + return false +} + // IsTendisplusInstanceDbType 存储端是否是tendisplus类型 func IsTendisplusInstanceDbType(dbType string) bool { if dbType == TendisTypePredixyTendisplusCluster || diff --git a/dbm-services/redis/db-tools/dbactuator/pkg/consts/test.go b/dbm-services/redis/db-tools/dbactuator/pkg/consts/test.go index 8dd5594c3e..e72b1184b4 100644 --- a/dbm-services/redis/db-tools/dbactuator/pkg/consts/test.go +++ b/dbm-services/redis/db-tools/dbactuator/pkg/consts/test.go @@ -10,6 +10,8 @@ const ( TestTendisPlusMasterStartPort = 11000 // TestTendisPlusSlaveStartPort slave start port TestTendisPlusSlaveStartPort = 12000 + // TestTendisPlusForgetPort TestTendisPlusForgetPort + TestTendisPlusForgetPort = 16600 // ExpansionTestTendisPlusMasterStartPort master start port ExpansionTestTendisPlusMasterStartPort = 11100 diff --git a/dbm-services/redis/db-tools/dbactuator/pkg/jobmanager/jobmanager.go b/dbm-services/redis/db-tools/dbactuator/pkg/jobmanager/jobmanager.go index 5d7bc9df5f..1cea54b8fe 100644 --- a/dbm-services/redis/db-tools/dbactuator/pkg/jobmanager/jobmanager.go +++ b/dbm-services/redis/db-tools/dbactuator/pkg/jobmanager/jobmanager.go @@ -3,13 +3,6 @@ package jobmanager import ( "context" - "dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atommongodb" - "dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomproxy" - "dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis" - "dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomsys" - "dbm-services/redis/db-tools/dbactuator/pkg/consts" - "dbm-services/redis/db-tools/dbactuator/pkg/jobruntime" - "dbm-services/redis/db-tools/dbactuator/pkg/util" "fmt" "log" "math/rand" @@ -19,6 +12,14 @@ import ( "sync" "time" + "dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atommongodb" + "dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomproxy" + "dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis" + "dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomsys" + "dbm-services/redis/db-tools/dbactuator/pkg/consts" + "dbm-services/redis/db-tools/dbactuator/pkg/jobruntime" + "dbm-services/redis/db-tools/dbactuator/pkg/util" + "github.com/gofrs/flock" ) @@ -174,6 +175,7 @@ func (m *JobGenericManager) atomjobsMapperLoading() { m.atomJobMapper[atomredis.NewRedisInstall().Name()] = atomredis.NewRedisInstall m.atomJobMapper[atomredis.NewRedisReplicaOf().Name()] = atomredis.NewRedisReplicaOf m.atomJobMapper[atomredis.NewRedisReplicaBatch().Name()] = atomredis.NewRedisReplicaBatch + m.atomJobMapper[atomredis.NewRedisClusterForget().Name()] = atomredis.NewRedisClusterForget m.atomJobMapper[atomredis.NewClusterMeetSlotsAssign().Name()] = atomredis.NewClusterMeetSlotsAssign m.atomJobMapper[atomproxy.NewTwemproxyInstall().Name()] = atomproxy.NewTwemproxyInstall m.atomJobMapper[atomredis.NewRedisBackup().Name()] = atomredis.NewRedisBackup @@ -191,7 +193,12 @@ func (m *JobGenericManager) atomjobsMapperLoading() { m.atomJobMapper[atomredis.NewBkDbmonInstall().Name()] = atomredis.NewBkDbmonInstall m.atomJobMapper[atomredis.NewTendisPlusMigrateSlots().Name()] = atomredis.NewTendisPlusMigrateSlots m.atomJobMapper[atomredis.NewRedisDtsDataCheck().Name()] = atomredis.NewRedisDtsDataCheck - m.atomJobMapper[atomredis.NewRedisDtsDataRepaire().Name()] = atomredis.NewRedisDtsDataRepaire + m.atomJobMapper[atomredis.NewRedisDtsDataRepair().Name()] = atomredis.NewRedisDtsDataRepair + m.atomJobMapper[atomredis.NewRedisAddDtsServer().Name()] = atomredis.NewRedisAddDtsServer + m.atomJobMapper[atomredis.NewRedisRemoveDtsServer().Name()] = atomredis.NewRedisRemoveDtsServer + m.atomJobMapper[atomredis.NewRedisDataRecover().Name()] = atomredis.NewRedisDataRecover + m.atomJobMapper[atomredis.NewClusterMeetCheckFinish().Name()] = atomredis.NewClusterMeetCheckFinish + m.atomJobMapper[atomredis.NewRedisDtsOnlineSwitch().Name()] = atomredis.NewRedisDtsOnlineSwitch // scene needs. m.atomJobMapper[atomproxy.NewTwemproxySceneCheckBackends().Name()] = atomproxy.NewTwemproxySceneCheckBackends m.atomJobMapper[atomredis.NewRedisSceneSyncCheck().Name()] = atomredis.NewRedisSceneSyncCheck diff --git a/dbm-services/redis/db-tools/dbactuator/pkg/jobruntime/jobruntime.go b/dbm-services/redis/db-tools/dbactuator/pkg/jobruntime/jobruntime.go index 10a4bfca03..0ed1558930 100644 --- a/dbm-services/redis/db-tools/dbactuator/pkg/jobruntime/jobruntime.go +++ b/dbm-services/redis/db-tools/dbactuator/pkg/jobruntime/jobruntime.go @@ -3,10 +3,6 @@ package jobruntime import ( "context" - "dbm-services/common/go-pubpkg/logger" - "dbm-services/redis/db-tools/dbactuator/mylog" - "dbm-services/redis/db-tools/dbactuator/pkg/consts" - "dbm-services/redis/db-tools/dbactuator/pkg/util" "encoding/base64" "encoding/json" "fmt" @@ -15,6 +11,11 @@ import ( "os/exec" "path/filepath" "time" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/redis/db-tools/dbactuator/mylog" + "dbm-services/redis/db-tools/dbactuator/pkg/consts" + "dbm-services/redis/db-tools/dbactuator/pkg/util" ) const ( @@ -132,8 +133,9 @@ func (r *JobGenericRuntime) OutputPipeContextData() { return } // decode函数: base64.StdEncoding.DecodeString - base64Ret := base64.StdEncoding.EncodeToString(tmpBytes) - r.PrintToStdout("" + base64Ret + "") + // base64Ret := base64.StdEncoding.EncodeToString(tmpBytes) + // r.PrintToStdout("" + base64Ret + "") + r.PrintToStdout("" + string(tmpBytes) + "") } // StartHeartbeat 开始心跳 diff --git a/dbm-services/redis/db-tools/dbactuator/pkg/util/net.go b/dbm-services/redis/db-tools/dbactuator/pkg/util/net.go index 413be8aceb..c6f528693d 100644 --- a/dbm-services/redis/db-tools/dbactuator/pkg/util/net.go +++ b/dbm-services/redis/db-tools/dbactuator/pkg/util/net.go @@ -2,6 +2,7 @@ package util import ( "fmt" + "io" "net" ) @@ -65,3 +66,51 @@ func GetInterfaceIpv4Addr(interfaceName string) (addr string, err error) { } return ipv4Addr.String(), nil } + +// CheckIPBelongToLocalServer 检查ip是否属于本机 +func CheckIPBelongToLocalServer(ip string) (ok bool, err error) { + addrs, err := net.InterfaceAddrs() + if err != nil { + err = fmt.Errorf("net.InterfaceAddrs fail,err:%v", err) + return + } + for _, addr := range addrs { + if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { + // check if IPv4 or IPv6 is not nil + if ipnet.IP.To4() != nil || ipnet.IP.To16() != nil { + if ip == ipnet.IP.String() { + return true, nil + } + } + } + } + return false, nil +} + +// NetCatTcpClient 向tcp端口发送一条指令 并接受 返回(模仿netcat) +func NetCatTcpClient(addr01, cmd string) (ret string, err error) { + client, err := net.Dial("tcp", addr01) + if err != nil { + err = fmt.Errorf("net.Dial fail,err:%v", err) + return "", err + } + defer client.Close() + + _, err = client.Write([]byte(cmd)) + if err != nil { + err = fmt.Errorf("tcp client.Write fail,err:%v,addr:%s,command:%s", err, addr01, cmd) + return "", err + } + buf := make([]byte, 1024) + for { + readCnt, err := client.Read(buf) + if err != nil { + if err == io.EOF { + return ret, nil + } + err = fmt.Errorf("tcp client.read fail,err:%v,addr:%s,command:%s", err, addr01, cmd) + return "", err + } + ret = ret + string(buf[:readCnt]) + } +} diff --git a/dbm-services/redis/db-tools/dbactuator/pkg/util/util.go b/dbm-services/redis/db-tools/dbactuator/pkg/util/util.go index 0c05122a44..0c9f9e52b9 100644 --- a/dbm-services/redis/db-tools/dbactuator/pkg/util/util.go +++ b/dbm-services/redis/db-tools/dbactuator/pkg/util/util.go @@ -179,6 +179,13 @@ func LocalDirChownMysql(localDir string) (err error) { return } +// LocalFileChmodAllRead 改变localFile的权限为所有人可读 +func LocalFileChmodAllRead(localFile string) (err error) { + cmd := fmt.Sprintf("chmod a+r %s", localFile) + _, err = RunBashCmd(cmd, "", nil, 1*time.Hour) + return +} + // HostDiskUsage 本地路径所在磁盘使用情况 type HostDiskUsage struct { TotalSize uint64 `json:"ToTalSize"` // bytes @@ -268,3 +275,8 @@ func IsDbmSysKeys(key string) bool { } return false } + +// NewNotFoundErr TODO +func NewNotFoundErr() error { + return errors.New(NotFound) +} diff --git a/dbm-services/redis/db-tools/dbactuator/pkg/util/validate.go b/dbm-services/redis/db-tools/dbactuator/pkg/util/validate.go new file mode 100644 index 0000000000..f10b97be1a --- /dev/null +++ b/dbm-services/redis/db-tools/dbactuator/pkg/util/validate.go @@ -0,0 +1,32 @@ +package util + +import ( + "dbm-services/redis/db-tools/dbactuator/mylog" + + "github.com/go-playground/validator/v10" +) + +// ValidateStruct 校验参数有效性 +func ValidateStruct(params interface{}) error { + // 创建一个新的验证器实例 + validate := validator.New() + // 使用验证器对参数进行校验 + err := validate.Struct(params) + if err != nil { + // 如果校验错误是 InvalidValidationError 类型,记录错误并返回 + if _, ok := err.(*validator.InvalidValidationError); ok { + mylog.Logger.Error(" params validate failed,err:%v,params:%+v", + err, params) + return err + } + // 如果校验错误是 ValidationErrors 类型,遍历每个错误并记录后返回 + for _, err := range err.(validator.ValidationErrors) { + mylog.Logger.Error("params validate failed,err:%v,params:%+v", + err, params) + return err + } + } + mylog.Logger.Info("params validate success") + return nil + +} diff --git a/dbm-services/redis/db-tools/dbactuator/tests/clustertest/predixy_cluster.go b/dbm-services/redis/db-tools/dbactuator/tests/clustertest/predixy_cluster.go index c7c432e477..c7f2b473d3 100644 --- a/dbm-services/redis/db-tools/dbactuator/tests/clustertest/predixy_cluster.go +++ b/dbm-services/redis/db-tools/dbactuator/tests/clustertest/predixy_cluster.go @@ -207,3 +207,51 @@ func PredixyRedisClusterInstallTest(serverIP, } return nil } + +// PredixyTendisplusClusterForgetTest predixy+tendisplus cluster集群Forget +func PredixyTendisplusClusterForgetTest(serverIP, + tendisplusPkgName, tendisplusPkgMd5, + dbtoolsPkgName, dbtoolsPkgMd5 string) (err error) { + forgetNodeNum := 2 + + fmt.Println("==================================================") + // 先清理🧹 + if err = redistest.TendisplusClear(serverIP, + consts.TendisTypePredixyTendisplusCluster, true, + consts.TestTendisPlusForgetPort, forgetNodeNum, + ); err != nil { + return + } + + // 再安装tendisplus + if err = redistest.TendisplusInstall( + serverIP, tendisplusPkgName, tendisplusPkgMd5, + dbtoolsPkgName, dbtoolsPkgMd5, + consts.TendisTypePredixyTendisplusCluster, + consts.TestTendisPlusForgetPort, forgetNodeNum); err != nil { + return + } + + // 加入集群 + if err = redistest.ClusterMeetSingleNode(serverIP, + consts.TestTendisPlusForgetPort, forgetNodeNum, + ); err != nil { + return + } + + forgetTest := redistest.ClusterForgetTest{} + forgetTest.SetClusterMeta(serverIP, consts.TestTendisPlusMasterStartPort, + consts.TestRedisInstanceNum) + forgetTest.SetForgetList(serverIP, consts.TestTendisPlusForgetPort, forgetNodeNum) + forgetTest.RunClusterForget() + + // 清理🧹 + if err = redistest.TendisplusClear(serverIP, + consts.TendisTypePredixyTendisplusCluster, true, + consts.TestTendisPlusForgetPort, forgetNodeNum, + ); err != nil { + return + } + fmt.Println("==================================================") + return nil +} diff --git a/dbm-services/redis/db-tools/dbactuator/tests/clustertest/twemproxy_cluster.go b/dbm-services/redis/db-tools/dbactuator/tests/clustertest/twemproxy_cluster.go index 02555b7776..bda8db5e60 100644 --- a/dbm-services/redis/db-tools/dbactuator/tests/clustertest/twemproxy_cluster.go +++ b/dbm-services/redis/db-tools/dbactuator/tests/clustertest/twemproxy_cluster.go @@ -1,7 +1,6 @@ package clustertest import ( - "encoding/base64" "encoding/json" "fmt" "path/filepath" @@ -145,17 +144,10 @@ func TwemproxyTendisSSDInstall(serverIP, if err != nil { return } - retDecoded, err := base64.StdEncoding.DecodeString(retBase64) - if err != nil { - err = fmt.Errorf("TwemproxyTendisSSDInstall base64 decode fail,err:%v,base64Len:%d,base64Data:%s", err, - len(retBase64), retBase64) - fmt.Println(err.Error()) - return - } backupTasks := []atomredis.BackupTask{} - err = json.Unmarshal(retDecoded, &backupTasks) + err = json.Unmarshal([]byte(retBase64), &backupTasks) if err != nil { - err = fmt.Errorf("TwemproxyTendisSSDInstall json.Unmarshal fail,err:%v,dataDecoded:%s", err, string(retDecoded)) + err = fmt.Errorf("TwemproxyTendisSSDInstall json.Unmarshal fail,err:%v,dataDecoded:%s", err, string(retBase64)) fmt.Println(err.Error()) return } diff --git a/dbm-services/redis/db-tools/dbactuator/tests/clustertest/twemproxy_swtich.go b/dbm-services/redis/db-tools/dbactuator/tests/clustertest/twemproxy_swtich.go index d7ba7bb1da..b56ad50769 100644 --- a/dbm-services/redis/db-tools/dbactuator/tests/clustertest/twemproxy_swtich.go +++ b/dbm-services/redis/db-tools/dbactuator/tests/clustertest/twemproxy_swtich.go @@ -68,8 +68,10 @@ func TwemproxyCacheSwitch(serverIP, SetBkDbmonPkg(bkdbmonPkgName, bkdbmonPkgMd5). SetDbtoolsPkg(dbtoolsPkgName, dbtoolsPkgMd5). SetBackupConf(). - AppendMasterServer(serverIP, consts.TestRedisMasterStartPort, consts.TestRedisInstanceNum). - AppendMasterServer(serverIP, consts.TestSyncRedisMasterStartPort, consts.TestRedisInstanceNum) + AppendMasterServer(serverIP, consts.TestRedisMasterStartPort, consts.TestRedisInstanceNum, + consts.TendisTypeTwemproxyRedisInstance). + AppendMasterServer(serverIP, consts.TestSyncRedisMasterStartPort, consts.TestRedisInstanceNum, + consts.TendisTypeTwemproxyRedisInstance) if installTest.Err != nil { return installTest.Err } @@ -172,8 +174,10 @@ func TwemproxyCacheSwitchRestoreEnv(serverIP, SetBkDbmonPkg(bkdbmonPkgName, bkdbmonPkgMd5). SetDbtoolsPkg(dbtoolsPkgName, dbtoolsPkgMd5). SetBackupConf(). - AppendMasterServer(serverIP, consts.TestRedisMasterStartPort, consts.TestRedisInstanceNum). - AppendMasterServer(serverIP, consts.TestSyncRedisMasterStartPort, consts.TestRedisInstanceNum) + AppendMasterServer(serverIP, consts.TestRedisMasterStartPort, consts.TestRedisInstanceNum, + consts.TendisTypeTwemproxyRedisInstance). + AppendMasterServer(serverIP, consts.TestSyncRedisMasterStartPort, consts.TestRedisInstanceNum, + consts.TendisTypeTwemproxyRedisInstance) if installTest.Err != nil { return installTest.Err } diff --git a/dbm-services/redis/db-tools/dbactuator/tests/redistest/bkdbmon_install.go b/dbm-services/redis/db-tools/dbactuator/tests/redistest/bkdbmon_install.go index 9cea01261d..c87cf4c73c 100644 --- a/dbm-services/redis/db-tools/dbactuator/tests/redistest/bkdbmon_install.go +++ b/dbm-services/redis/db-tools/dbactuator/tests/redistest/bkdbmon_install.go @@ -94,8 +94,35 @@ func (test *BkDBmonInstallTest) SetBackupConf() *BkDBmonInstallTest { return test } +func getRedisClusterServerShargs(ip string, startPort, instNum int, dbType string) (shards map[string]string) { + if !consts.IsTwemproxyClusterType(dbType) { + return + } + if startPort == 0 { + startPort = consts.TestRedisMasterStartPort + } + if instNum == 0 { + instNum = 4 + } + var port int + var instStr string + var segStart int + var segEnd int + shards = make(map[string]string) + segStep := (consts.TwemproxyMaxSegment + 1) / instNum + for i := 0; i < instNum; i++ { + segStart = i * segStep + segEnd = (i+1)*segStep - 1 + port = startPort + i + instStr = fmt.Sprintf("%s:%d", ip, port) + shards[instStr] = fmt.Sprintf("%d-%d", segStart, segEnd) + } + return +} + // AppendMasterServer append master server -func (test *BkDBmonInstallTest) AppendMasterServer(masterIP string, startPort, instNum int) *BkDBmonInstallTest { +func (test *BkDBmonInstallTest) AppendMasterServer(masterIP string, startPort, instNum int, + dbType string) *BkDBmonInstallTest { if test.Err != nil { return test } @@ -112,10 +139,15 @@ func (test *BkDBmonInstallTest) AppendMasterServer(masterIP string, startPort, i svrItem := atomredis.ConfServerItem{ BkBizID: "200500194", BkCloudID: 246, + App: "testapp", + AppName: "测试app", ClusterDomain: "tendisx.aaaa.testapp.db", + ClusterName: "aaaa", + ClusterType: dbType, MetaRole: consts.MetaRoleRedisMaster, ServerIP: masterIP, ServerPorts: ports, + ServerShards: getRedisClusterServerShargs(masterIP, startPort, instNum, dbType), } test.Servers = append(test.Servers, svrItem) return test @@ -139,7 +171,8 @@ func (test *BkDBmonInstallTest) OnlyAEmptyServer(ip string) *BkDBmonInstallTest } // AppendSlaveServer append slave server -func (test *BkDBmonInstallTest) AppendSlaveServer(slaveIP string, startPort, instNum int) *BkDBmonInstallTest { +func (test *BkDBmonInstallTest) AppendSlaveServer(slaveIP string, startPort, instNum int, + dbType string) *BkDBmonInstallTest { if test.Err != nil { return test } @@ -160,10 +193,11 @@ func (test *BkDBmonInstallTest) AppendSlaveServer(slaveIP string, startPort, ins AppName: "测试app", ClusterDomain: "tendisx.aaaa.testapp.db", ClusterName: "aaaa", - ClusterType: consts.TendisTypePredixyTendisplusCluster, + ClusterType: dbType, MetaRole: consts.MetaRoleRedisSlave, ServerIP: slaveIP, ServerPorts: ports, + ServerShards: getRedisClusterServerShargs(slaveIP, startPort, instNum, dbType), } test.Servers = append(test.Servers, svrItem) return test @@ -236,8 +270,8 @@ func BkDbmonInstall(serverIP, dbtoolsPkgName, dbtoolsPkgMd5, bkdbmonPkgName, bkd SetBkDbmonPkg(bkdbmonPkgName, bkdbmonPkgMd5). SetDbtoolsPkg(dbtoolsPkgName, dbtoolsPkgMd5). SetBackupConf(). - AppendMasterServer(serverIP, masterStartPort, consts.TestRedisInstanceNum). - AppendSlaveServer(serverIP, slaveStartPort, consts.TestRedisInstanceNum) + AppendMasterServer(serverIP, masterStartPort, consts.TestRedisInstanceNum, dbType). + AppendSlaveServer(serverIP, slaveStartPort, consts.TestRedisInstanceNum, dbType) if bkdbmonTest.Err != nil { return } diff --git a/dbm-services/redis/db-tools/dbactuator/tests/redistest/redis_add_dts_server.go b/dbm-services/redis/db-tools/dbactuator/tests/redistest/redis_add_dts_server.go new file mode 100644 index 0000000000..6b02d9d99e --- /dev/null +++ b/dbm-services/redis/db-tools/dbactuator/tests/redistest/redis_add_dts_server.go @@ -0,0 +1,136 @@ +package redistest + +import ( + "encoding/json" + "fmt" + "time" + + "dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis" + "dbm-services/redis/db-tools/dbactuator/pkg/consts" + "dbm-services/redis/db-tools/dbactuator/pkg/util" +) + +// RedisAddDtsServerTest dts添加服务测试 +type RedisAddDtsServerTest struct { + atomredis.RedisAddDtsServerParams + Err error `json:"-"` +} + +// SetPkg set pkg信息 +func (test *RedisAddDtsServerTest) SetPkg(pkg, pkgMd5 string) *RedisAddDtsServerTest { + if test.Err != nil { + return test + } + if pkg == "" || pkgMd5 == "" { + pkg = "redis_dts.tar.gz" + pkgMd5 = "b816b2d47357c3ed7a7864d6730fd33f" + } + test.Pkg = pkg + test.PkgMd5 = pkgMd5 + return test +} + +// SetBkDbmNginxURL set bk dbm nginx url +func (test *RedisAddDtsServerTest) SetBkDbmNginxURL(dbmNginxURL string) *RedisAddDtsServerTest { + if test.Err != nil { + return test + } + test.BkDbmNginxURL = dbmNginxURL + return test +} + +// SetBkDbmCloudID set bk dbm cloud id +func (test *RedisAddDtsServerTest) SetBkDbmCloudID(dbmCloudID int64) *RedisAddDtsServerTest { + if test.Err != nil { + return test + } + test.BkDbmCloudID = dbmCloudID + return test +} + +// SetBkDbmCloudToken set bk dbm cloud token +func (test *RedisAddDtsServerTest) SetBkDbmCloudToken(dbmCloudToken string) *RedisAddDtsServerTest { + if test.Err != nil { + return test + } + test.BkDbmCloudToken = dbmCloudToken + return test +} + +// SetSystemUser set system user +func (test *RedisAddDtsServerTest) SetSystemUser(systemUser string) *RedisAddDtsServerTest { + if test.Err != nil { + return test + } + test.SystemUser = systemUser + return test +} + +// SetSystemPassword set system password +func (test *RedisAddDtsServerTest) SetSystemPassword(systemPassword string) *RedisAddDtsServerTest { + if test.Err != nil { + return test + } + test.SystemPassword = systemPassword + return test +} + +// SetCityName set city name +func (test *RedisAddDtsServerTest) SetCityName(cityName string) *RedisAddDtsServerTest { + if test.Err != nil { + return test + } + test.CityName = cityName + return test +} + +// SetWarningMsgNotifiers set warning msg notifiers +func (test *RedisAddDtsServerTest) SetWarningMsgNotifiers(notifiers string) *RedisAddDtsServerTest { + if test.Err != nil { + return test + } + test.WarningMsgNotifiers = notifiers + return test +} + +// RunRedisAddDtsServer 执行 redis add dts_server 原子任务 +func (test *RedisAddDtsServerTest) RunRedisAddDtsServer() (err error) { + msg := fmt.Sprintf("=========AddDtsServer test start============") + fmt.Println(msg) + + defer func() { + if test.Err != nil { + msg = fmt.Sprintf("=========AddDtsServer test fail============") + fmt.Println(test.Err) + } else { + msg = fmt.Sprintf("=========AddDtsServer test success============") + } + fmt.Println(msg) + }() + + paramBytes, _ := json.Marshal(test) + runcmd := fmt.Sprintf(consts.ActuatorTestCmd, atomredis.NewRedisAddDtsServer().Name(), string(paramBytes)) + fmt.Println(runcmd) + _, test.Err = util.RunBashCmd(runcmd, "", nil, 1*time.Hour) + if test.Err != nil { + return test.Err + } + + return test.Err +} + +// RedisAddDtsServer dts服务部署测试 +func RedisAddDtsServer(pkgName, pkgMD5, dbmNginxURL string, dbmCloudID int64, + dbmCloudToken, systemUser, systemPassword, cityName, notifiers string) (err error) { + test := &RedisAddDtsServerTest{} + test.SetPkg(pkgName, pkgMD5). + SetBkDbmNginxURL(dbmNginxURL). + SetBkDbmCloudID(dbmCloudID).SetBkDbmCloudToken(dbmCloudToken). + SetSystemUser(systemUser).SetSystemPassword(systemPassword). + SetCityName(cityName).SetWarningMsgNotifiers(notifiers) + if test.Err != nil { + return test.Err + } + err = test.RunRedisAddDtsServer() + return +} diff --git a/dbm-services/redis/db-tools/dbactuator/tests/redistest/redis_cluster.go b/dbm-services/redis/db-tools/dbactuator/tests/redistest/redis_cluster.go index 87f6893b06..5b515cd533 100644 --- a/dbm-services/redis/db-tools/dbactuator/tests/redistest/redis_cluster.go +++ b/dbm-services/redis/db-tools/dbactuator/tests/redistest/redis_cluster.go @@ -88,6 +88,89 @@ func (test *ClusterMeetTest) RunClusterMeetAndSlotsAssign() { return } +// ClusterForgetTest ClusterForgetTest +type ClusterForgetTest struct { + atomredis.ClusterForgetParam + Err error `json:"-"` +} + +// SetClusterMeta SetClusterMeta +func (test *ClusterForgetTest) SetClusterMeta( + serverIP string, startPort, instNum int) { + test.ClusterMeta.ClusterType = consts.TendisTypePredixyTendisplusCluster + test.ClusterMeta.StoragePassword = consts.RedisTestPasswd + test.ClusterMeta.ImmuteDomain = "hi.kipper.vi.db" + test.ClusterMeta.RedisMasterSet = []string{} + + for i := 0; i < instNum; i++ { + test.ClusterMeta.RedisMasterSet = append(test.ClusterMeta.RedisMasterSet, + fmt.Sprintf("%s:%d", serverIP, startPort+i)) + } +} + +// SetForgetList SetForgetList +func (test *ClusterForgetTest) SetForgetList(serverIP string, startPort, instNum int) { + test.ForgetList = []atomredis.InstanceParam{} + for i := 0; i < instNum; i++ { + test.ForgetList = append( + test.ForgetList, atomredis.InstanceParam{ + IP: serverIP, + Port: startPort + i, + }) + } +} + +// RunClusterForget 运行集群Forget节点 +func (test *ClusterForgetTest) RunClusterForget() { + fmt.Println("=========RunClusterForget test start============") + + defer func() { + if test.Err != nil { + fmt.Println("=========RunClusterForget test fail============") + } else { + fmt.Println("=========RunClusterForget test success============") + } + }() + + paramBytes, _ := json.Marshal(test) + // fmt.Printf("-------payload(raw)--------\n%s\n\n", string(paramBytes)) + // encodeStr := base64.StdEncoding.EncodeToString(paramBytes) + instllCmd := fmt.Sprintf(consts.ActuatorTestCmd, + atomredis.NewRedisClusterForget().Name(), string(paramBytes)) + fmt.Println(instllCmd) + _, test.Err = util.RunBashCmd(instllCmd, "", nil, 1*time.Hour) + if test.Err != nil { + return + } +} + +// ClusterMeetSingleNode 创建redis cluster +func ClusterMeetSingleNode(serverIP string, startPort, instNum int) (err error) { + // 建立cluster 关系 + replicaPairs := make([]atomredis.ClusterReplicaItem, 0, instNum) + replicaPairs = append(replicaPairs, + atomredis.ClusterReplicaItem{MasterIP: serverIP, MasterPort: consts.TestTendisPlusMasterStartPort}) + for i := 0; i < instNum; i++ { + replicaPairs = append(replicaPairs, atomredis.ClusterReplicaItem{ + MasterIP: serverIP, + MasterPort: startPort + i, + }) + } + plusClusterTest := ClusterMeetTest{} + plusClusterTest.SetPassword(consts.RedisTestPasswd). + SetSlotAutoAssign(false). + SetUseForExpansion(true). + SetClusterReplicaPairs(replicaPairs) + if plusClusterTest.Err != nil { + return plusClusterTest.Err + } + plusClusterTest.RunClusterMeetAndSlotsAssign() + if plusClusterTest.Err != nil { + return plusClusterTest.Err + } + return nil +} + // CreateClusterREPL 创建redis cluster func CreateClusterREPL(serverIP string, masterStartPort, slaveStartPort, instNum int, slotAutoAssign, useForExpansion bool) (err error) { diff --git a/dbm-services/redis/db-tools/dbactuator/tests/redistest/redis_dts_datarepair.go b/dbm-services/redis/db-tools/dbactuator/tests/redistest/redis_dts_datarepair.go new file mode 100644 index 0000000000..0e2f2be478 --- /dev/null +++ b/dbm-services/redis/db-tools/dbactuator/tests/redistest/redis_dts_datarepair.go @@ -0,0 +1,79 @@ +package redistest + +import ( + "encoding/json" + "fmt" + "strconv" + "strings" + "time" + + "dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis" + "dbm-services/redis/db-tools/dbactuator/pkg/consts" + "dbm-services/redis/db-tools/dbactuator/pkg/util" +) + +// RedisDtsDataRepairJobTest dts数据修复测试 +type RedisDtsDataRepairJobTest struct { + RedisDtsDataCheckJobTest +} + +// RunRedisDtsDataRepair 执行 redis dts datarepair原子任务 +func (test *RedisDtsDataCheckJobTest) RunRedisDtsDataRepair() (ret string) { + // 写入 400 : *string* ,*hash*,*list*,*set*, 各100个key,提取"hash* 和 *set* 共200 + msg := fmt.Sprintf("=========dtsDataRepair test start============") + fmt.Println(msg) + + defer func() { + if test.Err != nil { + msg = fmt.Sprintf("=========dtsDataRepair test fail============") + fmt.Println(test.Err) + } else { + msg = fmt.Sprintf("=========dtsDataRepair test success============") + } + fmt.Println(msg) + }() + + paramBytes, _ := json.Marshal(test) + // fmt.Printf("-------payload(raw)--------\n%s\n\n", string(paramBytes)) + // encodeStr := base64.StdEncoding.EncodeToString(paramBytes) + runcmd := fmt.Sprintf(consts.ActuatorTestCmd, atomredis.NewRedisDtsDataRepair().Name(), string(paramBytes)) + fmt.Println(runcmd) + ret, test.Err = util.RunBashCmd(runcmd, "", nil, 1*time.Hour) + if test.Err != nil && !strings.Contains(test.Err.Error(), "totalHotKeysCnt:") { + return + } else if test.Err != nil { + ret = test.Err.Error() + test.Err = nil + } + + return +} + +// RunReplicaPairDataRepair 利用一对主从 做数据修复 +func RunReplicaPairDataRepair(masterIP string, masterPort int, masterPasswd, + slaveIP string, slavePort int, slavePasswd string, dbtoolsPkgName, dbtoolsPkgMd5 string) (err error) { + masterAddr := masterIP + ":" + strconv.Itoa(masterPort) + slaveAddr := slaveIP + ":" + strconv.Itoa(slavePort) + + // 进行数据校验 + dataRepair := RedisDtsDataRepairJobTest{} + portSegmentList := []atomredis.PortAndSegment{} + portSegmentList = append(portSegmentList, atomredis.PortAndSegment{ + Port: masterPort, + SegmentStart: -1, + SegmentEnd: -1, + }) + dataRepair.SetBkBizID("testapp").SetIP(masterIP).SetPortSegmentList(portSegmentList). + SetPkg(dbtoolsPkgName, dbtoolsPkgMd5). + SetDtsType(consts.DtsTypeOneAppDiffCluster). + SetSrcClusterAddr(masterAddr).SetSrcReddisPassword(masterPasswd). + SetDtsClusterAddr(slaveAddr).SetDstClusterPassword(slavePasswd). + SetKeyWhiteRegex("*").SetKeyBlackRegex("") + + dataRepair.RunRedisDtsDataRepair() + if dataRepair.Err != nil { + return dataRepair.Err + } + + return +} diff --git a/dbm-services/redis/db-tools/dbactuator/tests/redistest/redis_dts_datarepaire.go b/dbm-services/redis/db-tools/dbactuator/tests/redistest/redis_dts_datarepaire.go deleted file mode 100644 index 90c9edd80e..0000000000 --- a/dbm-services/redis/db-tools/dbactuator/tests/redistest/redis_dts_datarepaire.go +++ /dev/null @@ -1,79 +0,0 @@ -package redistest - -import ( - "encoding/json" - "fmt" - "strconv" - "strings" - "time" - - "dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis" - "dbm-services/redis/db-tools/dbactuator/pkg/consts" - "dbm-services/redis/db-tools/dbactuator/pkg/util" -) - -// RedisDtsDataRepaireJobTest dts数据修复测试 -type RedisDtsDataRepaireJobTest struct { - RedisDtsDataCheckJobTest -} - -// RunRedisDtsDataRepaire 执行 redis dts datarepaire 原子任务 -func (test *RedisDtsDataCheckJobTest) RunRedisDtsDataRepaire() (ret string) { - // 写入 400 : *string* ,*hash*,*list*,*set*, 各100个key,提取"hash* 和 *set* 共200 - msg := fmt.Sprintf("=========dtsDataRepaire test start============") - fmt.Println(msg) - - defer func() { - if test.Err != nil { - msg = fmt.Sprintf("=========dtsDataRepaire test fail============") - fmt.Println(test.Err) - } else { - msg = fmt.Sprintf("=========dtsDataRepaire test success============") - } - fmt.Println(msg) - }() - - paramBytes, _ := json.Marshal(test) - // fmt.Printf("-------payload(raw)--------\n%s\n\n", string(paramBytes)) - // encodeStr := base64.StdEncoding.EncodeToString(paramBytes) - runcmd := fmt.Sprintf(consts.ActuatorTestCmd, atomredis.NewRedisDtsDataRepaire().Name(), string(paramBytes)) - fmt.Println(runcmd) - ret, test.Err = util.RunBashCmd(runcmd, "", nil, 1*time.Hour) - if test.Err != nil && !strings.Contains(test.Err.Error(), "totalHotKeysCnt:") { - return - } else if test.Err != nil { - ret = test.Err.Error() - test.Err = nil - } - - return -} - -// RunReplicaPairDataRepaire 利用一对主从 做数据修复 -func RunReplicaPairDataRepaire(masterIP string, masterPort int, masterPasswd, - slaveIP string, slavePort int, slavePasswd string, dbtoolsPkgName, dbtoolsPkgMd5 string) (err error) { - masterAddr := masterIP + ":" + strconv.Itoa(masterPort) - slaveAddr := slaveIP + ":" + strconv.Itoa(slavePort) - - // 进行数据校验 - dataRepaire := RedisDtsDataRepaireJobTest{} - portSegmentList := []atomredis.PortAndSegment{} - portSegmentList = append(portSegmentList, atomredis.PortAndSegment{ - Port: masterPort, - SegmentStart: -1, - SegmentEnd: -1, - }) - dataRepaire.SetBkBizID("testapp").SetIP(masterIP).SetPortSegmentList(portSegmentList). - SetPkg(dbtoolsPkgName, dbtoolsPkgMd5). - SetDtsType(consts.DtsTypeOneAppDiffCluster). - SetSrcClusterAddr(masterAddr).SetSrcReddisPassword(masterPasswd). - SetDtsClusterAddr(slaveAddr).SetDstClusterPassword(slavePasswd). - SetKeyWhiteRegex("*").SetKeyBlackRegex("") - - dataRepaire.RunRedisDtsDataRepaire() - if dataRepaire.Err != nil { - return dataRepaire.Err - } - - return -} diff --git a/dbm-services/redis/db-tools/dbactuator/tests/redistest/redis_install.go b/dbm-services/redis/db-tools/dbactuator/tests/redistest/redis_install.go index b155f1118e..411efbdb33 100644 --- a/dbm-services/redis/db-tools/dbactuator/tests/redistest/redis_install.go +++ b/dbm-services/redis/db-tools/dbactuator/tests/redistest/redis_install.go @@ -404,9 +404,6 @@ func (test *RedisInstallTest) SetTendisplusRedisConf() { "scanCntIndexMgr": "10000", "truncateBinlogIntervalMs": "100", "minbinlogkeepsec": "1800", - "binlogdelrange": "500000", - "migrate-gc-enabled": "false", - "deletefilesinrange-for-binlog": "1", "incrpushthreadnum": "10", "rename-command": `config confxx rename-command flushdb cleandb diff --git a/dbm-services/redis/db-tools/dbactuator/tests/redistest/redis_remove_dts_server.go b/dbm-services/redis/db-tools/dbactuator/tests/redistest/redis_remove_dts_server.go new file mode 100644 index 0000000000..0390149b98 --- /dev/null +++ b/dbm-services/redis/db-tools/dbactuator/tests/redistest/redis_remove_dts_server.go @@ -0,0 +1,68 @@ +package redistest + +import ( + "encoding/json" + "fmt" + "time" + + "dbm-services/redis/db-tools/dbactuator/pkg/atomjobs/atomredis" + "dbm-services/redis/db-tools/dbactuator/pkg/consts" + "dbm-services/redis/db-tools/dbactuator/pkg/util" +) + +// RedisRemoveDtsServerTest dts添加服务测试 +type RedisRemoveDtsServerTest struct { + atomredis.RedisRemoveDtsServerParams + Err error `json:"-"` +} + +// SetPkg set pkg信息 +func (test *RedisRemoveDtsServerTest) SetPkg(pkg, pkgMd5 string) *RedisRemoveDtsServerTest { + if test.Err != nil { + return test + } + if pkg == "" || pkgMd5 == "" { + pkg = "redis_dts.tar.gz" + pkgMd5 = "b816b2d47357c3ed7a7864d6730fd33f" + } + test.Pkg = pkg + test.PkgMd5 = pkgMd5 + return test +} + +// RunRedisRemoveDtsServer 执行 redis remove dts_server 原子任务 +func (test *RedisRemoveDtsServerTest) RunRedisRemoveDtsServer() (err error) { + msg := fmt.Sprintf("=========RemoveDtsServer test start============") + fmt.Println(msg) + + defer func() { + if test.Err != nil { + msg = fmt.Sprintf("=========RemoveDtsServer test fail============") + fmt.Println(test.Err) + } else { + msg = fmt.Sprintf("=========RemoveDtsServer test success============") + } + fmt.Println(msg) + }() + + paramBytes, _ := json.Marshal(test) + runcmd := fmt.Sprintf(consts.ActuatorTestCmd, atomredis.NewRedisRemoveDtsServer().Name(), string(paramBytes)) + fmt.Println(runcmd) + _, test.Err = util.RunBashCmd(runcmd, "", nil, 1*time.Hour) + if test.Err != nil { + return test.Err + } + + return test.Err +} + +// RedisRemoveDtsServer dts服务移除测试 +func RedisRemoveDtsServer(pkgName, pkgMD5 string) (err error) { + test := &RedisRemoveDtsServerTest{} + test.SetPkg(pkgName, pkgMD5) + if test.Err != nil { + return test.Err + } + err = test.RunRedisRemoveDtsServer() + return +} diff --git a/dbm-services/redis/db-tools/dbactuator/tests/test.go b/dbm-services/redis/db-tools/dbactuator/tests/test.go index c002d2ccf0..3b668272f2 100644 --- a/dbm-services/redis/db-tools/dbactuator/tests/test.go +++ b/dbm-services/redis/db-tools/dbactuator/tests/test.go @@ -99,6 +99,13 @@ func main() { return } + // PredixyTendisplusClusterForgetTest forget节点测试 + if err = clustertest.PredixyTendisplusClusterForgetTest(localIP, + tendisplusPkgName, tendisplusPkgMd5, + dbtoolsPkgName, dbtoolsPkgMd5); err != nil { + return + } + err = redistest.BkDbmonInstall(localIP, dbtoolsPkgName, dbtoolsPkgMd5, bkdbmonPkgName, bkdbmonPkgMd5, consts.TendisTypePredixyTendisplusCluster) @@ -221,7 +228,7 @@ func main() { if err != nil { return } - err = redistest.RunReplicaPairDataRepaire(localIP, consts.TestRedisMasterStartPort, consts.RedisTestPasswd, + err = redistest.RunReplicaPairDataRepair(localIP, consts.TestRedisMasterStartPort, consts.RedisTestPasswd, localIP, consts.TestRedisSlaveStartPort, consts.RedisTestPasswd, dbtoolsPkgName, dbtoolsPkgMd5) if err != nil { diff --git a/dbm-services/redis/db-tools/dbactuator/tests/test.sh b/dbm-services/redis/db-tools/dbactuator/tests/test.sh index 21ca002225..437b87b5e3 100644 --- a/dbm-services/redis/db-tools/dbactuator/tests/test.sh +++ b/dbm-services/redis/db-tools/dbactuator/tests/test.sh @@ -3,8 +3,8 @@ repoUser="" repoPassword="" -tendisplusPkgName="tendisplus-2.5.0-rocksdb-v6.23.3.tgz" -tendisplusPkgMd5="573fac8917f3cb6d73d4913471a6eacc" +tendisplusPkgName="tendisplus-2.6.0-rocksdb-v6.23.3.tgz" +tendisplusPkgMd5="eaf90d7072740fd232b157d9cb32a425" redisPkgName="redis-6.2.7.tar.gz" redisPkgMd5="1fc9e5c3a044ce523844a6f2717e5ac3" @@ -17,14 +17,14 @@ tendisssdPkgMd5="7bfe87efbe017c689c3f4a11bb2a8be9" predixyPkgName="predixy-1.4.0.tar.gz" predixyPkgMd5="24aba4a96dcf7f8581d2fde89d062455" -twemproxyPkgName="twemproxy-0.4.1-v23.tar.gz" -twemproxyPkgMd5="41850e44bebfce84ebd4d0cf4cce6833" +twemproxyPkgName="twemproxy-0.4.1-v27.tar.gz" +twemproxyPkgMd5="b7fcec49a43da9fdb5acde0a42287d43" dbtoolsPkgName="dbtools.tar.gz" dbtoolsPkgMd5="ced0fa280c63cb31536fefc1845f3ff0" -bkdbmonPkgName="bk-dbmon-v0.9.tar.gz" -bkdbmonPkgMd5="a579e2ffd74259f3dd66d23a10a170ba" +bkdbmonPkgName="bk-dbmon-v0.12.tar.gz" +bkdbmonPkgMd5="2a3a51c3b4a7dce4300e894e19f2f0ea" repoUrl="" @@ -144,7 +144,7 @@ if [[ -e $localBkDbmonPkgName ]]; then localBkDbmonPkgMd5=$(md5sum $localBkDbmonPkgName | awk '{print $1}') fi -wgetCmd="wget --user=$repoUser --password=$repoPassword $repoUrl/tendisplus/Tendisplus-2.5/$tendisplusPkgName -O $localTendisplusPkgName" +wgetCmd="wget --user=$repoUser --password=$repoPassword $repoUrl/tendisplus/Tendisplus-2.6/$tendisplusPkgName -O $localTendisplusPkgName" if [[ ! -e $localTendisplusPkgName ]]; then echo $wgetCmd $wgetCmd diff --git a/dbm-services/redis/db-tools/dbmon/cmd/root.go b/dbm-services/redis/db-tools/dbmon/cmd/root.go index 3c2d984051..c7b84a524f 100644 --- a/dbm-services/redis/db-tools/dbmon/cmd/root.go +++ b/dbm-services/redis/db-tools/dbmon/cmd/root.go @@ -4,9 +4,10 @@ package cmd import ( "fmt" "log" + "net/http" + _ "net/http/pprof" // pprof TODO "os" "runtime/debug" - "time" "dbm-services/redis/db-tools/dbmon/config" "dbm-services/redis/db-tools/dbmon/mylog" @@ -65,6 +66,7 @@ var rootCmd = &cobra.Command{ if hasRedis { report.InitGlobalHistoryClearJob(config.GlobalConf) redisfullbackup.InitGlobRedisFullBackupJob(config.GlobalConf) + redisfullbackup.InitGlobRedisFullCheckJob(config.GlobalConf) redisbinlogbackup.InitGlobRedisBinlogBackupJob(config.GlobalConf) redisheartbeat.InitGlobRedisHeartbeatJob(config.GlobalConf) redismonitor.InitGlobRedisMonitorJob(config.GlobalConf) @@ -76,13 +78,16 @@ var rootCmd = &cobra.Command{ log.Panicf("reportHistoryClear addjob fail,entryID:%d,err:%v\n", entryID, err) return } + mylog.Logger.Info(fmt.Sprintf("create cron GlobHistoryClearJob success,entryID:%d", entryID)) + if config.GlobalConf.RedisFullBackup.Cron != "" { entryID, err = c.AddJob(config.GlobalConf.RedisFullBackup.Cron, - cron.NewChain(cron.SkipIfStillRunning(mylog.AdapterLog)).Then(redisfullbackup.GlobRedisFullBakJob)) + cron.NewChain(cron.SkipIfStillRunning(mylog.AdapterLog)).Then(redisfullbackup.GlobRedisFullBackupJob)) if err != nil { log.Panicf("fullbackup addjob fail,entryID:%d,err:%v\n", entryID, err) return } + mylog.Logger.Info(fmt.Sprintf("create cron GlobRedisFullBackupJob success,entryID:%d", entryID)) } if config.GlobalConf.RedisBinlogBackup.Cron != "" { entryID, err = c.AddJob(config.GlobalConf.RedisBinlogBackup.Cron, @@ -91,6 +96,15 @@ var rootCmd = &cobra.Command{ log.Panicf("binlogbackup addjob fail,entryID:%d,err:%v\n", entryID, err) return } + mylog.Logger.Info(fmt.Sprintf("create cron GlobRedisBinlogBakJob success,entryID:%d", entryID)) + + entryID, err = c.AddJob(config.GlobalConf.RedisBinlogBackup.Cron, + cron.NewChain(cron.SkipIfStillRunning(mylog.AdapterLog)).Then(redisfullbackup.GlobRedisFullCheckJob)) + if err != nil { + log.Panicf("fullcheck addjob fail,entryID:%d,err:%v\n", entryID, err) + return + } + mylog.Logger.Info(fmt.Sprintf("create cron GlobRedisFullCheckJob success,entryID:%d", entryID)) } if config.GlobalConf.RedisHeartbeat.Cron != "" { entryID, err = c.AddJob(config.GlobalConf.RedisHeartbeat.Cron, @@ -99,6 +113,7 @@ var rootCmd = &cobra.Command{ fmt.Printf("heartbeat addjob fail,entryID:%d,err:%v\n", entryID, err) return } + mylog.Logger.Info(fmt.Sprintf("create cron GlobRedisHeartbeatJob success,entryID:%d", entryID)) } if config.GlobalConf.RedisMonitor.Cron != "" { entryID, err = c.AddJob(config.GlobalConf.RedisMonitor.Cron, @@ -107,6 +122,7 @@ var rootCmd = &cobra.Command{ fmt.Printf("monitor addjob fail,entryID:%d,err:%v\n", entryID, err) return } + mylog.Logger.Info(fmt.Sprintf("create cron GlobRedisMonitorJob success,entryID:%d", entryID)) } if config.GlobalConf.KeyLifeCycle.Cron != "" { entryID, err = c.AddJob(config.GlobalConf.KeyLifeCycle.Cron, @@ -115,6 +131,7 @@ var rootCmd = &cobra.Command{ fmt.Printf("keylifecycle addjob fail,entryID:%d,err:%v\n", entryID, err) return } + mylog.Logger.Info(fmt.Sprintf("create cron GlobRedisKeyLifeCycleJob success,entryID:%d", entryID)) } } else if hasMongo { @@ -136,10 +153,10 @@ var rootCmd = &cobra.Command{ } mylog.Logger.Info(fmt.Sprintf("start cron job,entryID:%d Listen:%s\n", entryID, config.GlobalConf.HttpAddress)) c.Start() + go func() { + http.ListenAndServe("127.0.0.1:6600", nil) + }() httpapi.StartListen(config.GlobalConf) - for { - time.Sleep(10 * time.Second) - } }, } diff --git a/dbm-services/redis/db-tools/dbmon/config/config.go b/dbm-services/redis/db-tools/dbmon/config/config.go index ba9853b52c..ab987f887c 100644 --- a/dbm-services/redis/db-tools/dbmon/config/config.go +++ b/dbm-services/redis/db-tools/dbmon/config/config.go @@ -12,17 +12,19 @@ import ( // ConfServerItem servers配置项 type ConfServerItem struct { - BkBizID string `json:"bk_biz_id" mapstructure:"bk_biz_id"` - BkCloudID int64 `json:"bk_cloud_id" mapstructure:"bk_cloud_id"` - App string `json:"app" mapstructure:"app"` - AppName string `json:"app_name" mapstructure:"app_name"` - ClusterDomain string `json:"cluster_domain" mapstructure:"cluster_domain"` - ClusterName string `json:"cluster_name" mapstructure:"cluster_name"` - ClusterType string `json:"cluster_type" mapstructure:"cluster_type"` - MetaRole string `json:"meta_role" mapstructure:"meta_role"` - ServerIP string `json:"server_ip" mapstructure:"server_ip"` - ServerPorts []int `json:"server_ports" mapstructure:"server_ports"` - Shard string `json:"shard" mapstructure:"shard"` + BkBizID string `json:"bk_biz_id" mapstructure:"bk_biz_id"` + BkCloudID int64 `json:"bk_cloud_id" mapstructure:"bk_cloud_id"` + App string `json:"app" mapstructure:"app"` + AppName string `json:"app_name" mapstructure:"app_name"` + ClusterDomain string `json:"cluster_domain" mapstructure:"cluster_domain"` + ClusterName string `json:"cluster_name" mapstructure:"cluster_name"` + ClusterType string `json:"cluster_type" mapstructure:"cluster_type"` + MetaRole string `json:"meta_role" mapstructure:"meta_role"` + ServerIP string `json:"server_ip" mapstructure:"server_ip"` + ServerPorts []int `json:"server_ports" mapstructure:"server_ports"` + ServerShards map[string]string `json:"server_shards" mapstructure:"server_shards"` + CacheBackupMode string `json:"cache_backup_mode" mapstructure:"cache_backup_mode"` // aof or rdb + Shard string `json:"shard" mapstructure:"shard"` } // ConfRedisFullBackup 全备配置 diff --git a/dbm-services/redis/db-tools/dbmon/main.go b/dbm-services/redis/db-tools/dbmon/main.go index 8a36001161..6296bf8167 100644 --- a/dbm-services/redis/db-tools/dbmon/main.go +++ b/dbm-services/redis/db-tools/dbmon/main.go @@ -5,7 +5,9 @@ Copyright © 2022 NAME HERE */ package main -import "dbm-services/redis/db-tools/dbmon/cmd" +import ( + "dbm-services/redis/db-tools/dbmon/cmd" +) func main() { cmd.Execute() diff --git a/dbm-services/redis/db-tools/dbmon/models/myredis/client.go b/dbm-services/redis/db-tools/dbmon/models/myredis/client.go index 583d367416..adcac28b14 100644 --- a/dbm-services/redis/db-tools/dbmon/models/myredis/client.go +++ b/dbm-services/redis/db-tools/dbmon/models/myredis/client.go @@ -1328,6 +1328,36 @@ func (db *RedisClient) IsTendisSSDReplicaStatusOk(masterIP, masterPort string) ( return } +// RedisClusterGetMasterNode 获取master节点信息(如果 addr是master则返回它的node信息,否则找到它的masterID,进而找到master的node信息) +func (db *RedisClient) RedisClusterGetMasterNode(addr string) (masterNode *ClusterNodeData, err error) { + addrToNodes, err := db.GetAddrMapToNodes() + if err != nil { + return + } + myNode, ok := addrToNodes[addr] + if !ok { + err = fmt.Errorf("addr:%s not found in cluster nodes", addr) + mylog.Logger.Error(err.Error()) + return + } + if myNode.GetRole() == consts.RedisMasterRole { + masterNode = myNode + return + } + masterNodeID := myNode.MasterID + idToNode, err := db.GetNodeIDMapToNodes() + if err != nil { + return + } + masterNode, ok = idToNode[masterNodeID] + if !ok { + err = fmt.Errorf("masterNodeID:%s not found in cluster nodes", masterNodeID) + mylog.Logger.Error(err.Error()) + return + } + return +} + // MaxMemory 'confxx get maxmemory' func (db *RedisClient) MaxMemory() (maxmemory uint64, err error) { var confRet map[string]string diff --git a/dbm-services/redis/db-tools/dbmon/models/myredis/myredis.go b/dbm-services/redis/db-tools/dbmon/models/myredis/myredis.go index 0f97b62877..78f3507ca8 100644 --- a/dbm-services/redis/db-tools/dbmon/models/myredis/myredis.go +++ b/dbm-services/redis/db-tools/dbmon/models/myredis/myredis.go @@ -52,10 +52,10 @@ func GetRedisPasswdFromConfFile(port int) (password string, err error) { func GetProxyPasswdFromConfFlie(port int, role string) (password string, err error) { dataDir := consts.GetRedisDataDir() var grepCmd string - if role == consts.MetaRolePredixy { + if role == consts.MetaRoleTwemproxy { grepCmd = fmt.Sprintf(`grep -w "password" %s/twemproxy*/%d/nutcracker.%d.yml|grep -vE "#"|awk '{print $2}'`, dataDir, port, port) - } else if role == consts.MetaRoleTwemproxy { + } else if role == consts.MetaRolePredixy { grepCmd = fmt.Sprintf(`grep -iw "auth" %s/predixy/%d/predixy.conf|awk '{print $2}'`, dataDir, port) } password, err = util.RunBashCmd(grepCmd, "", nil, 10*time.Second) diff --git a/dbm-services/redis/db-tools/dbmon/pkg/backupsys/backupclient.go b/dbm-services/redis/db-tools/dbmon/pkg/backupsys/backupclient.go new file mode 100644 index 0000000000..da8b531445 --- /dev/null +++ b/dbm-services/redis/db-tools/dbmon/pkg/backupsys/backupclient.go @@ -0,0 +1,203 @@ +package backupsys + +import ( + "bufio" + "encoding/json" + "fmt" + "strconv" + "strings" + "time" + + "dbm-services/common/go-pubpkg/backupclient" + "dbm-services/redis/db-tools/dbmon/mylog" + "dbm-services/redis/db-tools/dbmon/pkg/consts" + "dbm-services/redis/db-tools/dbmon/util" +) + +// BackupClient TODO +type BackupClient interface { + Upload(fileName string) (string, error) + TaskStatus(taskId string) (int, string, error) +} + +// COSBackupClient 新备份系统 +type COSBackupClient struct { + backupClient *backupclient.BackupClient +} + +// NewCosBackupClient new +func NewCosBackupClient(toolPath string, authFile string, fileTag string) (*COSBackupClient, error) { + if backupClient, err := backupclient.New(toolPath, authFile, fileTag); err != nil { + mylog.Logger.Error(fmt.Sprintf("NewCosBackupClient failed,err:%v", err)) + return nil, err + } else { + return &COSBackupClient{backupClient: backupClient}, nil + } +} + +// Upload 上传文件 +func (o *COSBackupClient) Upload(fileName string) (taskId string, err error) { + taskId, err = o.backupClient.Upload(fileName) + if err != nil { + mylog.Logger.Error(fmt.Sprintf("CosBackupClient upload failed,err:%v", err)) + return + } + return +} + +// TaskStatus 备份任务状态 +func (o *COSBackupClient) TaskStatus(taskId string) (status int, statusMsg string, err error) { + status, statusMsg, err = o.backupClient.Query2(taskId) + if err != nil { + mylog.Logger.Error(fmt.Sprintf("CosBackupClient Query2 failed,err:%v", err)) + return + } + return +} + +// IBSBackupClient 旧备份系统 +type IBSBackupClient struct { + ToolPath string `json:"tool_path"` + FileTag string `json:"file_tag"` +} + +// NewIBSBackupClient new +func NewIBSBackupClient(toolPath, fileTag string) *IBSBackupClient { + return &IBSBackupClient{ToolPath: toolPath, FileTag: fileTag} +} + +// Upload 上传文件 +func (o *IBSBackupClient) Upload(fileName string) (taskId string, err error) { + bkCmd := fmt.Sprintf("%s -n -f %s --with-md5 -t %s 2>/dev/null|grep 'taskid'|awk -F: '{print $2}'", + o.ToolPath, fileName, o.FileTag) + mylog.Logger.Info(bkCmd) + taskId, err = util.RunBashCmd(bkCmd, "", nil, 10*time.Minute) + if err != nil { + return "", err + } + if taskId == "" { + err = fmt.Errorf("%s failed,taskId is empty", bkCmd) + mylog.Logger.Error(err.Error()) + return "", err + } + return taskId, nil +} + +// TaskStatus 备份任务状态 +func (o *IBSBackupClient) TaskStatus(taskId string) (status int, statusMsg string, err error) { + ret, err := o.GetTaskStatus(taskId) + if err != nil { + return + } + return ret.Status, ret.StatusInfo, nil +} + +// IBSTaskStatus backup_client -q --taskid=xxxx 命令的结果 +type IBSTaskStatus struct { + File string `json:"file"` + Host string `json:"host"` + SednupDateTime time.Time `json:"sendup_datetime"` + Status int `json:"status"` + StatusInfo string `json:"status_info"` + StartTime time.Time `json:"start_time"` + CompleteTime time.Time `json:"complete_time"` + ExpireTime time.Time `json:"expire_time"` +} + +// String 用于打印 +func (status *IBSTaskStatus) String() string { + statusBytes, _ := json.Marshal(status) + return string(statusBytes) +} + +// GetTaskStatus 获取备份任务状态 +func (o *IBSBackupClient) GetTaskStatus(taskId string) (status IBSTaskStatus, err error) { + var cmdRet string + bkCmd := fmt.Sprintf("%s -q --taskid=%s", o.ToolPath, taskId) + cmdRet, err = util.RunBashCmd(bkCmd, "", nil, 30*time.Second) + if err != nil { + return + } + scanner := bufio.NewScanner(strings.NewReader(cmdRet)) + scanner.Split(bufio.ScanLines) + for scanner.Scan() { + line := scanner.Text() + line = strings.TrimSpace(line) + if line == "" { + continue + } + l01 := strings.SplitN(line, ":", 2) + if len(l01) != 2 { + err = fmt.Errorf("len()!=2,cmd:%s,result format not correct:%s", bkCmd, cmdRet) + mylog.Logger.Error(err.Error()) + return + } + first := strings.TrimSpace(l01[0]) + second := strings.TrimSpace(l01[1]) + switch first { + case "file": + status.File = second + case "host": + status.Host = second + case "sendup datetime": + if second == "0000-00-00 00:00:00" { + status.SednupDateTime = time.Time{} // "0000-01-00 00:00:00" + break + } + status.SednupDateTime, err = time.ParseInLocation(consts.UnixtimeLayout, second, time.Local) + if err != nil { + err = fmt.Errorf("time.Parse 'sendup datetime' failed,err:%v,value:%s,cmd:%s", err, second, bkCmd) + mylog.Logger.Error(err.Error()) + return + } + case "status": + status.Status, err = strconv.Atoi(second) + if err != nil { + err = fmt.Errorf("strconv.Atoi failed,err:%v,value:%s,cmd:%s", err, second, bkCmd) + mylog.Logger.Error(err.Error()) + return + } + case "status info": + status.StatusInfo = second + case "start_time": + if second == "0000-00-00 00:00:00" { + status.StartTime = time.Time{} // "0000-01-00 00:00:00" + break + } + status.StartTime, err = time.ParseInLocation(consts.UnixtimeLayout, second, time.Local) + if err != nil { + err = fmt.Errorf("time.Parse start_time failed,err:%v,value:%s,cmd:%s", err, second, bkCmd) + mylog.Logger.Error(err.Error()) + return + } + case "complete_time": + if second == "0000-00-00 00:00:00" { + status.CompleteTime = time.Time{} // "0000-01-00 00:00:00" + break + } + status.CompleteTime, err = time.ParseInLocation(consts.UnixtimeLayout, second, time.Local) + if err != nil { + err = fmt.Errorf("time.Parse complete_time failed,err:%v,value:%s,cmd:%s", err, second, bkCmd) + mylog.Logger.Error(err.Error()) + return + } + case "expire_time": + if second == "0000-00-00 00:00:00" { + status.ExpireTime = time.Time{} // "0000-01-00 00:00:00" + break + } + status.ExpireTime, err = time.ParseInLocation(consts.UnixtimeLayout, second, time.Local) + if err != nil { + err = fmt.Errorf("time.Parse expire_time failed,err:%v,value:%s,cmd:%s", err, second, bkCmd) + mylog.Logger.Error(err.Error()) + return + } + } + } + if err = scanner.Err(); err != nil { + err = fmt.Errorf("scanner.Scan failed,err:%v,cmd:%s", err, cmdRet) + mylog.Logger.Error(err.Error()) + return + } + return +} diff --git a/dbm-services/redis/db-tools/dbmon/pkg/backupsys/backupsys.go b/dbm-services/redis/db-tools/dbmon/pkg/backupsys/backupsys.go deleted file mode 100644 index 2e03a72d82..0000000000 --- a/dbm-services/redis/db-tools/dbmon/pkg/backupsys/backupsys.go +++ /dev/null @@ -1,231 +0,0 @@ -// Package backupsys 备份系统 -package backupsys - -import ( - "bufio" - "encoding/json" - "fmt" - "strconv" - "strings" - "time" - - "dbm-services/redis/db-tools/dbmon/mylog" - "dbm-services/redis/db-tools/dbmon/pkg/consts" - "dbm-services/redis/db-tools/dbmon/util" -) - -// UploadTask 操作备份系统 -type UploadTask struct { - Files []string `json:"files"` // 全路径 - TaskIDs []uint64 `json:"taskids"` - Tag string `json:"tag"` -} - -// UploadFiles 上传文件 -func (task *UploadTask) UploadFiles() (err error) { - var taskIDStr string - var taskIDNum uint64 - if len(task.Files) == 0 { - return - } - if task.Tag == "" { - err = fmt.Errorf("BackupSystem uploadFiles tag(%s) cannot be empty", task.Tag) - mylog.Logger.Error(err.Error()) - return - } - for _, file := range task.Files { - if !util.FileExists(file) { - err = fmt.Errorf("BackupSystem uploadFiles %s not exists", file) - mylog.Logger.Error(err.Error()) - return - } - } - for _, bkfile := range task.Files { - bkCmd := fmt.Sprintf("%s -n -f %s --with-md5 -t %s|grep 'taskid'|awk -F: '{print $2}'", - consts.BackupClient, bkfile, task.Tag) - mylog.Logger.Info(bkCmd) - taskIDStr, err = util.RunBashCmd(bkCmd, "", nil, 10*time.Minute) - if err != nil { - return - } - taskIDNum, err = strconv.ParseUint(taskIDStr, 10, 64) - if err != nil { - err = fmt.Errorf("%s ParseUint failed,err:%v", taskIDStr, err) - mylog.Logger.Error(err.Error()) - return - } - task.TaskIDs = append(task.TaskIDs, taskIDNum) - } - return -} - -// CheckTasksStatus 检查tasks状态 -func (task *UploadTask) CheckTasksStatus() (runningTaskIDs, failTaskIDs, succTaskIDs []uint64, - runningFiles, failedFiles, succFiles []string, failMsgs []string, err error) { - var status TaskStatus - for idx, taskID := range task.TaskIDs { - status, err = GetTaskStatus(taskID) - if err != nil { - return - } - if status.Status > 4 { - // err = fmt.Errorf("ToBackupSystem %s failed,err:%s,taskid:%d", - // status.File, status.StatusInfo, taskID) - // mylog.Logger.Error(err.Error()) - failMsgs = append(failMsgs, fmt.Sprintf("taskid:%d,failMsg:%s", taskID, status.StatusInfo)) - failedFiles = append(failedFiles, task.Files[idx]) - failTaskIDs = append(failTaskIDs, task.TaskIDs[idx]) - } else if status.Status == 4 { - succFiles = append(succFiles, task.Files[idx]) - succTaskIDs = append(succTaskIDs, task.TaskIDs[idx]) - } else if status.Status < 4 { - runningFiles = append(runningFiles, task.Files[idx]) - runningTaskIDs = append(runningTaskIDs, task.TaskIDs[idx]) - } - } - return -} - -// WaitForUploadFinish 等待所有files上传成功 -func (task *UploadTask) WaitForUploadFinish() (err error) { - var times int64 - var msg string - var runningFiles, failFiles, succFiles, failMsgs []string - for { - times++ - _, _, _, runningFiles, failFiles, succFiles, failMsgs, err = task.CheckTasksStatus() - if err != nil { - return - } - // 只要有running的task,则继续等待 - if len(runningFiles) > 0 { - if times%6 == 0 { - // 每分钟打印一次日志 - msg = fmt.Sprintf("files[%+v] cnt:%d upload to backupSystem still running", runningFiles, len(runningFiles)) - mylog.Logger.Info(msg) - } - time.Sleep(10 * time.Second) - continue - } - if len(failMsgs) > 0 { - err = fmt.Errorf("failCnt:%d,failFiles:[%+v],err:%s", len(failFiles), failFiles, strings.Join(failFiles, ",")) - mylog.Logger.Error(err.Error()) - return - } - if len(succFiles) == len(task.Files) { - return nil - } - break - } - return -} - -// TaskStatus backup_client -q --taskid=xxxx 命令的结果 -type TaskStatus struct { - File string `json:"file"` - Host string `json:"host"` - SednupDateTime time.Time `json:"sendup_datetime"` - Status int `json:"status"` - StatusInfo string `json:"status_info"` - StartTime time.Time `json:"start_time"` - CompleteTime time.Time `json:"complete_time"` - ExpireTime time.Time `json:"expire_time"` -} - -// String 用于打印 -func (status *TaskStatus) String() string { - statusBytes, _ := json.Marshal(status) - return string(statusBytes) -} - -// GetTaskStatus 执行backup_client -q --taskid=xxxx 命令的结果并解析 -func GetTaskStatus(taskid uint64) (status TaskStatus, err error) { - var cmdRet string - bkCmd := fmt.Sprintf("%s -q --taskid=%d", consts.BackupClient, taskid) - cmdRet, err = util.RunBashCmd(bkCmd, "", nil, 30*time.Second) - if err != nil { - return - } - scanner := bufio.NewScanner(strings.NewReader(cmdRet)) - scanner.Split(bufio.ScanLines) - for scanner.Scan() { - line := scanner.Text() - line = strings.TrimSpace(line) - if line == "" { - continue - } - l01 := strings.SplitN(line, ":", 2) - if len(l01) != 2 { - err = fmt.Errorf("len()!=2,cmd:%s,result format not correct:%s", bkCmd, cmdRet) - mylog.Logger.Error(err.Error()) - return - } - first := strings.TrimSpace(l01[0]) - second := strings.TrimSpace(l01[1]) - switch first { - case "file": - status.File = second - case "host": - status.Host = second - case "sendup datetime": - if second == "0000-00-00 00:00:00" { - status.SednupDateTime = time.Time{} // "0000-01-00 00:00:00" - break - } - status.SednupDateTime, err = time.ParseInLocation(consts.UnixtimeLayout, second, time.Local) - if err != nil { - err = fmt.Errorf("time.Parse 'sendup datetime' failed,err:%v,value:%s,cmd:%s", err, second, bkCmd) - mylog.Logger.Error(err.Error()) - return - } - case "status": - status.Status, err = strconv.Atoi(second) - if err != nil { - err = fmt.Errorf("strconv.Atoi failed,err:%v,value:%s,cmd:%s", err, second, bkCmd) - mylog.Logger.Error(err.Error()) - return - } - case "status info": - status.StatusInfo = second - case "start_time": - if second == "0000-00-00 00:00:00" { - status.StartTime = time.Time{} // "0000-01-00 00:00:00" - break - } - status.StartTime, err = time.ParseInLocation(consts.UnixtimeLayout, second, time.Local) - if err != nil { - err = fmt.Errorf("time.Parse start_time failed,err:%v,value:%s,cmd:%s", err, second, bkCmd) - mylog.Logger.Error(err.Error()) - return - } - case "complete_time": - if second == "0000-00-00 00:00:00" { - status.CompleteTime = time.Time{} // "0000-01-00 00:00:00" - break - } - status.CompleteTime, err = time.ParseInLocation(consts.UnixtimeLayout, second, time.Local) - if err != nil { - err = fmt.Errorf("time.Parse complete_time failed,err:%v,value:%s,cmd:%s", err, second, bkCmd) - mylog.Logger.Error(err.Error()) - return - } - case "expire_time": - if second == "0000-00-00 00:00:00" { - status.ExpireTime = time.Time{} // "0000-01-00 00:00:00" - break - } - status.ExpireTime, err = time.ParseInLocation(consts.UnixtimeLayout, second, time.Local) - if err != nil { - err = fmt.Errorf("time.Parse expire_time failed,err:%v,value:%s,cmd:%s", err, second, bkCmd) - mylog.Logger.Error(err.Error()) - return - } - } - } - if err = scanner.Err(); err != nil { - err = fmt.Errorf("scanner.Scan failed,err:%v,cmd:%s", err, cmdRet) - mylog.Logger.Error(err.Error()) - return - } - return -} diff --git a/dbm-services/redis/db-tools/dbmon/pkg/consts/consts.go b/dbm-services/redis/db-tools/dbmon/pkg/consts/consts.go index ae315cc833..ea4d840b53 100644 --- a/dbm-services/redis/db-tools/dbmon/pkg/consts/consts.go +++ b/dbm-services/redis/db-tools/dbmon/pkg/consts/consts.go @@ -3,7 +3,7 @@ package consts // version const ( - BkDbmonVersion = "v0.9" + BkDbmonVersion = "v0.12" ) const ( @@ -136,7 +136,8 @@ const ( const ( NormalBackupType = "normal_backup" ForeverBackupType = "forever_backup" - BackupClient = "/usr/local/bin/backup_client" + IBSBackupClient = "/usr/local/bin/backup_client" + COSBackupClient = "/usr/local/backup_client/bin/backup_client" RedisFullBackupTAG = "REDIS_FULL" RedisBinlogTAG = "REDIS_BINLOG" @@ -161,6 +162,9 @@ const ( BackupStatusToBakSysSuccess = "to_backup_system_success" BackupStatusFailed = "failed" BackupStatusLocalSuccess = "local_success" + + CacheBackupModeAof = "aof" + CacheBackupModeRdb = "rdb" ) const ( diff --git a/dbm-services/redis/db-tools/dbmon/pkg/redisbinlogbackup/job.go b/dbm-services/redis/db-tools/dbmon/pkg/redisbinlogbackup/job.go index 9f5a3d0b2f..314bff5771 100644 --- a/dbm-services/redis/db-tools/dbmon/pkg/redisbinlogbackup/job.go +++ b/dbm-services/redis/db-tools/dbmon/pkg/redisbinlogbackup/job.go @@ -7,7 +7,6 @@ import ( "os" "path/filepath" "strconv" - "strings" "time" "dbm-services/redis/db-tools/dbmon/config" @@ -28,7 +27,8 @@ type Job struct { // NOCC:golint/naming(其他:设计如此) Tasks []*Task `json:"tasks"` RealBackupDir string `json:"real_backup_dir"` // 如 /data/dbbak Reporter report.Reporter `json:"-"` - Err error `json:"-"` + backupClient backupsys.BackupClient + Err error `json:"-"` } // InitGlobRedisBinlogBackupJob 新建例行binlog备份任务 @@ -59,16 +59,11 @@ func (job *Job) Run() { } defer job.Reporter.Close() - // 检查历史备份任务状态 并 删除过旧的本地文件 - for _, svrItem := range job.Conf.Servers { - if !consts.IsRedisMetaRole(svrItem.MetaRole) { - continue - } - for _, port := range svrItem.ServerPorts { - job.CheckOldBinlogBackupStatus(port) - job.DeleteTooOldBinlogbackup(port) - } - } + job.backupClient = backupsys.NewIBSBackupClient(consts.IBSBackupClient, consts.RedisBinlogTAG) + // job.backupClient, job.Err = backupsys.NewCosBackupClient(consts.COSBackupClient, "", consts.RedisBinlogTAG) + // if job.Err != nil { + // return + // } job.createTasks() if job.Err != nil { return @@ -82,6 +77,17 @@ func (job *Job) Run() { continue } } + + // 检查历史备份任务状态 并 删除过旧的本地文件 + for _, svrItem := range job.Conf.Servers { + if !consts.IsRedisMetaRole(svrItem.MetaRole) { + continue + } + for _, port := range svrItem.ServerPorts { + job.CheckOldBinlogBackupStatus(port) + job.DeleteTooOldBinlogbackup(port) + } + } } // GetRealBackupDir 获取本地binlog保存路径 @@ -104,7 +110,9 @@ func (job *Job) createTasks() { var task *Task var password string var taskBackupDir string + var instStr string + job.Tasks = []*Task{} for _, svrItem := range job.Conf.Servers { if !consts.IsRedisMetaRole(svrItem.MetaRole) { continue @@ -114,13 +122,15 @@ func (job *Job) createTasks() { if job.Err != nil { return } + instStr = fmt.Sprintf("%s:%d", svrItem.ServerIP, port) taskBackupDir = filepath.Join(job.RealBackupDir, "binlog", strconv.Itoa(port)) util.MkDirsIfNotExists([]string{taskBackupDir}) util.LocalDirChownMysql(taskBackupDir) task = NewBinlogBackupTask(svrItem.BkBizID, svrItem.BkCloudID, svrItem.ClusterDomain, svrItem.ServerIP, port, password, job.Conf.RedisBinlogBackup.ToBackupSystem, - taskBackupDir, job.Conf.RedisBinlogBackup.OldFileLeftDay, job.Reporter) + taskBackupDir, svrItem.ServerShards[instStr], + job.Conf.RedisBinlogBackup.OldFileLeftDay, job.Reporter) job.Tasks = append(job.Tasks, task) } } @@ -136,8 +146,8 @@ func (job *Job) CheckOldBinlogBackupStatus(port int) { var doingHandler, tempHandler, doneHandler *os.File var line string var err error - var failMsgs []string - var runningTaskIDs, failedTaskIDs []uint64 + var taskStatus int + var statusMsg string task := Task{} oldFileLeftSec := job.Conf.RedisBinlogBackup.OldFileLeftDay * 24 * 3600 nowTime := time.Now().Local() @@ -196,8 +206,9 @@ func (job *Job) CheckOldBinlogBackupStatus(port int) { continue } task.reporter = job.Reporter + task.backupClient = job.backupClient // 删除旧文件 - if nowTime.Sub(task.BackupFileMTime.Time).Seconds() > float64(oldFileLeftSec) { + if nowTime.Sub(task.EndTime.Time).Seconds() > float64(oldFileLeftSec) { mylog.Logger.Info(fmt.Sprintf("%s start removing...", task.BackupFile)) if util.FileExists(task.BackupFile) { err = os.Remove(task.BackupFile) @@ -229,27 +240,26 @@ func (job *Job) CheckOldBinlogBackupStatus(port int) { } // 判断是否上传成功 - if task.BackupTaskID > 0 { - uploadTask := backupsys.UploadTask{ - Files: []string{task.BackupFile}, - TaskIDs: []uint64{task.BackupTaskID}, - } - runningTaskIDs, failedTaskIDs, _, _, _, _, failMsgs, job.Err = uploadTask.CheckTasksStatus() + if task.BackupTaskID != "" { + taskStatus, statusMsg, job.Err = task.backupClient.TaskStatus(task.BackupTaskID) if job.Err != nil { tempHandler.WriteString(line + "\n") // 获取tasks状态失败,下次重试 continue } - if len(failedTaskIDs) > 0 { + // taskStatus>4,上传失败; + // taskStatus==4,上传成功; + // taskStatus<4,上传中 + if taskStatus > 4 { if task.Status != consts.BackupStatusFailed { // 失败状态不重复上报 task.Status = consts.BackupStatusFailed - task.Message = fmt.Sprintf("上传失败,err:%s", strings.Join(failMsgs, ",")) + task.Message = fmt.Sprintf("上传失败,err:%s", statusMsg) task.BackupRecordReport() line = task.ToString() } tempHandler.WriteString(line + "\n") // 上传失败,下次继续重试 - } else if len(runningTaskIDs) > 0 { + } else if taskStatus < 4 { tempHandler.WriteString(line + "\n") // 上传中,下次继续探测 - } else { + } else if taskStatus == 4 { // 上传成功 task.Status = consts.BackupStatusToBakSysSuccess task.Message = "上传备份系统成功" @@ -322,7 +332,7 @@ func (job *Job) DeleteTooOldBinlogbackup(port int) { mylog.Logger.Warn(err.Error()) continue } - if nowTime.Sub(task.BackupFileMTime.Time).Seconds() > float64(oldFileLeftSec) { + if nowTime.Sub(task.EndTime.Time).Seconds() > float64(oldFileLeftSec) { if util.FileExists(task.BackupFile) { err = os.Remove(task.BackupFile) if err != nil { diff --git a/dbm-services/redis/db-tools/dbmon/pkg/redisbinlogbackup/task.go b/dbm-services/redis/db-tools/dbmon/pkg/redisbinlogbackup/task.go index 5a8bdd649a..936c90cc92 100644 --- a/dbm-services/redis/db-tools/dbmon/pkg/redisbinlogbackup/task.go +++ b/dbm-services/redis/db-tools/dbmon/pkg/redisbinlogbackup/task.go @@ -30,42 +30,44 @@ var tendisBinlogReg = regexp.MustCompile(`binlog-(\d+)-(\d+)-(\d+).log`) // Task redis binlog备份task type Task struct { - ReportType string `json:"report_type"` - BkBizID string `json:"bk_biz_id"` - BkCloudID int64 `json:"bk_cloud_id"` - ServerIP string `json:"server_ip"` - ServerPort int `json:"server_port"` - Domain string `json:"domain"` - Password string `json:"-"` - ToBackupSystem string `json:"-"` - OldFileLeftDay int `json:"-"` - DbType string `json:"db_type"` // TendisplusInstance or TendisSSDInstance - RealRole string `json:"role"` - DumpDir string `json:"-"` - KvStoreCount int `json:"-"` - BackupDir string `json:"backup_dir"` // 备份路径,如 /data/dbbak/binlog/30000 - BackupFile string `json:"backup_file"` // 备份的目标文件(已压缩) - KvstoreIdx int `json:"kvstoreidx"` // binlog对应的 kvstoreidx - BackupFileSize int64 `json:"backup_file_size"` // 备份文件大小(已压缩) - BackupFileStartTime customtime.CustomTime `json:"backup_file_start_time"` // binlog文件生成时间(非压缩) - BackupFileMTime customtime.CustomTime `json:"backup_file_mtime"` // binlog文件最后修改时间(非压缩) - BackupTaskID uint64 `json:"backup_taskid"` - BackupMD5 string `json:"backup_md5"` // 目前为空 - BackupTag string `json:"backup_tag"` // REDIS_BINLOG - Status string `json:"status"` - Message string `json:"message"` - Cli *myredis.RedisClient `json:"-"` - reporter report.Reporter - lockFile string `json:"-"` - Err error `json:"-"` + ReportType string `json:"report_type"` + BkBizID string `json:"bk_biz_id"` + BkCloudID int64 `json:"bk_cloud_id"` + ServerIP string `json:"server_ip"` + ServerPort int `json:"server_port"` + Domain string `json:"domain"` + Password string `json:"-"` + ToBackupSystem string `json:"-"` + OldFileLeftDay int `json:"-"` + DbType string `json:"db_type"` // TendisplusInstance or TendisSSDInstance + RealRole string `json:"role"` + DumpDir string `json:"-"` + KvStoreCount int `json:"-"` + BackupDir string `json:"backup_dir"` // 备份路径,如 /data/dbbak/binlog/30000 + BackupFile string `json:"backup_file"` // 备份的目标文件(已压缩) + KvstoreIdx int `json:"kvstoreidx"` // binlog对应的 kvstoreidx + BackupFileSize int64 `json:"backup_file_size"` // 备份文件大小(已压缩) + StartTime customtime.CustomTime `json:"start_time"` // binlog文件生成时间(非压缩) + EndTime customtime.CustomTime `json:"end_time"` // binlog文件最后修改时间(非压缩) + BackupTaskID string `json:"backup_taskid"` + BackupMD5 string `json:"backup_md5"` // 目前为空 + BackupTag string `json:"backup_tag"` // REDIS_BINLOG + ShardValue string `json:"shard_value"` // shard值 + Status string `json:"status"` + Message string `json:"message"` + Cli *myredis.RedisClient `json:"-"` + reporter report.Reporter + backupClient backupsys.BackupClient + lockFile string `json:"-"` + Err error `json:"-"` } // NewBinlogBackupTask new binlog backup task func NewBinlogBackupTask(bkBizID string, bkCloudID int64, domain, ip string, port int, - password, toBackupSys, backupDir string, oldFileLeftDay int, + password, toBackupSys, backupDir, shardValue string, oldFileLeftDay int, reporter report.Reporter) *Task { - return &Task{ + ret := &Task{ ReportType: consts.RedisBinlogBackupReportType, BkBizID: bkBizID, BkCloudID: bkCloudID, @@ -78,7 +80,11 @@ func NewBinlogBackupTask(bkBizID string, bkCloudID int64, domain, ip string, por BackupDir: backupDir, BackupTag: consts.RedisBinlogTAG, reporter: reporter, + ShardValue: shardValue, } + ret.backupClient = backupsys.NewIBSBackupClient(consts.IBSBackupClient, consts.RedisBinlogTAG) + // ret.backupClient, ret.Err = backupsys.NewCosBackupClient(consts.COSBackupClient, "", consts.RedisBinlogTAG) + return ret } // Addr string @@ -108,6 +114,11 @@ func (task *Task) BackupLocalBinlogs() { return } + task.reGetShardValWhenClusterEnabled() + if task.Err != nil { + return + } + // 获取文件锁 lockFile := fmt.Sprintf("lock.%s.%d", task.ServerIP, task.ServerPort) lockFile = filepath.Join(task.BackupDir, lockFile) @@ -152,8 +163,8 @@ func (task *Task) BackupLocalBinlogs() { } task.BackupFile = item.File task.KvstoreIdx = item.KvStoreIdx - task.BackupFileStartTime.Time = item.StartTime - task.BackupFileMTime.Time = item.FileMtime + task.StartTime.Time = item.StartTime + task.EndTime.Time = item.FileMtime task.compressAndUpload() // 无论成功还是失败,都继续下一个binlog file } } @@ -187,6 +198,23 @@ func (task *Task) newConnect() { return } +func (task *Task) reGetShardValWhenClusterEnabled() { + var enabled bool + var masterNode *myredis.ClusterNodeData + enabled, task.Err = task.Cli.IsClusterEnabled() + if task.Err != nil { + return + } + if !enabled { + return + } + masterNode, task.Err = task.Cli.RedisClusterGetMasterNode(task.Addr()) + if task.Err != nil { + return + } + task.ShardValue = masterNode.SlotSrcStr +} + type tendisBinlogItem struct { File string `json:"file"` // full path KvStoreIdx int `json:"kvstoreidx"` @@ -340,6 +368,7 @@ func (task *Task) compressAndUpload() { task.Message = task.Err.Error() return } + util.LocalFileChmodAllRead(task.BackupFile) fileInfo, _ := os.Stat(task.BackupFile) task.BackupFileSize = fileInfo.Size() } @@ -361,26 +390,21 @@ func (task *Task) compressAndUpload() { // TransferToBackupSystem 备份文件上传到备份系统 func (task *Task) TransferToBackupSystem() { var msg string - cliFileInfo, err := os.Stat(consts.BackupClient) + cliFileInfo, err := os.Stat(consts.IBSBackupClient) if err != nil { - err = fmt.Errorf("os.stat(%s) failed,err:%v", consts.BackupClient, err) + err = fmt.Errorf("os.stat(%s) failed,err:%v", consts.IBSBackupClient, err) mylog.Logger.Error(err.Error()) return } if !util.IsExecOther(cliFileInfo.Mode().Perm()) { - err = fmt.Errorf("%s is unable to execute by other", consts.BackupClient) + err = fmt.Errorf("%s is unable to execute by other", consts.IBSBackupClient) mylog.Logger.Error(err.Error()) return } - uploader := backupsys.UploadTask{ - Files: []string{task.BackupFile}, - Tag: task.BackupTag, - } - task.Err = uploader.UploadFiles() + task.BackupTaskID, task.Err = task.backupClient.Upload(task.BackupFile) if task.Err != nil { return } - task.BackupTaskID = uploader.TaskIDs[0] msg = fmt.Sprintf("redis(%s) backupFile:%s taskid(%+v) uploading to backupSystem", task.Addr(), task.BackupFile, task.BackupTaskID) mylog.Logger.Info(msg) diff --git a/dbm-services/redis/db-tools/dbmon/pkg/redisfullbackup/backupjob.go b/dbm-services/redis/db-tools/dbmon/pkg/redisfullbackup/backupjob.go new file mode 100644 index 0000000000..6667a5163d --- /dev/null +++ b/dbm-services/redis/db-tools/dbmon/pkg/redisfullbackup/backupjob.go @@ -0,0 +1,375 @@ +// Package redisfullbackup redis备份任务 +package redisfullbackup + +import ( + "bufio" + "encoding/json" + "fmt" + "os" + "path/filepath" + "time" + + "dbm-services/redis/db-tools/dbmon/config" + "dbm-services/redis/db-tools/dbmon/models/myredis" + "dbm-services/redis/db-tools/dbmon/mylog" + "dbm-services/redis/db-tools/dbmon/pkg/backupsys" + "dbm-services/redis/db-tools/dbmon/pkg/consts" + "dbm-services/redis/db-tools/dbmon/pkg/report" + "dbm-services/redis/db-tools/dbmon/util" + + "go.uber.org/zap" +) + +// GlobRedisFullBackupJob global var +var GlobRedisFullBackupJob *Job + +// Job 例行备份任务 +type Job struct { // NOCC:golint/naming(其他:设计如此) + Conf *config.Configuration `json:"conf"` + Tasks []*BackupTask `json:"tasks"` + RealBackupDir string `json:"real_backup_dir"` + Reporter report.Reporter `json:"-"` + backupClient backupsys.BackupClient + Err error `json:"-"` +} + +// InitGlobRedisFullBackupJob 新建例行备份任务 +func InitGlobRedisFullBackupJob(conf *config.Configuration) { + GlobRedisFullBackupJob = &Job{ + Conf: conf, + } +} + +// Run 执行例行备份 +func (job *Job) Run() { + mylog.Logger.Info("redisfullbackup wakeup,start running...", zap.String("conf", util.ToString(job.Conf))) + defer func() { + if job.Err != nil { + mylog.Logger.Info(fmt.Sprintf("redisfullbackup end fail,err:%v", job.Err)) + } else { + mylog.Logger.Info("redisfullbackup end succ") + } + }() + job.Err = nil + job.GetRealBackupDir() + if job.Err != nil { + return + } + job.GetReporter() + if job.Err != nil { + return + } + defer job.Reporter.Close() + + job.backupClient = backupsys.NewIBSBackupClient(consts.IBSBackupClient, consts.RedisFullBackupTAG) + // job.backupClient, job.Err = backupsys.NewCosBackupClient(consts.COSBackupClient, "", consts.RedisBinlogTAG) + // if job.Err != nil { + // return + // } + job.createTasks() + if job.Err != nil { + return + } + // 本地串行备份 + for _, task := range job.Tasks { + bakTask := task + bakTask.BakcupToLocal() + if bakTask.Err != nil { + job.Err = bakTask.Err + continue + } + } + + // 检查历史备份任务状态 并 删除过旧的本地文件 + for _, svrItem := range job.Conf.Servers { + if !consts.IsRedisMetaRole(svrItem.MetaRole) { + continue + } + for _, port := range svrItem.ServerPorts { + job.CheckOldFullbackupStatus(port) + job.DeleteTooOldFullbackup(port) + } + } +} + +// GetRealBackupDir 获取本地全备保存路径 +func (job *Job) GetRealBackupDir() { + job.RealBackupDir = consts.GetRedisBackupDir() + job.RealBackupDir = filepath.Join(job.RealBackupDir, "dbbak") + // /data/dbbak/backup 目录需要 + util.MkDirsIfNotExists([]string{ + filepath.Join(job.RealBackupDir, "backup"), + }) + util.LocalDirChownMysql(job.RealBackupDir) +} + +// GetReporter 上报者 +func (job *Job) GetReporter() { + reportDir := filepath.Join(job.Conf.ReportSaveDir, "redis") + util.MkDirsIfNotExists([]string{reportDir}) + util.LocalDirChownMysql(reportDir) + reportFile := fmt.Sprintf(consts.RedisFullbackupRepoter, time.Now().Local().Format(consts.FilenameDayLayout)) + job.Reporter, job.Err = report.NewFileReport(filepath.Join(reportDir, reportFile)) +} + +func (job *Job) createTasks() { + var task *BackupTask + var password string + var instStr string + + mylog.Logger.Info(fmt.Sprintf("start create fullback tasks,Servers:%s", util.ToString(job.Conf.Servers))) + job.Tasks = []*BackupTask{} + + for _, svrItem := range job.Conf.Servers { + if !consts.IsRedisMetaRole(svrItem.MetaRole) { + continue + } + for _, port := range svrItem.ServerPorts { + password, job.Err = myredis.GetRedisPasswdFromConfFile(port) + if job.Err != nil { + return + } + instStr = fmt.Sprintf("%s:%d", svrItem.ServerIP, port) + task = NewFullBackupTask(svrItem.BkBizID, svrItem.BkCloudID, + svrItem.ClusterDomain, svrItem.ServerIP, port, password, + job.Conf.RedisFullBackup.ToBackupSystem, + consts.NormalBackupType, svrItem.CacheBackupMode, job.RealBackupDir, + job.Conf.RedisFullBackup.TarSplit, job.Conf.RedisFullBackup.TarSplitPartSize, + svrItem.ServerShards[instStr], job.Reporter) + job.Tasks = append(job.Tasks, task) + } + } + mylog.Logger.Info(fmt.Sprintf("redisfullbackup createTasks tasks:%s", util.ToString(job.Tasks))) +} + +// CheckOldFullbackupStatus 检查历史全备任务状态 +// 1. 遍历 redis_backup_file_list_${port}_doing 文件 +// 2. 已超过时间的任务,删除本地文件,从 redis_backup_file_list_${port}_doing 中剔除 +// 3. 上传备份系统 运行中 or 失败的任务 记录到 redis_backup_file_list_${port}_doing_temp +// 4. 已成功的任务,记录到 redis_backup_file_list_${port}_done +// 5. rename redis_backup_file_list_${port}_doing_temp to redis_backup_file_list_${port}_doing +func (job *Job) CheckOldFullbackupStatus(port int) { + mylog.Logger.Info(fmt.Sprintf("port:%d start CheckOldFullbackupStatus", port)) + var doingHandler, tempHandler, doneHandler *os.File + var line string + task := BackupTask{} + var err error + var taskStatus int + var statusMsg string + oldFileLeftSec := job.Conf.RedisFullBackup.OldFileLeftDay * 24 * 3600 + nowTime := time.Now().Local() + // 示例: /data/dbbak/backup/redis_backup_file_list_30000_doing + doingFile := filepath.Join(job.RealBackupDir, "backup", fmt.Sprintf(consts.DoingRedisFullBackFileList, port)) + if !util.FileExists(doingFile) { + return + } + // 示例: /data/dbbak/backup/redis_backup_file_list_30000_doing_temp + tempDoingFile := doingFile + "_temp" + // 示例: /data/dbbak/backup/redis_backup_file_list_30000_done + doneFile := filepath.Join(job.RealBackupDir, "backup", fmt.Sprintf(consts.DoneRedisFullBackFileList, port)) + + defer func() { + if job.Err == nil { + mylog.Logger.Info(fmt.Sprintf("rename %s to %s", tempDoingFile, doingFile)) + os.Rename(tempDoingFile, doingFile) // rename + } + }() + + doingHandler, job.Err = os.Open(doingFile) + if job.Err != nil { + job.Err = fmt.Errorf("os.Open file:%s fail,err:%v", doingFile, job.Err) + mylog.Logger.Error(job.Err.Error()) + return + } + defer doingHandler.Close() + + tempHandler, job.Err = os.OpenFile(tempDoingFile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0744) + if job.Err != nil { + job.Err = fmt.Errorf("os.OpenFile %s failed,err:%v", tempDoingFile, job.Err) + mylog.Logger.Error(job.Err.Error()) + return + } + defer tempHandler.Close() + + doneHandler, job.Err = os.OpenFile(doneFile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0744) + if job.Err != nil { + job.Err = fmt.Errorf("os.OpenFile %s failed,err:%v", doneFile, job.Err) + mylog.Logger.Error(job.Err.Error()) + return + } + defer doneHandler.Close() + + scanner := bufio.NewScanner(doingHandler) + scanner.Split(bufio.ScanLines) + for scanner.Scan() { + line = scanner.Text() + err = json.Unmarshal([]byte(line), &task) + if err != nil { + // json.Unmarshal failed,skip ... + err = fmt.Errorf("json.Unmarshal fail,err:%s,data:%s,skip it", err, line) + mylog.Logger.Error(err.Error()) + continue + } + task.reporter = job.Reporter + task.backupClient = job.backupClient + // 删除旧文件 + if nowTime.Sub(task.EndTime.Time).Seconds() > float64(oldFileLeftSec) { + mylog.Logger.Info(fmt.Sprintf("%+v start removing...", task.BackupFile)) + removeOK := true + if util.FileExists(task.BackupFile) { + err = os.Remove(task.BackupFile) + if err != nil { + err = fmt.Errorf("os.Remove fail,err:%s,file:%s", err, task.BackupFile) + mylog.Logger.Error(err.Error()) + removeOK = false + } + } + if !removeOK { + mylog.Logger.Info(fmt.Sprintf("%+v remove fail,continue add tempFile", task.BackupFile)) + _, job.Err = tempHandler.WriteString(line + "\n") // 删除失败的,记录到temp文件,下次继续重试 + if job.Err != nil { + job.Err = fmt.Errorf("%s WriteString fail,err:%v", tempDoingFile, job.Err) + mylog.Logger.Error(job.Err.Error()) + return + } + } + continue + } + // 无需上传备份系统,本地已备份成功的情况 + if task.Status == consts.BackupStatusLocalSuccess { + _, job.Err = doneHandler.WriteString(line + "\n") + if job.Err != nil { + job.Err = fmt.Errorf("%s WriteString fail,err:%v", doneFile, job.Err) + mylog.Logger.Error(job.Err.Error()) + return + } + continue + } + // 上传备份系统失败的情况,重试上传并写入temp文件中 + if task.Status == consts.BackupStatusToBakSystemFailed { + task.TransferToBackupSystem() + if task.Err != nil { + task.Message = task.Err.Error() + } else { + task.Status = consts.BackupStatusToBakSystemStart + task.Message = "上传备份系统中" + } + tempHandler.WriteString(task.ToString() + "\n") + continue + } + // 判断是否上传成功 + if task.BackupTaskID != "" { + taskStatus, statusMsg, job.Err = task.backupClient.TaskStatus(task.BackupTaskID) + if job.Err != nil { + tempHandler.WriteString(line + "\n") // 获取tasks状态失败,下次重试 + continue + } + // taskStatus>4,上传失败; + // taskStatus==4,上传成功; + // taskStatus<4,上传中; + if taskStatus > 4 { + if task.Status != consts.BackupStatusFailed { // 失败状态不重复上报 + task.Status = consts.BackupStatusFailed + task.Message = fmt.Sprintf("上传失败,err:%s", statusMsg) + task.BackupRecordReport() + line = task.ToString() + } + tempHandler.WriteString(line + "\n") // 上传失败,下次继续重试 + } else if taskStatus < 4 { + tempHandler.WriteString(line + "\n") // 上传中,下次继续探测 + } else if taskStatus == 4 { + // 上传成功 + task.Status = consts.BackupStatusToBakSysSuccess + task.Message = "上传备份系统成功" + task.BackupRecordReport() + doneHandler.WriteString(task.ToString() + "\n") + } + } + // 其他失败的情况,写到done文件中 + if task.Status == consts.BackupStatusFailed { + doneHandler.WriteString(line + "\n") + continue + } + } + if job.Err = scanner.Err(); job.Err != nil { + job.Err = fmt.Errorf("scanner.Scan fail,err:%v,file:%v", job.Err, doingFile) + mylog.Logger.Error(job.Err.Error()) + return + } +} + +// DeleteTooOldFullbackup 根据 redis_backup_file_list_{port}_done 删除太旧的本地文件 +// 将删除失败 or 不到OldFileLeftDay天数的task继续回写到 redis_backup_file_list_{port}_done 文件中 +func (job *Job) DeleteTooOldFullbackup(port int) { + var doneHandler *os.File + task := BackupTask{} + var line string + var err error + keepTasks := []string{} + oldFileLeftSec := job.Conf.RedisFullBackup.OldFileLeftDay * 24 * 3600 + nowTime := time.Now().Local() + + mylog.Logger.Info(fmt.Sprintf("port:%d start DeleteTooOldFullbackup", port)) + // 示例: /data/dbbak/backup/redis_backup_file_list_30000_done + doneFile := filepath.Join(job.RealBackupDir, "backup", fmt.Sprintf(consts.DoneRedisFullBackFileList, port)) + if !util.FileExists(doneFile) { + return + } + + defer func() { + if len(keepTasks) > 0 { + // 回写到 doneFile中 + done02, err01 := os.OpenFile(doneFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755) + if err01 != nil { + job.Err = fmt.Errorf("os.Openfile fail,err:%v,file:%s", err01, doneFile) + mylog.Logger.Error(job.Err.Error()) + return + } + defer done02.Close() + for _, line := range keepTasks { + done02.WriteString(line + "\n") + } + } + }() + doneHandler, job.Err = os.Open(doneFile) + if job.Err != nil { + job.Err = fmt.Errorf("os.OpenFile %s failed,err:%v", doneFile, job.Err) + mylog.Logger.Error(job.Err.Error()) + return + } + defer doneHandler.Close() + + scanner := bufio.NewScanner(doneHandler) + scanner.Split(bufio.ScanLines) + for scanner.Scan() { + line = scanner.Text() + err = json.Unmarshal([]byte(line), &task) + if err != nil { + // json.Unmarshal failed,skip ... + err = fmt.Errorf("json.Unmarshal fail,err:%v,data:%s,file:%s", err, line, doneFile) + mylog.Logger.Warn(err.Error()) + continue + } + if nowTime.Sub(task.EndTime.Time).Seconds() > float64(oldFileLeftSec) { + removeOK := true + if util.FileExists(task.BackupFile) { + err = os.Remove(task.BackupFile) + if err != nil { + err = fmt.Errorf("os.Remove fail,err:%v,file:%s", err, task.BackupFile) + mylog.Logger.Warn(err.Error()) + removeOK = false + } + } + if !removeOK { + keepTasks = append(keepTasks, line) // 删除失败的,下次继续重试 + } + } else { + keepTasks = append(keepTasks, line) + } + } + if err = scanner.Err(); err != nil { + job.Err = fmt.Errorf("scanner.Scan fail,err:%v,file:%v", err, doneFile) + mylog.Logger.Error(job.Err.Error()) + return + } +} diff --git a/dbm-services/redis/db-tools/dbmon/pkg/redisfullbackup/checkjob.go b/dbm-services/redis/db-tools/dbmon/pkg/redisfullbackup/checkjob.go new file mode 100644 index 0000000000..82dc8c1608 --- /dev/null +++ b/dbm-services/redis/db-tools/dbmon/pkg/redisfullbackup/checkjob.go @@ -0,0 +1,71 @@ +// Package redisfullbackup redis备份任务 +package redisfullbackup + +import ( + "fmt" + + "dbm-services/redis/db-tools/dbmon/config" + "dbm-services/redis/db-tools/dbmon/mylog" + "dbm-services/redis/db-tools/dbmon/pkg/backupsys" + "dbm-services/redis/db-tools/dbmon/pkg/consts" + "dbm-services/redis/db-tools/dbmon/util" + + "go.uber.org/zap" +) + +// GlobRedisFullCheckJob global var +var GlobRedisFullCheckJob *CheckJob + +// CheckJob TODO +// Job 例行备份任务 +type CheckJob struct { // NOCC:golint/naming(其他:设计如此) + Job +} + +// InitGlobRedisFullCheckJob 新建例行备份任务 +func InitGlobRedisFullCheckJob(conf *config.Configuration) { + GlobRedisFullCheckJob = &CheckJob{ + Job: Job{ + Conf: conf, + }, + } +} + +// Run 执行例行备份 +func (job *CheckJob) Run() { + mylog.Logger.Info("redisfullbackup wakeup,start running...", zap.String("conf", util.ToString(job.Conf))) + defer func() { + if job.Err != nil { + mylog.Logger.Info(fmt.Sprintf("redisfullbackup end fail,err:%v", job.Err)) + } else { + mylog.Logger.Info("redisfullbackup end succ") + } + }() + job.Err = nil + job.GetRealBackupDir() + if job.Err != nil { + return + } + job.GetReporter() + if job.Err != nil { + return + } + defer job.Reporter.Close() + + job.backupClient = backupsys.NewIBSBackupClient(consts.IBSBackupClient, consts.RedisFullBackupTAG) + // job.backupClient, job.Err = backupsys.NewCosBackupClient(consts.COSBackupClient, "", consts.RedisBinlogTAG) + // if job.Err != nil { + // return + // } + + // 检查历史备份任务状态 并 删除过旧的本地文件 + for _, svrItem := range job.Conf.Servers { + if !consts.IsRedisMetaRole(svrItem.MetaRole) { + continue + } + for _, port := range svrItem.ServerPorts { + job.CheckOldFullbackupStatus(port) + job.DeleteTooOldFullbackup(port) + } + } +} diff --git a/dbm-services/redis/db-tools/dbmon/pkg/redisfullbackup/job.go b/dbm-services/redis/db-tools/dbmon/pkg/redisfullbackup/job.go deleted file mode 100644 index 9cd0d4c91c..0000000000 --- a/dbm-services/redis/db-tools/dbmon/pkg/redisfullbackup/job.go +++ /dev/null @@ -1,386 +0,0 @@ -// Package redisfullbackup redis备份任务 -package redisfullbackup - -import ( - "bufio" - "encoding/json" - "fmt" - "os" - "path/filepath" - "strings" - "time" - - "dbm-services/redis/db-tools/dbmon/config" - "dbm-services/redis/db-tools/dbmon/models/myredis" - "dbm-services/redis/db-tools/dbmon/mylog" - "dbm-services/redis/db-tools/dbmon/pkg/backupsys" - "dbm-services/redis/db-tools/dbmon/pkg/consts" - "dbm-services/redis/db-tools/dbmon/pkg/report" - "dbm-services/redis/db-tools/dbmon/util" - - "go.uber.org/zap" -) - -// GlobRedisFullBakJob global var -var GlobRedisFullBakJob *Job - -// Job 例行备份任务 -type Job struct { // NOCC:golint/naming(其他:设计如此) - Conf *config.Configuration `json:"conf"` - Tasks []*BackupTask `json:"tasks"` - RealBackupDir string `json:"real_backup_dir"` - Reporter report.Reporter `json:"-"` - Err error `json:"-"` -} - -// InitGlobRedisFullBackupJob 新建例行备份任务 -func InitGlobRedisFullBackupJob(conf *config.Configuration) { - GlobRedisFullBakJob = &Job{ - Conf: conf, - } -} - -// Run 执行例行备份 -func (job *Job) Run() { - mylog.Logger.Info("redisfullbackup wakeup,start running...", zap.String("conf", util.ToString(job.Conf))) - defer func() { - if job.Err != nil { - mylog.Logger.Info(fmt.Sprintf("redisfullbackup end fail,err:%v", job.Err)) - } else { - mylog.Logger.Info("redisfullbackup end succ") - } - }() - job.Err = nil - job.GetRealBackupDir() - if job.Err != nil { - return - } - job.GetReporter() - if job.Err != nil { - return - } - defer job.Reporter.Close() - - // 检查历史备份任务状态 并 删除过旧的本地文件 - for _, svrItem := range job.Conf.Servers { - if !consts.IsRedisMetaRole(svrItem.MetaRole) { - continue - } - for _, port := range svrItem.ServerPorts { - job.CheckOldFullbackupStatus(port) - job.DeleteTooOldFullbackup(port) - } - } - job.createTasks() - if job.Err != nil { - return - } - // 本地串行备份 - for _, task := range job.Tasks { - bakTask := task - bakTask.BakcupToLocal() - if bakTask.Err != nil { - job.Err = bakTask.Err - continue - } - } -} - -// GetRealBackupDir 获取本地全备保存路径 -func (job *Job) GetRealBackupDir() { - job.RealBackupDir = consts.GetRedisBackupDir() - job.RealBackupDir = filepath.Join(job.RealBackupDir, "dbbak") - // /data/dbbak/backup 目录需要 - util.MkDirsIfNotExists([]string{ - filepath.Join(job.RealBackupDir, "backup"), - }) - util.LocalDirChownMysql(job.RealBackupDir) -} - -// GetReporter 上报者 -func (job *Job) GetReporter() { - reportDir := filepath.Join(job.Conf.ReportSaveDir, "redis") - util.MkDirsIfNotExists([]string{reportDir}) - util.LocalDirChownMysql(reportDir) - reportFile := fmt.Sprintf(consts.RedisFullbackupRepoter, time.Now().Local().Format(consts.FilenameDayLayout)) - job.Reporter, job.Err = report.NewFileReport(filepath.Join(reportDir, reportFile)) -} - -func (job *Job) createTasks() { - var task *BackupTask - var password string - - for _, svrItem := range job.Conf.Servers { - if !consts.IsRedisMetaRole(svrItem.MetaRole) { - continue - } - for _, port := range svrItem.ServerPorts { - password, job.Err = myredis.GetRedisPasswdFromConfFile(port) - if job.Err != nil { - return - } - task = NewFullBackupTask(svrItem.BkBizID, svrItem.BkCloudID, - svrItem.ClusterDomain, svrItem.ServerIP, port, password, - job.Conf.RedisFullBackup.ToBackupSystem, consts.NormalBackupType, job.RealBackupDir, - job.Conf.RedisFullBackup.TarSplit, job.Conf.RedisFullBackup.TarSplitPartSize, - job.Reporter) - job.Tasks = append(job.Tasks, task) - } - } - mylog.Logger.Info(fmt.Sprintf("redisfullbackup createTasks tasks:%s", util.ToString(job.Tasks))) -} - -// CheckOldFullbackupStatus 检查历史全备任务状态 -// 1. 遍历 redis_backup_file_list_${port}_doing 文件 -// 2. 已超过时间的任务,删除本地文件,从 redis_backup_file_list_${port}_doing 中剔除 -// 3. 上传备份系统 运行中 or 失败的任务 记录到 redis_backup_file_list_${port}_doing_temp -// 4. 已成功的任务,记录到 redis_backup_file_list_${port}_done -// 5. rename redis_backup_file_list_${port}_doing_temp to redis_backup_file_list_${port}_doing -func (job *Job) CheckOldFullbackupStatus(port int) { - mylog.Logger.Info(fmt.Sprintf("port:%d start CheckOldFullbackupStatus", port)) - var doingHandler, tempHandler, doneHandler *os.File - var line string - task := BackupTask{} - var err error - var failMsgs []string - var runningTaskIDs, failedTaskIDs []uint64 - oldFileLeftSec := job.Conf.RedisFullBackup.OldFileLeftDay * 24 * 3600 - nowTime := time.Now().Local() - // 示例: /data/dbbak/backup/redis_backup_file_list_30000_doing - doingFile := filepath.Join(job.RealBackupDir, "backup", fmt.Sprintf(consts.DoingRedisFullBackFileList, port)) - if !util.FileExists(doingFile) { - return - } - // 示例: /data/dbbak/backup/redis_backup_file_list_30000_doing_temp - tempDoingFile := doingFile + "_temp" - // 示例: /data/dbbak/backup/redis_backup_file_list_30000_done - doneFile := filepath.Join(job.RealBackupDir, "backup", fmt.Sprintf(consts.DoneRedisFullBackFileList, port)) - - defer func() { - if job.Err == nil { - mylog.Logger.Info(fmt.Sprintf("rename %s to %s", tempDoingFile, doingFile)) - os.Rename(tempDoingFile, doingFile) // rename - } - }() - - doingHandler, job.Err = os.Open(doingFile) - if job.Err != nil { - job.Err = fmt.Errorf("os.Open file:%s fail,err:%v", doingFile, job.Err) - mylog.Logger.Error(job.Err.Error()) - return - } - defer doingHandler.Close() - - tempHandler, job.Err = os.OpenFile(tempDoingFile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0744) - if job.Err != nil { - job.Err = fmt.Errorf("os.OpenFile %s failed,err:%v", tempDoingFile, job.Err) - mylog.Logger.Error(job.Err.Error()) - return - } - defer tempHandler.Close() - - doneHandler, job.Err = os.OpenFile(doneFile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0744) - if job.Err != nil { - job.Err = fmt.Errorf("os.OpenFile %s failed,err:%v", doneFile, job.Err) - mylog.Logger.Error(job.Err.Error()) - return - } - defer doneHandler.Close() - - scanner := bufio.NewScanner(doingHandler) - scanner.Split(bufio.ScanLines) - for scanner.Scan() { - line = scanner.Text() - err = json.Unmarshal([]byte(line), &task) - if err != nil { - // json.Unmarshal failed,skip ... - err = fmt.Errorf("json.Unmarshal fail,err:%s,data:%s,skip it", err, line) - mylog.Logger.Error(err.Error()) - continue - } - task.reporter = job.Reporter - // 删除旧文件 - if nowTime.Sub(task.EndTime.Time).Seconds() > float64(oldFileLeftSec) { - mylog.Logger.Info(fmt.Sprintf("%+v start removing...", task.BackupFiles)) - removeOK := true - for _, bakFile := range task.BackupFiles { - if util.FileExists(bakFile) { - err = os.Remove(bakFile) - if err != nil { - err = fmt.Errorf("os.Remove fail,err:%s,file:%s", err, bakFile) - mylog.Logger.Error(err.Error()) - removeOK = false - } - } - } - if !removeOK { - mylog.Logger.Info(fmt.Sprintf("%+v remove fail,continue add tempFile", task.BackupFiles)) - _, job.Err = tempHandler.WriteString(line + "\n") // 删除失败的,记录到temp文件,下次继续重试 - if job.Err != nil { - job.Err = fmt.Errorf("%s WriteString fail,err:%v", tempDoingFile, job.Err) - mylog.Logger.Error(job.Err.Error()) - return - } - } - continue - } - // 无需上传备份系统,本地已备份成功的情况 - if task.Status == consts.BackupStatusLocalSuccess { - _, job.Err = doneHandler.WriteString(line + "\n") - if job.Err != nil { - job.Err = fmt.Errorf("%s WriteString fail,err:%v", doneFile, job.Err) - mylog.Logger.Error(job.Err.Error()) - return - } - continue - } - // 上传备份系统失败的情况,重试上传并写入temp文件中 - if task.Status == consts.BackupStatusToBakSystemFailed { - task.TransferToBackupSystem() - if task.Err != nil { - task.Message = task.Err.Error() - } else { - task.Status = consts.BackupStatusToBakSystemStart - task.Message = "上传备份系统中" - } - tempHandler.WriteString(task.ToString() + "\n") - continue - } - // 判断是否上传成功 - if len(task.BackupTaskIDs) > 0 { - uploadTask := backupsys.UploadTask{ - Files: task.BackupFiles, - TaskIDs: task.BackupTaskIDs, - } - runningTaskIDs, failedTaskIDs, _, _, _, _, failMsgs, job.Err = uploadTask.CheckTasksStatus() - if job.Err != nil { - _, job.Err = tempHandler.WriteString(line + "\n") // 获取tasks状态失败,下次重试 - if job.Err != nil { - job.Err = fmt.Errorf("%s WriteString fail,err:%v", tempDoingFile, job.Err) - mylog.Logger.Error(job.Err.Error()) - return - } - continue - } - if len(failedTaskIDs) > 0 { - if task.Status != consts.BackupStatusFailed { // 失败状态不重复上报 - task.Status = consts.BackupStatusFailed - task.Message = fmt.Sprintf("上传失败,err:%s", strings.Join(failMsgs, ",")) - task.BackupRecordReport() - line = task.ToString() - } - _, job.Err = tempHandler.WriteString(line + "\n") // 上传失败,下次继续重试 - if job.Err != nil { - job.Err = fmt.Errorf("%s WriteString fail,err:%v", tempDoingFile, job.Err) - mylog.Logger.Error(job.Err.Error()) - return - } - } else if len(runningTaskIDs) > 0 { - _, job.Err = tempHandler.WriteString(line + "\n") // 上传中,下次继续探测 - if job.Err != nil { - job.Err = fmt.Errorf("%s WriteString fail,err:%v", tempDoingFile, job.Err) - mylog.Logger.Error(job.Err.Error()) - return - } - } else { - // 上传成功 - task.Status = consts.BackupStatusToBakSysSuccess - task.Message = "上传备份系统成功" - task.BackupRecordReport() - _, job.Err = doneHandler.WriteString(task.ToString() + "\n") - if job.Err != nil { - job.Err = fmt.Errorf("%s WriteString fail,err:%v", doneFile, job.Err) - mylog.Logger.Error(job.Err.Error()) - return - } - } - } - // 其他失败的情况,写到done文件中 - if task.Status == consts.BackupStatusFailed { - doneHandler.WriteString(line + "\n") - continue - } - } - if job.Err = scanner.Err(); job.Err != nil { - job.Err = fmt.Errorf("scanner.Scan fail,err:%v,file:%v", job.Err, doingFile) - mylog.Logger.Error(job.Err.Error()) - return - } -} - -// DeleteTooOldFullbackup 根据 redis_backup_file_list_{port}_done 删除太旧的本地文件 -// 将删除失败 or 不到OldFileLeftDay天数的task继续回写到 redis_backup_file_list_{port}_done 文件中 -func (job *Job) DeleteTooOldFullbackup(port int) { - var doneHandler *os.File - task := BackupTask{} - var line string - var err error - keepTasks := []string{} - oldFileLeftSec := job.Conf.RedisFullBackup.OldFileLeftDay * 24 * 3600 - nowTime := time.Now().Local() - - // 示例: /data/dbbak/backup/redis_backup_file_list_30000_done - doneFile := filepath.Join(job.RealBackupDir, "backup", fmt.Sprintf(consts.DoneRedisFullBackFileList, port)) - if !util.FileExists(doneFile) { - return - } - - defer func() { - if len(keepTasks) > 0 { - // 回写到 doneFile中 - done02, err01 := os.OpenFile(doneFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755) - if err01 != nil { - job.Err = fmt.Errorf("os.Openfile fail,err:%v,file:%s", err01, doneFile) - mylog.Logger.Error(job.Err.Error()) - return - } - defer done02.Close() - for _, line := range keepTasks { - done02.WriteString(line + "\n") - } - } - }() - doneHandler, job.Err = os.Open(doneFile) - if job.Err != nil { - job.Err = fmt.Errorf("os.OpenFile %s failed,err:%v", doneFile, job.Err) - mylog.Logger.Error(job.Err.Error()) - return - } - defer doneHandler.Close() - - scanner := bufio.NewScanner(doneHandler) - scanner.Split(bufio.ScanLines) - for scanner.Scan() { - line = scanner.Text() - err = json.Unmarshal([]byte(line), &task) - if err != nil { - // json.Unmarshal failed,skip ... - err = fmt.Errorf("json.Unmarshal fail,err:%v,data:%s,file:%s", err, line, doneFile) - mylog.Logger.Warn(err.Error()) - continue - } - if nowTime.Sub(task.EndTime.Time).Seconds() > float64(oldFileLeftSec) { - removeOK := true - for _, bakFile := range task.BackupFiles { - if util.FileExists(bakFile) { - err = os.Remove(bakFile) - if err != nil { - err = fmt.Errorf("os.Remove fail,err:%v,file:%s", err, bakFile) - mylog.Logger.Warn(err.Error()) - removeOK = false - } - } - } - if !removeOK { - keepTasks = append(keepTasks, line) // 删除失败的,下次继续重试 - } - } else { - keepTasks = append(keepTasks, line) - } - } - if err = scanner.Err(); err != nil { - job.Err = fmt.Errorf("scanner.Scan fail,err:%v,file:%v", err, doneFile) - mylog.Logger.Error(job.Err.Error()) - return - } -} diff --git a/dbm-services/redis/db-tools/dbmon/pkg/redisfullbackup/task.go b/dbm-services/redis/db-tools/dbmon/pkg/redisfullbackup/task.go index 8c2870bb67..51c84b39a3 100644 --- a/dbm-services/redis/db-tools/dbmon/pkg/redisfullbackup/task.go +++ b/dbm-services/redis/db-tools/dbmon/pkg/redisfullbackup/task.go @@ -29,44 +29,45 @@ type TendisSSDSetLogCount struct { // BackupTask redis备份task type BackupTask struct { - ReportType string `json:"report_type"` - BkBizID string `json:"bk_biz_id"` - BkCloudID int64 `json:"bk_cloud_id"` - ServerIP string `json:"server_ip"` - ServerPort int `json:"server_port"` - Domain string `json:"domain"` - Password string `json:"-"` - ToBackupSystem string `json:"-"` - DbType string `json:"db_type"` // RedisInstance or TendisplusInstance or TendisSSDInstance - BackupType string `json:"-"` // 常规备份、下线备份 - RealRole string `json:"role"` - DataSize uint64 `json:"-"` // redis实例数据大小 - DataDir string `json:"-"` - BackupDir string `json:"backup_dir"` - TarSplit bool `json:"-"` // 是否对tar文件做split - TarSplitPartSize string `json:"-"` - BackupFiles []string `json:"backup_files"` // 备份的目标文件,如果文件过大会切割成多个 - BackupFilesSize []int64 `json:"backup_files_size"` // 备份文件大小(已切割 or 已压缩 or 已打包) - BackupTaskIDs []uint64 `json:"backup_taskids"` - BackupMD5s []string `json:"backup_md5s"` // 目前为空 - BackupTag string `json:"backup_tag"` // REDIS_FULL or REDIS_BINLOG - // 全备尽管会切成多个文件,但其生成的起始时间、结束时间一样 - StartTime customtime.CustomTime `json:"start_time"` // 生成全备的起始时间 - EndTime customtime.CustomTime `json:"end_time"` // //生成全备的结束时间 - Status string `json:"status"` - Message string `json:"message"` - Cli *myredis.RedisClient `json:"-"` - SSDLogCount TendisSSDSetLogCount `json:"-"` - reporter report.Reporter - lockFile string `json:"-"` - Err error `json:"-"` + ReportType string `json:"report_type"` + BkBizID string `json:"bk_biz_id"` + BkCloudID int64 `json:"bk_cloud_id"` + ServerIP string `json:"server_ip"` + ServerPort int `json:"server_port"` + Domain string `json:"domain"` + Password string `json:"-"` + ToBackupSystem string `json:"-"` + DbType string `json:"db_type"` // RedisInstance or TendisplusInstance or TendisSSDInstance + BackupType string `json:"-"` // 常规备份、下线备份 + CacheBackupMode string `json:"-"` // aof or rdb + RealRole string `json:"role"` + DataSize uint64 `json:"-"` // redis实例数据大小 + DataDir string `json:"-"` + BackupDir string `json:"backup_dir"` + TarSplit bool `json:"-"` // 是否对tar文件做split + TarSplitPartSize string `json:"-"` + BackupFile string `json:"backup_file"` // 备份的目标文件,如果文件过大会切割成多个 + BackupFileSize int64 `json:"backup_file_size"` // 备份文件大小(已切割 or 已压缩 or 已打包) + BackupTaskID string `json:"backup_taskid"` + BackupMD5 string `json:"backup_md5"` // 目前为空 + BackupTag string `json:"backup_tag"` // REDIS_FULL + ShardValue string `json:"shard_value"` // shard值 + StartTime customtime.CustomTime `json:"start_time"` // 生成全备的起始时间 + EndTime customtime.CustomTime `json:"end_time"` // //生成全备的结束时间 + Status string `json:"status"` + Message string `json:"message"` + Cli *myredis.RedisClient `json:"-"` + SSDLogCount TendisSSDSetLogCount `json:"-"` + reporter report.Reporter + backupClient backupsys.BackupClient + Err error `json:"-"` } // NewFullBackupTask new backup task func NewFullBackupTask(bkBizID string, bkCloudID int64, domain, ip string, port int, password, - toBackupSys, backupType, backupDir string, tarSplit bool, tarSplitSize string, + toBackupSys, backupType, cacheBackupMode, backupDir string, tarSplit bool, tarSplitSize, shardValue string, reporter report.Reporter) *BackupTask { - return &BackupTask{ + ret := &BackupTask{ ReportType: consts.RedisFullBackupReportType, BkBizID: bkBizID, BkCloudID: bkCloudID, @@ -76,14 +77,22 @@ func NewFullBackupTask(bkBizID string, bkCloudID int64, domain, ip string, port Password: password, ToBackupSystem: toBackupSys, BackupType: backupType, + CacheBackupMode: cacheBackupMode, BackupDir: backupDir, TarSplit: tarSplit, TarSplitPartSize: tarSplitSize, - BackupTaskIDs: []uint64{}, - BackupMD5s: []string{}, + BackupTaskID: "", + BackupMD5: "", BackupTag: consts.RedisFullBackupTAG, + ShardValue: shardValue, reporter: reporter, } + ret.backupClient = backupsys.NewIBSBackupClient(consts.IBSBackupClient, consts.RedisFullBackupTAG) + // ret.backupClient, ret.Err = backupsys.NewCosBackupClient(consts.COSBackupClient, "", consts.RedisBinlogTAG) + // if ret.Err != nil { + // return ret + // } + return ret } // Addr string @@ -99,7 +108,6 @@ func (task *BackupTask) ToString() string { // BakcupToLocal 执行备份task,备份到本地 func (task *BackupTask) BakcupToLocal() { - var infoRet map[string]string var connSlaves int var locked bool task.newConnect() @@ -108,13 +116,14 @@ func (task *BackupTask) BakcupToLocal() { } defer task.Cli.Close() - infoRet, task.Err = task.Cli.Info("replication") - if task.Err != nil { - return - } - connSlaves, _ = strconv.Atoi(infoRet["connectedSlaves"]) + connSlaves, task.Err = task.Cli.ConnectedSlaves() // 如果是redis_master且对应的slave大于0,则跳过备份 if task.RealRole == consts.RedisMasterRole && connSlaves > 0 { + mylog.Logger.Info(fmt.Sprintf("redis(%s) is master and has slaves,skip backup", task.Addr())) + return + } + task.reGetShardValWhenClusterEnabled() + if task.Err != nil { return } @@ -266,6 +275,23 @@ func (task *BackupTask) PrecheckDisk() { task.Addr(), task.DataSize/1024/1024, task.BackupDir, bakDiskUsg.AvailSize/1024/1024)) } +func (task *BackupTask) reGetShardValWhenClusterEnabled() { + var enabled bool + var masterNode *myredis.ClusterNodeData + enabled, task.Err = task.Cli.IsClusterEnabled() + if task.Err != nil { + return + } + if !enabled { + return + } + masterNode, task.Err = task.Cli.RedisClusterGetMasterNode(task.Addr()) + if task.Err != nil { + return + } + task.ShardValue = masterNode.SlotSrcStr +} + // RedisInstanceBackup redis(cache)实例备份 func (task *BackupTask) RedisInstanceBackup() { var srcFile string @@ -274,7 +300,8 @@ func (task *BackupTask) RedisInstanceBackup() { var fileSize int64 nowtime := time.Now().Local().Format(consts.FilenameTimeLayout) task.StartTime.Time = time.Now().Local() - if task.RealRole == consts.RedisMasterRole { + if task.RealRole == consts.RedisMasterRole || + task.CacheBackupMode == consts.CacheBackupModeRdb { // redis master backup rdb confMap, task.Err = task.Cli.ConfigGet("dbfilename") if task.Err != nil { @@ -310,14 +337,14 @@ func (task *BackupTask) RedisInstanceBackup() { return } } - // task.BackupFiles = append(task.BackupFiles, filepath.Base(targetFile)) - task.BackupFiles = append(task.BackupFiles, targetFile) + util.LocalFileChmodAllRead(targetFile) + task.BackupFile = targetFile fileSize, task.Err = util.GetFileSize(targetFile) if task.Err != nil { mylog.Logger.Error(task.Err.Error()) return } - task.BackupFilesSize = append(task.BackupFilesSize, fileSize) + task.BackupFileSize = fileSize util.LocalDirChownMysql(task.BackupDir) mylog.Logger.Info(fmt.Sprintf("redis(%s) local backup success", task.Addr())) return @@ -341,12 +368,8 @@ func (task *BackupTask) TendisplusInstanceBackup() { return } task.EndTime.Time = time.Now().Local() - if task.TarSplit && task.TarSplitPartSize != "" { - task.BackupFiles, task.Err = util.TarAndSplitADir(backupFullDir, task.BackupDir, task.TarSplitPartSize, true) - } else { - tarFile, task.Err = util.TarADir(backupFullDir, task.BackupDir, true) - task.BackupFiles = append(task.BackupFiles, tarFile) - } + tarFile, task.Err = util.TarADir(backupFullDir, task.BackupDir, true) + task.BackupFile = tarFile if task.Err != nil { mylog.Logger.Error(task.Err.Error()) return @@ -355,6 +378,7 @@ func (task *BackupTask) TendisplusInstanceBackup() { if task.Err != nil { return } + util.LocalFileChmodAllRead(task.BackupFile) util.LocalDirChownMysql(task.BackupDir) mylog.Logger.Info(fmt.Sprintf("tendisplus(%s) local backup success", task.Addr())) return @@ -419,20 +443,17 @@ func (task *BackupTask) TendisSSDInstanceBackup() { backupFullDir = fileWithBinlogPos // 只做打包,不做压缩,rocksdb中已经做了压缩 - if task.TarSplit && task.TarSplitPartSize != "" { - task.BackupFiles, task.Err = util.TarAndSplitADir(backupFullDir, task.BackupDir, task.TarSplitPartSize, true) - } else { - tarFile, task.Err = util.TarADir(backupFullDir, task.BackupDir, true) - task.BackupFiles = append(task.BackupFiles, filepath.Join(task.BackupDir, tarFile)) - } + tarFile, task.Err = util.TarADir(backupFullDir, task.BackupDir, true) if task.Err != nil { mylog.Logger.Error(task.Err.Error()) return } + task.BackupFile = tarFile task.GetBakFilesSize() if task.Err != nil { return } + util.LocalFileChmodAllRead(task.BackupFile) util.LocalDirChownMysql(task.BackupDir) mylog.Logger.Info(fmt.Sprintf("tendisSSD(%s) local backup success", task.Addr())) return @@ -441,15 +462,12 @@ func (task *BackupTask) TendisSSDInstanceBackup() { // GetBakFilesSize 获取备份文件大小 func (task *BackupTask) GetBakFilesSize() { var fileSize int64 - task.BackupFilesSize = make([]int64, 0, len(task.BackupFiles)) - for _, bakFile := range task.BackupFiles { - fileSize, task.Err = util.GetFileSize(bakFile) - if task.Err != nil { - mylog.Logger.Error(task.Err.Error()) - return - } - task.BackupFilesSize = append(task.BackupFilesSize, fileSize) + fileSize, task.Err = util.GetFileSize(task.BackupFile) + if task.Err != nil { + mylog.Logger.Error(task.Err.Error()) + return } + task.BackupFileSize = fileSize } // TendisSSDSetLougCount tendisSSD设置log-count参数 @@ -471,37 +489,24 @@ func (task *BackupTask) TendisSSDSetLougCount() { // TransferToBackupSystem 备份文件上传到备份系统 func (task *BackupTask) TransferToBackupSystem() { var msg string - cliFileInfo, err := os.Stat(consts.BackupClient) + cliFileInfo, err := os.Stat(consts.IBSBackupClient) if err != nil { - err = fmt.Errorf("os.stat(%s) failed,err:%v", consts.BackupClient, err) + err = fmt.Errorf("os.stat(%s) failed,err:%v", consts.IBSBackupClient, err) mylog.Logger.Error(err.Error()) return } if !util.IsExecOther(cliFileInfo.Mode().Perm()) { - err = fmt.Errorf("%s is unable to execute by other", consts.BackupClient) + err = fmt.Errorf("%s is unable to execute by other", consts.IBSBackupClient) mylog.Logger.Error(err.Error()) return } - mylog.Logger.Info(fmt.Sprintf("redis(%s) backupFiles:%+v start upload backupSystem", task.Addr(), task.BackupFiles)) - bkTag := consts.RedisFullBackupTAG - if task.BackupType == consts.ForeverBackupType { - bkTag = consts.RedisForeverBackupTAG - } - uploader := backupsys.UploadTask{ - Files: task.BackupFiles, - Tag: bkTag, - } - task.Err = uploader.UploadFiles() + mylog.Logger.Info(fmt.Sprintf("redis(%s) backupFiles:%+v start upload backupSystem", task.Addr(), task.BackupFile)) + task.BackupTaskID, task.Err = task.backupClient.Upload(task.BackupFile) if task.Err != nil { return } - task.BackupTaskIDs = uploader.TaskIDs - // task.Err = uploader.WaitForUploadFinish() - // if task.Err != nil { - // return - // } msg = fmt.Sprintf("redis(%s) backupFiles%+v taskid(%+v) uploading to backupSystem", - task.Addr(), task.BackupFiles, task.BackupTaskIDs) + task.Addr(), task.BackupFile, task.BackupTaskID) mylog.Logger.Info(msg) return } @@ -518,13 +523,7 @@ func (task *BackupTask) BackupRecordReport() { // BackupRecordSaveToDoingFile 备份记录保存到本地 redis_backup_file_list_${port}_doing 文件中 func (task *BackupTask) BackupRecordSaveToDoingFile() { - var backupDir string - if len(task.BackupFiles) == 0 { - mylog.Logger.Warn(fmt.Sprintf("redis(%s) backupFiles:%+v empty", task.Addr(), task.BackupFiles)) - backupDir = task.BackupDir - } else { - backupDir = filepath.Dir(task.BackupFiles[0]) - } + backupDir := filepath.Dir(task.BackupFile) // 例如: /data/dbbak/backup/redis_backup_file_list_30000_doing doingFile := filepath.Join(backupDir, "backup", fmt.Sprintf(consts.DoingRedisFullBackFileList, task.ServerPort)) f, err := os.OpenFile(doingFile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0744) diff --git a/dbm-services/redis/db-tools/dbmon/pkg/redismonitor/redis_task.go b/dbm-services/redis/db-tools/dbmon/pkg/redismonitor/redis_task.go index 821de3bff8..7fbb600dee 100644 --- a/dbm-services/redis/db-tools/dbmon/pkg/redismonitor/redis_task.go +++ b/dbm-services/redis/db-tools/dbmon/pkg/redismonitor/redis_task.go @@ -304,6 +304,17 @@ func (task *RedisMonitorTask) CheckPersist() { continue } appendonly, _ = confmap["appendonly"] + if task.ServerConf.CacheBackupMode == consts.CacheBackupModeRdb { + if strings.ToLower(appendonly) == "yes" { + // 如果集群是rdb备份,但是aof开启,则关闭aof + _, task.Err = cliItem.ConfigSet("appendonly", "yes") + if task.Err != nil { + continue + } + cliItem.ConfigRewrite() + } + return + } if strings.ToLower(appendonly) == "no" { msg = fmt.Sprintf("redis_slave(%s) appendonly==%s", cliItem.Addr, appendonly) mylog.Logger.Warn(msg) diff --git a/dbm-services/redis/db-tools/dbmon/util/util.go b/dbm-services/redis/db-tools/dbmon/util/util.go index 2cf76bb145..d2a3a756af 100644 --- a/dbm-services/redis/db-tools/dbmon/util/util.go +++ b/dbm-services/redis/db-tools/dbmon/util/util.go @@ -202,11 +202,21 @@ func IsExecAll(mode os.FileMode) bool { // LocalDirChownMysql 改变localDir的属主为mysql func LocalDirChownMysql(localDir string) (err error) { + if !strings.HasSuffix(localDir, "/") { + localDir += string(filepath.Separator) + } cmd := fmt.Sprintf("chown -R %s.%s %s", consts.MysqlAaccount, consts.MysqlGroup, localDir) _, err = RunBashCmd(cmd, "", nil, 1*time.Hour) return } +// LocalFileChmodAllRead 改变localFile的权限为所有人可读 +func LocalFileChmodAllRead(localFile string) (err error) { + cmd := fmt.Sprintf("chmod a+r %s", localFile) + _, err = RunBashCmd(cmd, "", nil, 1*time.Hour) + return +} + // HostDiskUsage 本地路径所在磁盘使用情况 type HostDiskUsage struct { TotalSize uint64 `json:"ToTalSize"` // bytes diff --git a/dbm-services/redis/redis-dts/Makefile b/dbm-services/redis/redis-dts/Makefile index 2f08b1fa67..95cac954d2 100644 --- a/dbm-services/redis/redis-dts/Makefile +++ b/dbm-services/redis/redis-dts/Makefile @@ -1,4 +1,4 @@ -SRV_NAME= redis-dts +SRV_NAME= redis_dts_server binDir=bin clean: diff --git a/dbm-services/redis/redis-dts/bin/config-template.yaml b/dbm-services/redis/redis-dts/bin/config-template.yaml index 7295c4fff6..12366e9695 100644 --- a/dbm-services/redis/redis-dts/bin/config-template.yaml +++ b/dbm-services/redis/redis-dts/bin/config-template.yaml @@ -22,17 +22,18 @@ importMaxRetryTimes: 5 #非list类型的key,导入可重试,最大重试次数 tredisdumpTheadCnt: 10 #tredisdump --thread 参数 tendisplusMakeSyncParallelLimit: 5 memSizePerTendisplusKvStoreSync: 500MiB # 每个tendisplus kvstore启动一个redis-sync,默认其占用500MiB内存 -dtsRemoteTendisxk8s: - rootUrl: http://api.xxxx - secret_key: xxxx - secret_id: xxxx -gormlog: false -ABSUSER: mysql -ABSPASSWORD: xxxxxx +bkDbm: + rootUrl: {{bk_dbm_nginx_url}} + bk_cloud_id: {{bk_dbm_cloud_id}} + db_cloud_token: {{bk_dbm_cloud_token}} +serviceName: bkDbm +zoneName: {{city_name}} +ABSUSER: {{system_user}} +ABSPASSWORD: {{system_password}} ABSPORT: 36000 ABSTIMEOUT: 14400 RsyncPullBwLimit: 400000 # rsync bandwidth limit, kbit/s RsyncPullTimeout: 36000 # rsync timeout,10 hour -WarnMessageNotifier: xxxx #告警通知人 -DtsServerDiskMaxUsgRatio: 90 #dtsserver磁盘最大使用90%,超过则发送告警 +WarnMessageNotifier: {{warning_msg_notifiers}} #告警通知人 +DtsServerDiskMaxUsgRatio: 85 #dtsserver磁盘最大使用90%,超过则发送告警 DtsServerMemMaxUsgRatio: 80 #dtsserver内存最大使用90%,超过则发送告警 \ No newline at end of file diff --git a/dbm-services/redis/redis-dts/bin/start.sh b/dbm-services/redis/redis-dts/bin/start.sh index 1da2b3a4b3..05b0646a17 100755 --- a/dbm-services/redis/redis-dts/bin/start.sh +++ b/dbm-services/redis/redis-dts/bin/start.sh @@ -2,7 +2,7 @@ #检查必要文件是否存在 confFile="./config.yaml" -dtsBinFile="./redis-dts" +dtsBinFile="./redis_dts_server" tredisdumpBinFile="./tredisdump" redisCliBinFile="./redis-cli" syncTemplateConfFile="./tendisssd-sync-template.conf" @@ -43,16 +43,16 @@ then exit -1 fi -# 如果已经有一个 redis-dts 在运行,不能直接启动 +# 如果已经有一个 redis_dts_server 在运行,不能直接启动 processCnt=$(ps -ef|grep $dtsBinFile|grep -v grep|grep -v sync|wc -l) if [[ $processCnt -ge 1 ]] then - echo "error:there are a 'redis-dts' running" + echo "error:there are a 'redis_dts_server' running" ps -ef|grep $dtsBinFile|grep -v grep|grep -v sync exit -1 fi -# 启动 redis-dts +# 启动 redis_dts_server chmod u+x $dtsBinFile nohup $dtsBinFile & diff --git a/dbm-services/redis/redis-dts/bin/stop.sh b/dbm-services/redis/redis-dts/bin/stop.sh index 84e75eb4f4..4f52ad3790 100755 --- a/dbm-services/redis/redis-dts/bin/stop.sh +++ b/dbm-services/redis/redis-dts/bin/stop.sh @@ -22,22 +22,22 @@ then ps -ef|grep redis-cli|grep '\-\-pipe'|grep -v grep exit -1 fi -processCnt=$(ps -ef|grep redis-dts|grep -v grep|grep -v sync|wc -l) +processCnt=$(ps -ef|grep redis_dts_server|grep -v grep|grep -v sync|wc -l) if [[ $processCnt -eq 0 ]] then - echo "success:redis-dts not running" + echo "success:redis_dts_server not running" exit 0 fi -ps -ef|grep redis-dts|grep -v grep|grep -vi sync|awk '{print $2}'|xargs kill +ps -ef|grep redis_dts_server|grep -v grep|grep -vi sync|awk '{print $2}'|xargs kill #再次检查是否stop成功 -processCnt=$(ps -ef|grep redis-dts|grep -v grep|grep -v sync|wc -l) +processCnt=$(ps -ef|grep redis_dts_server|grep -v grep|grep -v sync|wc -l) if [[ $processCnt -eq 0 ]] then - echo "success:stop redis-dts success" + echo "success:stop redis_dts_server success" exit 0 else - echo "error: stop redis-dts fail" + echo "error: stop redis_dts_server fail" exit -1 fi \ No newline at end of file diff --git a/dbm-services/redis/redis-dts/config/config.go b/dbm-services/redis/redis-dts/config/config.go index 23baa62357..38d67db0b6 100644 --- a/dbm-services/redis/redis-dts/config/config.go +++ b/dbm-services/redis/redis-dts/config/config.go @@ -2,16 +2,11 @@ package config import ( - "flag" - "github.com/spf13/viper" ) -var cfgFile = flag.String("config-file", "./config.yaml", "Input your config file") - // InitConfig 加载配置文件 -func InitConfig() { - flag.Parse() +func InitConfig(cfgFile *string) { if *cfgFile != "" { viper.SetConfigFile(*cfgFile) diff --git a/dbm-services/redis/redis-dts/main.go b/dbm-services/redis/redis-dts/main.go index 86de4b302c..f38f8f113e 100644 --- a/dbm-services/redis/redis-dts/main.go +++ b/dbm-services/redis/redis-dts/main.go @@ -1,6 +1,7 @@ package main import ( + "flag" "fmt" "log" "os" @@ -24,6 +25,35 @@ func main() { } }() + cfgFile := flag.String("config-file", "./config.yaml", "Input your config file") + showVersion := flag.Bool("version", false, "Output version and exit.") + showHelp := flag.Bool("help", false, "Show help message.") + + flag.Parse() + + if *showVersion { + fmt.Println(constvar.TendisDTSVersion) + return + } + if *showHelp { + printHelp() + return + } + if *cfgFile == "" { + fmt.Println("[-config-file string] is required") + printHelp() + os.Exit(1) + } + + if _, err := os.Stat(*cfgFile); err != nil { + fmt.Printf("config file %s not exist\n", *cfgFile) + os.Exit(1) + } + + config.InitConfig(cfgFile) + tclog.InitMainlog() + // mysql.DB.Init() //连接MySQL + debug := viper.GetBool("TENDIS_DEBUG") tclog.Logger.Info(fmt.Sprintf("TENDIS_DEBUG:%v", debug)) @@ -54,8 +84,13 @@ func main() { wg.Wait() } -func init() { - config.InitConfig() - tclog.InitMainlog() - // mysql.DB.Init() //不连接MySQL +func printHelp() { + fmt.Println("Usage: redis_dts_server [options]") + fmt.Println("Options:") + fmt.Println(" -config-file string") + fmt.Println(" Input your config file (default \"./config.yaml\")") + fmt.Println(" -version") + fmt.Println(" Output version and exit.") + fmt.Println(" -help") + fmt.Println(" Show help message.") } diff --git a/dbm-services/redis/redis-dts/models/mysql/tendisdb/job.go b/dbm-services/redis/redis-dts/models/mysql/tendisdb/job.go index 55b48b9579..8198455536 100644 --- a/dbm-services/redis/redis-dts/models/mysql/tendisdb/job.go +++ b/dbm-services/redis/redis-dts/models/mysql/tendisdb/job.go @@ -15,30 +15,34 @@ import ( // TbTendisDTSJob tendisSSD迁移数据到Tendisx任务行 type TbTendisDTSJob struct { - ID int64 `json:"id" gorm:"column:id;primary_key"` - BillID int64 `json:"bill_id" gorm:"column:bill_id"` - App string `json:"app" gorm:"column:app"` - BkCloudID int64 `json:"bk_cloud_id" gorm:"column:bk_cloud_id"` - User string `json:"user" gorm:"column:user"` - DtsBillType string `json:"dts_bill_type" gorm:"column:dts_bill_type"` - DtsCopyType string `json:"dts_copy_type" gorm:"column:dts_copy_type"` - OnlineSwitchType string `json:"online_switch_type" gorm:"column:online_switch_type"` - DataCheck int `json:"data_check" gorm:"column:data_check"` - DataRepair int `json:"data_repair" gorm:"column:data_repair"` - DataRapairMode string `json:"data_repair_mode" gorm:"column:data_repair_mode"` - SrcCluster string `json:"src_cluster" gorm:"column:src_cluster"` - SrcClusterType string `json:"src_cluster_type" gorm:"column:src_cluster_type"` - SrcRollbackBillID int64 `json:"src_rollback_bill_id" gorm:"column:src_rollback_bill_id"` - SrcRollbackInstances string `json:"src_rollback_instances" gorm:"column:src_rollback_instances"` - DstBkBizID string `json:"dst_bk_biz_id" gorm:"column:dst_bk_biz_id"` - DstCluster string `json:"dst_cluster" gorm:"column:dst_cluster"` - DstClusterType string `json:"dst_cluster_type" gorm:"column:dst_cluster_type"` - KeyWhiteRegex string `json:"key_white_regex" gorm:"column:key_white_regex"` - KeyBlackRegex string `json:"key_black_regex" gorm:"column:key_black_regex"` - Status int `json:"status" gorm:"column:status"` - Reason string `json:"reason" gorm:"column:reason"` - CreateTime customtime.CustomTime `json:"createTime" gorm:"column:create_time"` - UpdateTime customtime.CustomTime `json:"updateTime" gorm:"column:update_time"` + ID int64 `json:"id" gorm:"column:id;primary_key"` + BillID int64 `json:"bill_id" gorm:"column:bill_id"` + App string `json:"app" gorm:"column:app"` + BkCloudID int64 `json:"bk_cloud_id" gorm:"column:bk_cloud_id"` + User string `json:"user" gorm:"column:user"` + DtsBillType string `json:"dts_bill_type" gorm:"column:dts_bill_type"` + DtsCopyType string `json:"dts_copy_type" gorm:"column:dts_copy_type"` + WriteMode string `json:"write_mode" gorm:"column:write_mode"` + OnlineSwitchType string `json:"online_switch_type" gorm:"column:online_switch_type"` + SyncDisconnectType string `json:"sync_disconnect_type" gorm:"column:sync_disconnect_type"` + SyncDisconnectReminderFrequency string `json:"sync_disconnect_reminder_frequency" gorm:"column:sync_disconnect_reminder_frequency"` + DataCheckRepairType string `json:"data_check_repair_type" gorm:"column:data_check_repair_type"` + DataCheckRepairExecutionFrequency string `json:"data_check_repair_execution_frequency" gorm:"column:data_check_repair_execution_frequency"` + LastDataCheckRepairBillID int64 `json:"last_data_check_repair_bill_id" gorm:"column:last_data_check_repair_bill_id"` + LastDataCheckRepairBillStatus int `json:"last_data_check_repair_bill_status" gorm:"column:last_data_check_repair_bill_status"` + SrcCluster string `json:"src_cluster" gorm:"column:src_cluster"` + SrcClusterType string `json:"src_cluster_type" gorm:"column:src_cluster_type"` + SrcRollbackBillID int64 `json:"src_rollback_bill_id" gorm:"column:src_rollback_bill_id"` + SrcRollbackInstances string `json:"src_rollback_instances" gorm:"column:src_rollback_instances"` + DstBkBizID string `json:"dst_bk_biz_id" gorm:"column:dst_bk_biz_id"` + DstCluster string `json:"dst_cluster" gorm:"column:dst_cluster"` + DstClusterType string `json:"dst_cluster_type" gorm:"column:dst_cluster_type"` + KeyWhiteRegex string `json:"key_white_regex" gorm:"column:key_white_regex"` + KeyBlackRegex string `json:"key_black_regex" gorm:"column:key_black_regex"` + Status int `json:"status" gorm:"column:status"` + Reason string `json:"reason" gorm:"column:reason"` + CreateTime customtime.CustomTime `json:"createTime" gorm:"column:create_time"` + UpdateTime customtime.CustomTime `json:"updateTime" gorm:"column:update_time"` } // TableName sets the insert table name for this struct type diff --git a/dbm-services/redis/redis-dts/models/mysql/tendisdb/task.go b/dbm-services/redis/redis-dts/models/mysql/tendisdb/task.go index 5a896eeac1..6f400cb71b 100644 --- a/dbm-services/redis/redis-dts/models/mysql/tendisdb/task.go +++ b/dbm-services/redis/redis-dts/models/mysql/tendisdb/task.go @@ -25,10 +25,11 @@ var ( // TbTendisDTSTask 迁移task type TbTendisDTSTask struct { ID int64 `json:"id" gorm:"column:id;primary_key"` - BillID int64 `json:"bill_id" gorm:"column:bill_id"` // 单据号 - App string `json:"app" gorm:"column:app"` // 业务英文名 - BkCloudID int64 `json:"bk_cloud_id" gorm:"column:bk_cloud_id"` // 云区域id - DtsServer string `json:"dts_server" gorm:"column:dts_server"` // 执行迁移任务的server ip + BillID int64 `json:"bill_id" gorm:"column:bill_id"` // 单据号 + App string `json:"app" gorm:"column:app"` // 业务英文名 + BkCloudID int64 `json:"bk_cloud_id" gorm:"column:bk_cloud_id"` // 云区域id + DtsServer string `json:"dts_server" gorm:"column:dts_server"` // 执行迁移任务的server ip + WriteMode string `json:"write_mode" gorm:"column:write_mode"` User string `json:"user" gorm:"column:user"` // 申请人 SrcCluster string `json:"src_cluster" gorm:"column:src_cluster"` // 源集群域名 SrcClusterPriority int `json:"src_cluster_priority" gorm:"column:src_cluster_priority"` // 源集群优先级,值越大,优先级越高 diff --git a/dbm-services/redis/redis-dts/pkg/constvar/constvar.go b/dbm-services/redis/redis-dts/pkg/constvar/constvar.go index d3168e8b0e..b75a1aa67c 100644 --- a/dbm-services/redis/redis-dts/pkg/constvar/constvar.go +++ b/dbm-services/redis/redis-dts/pkg/constvar/constvar.go @@ -9,7 +9,7 @@ import ( // version const ( - TendisDTSVersion = "v0.5" + TendisDTSVersion = "v0.10" ) // kibis of bits diff --git a/dbm-services/redis/redis-dts/pkg/dtsTask/tendisssd/backupFileFetch.go b/dbm-services/redis/redis-dts/pkg/dtsTask/tendisssd/backupFileFetch.go index 829eee37a0..d5633a03b1 100644 --- a/dbm-services/redis/redis-dts/pkg/dtsTask/tendisssd/backupFileFetch.go +++ b/dbm-services/redis/redis-dts/pkg/dtsTask/tendisssd/backupFileFetch.go @@ -3,10 +3,11 @@ package tendisssd import ( "path/filepath" + "dbm-services/redis/db-tools/dbactuator/pkg/util" "dbm-services/redis/redis-dts/models/mysql/tendisdb" "dbm-services/redis/redis-dts/pkg/constvar" "dbm-services/redis/redis-dts/pkg/dtsTask" - "dbm-services/redis/redis-dts/pkg/remoteOperation" + "dbm-services/redis/redis-dts/pkg/scrdbclient" ) // BakcupFileFetchTask 备份拉取task @@ -65,18 +66,49 @@ func (task *BakcupFileFetchTask) Execute() { } // 从srcIP上拉取备份文件 - var absCli remoteOperation.RemoteOperation - absCli, task.Err = remoteOperation.NewIAbsClientByEnvVars(task.RowData.SrcIP, task.Logger) + // var absCli remoteOperation.RemoteOperation + // absCli, task.Err = remoteOperation.NewIAbsClientByEnvVars(task.RowData.SrcIP, task.Logger) + // if task.Err != nil { + // return + // } + // task.Err = absCli.RemoteDownload( + // filepath.Dir(task.RowData.TendisbackupFile), + // task.TaskDir, + // filepath.Base(task.RowData.TendisbackupFile), + // constvar.GetABSPullBwLimit(), + // ) + // if task.Err != nil { + // return + // } + var localIP string + localIP, task.Err = util.GetLocalIP() if task.Err != nil { return } - task.Err = absCli.RemoteDownload( - filepath.Dir(task.RowData.TendisbackupFile), - task.TaskDir, - filepath.Base(task.RowData.TendisbackupFile), - constvar.GetABSPullBwLimit(), - ) - if task.Err != nil { + cli, err := scrdbclient.NewClient(constvar.BkDbm, task.Logger) + if err != nil { + task.Err = err + return + } + param := scrdbclient.TransferFileReq{} + param.SourceList = append(param.SourceList, scrdbclient.TransferFileSourceItem{ + BkCloudID: int(task.RowData.BkCloudID), + IP: task.RowData.SrcIP, + Account: "mysql", + FileList: []string{ + task.RowData.TendisbackupFile + string(filepath.Separator), + }, + }) + param.TargetAccount = "mysql" + param.TargetDir = task.TaskDir + param.TargetIPList = append(param.TargetIPList, scrdbclient.IPItem{ + BkCloudID: int(task.RowData.BkCloudID), + IP: localIP, + }) + param.Timeout = 2 * 86400 + err = cli.SendNew(param, 5) + if err != nil { + task.Err = err return } diff --git a/dbm-services/riak/db-tools/dbactuator/cmd/cmd.go b/dbm-services/riak/db-tools/dbactuator/cmd/cmd.go new file mode 100644 index 0000000000..af491fc624 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/cmd/cmd.go @@ -0,0 +1,164 @@ +// Package main 总入口 +/* + * @Description: dbactuator 入口函数,主要实现数据侧一些操作的命令,比如安装mysql 等等一系列的操作集合 + * @Useage: dbactuator --help + */ +package main + +import ( + "dbm-services/riak/db-tools/dbactuator/internal/subcmd/sysinitcmd" + "fmt" + "os" + "runtime/debug" + "strings" + "time" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/riak/db-tools/dbactuator/internal/subcmd" + "dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd" + "dbm-services/riak/db-tools/dbactuator/pkg/core/cst" + "dbm-services/riak/db-tools/dbactuator/pkg/util/templates" + + "github.com/spf13/cobra" +) + +const ( + // CMD actor入口 + CMD = "dbactuator" +) + +var ( + buildstamp, githash, version, external string +) + +// @title dbactuator API +// @version 0.0.1 +// @description This is a dbactuator command collection. +// @termsOfService http://swagger.io/terms/ +// @Schemes http +// @contact.name API Support +// @contact.url http://www.swagger.io/support +// @contact.email support@swagger.io + +// @license.name Apache 2.0 +// @license.url http://www.apache.org/licenses/LICENSE-2.0.html + +// @host ./dbactuator +// @BasePath / + +// main godoc +func main() { + defer func() { + if err := recover(); err != nil { + fmt.Println(err) + logger.Error("panic goroutine inner error!%v;%s", err, string(debug.Stack())) + os.Exit(1) + return + } + }() + if err := NewDbActuatorCommand().Execute(); err != nil { + fmt.Fprint(os.Stderr, err.Error()) + logger.Error("NewDbActuatorCommand run failed:%s", err.Error()) + os.Exit(1) + } +} + +// NewDbActuatorCommand 新建命令 +func NewDbActuatorCommand() *cobra.Command { + cmds := &cobra.Command{ + Use: CMD, + Short: fmt.Sprintf( + `Db Operation Command Line Interface +Version: %s +Githash: %s +External: %s +Buildstamp:%s`, version, githash, strings.ToUpper(external), buildstamp, + ), + Args: cobra.OnlyValidArgs, + PersistentPreRun: func(cmd *cobra.Command, args []string) { + if !cmd.IsAvailableCommand() { + runHelp(cmd, args) + return + } + subcmd.SetLogger(cmd, subcmd.GBaseOptions) + if subcmd.PrintSubCommandHelper(cmd, subcmd.GBaseOptions) { + runHelp(cmd, args) + } + // 定时输出标准心跳输出 + startHeartbeat(10 * time.Second) + }, + Run: runHelp, + SuggestFor: []string{CMD}, + } + groups := templates.CommandGroups{ + { + Message: "sysinit operation sets", + Commands: []*cobra.Command{ + sysinitcmd.NewSysInitCommand(), + }, + }, + { + Message: "riak sets", + Commands: []*cobra.Command{ + riakcmd.NewRiakCommand(), + }, + }, + } + groups.Add(cmds) + // 标志可以是 "persistent" 的,这意味着该标志将可用于分配给它的命令以及该命令下的每个命令。对于全局标志,将标志分配为根上的持久标志。 + // 默认每个subcomand 都默认带这些参数 + cmds.PersistentFlags().StringVarP( + &subcmd.GBaseOptions.Payload, "payload", "p", subcmd.GBaseOptions.Payload, + "command payload ", + ) + cmds.PersistentFlags().StringVarP( + &subcmd.GBaseOptions.PayloadFormat, "payload-format", "m", + subcmd.GBaseOptions.PayloadFormat, "command payload format, default base64, value_allowed: base64|raw", + ) + cmds.PersistentFlags().StringVarP(&subcmd.GBaseOptions.Uid, "uid", "U", subcmd.GBaseOptions.Uid, "bill id") + cmds.PersistentFlags().StringVarP(&subcmd.GBaseOptions.RootId, "root_id", "R", subcmd.GBaseOptions.NodeId, + "process id") + cmds.PersistentFlags().StringVarP(&subcmd.GBaseOptions.NodeId, "node_id", "N", subcmd.GBaseOptions.NodeId, "node id") + cmds.PersistentFlags().StringVarP( + &subcmd.GBaseOptions.VersionId, "version_id", "V", subcmd.GBaseOptions.NodeId, + "run version id", + ) + cmds.PersistentFlags().BoolVarP( + &subcmd.GBaseOptions.RollBack, + "rollback", + "r", + subcmd.GBaseOptions.RollBack, + "rollback task", + ) + cmds.PersistentFlags().BoolVarP( + &subcmd.GBaseOptions.Helper, + "helper", + "E", + subcmd.GBaseOptions.Helper, + "payload parameter description", + ) + subcmd.GBaseOptions.External = external + // @todo add --daemon mode to serve http to call subcmd/components + return cmds +} + +func runHelp(cmd *cobra.Command, args []string) { + cmd.Help() + os.Exit(1) +} + +// startHeartbeat 定時输出日志 +func startHeartbeat(period time.Duration) { + go func() { + ticker := time.NewTicker(period) + defer ticker.Stop() + var hearbeatTime string + for { + select { + case <-ticker.C: + hearbeatTime = time.Now().Local().Format(cst.TIMELAYOUT) + fmt.Fprintf(os.Stdin, "["+hearbeatTime+"]hearbeating ...\n") + } + } + }() +} diff --git a/dbm-ui/backend/db_services/redis_dts/migrations/__init__.py b/dbm-services/riak/db-tools/dbactuator/docs/.gitkeep similarity index 100% rename from dbm-ui/backend/db_services/redis_dts/migrations/__init__.py rename to dbm-services/riak/db-tools/dbactuator/docs/.gitkeep diff --git a/dbm-services/riak/db-tools/dbactuator/docs/dbactuator.md b/dbm-services/riak/db-tools/dbactuator/docs/dbactuator.md new file mode 100644 index 0000000000..3fa75ae070 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/docs/dbactuator.md @@ -0,0 +1,30 @@ +# dbactuator + +数据库操作指令集合,实现MySQL、Proxy、监控、备份 部署,MySQL、Proxy 变更等原子任务操作,由上层Pipeline 编排组合不同的指令,来完成不同的场景化的任务 +``` +Db Operation Command Line Interface +Version: 0.0.1 +Githash: 212617a717c3a3a968eb0c7d3a2c4ea2bc21abc2 +Buildstamp:2022-05-27_06:42:56AM + +Usage: + dbactuator [flags] + dbactuator [command] + +Available Commands: + completion Generate the autocompletion script for the specified shell + help Help about any command + mysql MySQL Operation Command Line Interface + proxy MySQL Proxy Operation Command Line Interface + sysinit Exec sysinit_mysql.sh,Init mysql default os user,password + +Flags: + -h, --help help for dbactuator + -n, --node_id string 节点id + -p, --payload string command payload + -r, --rollback 回滚任务 + -x, --show-payload show payload for man + -u, --uid string 单据id + +Use "dbactuator [command] --help" for more information about a command. +``` \ No newline at end of file diff --git a/dbm-services/riak/db-tools/dbactuator/docs/docs.go b/dbm-services/riak/db-tools/dbactuator/docs/docs.go new file mode 100644 index 0000000000..67384e3939 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/docs/docs.go @@ -0,0 +1,2 @@ +// Package docs TODO +package docs diff --git a/dbm-services/riak/db-tools/dbactuator/docs/embed_docs.go b/dbm-services/riak/db-tools/dbactuator/docs/embed_docs.go new file mode 100644 index 0000000000..f7c1683e24 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/docs/embed_docs.go @@ -0,0 +1,8 @@ +package docs + +import "embed" + +// SwaggerDocs TODO +// +//go:embed swagger.json +var SwaggerDocs embed.FS diff --git a/dbm-services/riak/db-tools/dbactuator/docs/swagger.json b/dbm-services/riak/db-tools/dbactuator/docs/swagger.json new file mode 100644 index 0000000000..dc9d750987 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/docs/swagger.json @@ -0,0 +1,3170 @@ +{ + "schemes": [ + "http" + ], + "swagger": "2.0", + "info": { + "description": "This is a dbactuator command collection.", + "title": "dbactuator API", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "name": "API Support", + "url": "http://www.swagger.io/support", + "email": "support@swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + }, + "version": "0.0.1" + }, + "host": "./dbactuator", + "basePath": "/", + "paths": { + "/common/file-server": { + "post": { + "description": "通过 http 暴露指定目录可用于下载,可用于在重建备库时,从其它机器下载备份\n在 OS 不允许 ssh 登录(scp/sftp)时,可以临时启动该服务来获取备份文件", + "consumes": [ + "application/json" + ], + "tags": [ + "common" + ], + "summary": "简单文件服务", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_fileserver.FileServerComp" + } + } + ], + "responses": {} + } + }, + "/common/rm-file": { + "post": { + "consumes": [ + "application/json" + ], + "tags": [ + "common" + ], + "summary": "限速删除大文件", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/internal_subcmd_commoncmd.RMLargeFileParam" + } + } + ], + "responses": {} + } + }, + "/download/http": { + "post": { + "description": "支持限速、basicAuth 认证. 一般配合 common fileserver 使用\n# server1\n./dbactuator common file-server \\\n--payload-format raw \\\n--payload '{\"extend\":{\"bind_address\":\":8082\",\"mount_path\":\"/data/dbbak\",\"user\":\"xiaog\",\"password\":\"xxxx\",\"proc_maxidle_duration\":\"60s\"}}'\n\n# server2\ncurl -u 'xiaog:xxxx' 'http://server1:8082/datadbbak8082/dbactuator' -o dbactuator.bin --limit-rate 10k", + "consumes": [ + "application/json" + ], + "tags": [ + "download" + ], + "summary": "http下载文件", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.DFHttpParam" + } + } + ], + "responses": {} + } + }, + "/download/ibs-query": { + "post": { + "description": "filename 会进行模糊匹配,返回 task_id 用于下载", + "consumes": [ + "application/json" + ], + "tags": [ + "download" + ], + "summary": "从 ieg 备份系统查询文件", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSQueryComp" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSQueryResult" + } + } + } + } + }, + "/download/ibs-recover": { + "post": { + "description": "提供 task_id,从 ieg 备份系统下载文件\ntask_files_wild: 模糊搜索文件并下载, task_files: 精确文件查询并下载\ntask_files_wild, task_files 二选一\n启用 skip_local_exists=true 时,如果目标目录已存在要下载的文件,会自动跳过", + "consumes": [ + "application/json" + ], + "tags": [ + "download" + ], + "summary": "从 ieg 备份系统下载文件", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSRecoverComp" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSRecoverTask" + } + } + } + } + }, + "/download/scp": { + "post": { + "description": "支持限速", + "consumes": [ + "application/json" + ], + "tags": [ + "download" + ], + "summary": "scp下载文件", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.DFScpParam" + } + } + ], + "responses": {} + } + }, + "/mysql/change-master": { + "post": { + "description": "执行 change master to", + "consumes": [ + "application/json" + ], + "tags": [ + "mysql" + ], + "summary": "建立主从关系", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BuildMSRelationComp" + } + } + ], + "responses": {} + } + }, + "/mysql/clean-mysql": { + "post": { + "description": "清空本地实例,保留系统库", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "mysql" + ], + "summary": "清空实例,高危", + "parameters": [ + { + "description": "description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.CleanMysqlComp" + } + } + ], + "responses": {} + } + }, + "/mysql/deploy": { + "post": { + "description": "部署 mysql 实例说明", + "consumes": [ + "application/json" + ], + "tags": [ + "mysql" + ], + "summary": "部署 mysql 实例", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLComp" + } + } + ], + "responses": {} + } + }, + "/mysql/deploy-dbbackup": { + "post": { + "description": "部署GO版本备份程序", + "consumes": [ + "application/json" + ], + "tags": [ + "mysql" + ], + "summary": "部署备份程序", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallNewDbBackupComp" + } + } + ], + "responses": {} + } + }, + "/mysql/find-local-backup": { + "post": { + "description": "查找本地备份", + "consumes": [ + "application/json" + ], + "tags": [ + "mysql" + ], + "summary": "查找本地备份", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.FindLocalBackupParam" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.FindLocalBackupResp" + } + } + } + } + }, + "/mysql/flashback-binlog": { + "post": { + "description": "通过 `mysqlbinlog --flashback xxx | mysql` 导入 binlog", + "consumes": [ + "application/json" + ], + "tags": [ + "mysql" + ], + "summary": "导入 binlog", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_rollback.FlashbackComp" + } + } + ], + "responses": {} + } + }, + "/mysql/grant-repl": { + "post": { + "description": "在目标机器新建 repl 账号", + "consumes": [ + "application/json" + ], + "tags": [ + "mysql" + ], + "summary": "建立复制账号", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_grant.GrantReplComp" + } + } + ], + "responses": {} + } + }, + "/mysql/init-cluster-routing": { + "post": { + "description": "初始化tendb cluster 集群的路由关系说明", + "consumes": [ + "application/json" + ], + "tags": [ + "spiderctl" + ], + "summary": "初始化tendb cluster 集群的路由关系", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_spiderctl.InitClusterRoutingComp" + } + } + ], + "responses": {} + } + }, + "/mysql/install-checksum": { + "post": { + "description": "安装mysql校验", + "consumes": [ + "application/json" + ], + "tags": [ + "mysql" + ], + "summary": "安装mysql校验", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLChecksumComp" + } + } + ], + "responses": {} + } + }, + "/mysql/install-dbatoolkit": { + "post": { + "description": "部署 /home/mysql/dba_toolkit,覆盖", + "consumes": [ + "application/json" + ], + "tags": [ + "mysql" + ], + "summary": "部署DBA工具箱", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallDBAToolkitComp" + } + } + ], + "responses": {} + } + }, + "/mysql/mycnf-change": { + "post": { + "description": "修改mysql配置", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "mysql" + ], + "summary": "修改mysql配置", + "parameters": [ + { + "description": "description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.MycnfChangeComp" + } + } + ], + "responses": {} + } + }, + "/mysql/mycnf-clone": { + "post": { + "description": "用于 slave 重建或迁移,保持新实例与 my.cnf 实例关键参数相同的场景\n默认 clone 参数:", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "mysql" + ], + "summary": "从源实例克隆 my.cnf 部分参数到目标实例", + "parameters": [ + { + "description": "description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.MycnfCloneComp" + } + } + ], + "responses": {} + } + }, + "/mysql/parse-binlog-time": { + "post": { + "description": "获取 binlog FileDescriptionFormat 和 RotateEvent 事件", + "consumes": [ + "application/json" + ], + "tags": [ + "mysql" + ], + "summary": "获取 binlog 的开始和结束时间", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BinlogTimeComp" + } + } + ], + "responses": {} + } + }, + "/mysql/pt-table-checksum": { + "post": { + "description": "数据校验", + "consumes": [ + "application/json" + ], + "tags": [ + "mysql" + ], + "summary": "数据校验", + "parameters": [ + { + "description": "description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.PtTableChecksumComp" + } + } + ], + "responses": {} + } + }, + "/mysql/recover-binlog": { + "post": { + "description": "通过 `mysqlbinlog xxx | mysql` 导入 binlog", + "consumes": [ + "application/json" + ], + "tags": [ + "mysql" + ], + "summary": "导入 binlog", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RecoverBinlogComp" + } + } + ], + "responses": {} + } + }, + "/mysql/restore-dr": { + "post": { + "description": "物理备份、逻辑备份恢复", + "consumes": [ + "application/json" + ], + "tags": [ + "mysql" + ], + "summary": "备份恢复", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RestoreDRComp" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_util_mysqlutil.ChangeMaster" + } + } + } + } + }, + "/mysql/semantic-check": { + "post": { + "description": "运行语义检查", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "mysql" + ], + "summary": "运行语义检查", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SemanticCheckComp" + } + } + ], + "responses": {} + } + }, + "/mysql/semantic-dumpschema": { + "post": { + "description": "运行语义检查", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "mysql" + ], + "summary": "运行语义检查", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SemanticDumpSchemaComp" + } + } + ], + "responses": {} + } + }, + "/spdierctl/deploy": { + "post": { + "description": "部署 spider ctl 实例说明", + "consumes": [ + "application/json" + ], + "tags": [ + "spiderctl" + ], + "summary": "部署 spider ctl 实例", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLComp" + } + } + ], + "responses": {} + } + }, + "/spider/deploy": { + "post": { + "description": "部署 spider 实例说明", + "consumes": [ + "application/json" + ], + "tags": [ + "spider" + ], + "summary": "部署 spider 实例", + "parameters": [ + { + "description": "short description", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLComp" + } + } + ], + "responses": {} + } + } + }, + "definitions": { + "dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam": { + "type": "object", + "properties": { + "runtime_account": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.RuntimeAccountParam" + }, + "runtime_extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.RuntimeExtend" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components.RuntimeAccountParam": { + "type": "object", + "properties": { + "admin_pwd": { + "description": "mysql admin 密码,环境变量 GENERAL_ACCOUNT_admin_pwd", + "type": "string" + }, + "admin_user": { + "description": "mysql admin 账户,环境变量 GENERAL_ACCOUNT_admin_user", + "type": "string" + }, + "backup_pwd": { + "description": "dbbackup pwd", + "type": "string" + }, + "backup_user": { + "description": "dbbackup user", + "type": "string" + }, + "monitor_access_all_pwd": { + "description": "mysql monitor@% 密码", + "type": "string" + }, + "monitor_access_all_user": { + "description": "mysql monitor@%", + "type": "string" + }, + "monitor_pwd": { + "description": "mysql monitor 密码,环境变量 GENERAL_ACCOUNT_monitor_pwd", + "type": "string" + }, + "monitor_user": { + "description": "mysql monitor 账户,环境变量 GENERAL_ACCOUNT_monitor_user", + "type": "string" + }, + "proxy_admin_pwd": { + "description": "proxy admin pwd", + "type": "string" + }, + "proxy_admin_user": { + "description": "proxy admin user", + "type": "string" + }, + "repl_pwd": { + "description": "repl pwd, 环境变量 GENERAL_ACCOUNT_repl_pwd", + "type": "string" + }, + "repl_user": { + "description": "repl user, 环境变量 GENERAL_ACCOUNT_repl_user", + "type": "string" + }, + "tdbctl_pwd": { + "type": "string" + }, + "tdbctl_user": { + "type": "string" + }, + "yw_pwd": { + "description": "yw pwd", + "type": "string" + }, + "yw_user": { + "description": "yw user", + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components.RuntimeExtend": { + "type": "object", + "properties": { + "mysql_sys_users": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.DFHttpParam": { + "type": "object", + "required": [ + "file_list", + "path_tgt", + "server" + ], + "properties": { + "auth_pass": { + "description": "http url basic auth pass", + "type": "string" + }, + "auth_user": { + "description": "http url basic auth user", + "type": "string" + }, + "bk_biz_id": { + "type": "integer" + }, + "bwlimit_mb": { + "description": "单文件下载限速,单位 MB/s", + "type": "integer" + }, + "curl_options": { + "type": "array", + "items": { + "type": "string" + } + }, + "curl_path": { + "description": "curl 命令路径,默认留空. 目前只用于测试 url", + "type": "string" + }, + "file_list": { + "description": "下载哪些文件", + "type": "array", + "items": { + "type": "string" + } + }, + "max_concurrency": { + "description": "并发下载数", + "type": "integer" + }, + "path_tgt": { + "description": "文件存放到本机哪个目录", + "type": "string" + }, + "server": { + "description": "下载 url", + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.DFScpParam": { + "type": "object", + "required": [ + "file_src", + "file_tgt" + ], + "properties": { + "bk_biz_id": { + "type": "integer" + }, + "bwlimit_mb": { + "description": "单文件下载限速,单位 MB/s", + "type": "integer" + }, + "file_src": { + "description": "下载源", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.FileSrc" + } + ] + }, + "file_tgt": { + "description": "下载目标", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.FileTgt" + } + ] + }, + "max_concurrency": { + "description": "并发下载数", + "type": "integer" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.FileSrc": { + "type": "object", + "required": [ + "file_list", + "path", + "ssh_host", + "ssh_port", + "ssh_user" + ], + "properties": { + "file_list": { + "description": "源文件名列表,相对上面的 path", + "type": "array", + "items": { + "type": "string" + } + }, + "match": { + "type": "string" + }, + "path": { + "description": "源文件所在目录", + "type": "string" + }, + "ssh_host": { + "type": "string" + }, + "ssh_pass": { + "type": "string" + }, + "ssh_port": { + "type": "string" + }, + "ssh_user": { + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.FileTgt": { + "type": "object", + "required": [ + "path" + ], + "properties": { + "path": { + "description": "文件下载目标目录", + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSBaseInfo": { + "type": "object", + "required": [ + "key", + "sys_id", + "url" + ], + "properties": { + "key": { + "description": "16位字串,由备份系统分配,可从环境变量获取 IBS_INFO__key", + "type": "string" + }, + "sys_id": { + "description": "application标识,亦即哪个系统需要访问本接口,可从环境变量获取 IBS_INFO_sys_id", + "type": "string" + }, + "ticket": { + "description": "OA验证的ticket,一个长串,通常附加在访问内网应用的URL上,主要用来验证用户身份,可以留空", + "type": "string" + }, + "url": { + "description": "ieg 备份系统 api url 地址,会在后面拼接 /query /recover 后缀进行请求\n可从环境变量获取 IBS_INFO_url", + "type": "string", + "example": "http://127.0.0.1/backupApi" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSQueryComp": { + "type": "object", + "properties": { + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSQueryParam" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSQueryForRecover": { + "type": "object", + "properties": { + "begin_date": { + "description": "查询文件起始时间,备份系统以 file_last_mtime 为条件", + "type": "string" + }, + "end_date": { + "description": "哪一天提交,结束时间,与begin_date形成一个时间范围,建议begin_date与end_date形成的时间范围不要超过3天", + "type": "string" + }, + "source_ip": { + "description": "来源IP,即提交备份任务的机器IP", + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSQueryParam": { + "type": "object", + "required": [ + "begin_date", + "end_date", + "filename", + "ibs_info", + "source_ip" + ], + "properties": { + "begin_date": { + "description": "哪一天提交,起始时间", + "type": "string" + }, + "end_date": { + "description": "哪一天提交,结束时间,与begin_date形成一个时间范围,建议begin_date与end_date形成的时间范围不要超过3天", + "type": "string" + }, + "filename": { + "description": "文件名", + "type": "string" + }, + "ibs_info": { + "description": "ieg backup system url and auth params", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSBaseInfo" + } + ] + }, + "source_ip": { + "description": "来源IP,即提交备份任务的机器IP", + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSQueryResult": { + "type": "object", + "properties": { + "bkstif": { + "description": "备份状态信息, 'done, success', 'Fail: bad md5' 等", + "type": "string" + }, + "createTime": { + "description": "非备份系统字段,全备(截取文件名中的字段),binlog 打开文件读取", + "type": "string" + }, + "expire_time": { + "type": "string" + }, + "expired": { + "type": "string" + }, + "file_last_mtime": { + "description": "文件最后修改时间", + "type": "string" + }, + "file_name": { + "type": "string" + }, + "file_tag": { + "type": "string" + }, + "md5": { + "type": "string" + }, + "path": { + "description": "非备份系统字段", + "type": "string" + }, + "size": { + "description": "文件大小", + "type": "string" + }, + "source_ip": { + "description": "上报该备份任务的IP", + "type": "string" + }, + "source_port": { + "description": "非备份系统字段", + "type": "string" + }, + "status": { + "description": "文件状态", + "type": "string" + }, + "task_id": { + "description": "任务ID,用于下载", + "type": "string" + }, + "uptime": { + "description": "备份任务上报时间", + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSRecoverComp": { + "type": "object", + "properties": { + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSRecoverParam" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSRecoverParam": { + "type": "object", + "required": [ + "dest_ip", + "diretory", + "ibs_info", + "login_user" + ], + "properties": { + "dest_ip": { + "description": "目标IP,文件恢复到哪一台机器上的", + "type": "string", + "example": "127.0.0.1" + }, + "diretory": { + "description": "diretory 是备份系统参数错误拼写", + "type": "string", + "example": "/data/dbbak" + }, + "ibs_info": { + "description": "ieg backup system url and auth params", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSBaseInfo" + } + ] + }, + "ibs_query": { + "description": "根据文件名下载,或者判断是否跳过下载时,需要提供 ibs_query 参数用于查询", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSQueryForRecover" + } + ] + }, + "login_passwd": { + "description": "登录 dest_ip 的用户名的密码, ieg 传统scp 方式下载才需要。如果是 cos 下载则不需要", + "type": "string" + }, + "login_user": { + "description": "登录 dest_ip 的用户名,下载后的文件属组是该用户", + "type": "string" + }, + "reason": { + "description": "恢复原因(备注用途)", + "type": "string" + }, + "skip_local_exists": { + "description": "如果本地目标目录已经存在对应文件,是否保留(即跳过下载). 默认 false", + "type": "boolean", + "example": false + }, + "task_files": { + "description": "如果是精确文件名下载,用 task_files。提供需要下载的文件列表,提供 task_id 或者完整的 file_name 即可\n如果顺便提供了 size 信息则不用请求备份系统获取大小 来决定文件是否需要重新下载", + "type": "array", + "items": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.TaskFilesExact" + } + }, + "task_files_wild": { + "description": "如果是模糊匹配搜索并下载,用 task_files_wild", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.TaskFilesWild" + } + ] + }, + "taskid_list": { + "description": "taskid 列表,,逗号分隔。会根据 task_files 里的信息,追加到这里。这里一般不传值,在 task_files 里提供 task_id 或者 file_name", + "type": "string", + "example": "10000,100001" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSRecoverTask": { + "type": "object", + "properties": { + "file_last_mtime": { + "description": "文件最后修改时间", + "type": "string" + }, + "file_name": { + "type": "string" + }, + "file_tag": { + "type": "string" + }, + "md5": { + "type": "string" + }, + "size": { + "description": "文件大小", + "type": "string" + }, + "source_ip": { + "description": "上报该备份任务的IP", + "type": "string" + }, + "status": { + "description": "文件状态", + "type": "string" + }, + "task_id": { + "description": "任务ID,用于下载", + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.TaskFilesExact": { + "type": "object", + "properties": { + "file_name": { + "type": "string" + }, + "md5": { + "type": "string" + }, + "size": { + "description": "文件大小", + "type": "string" + }, + "task_id": { + "description": "任务ID,用于下载", + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.TaskFilesWild": { + "type": "object", + "properties": { + "file_tag": { + "type": "string" + }, + "name_regex": { + "description": "在搜索的结果里面,应用该正则进行过滤", + "type": "string" + }, + "name_search": { + "description": "搜索的模糊条件,不用带 *", + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_fileserver.FileServer": { + "type": "object", + "required": [ + "auth_user", + "bind_address", + "mount_path" + ], + "properties": { + "acls": { + "description": "访问来源限制,从前往后匹配。格式 `[\"allow 127.0.0.1/32\", \"deny all\"]`", + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "allow all" + ] + }, + "auth_pass": { + "description": "http basic auth pass,为空时会随机生成密码", + "type": "string" + }, + "auth_user": { + "description": "http basic auth user", + "type": "string" + }, + "bind_address": { + "description": "http file-server 监听地址. 不提供端口,会在 12000-19999 之间随机选择一个端口,不提供 ip 时默认 localhost", + "type": "string" + }, + "enable_tls": { + "description": "暂不支持", + "type": "boolean" + }, + "max_connections": { + "description": "限制最大连接数,超过需要等待. 为 0 时表示不限制", + "type": "integer" + }, + "mount_path": { + "description": "将本地哪个目录通过 http 分享", + "type": "string" + }, + "path_prefix": { + "description": "path_prefix 用在生成 url 时的路径前缀. 可留空", + "type": "string" + }, + "print_download": { + "description": "输出 download http 的信息,方便使用", + "type": "boolean" + }, + "proc_maxidle_duration": { + "description": "超过最大空闲时间,自动退出. 示例 3600s, 60m, 1h", + "type": "string", + "example": "1h" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_fileserver.FileServerComp": { + "type": "object", + "properties": { + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_fileserver.FileServer" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BackupOptions": { + "type": "object", + "required": [ + "BackupType", + "CrontabTime", + "Master" + ], + "properties": { + "BackupType": { + "type": "string" + }, + "CrontabTime": { + "type": "string" + }, + "Logical": { + "type": "object", + "properties": { + "ExcludeDatabases": { + "description": "\"mysql,test,db_infobase,information_schema,performance_schema,sys\"", + "type": "string" + }, + "ExcludeTables": { + "type": "string" + } + } + }, + "Master": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_dbbackup.LogicBackupDataOption" + }, + "Slave": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_dbbackup.LogicBackupDataOption" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BinlogTimeComp": { + "type": "object", + "properties": { + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BinlogTimeParam" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BinlogTimeParam": { + "type": "object", + "required": [ + "binlog_dir", + "binlog_files" + ], + "properties": { + "binlog_dir": { + "type": "string" + }, + "binlog_files": { + "type": "array", + "items": { + "type": "string" + } + }, + "format": { + "type": "string", + "enum": [ + "", + "json", + "dump" + ] + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BuildMSRelationComp": { + "type": "object", + "properties": { + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BuildMSRelationParam" + }, + "general": { + "description": "本地使用 ADMIN, change master 使用 repl", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam" + } + ] + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BuildMSRelationParam": { + "type": "object", + "required": [ + "bin_file", + "bin_position", + "host", + "master_host", + "master_port", + "port" + ], + "properties": { + "bin_file": { + "description": "binlog 文件名称", + "type": "string" + }, + "bin_position": { + "description": "binlog 位点信息", + "type": "integer", + "minimum": 0 + }, + "force": { + "description": "如果当前实例存在主从关系是否直接reset slave后,强制change master", + "type": "boolean", + "example": false + }, + "host": { + "description": "具体操作内容需要操作的参数", + "type": "string" + }, + "is_gtid": { + "description": "是否启动GID方式进行建立主从", + "type": "boolean" + }, + "master_host": { + "description": "change master to 主库ip", + "type": "string" + }, + "master_port": { + "description": "change master to 主库端口", + "type": "integer", + "maximum": 65535, + "minimum": 3306 + }, + "max_tolerate_delay": { + "description": "最大容忍延迟, 当 主从延迟 小于 该值, 认为建立主从关系成功. 不传或者为 0 时,表示不检查", + "type": "integer" + }, + "not_start_io_thread": { + "description": "不启动 io_thread。默认false 表示启动 io_thread", + "type": "boolean", + "example": false + }, + "not_start_sql_thread": { + "description": "不启动 sql_thread。默认false 表示启动 sql_thread", + "type": "boolean", + "example": false + }, + "port": { + "description": "当前实例的端口", + "type": "integer", + "maximum": 65535, + "minimum": 3306 + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.CleanMysqlComp": { + "type": "object", + "properties": { + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.CleanMysqlParam" + }, + "general": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.CleanMysqlParam": { + "type": "object", + "required": [ + "tgt_instance" + ], + "properties": { + "check_interval_sec": { + "type": "integer" + }, + "drop_database": { + "description": "是否执行 drop database,这里是确认行为. 如果 false 则只把 drop 命令打印到输出", + "type": "boolean" + }, + "force": { + "description": "当实例不空闲时是否强制清空", + "type": "boolean" + }, + "reset_slave": { + "description": "是否执行 reset slave all", + "type": "boolean" + }, + "restart": { + "description": "drop_database 之后是否重启实例", + "type": "boolean" + }, + "stop_slave": { + "description": "是否执行 stop slave", + "type": "boolean" + }, + "tgt_instance": { + "description": "清空目标实例", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.Instance" + } + ] + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.ConfItemOp": { + "type": "object", + "required": [ + "op_type" + ], + "properties": { + "conf_value": { + "description": "ConfName string `json:\"conf_name\" validate:\"required\"`", + "type": "string" + }, + "need_restart": { + "type": "boolean" + }, + "op_type": { + "description": "配置项修改动作,允许值 `upsert`,`remove`", + "type": "string", + "enum": [ + "upsert", + "remove" + ] + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.DBHAAccount": { + "type": "object", + "required": [ + "pwd", + "user" + ], + "properties": { + "access_hosts": { + "type": "array", + "items": { + "type": "string" + } + }, + "pwd": { + "type": "string" + }, + "user": { + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.DumpSchemaParam": { + "type": "object", + "required": [ + "charset", + "host", + "port" + ], + "properties": { + "backup_dir": { + "type": "string" + }, + "backup_file_name": { + "type": "string" + }, + "bk_cloud_id": { + "description": "所在的云区域", + "type": "integer" + }, + "charset": { + "description": "字符集参数", + "type": "string" + }, + "db_cloud_token": { + "description": "云区域token", + "type": "string" + }, + "fileserver": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.FileServer" + }, + "host": { + "description": "当前实例的主机地址", + "type": "string" + }, + "port": { + "description": "当前实例的端口", + "type": "integer", + "minimum": 3306 + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.ExcuteSQLFileObj": { + "type": "object", + "properties": { + "dbnames": { + "description": "需要变更的DBNames,支持模糊匹配", + "type": "array", + "items": { + "type": "string" + } + }, + "ignore_dbnames": { + "description": "忽略的,需要排除变更的dbName,支持模糊匹配", + "type": "array", + "items": { + "type": "string" + } + }, + "sql_file": { + "description": "变更文件名称", + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.FileServer": { + "type": "object", + "properties": { + "bucket": { + "description": "目标bucket", + "type": "string" + }, + "password": { + "description": "制品库 password", + "type": "string" + }, + "project": { + "description": "制品库 project", + "type": "string" + }, + "upload_path": { + "description": "上传路径", + "type": "string" + }, + "url": { + "description": "制品库地址", + "type": "string" + }, + "username": { + "description": "制品库 username", + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.FindLocalBackupParam": { + "type": "object", + "required": [ + "backup_dirs", + "tgt_instance" + ], + "properties": { + "backup_dirs": { + "type": "array", + "items": { + "type": "string" + } + }, + "cluster_id": { + "description": "指定查询哪个 cluster_id 的备份,如果不指定可能查询到其它非法的备份", + "type": "integer" + }, + "file_server": { + "type": "boolean" + }, + "tgt_instance": { + "description": "查找哪个实例的备份", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.Instance" + } + ] + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.FindLocalBackupResp": { + "type": "object", + "properties": { + "backups": { + "description": "backups key 是 .info 文件", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.LocalBackupObj" + } + }, + "latest": { + "description": "记录上面 backups 最近的一次备份", + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallDBAToolkitComp": { + "type": "object", + "properties": { + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallDBAToolkitParam" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallDBAToolkitParam": { + "type": "object", + "required": [ + "pkg", + "pkg_md5" + ], + "properties": { + "exec_user": { + "description": "发起执行actor的用户,仅用于审计", + "type": "string" + }, + "pkg": { + "description": "安装包名", + "type": "string" + }, + "pkg_md5": { + "description": "安装包MD5", + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLChecksumComp": { + "type": "object", + "properties": { + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLChecksumParam" + }, + "general": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLChecksumParam": { + "type": "object", + "required": [ + "pkg", + "pkg_md5" + ], + "properties": { + "api_url": { + "type": "string" + }, + "exec_user": { + "type": "string" + }, + "instances_info": { + "type": "array", + "items": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstanceInfo" + } + }, + "pkg": { + "description": "安装包名", + "type": "string" + }, + "pkg_md5": { + "description": "安装包MD5", + "type": "string" + }, + "schedule": { + "type": "string" + }, + "system_dbs": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLComp": { + "type": "object", + "properties": { + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLParams" + }, + "general": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam" + }, + "timeZone": { + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLParams": { + "type": "object", + "required": [ + "charset", + "host", + "mycnf_configs", + "mysql_version", + "pkg", + "pkg_md5", + "ports" + ], + "properties": { + "allowDiskFileSystemTypes": { + "type": "array", + "items": { + "type": "string" + } + }, + "charset": { + "description": "字符集参数", + "type": "string" + }, + "dbha_account": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.DBHAAccount" + }, + "host": { + "type": "string" + }, + "inst_mem": { + "description": "安装实例的内存大小,可以不指定,会自动计算", + "type": "integer" + }, + "mycnf_configs": { + "description": "map[port]my.cnf", + "type": "array", + "items": { + "type": "integer" + } + }, + "mysql_version": { + "description": "MySQLVerion 只需5.6 5.7 这样的大版本号", + "type": "string" + }, + "pkg": { + "description": "安装包名", + "type": "string" + }, + "pkg_md5": { + "description": "安装包MD5", + "type": "string" + }, + "ports": { + "description": "Ports", + "type": "array", + "items": { + "type": "integer" + } + }, + "spider_auto_incr_mode_map": { + "type": "array", + "items": { + "type": "integer" + } + }, + "super_account": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SuperAccount" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallNewDbBackupComp": { + "type": "object", + "properties": { + "generalParam": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam" + }, + "params": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallNewDbBackupParam" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallNewDbBackupParam": { + "type": "object", + "required": [ + "bk_biz_id", + "configs", + "host", + "options", + "pkg", + "pkg_md5", + "ports", + "role" + ], + "properties": { + "bk_biz_id": { + "description": "bkbizid", + "type": "string" + }, + "bk_cloud_id": { + "description": "bk_cloud_id", + "type": "string" + }, + "cluster_address": { + "description": "cluster addresss", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "cluster_id": { + "description": "cluster id", + "type": "object", + "additionalProperties": { + "type": "integer" + } + }, + "configs": { + "description": "模板配置", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_dbbackup.Cnf" + } + ] + }, + "exec_user": { + "description": "执行Job的用户", + "type": "string" + }, + "host": { + "description": "当前实例的主机地址", + "type": "string" + }, + "options": { + "description": "选项参数配置", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BackupOptions" + } + ] + }, + "pkg": { + "description": "安装包名", + "type": "string" + }, + "pkg_md5": { + "description": "安装包MD5", + "type": "string" + }, + "ports": { + "description": "被监控机器的上所有需要监控的端口", + "type": "array", + "items": { + "type": "integer" + } + }, + "role": { + "description": "当前主机安装的mysqld的角色", + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstanceInfo": { + "type": "object", + "properties": { + "bk_biz_id": { + "type": "integer" + }, + "bk_instance_id": { + "description": "0 被视为空, 不序列化", + "type": "integer" + }, + "cluster_id": { + "type": "integer" + }, + "immute_domain": { + "type": "string" + }, + "ip": { + "type": "string" + }, + "port": { + "type": "integer" + }, + "role": { + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.LocalBackupObj": { + "type": "object", + "properties": { + "backup_dir": { + "description": "备份所在目录", + "type": "string" + }, + "backup_id": { + "type": "string" + }, + "backup_time": { + "description": "备份时间,目前是备份开始时间", + "type": "string" + }, + "backup_type": { + "type": "string" + }, + "bill_id": { + "type": "string" + }, + "bk_biz_id": { + "type": "string" + }, + "cluster_id": { + "type": "integer" + }, + "data_schema_grant": { + "type": "string" + }, + "db_role": { + "type": "string" + }, + "file_list": { + "description": "InfoFile common.InfoFileDetail `json:\"info_file\"`\n备份文件列表", + "type": "array", + "items": { + "type": "string" + } + }, + "index_file": { + "type": "string" + }, + "inst_host": { + "description": "备份所属 host", + "type": "string" + }, + "inst_port": { + "description": "备份所属 port", + "type": "integer" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.MycnfChangeComp": { + "type": "object", + "properties": { + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.MycnfChangeParam" + }, + "general": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.MycnfChangeParam": { + "type": "object", + "required": [ + "items", + "persistent", + "restart", + "tgt_instance" + ], + "properties": { + "items": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.ConfItemOp" + } + }, + "persistent": { + "description": "是否持久化到 my.cnf 文件,-1: 不持久化,1: 持久化, 2: 仅持久化但不修改运行时", + "type": "integer", + "enum": [ + -1, + 1, + 2 + ] + }, + "restart": { + "description": "指定是否 允许重启, -1:不重启, 1: 重启, 2:根据 items need_restart 自动判断是否重启", + "type": "integer", + "enum": [ + -1, + 1, + 2 + ] + }, + "tgt_instance": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.InsObject" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.MycnfCloneComp": { + "type": "object", + "properties": { + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.MycnfCloneParam" + }, + "general": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.MycnfCloneParam": { + "type": "object", + "required": [ + "persistent", + "restart", + "src_instance", + "tgt_instance" + ], + "properties": { + "items": { + "description": "需要克隆哪些变量, 考虑到不同版本参数不一样,这里不要求指定一定存在; 只修改 mysqld 区。即失败忽略\n有些参数是 readonly 的,只会保存到 my.cnf 中,如果与运行值不一样需要用户重启\n默认值见 MycnfCloneItemsDefault", + "type": "array", + "items": { + "type": "string" + } + }, + "persistent": { + "description": "是否持久化到 my.cnf 文件,0: 不持久化,1: 持久化, 2: 仅持久化但不修改运行时", + "type": "integer", + "enum": [ + 0, + 1, + 2 + ], + "example": 1 + }, + "restart": { + "description": "指定是否 允许重启, 0:不重启, 1: 重启, 2:根据 items need_restart 自动判断是否重启", + "type": "integer", + "enum": [ + 0, + 1, + 2 + ], + "example": 2 + }, + "src_instance": { + "description": "参数克隆,获取源实例,可以提供 repl 账号权限", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.InsObject" + } + ] + }, + "tgt_instance": { + "description": "应用到本地目标实例,需要有 ADMIN 权限", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.InsObject" + } + ] + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.PtTableChecksumComp": { + "type": "object", + "properties": { + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.PtTableChecksumParam" + }, + "general": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.PtTableChecksumParam": { + "type": "object", + "properties": { + "bk_biz_id": { + "description": "业务 id", + "type": "integer" + }, + "cluster_id": { + "description": "集群 id", + "type": "integer" + }, + "db_patterns": { + "description": "库表过滤选项", + "type": "array", + "items": { + "type": "string" + } + }, + "ignore_dbs": { + "description": "库表过滤选项", + "type": "array", + "items": { + "type": "string" + } + }, + "ignore_tables": { + "description": "库表过滤选项", + "type": "array", + "items": { + "type": "string" + } + }, + "immute_domain": { + "description": "集群域名", + "type": "string" + }, + "inner_role": { + "description": "执行校验的 db inner role, 应该是[master, repeater]", + "type": "string" + }, + "master_access_slave_password": { + "description": "从 db 访问 slave 的密码", + "type": "string" + }, + "master_access_slave_user": { + "description": "从 db 访问 slave 的用户名", + "type": "string" + }, + "master_ip": { + "description": "执行校验的 db ip", + "type": "string" + }, + "master_port": { + "description": "执行校验的 db port", + "type": "integer" + }, + "replicate_table": { + "description": "结果表, 带库前缀", + "type": "string" + }, + "runtime_hour": { + "description": "校验运行时长", + "type": "integer" + }, + "slaves": { + "description": "slave 列表", + "type": "array", + "items": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SlaveInfo" + } + }, + "system_dbs": { + "description": "系统表", + "type": "array", + "items": { + "type": "string" + } + }, + "table_patterns": { + "description": "库表过滤选项", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SemanticCheckComp": { + "type": "object", + "properties": { + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SenmanticCheckParam" + }, + "general": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SemanticDumpSchemaComp": { + "type": "object", + "properties": { + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.DumpSchemaParam" + }, + "general": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SenmanticCheckParam": { + "type": "object", + "required": [ + "host", + "port", + "remote_host", + "remote_port", + "schemafile" + ], + "properties": { + "execute_objects": { + "type": "array", + "items": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.ExcuteSQLFileObj" + } + }, + "host": { + "description": "语义检查实例的主机地址", + "type": "string" + }, + "port": { + "description": "语义检查实例的端口", + "type": "integer", + "minimum": 3306 + }, + "remote_host": { + "description": "用于获取目标实例的字符集,默认存储引擎", + "type": "string" + }, + "remote_port": { + "description": "获取表结构的源实例Port", + "type": "integer", + "minimum": 3306 + }, + "schemafile": { + "description": "表结构文件", + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SlaveInfo": { + "type": "object", + "properties": { + "id": { + "description": "slave id", + "type": "integer" + }, + "ip": { + "description": "slave ip", + "type": "string" + }, + "port": { + "description": "slave port", + "type": "integer" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SuperAccount": { + "type": "object", + "required": [ + "pwd", + "user" + ], + "properties": { + "access_hosts": { + "type": "array", + "items": { + "type": "string" + } + }, + "pwd": { + "type": "string" + }, + "user": { + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_dbbackup.Cnf": { + "type": "object", + "required": [ + "BackupClient", + "LogicalBackup", + "Public" + ], + "properties": { + "BackupClient": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_dbbackup.CnfBackupClient" + }, + "LogicalBackup": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_dbbackup.CnfLogicalBackup" + }, + "PhysicalBackup": { + "description": "LogicalLoad CnfLogicalLoad `json:\"LogicalLoad\" ini:\"LogicalLoad\"`", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_dbbackup.CnfPhysicalBackup" + } + ] + }, + "Public": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_dbbackup.CnfShared" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_dbbackup.CnfBackupClient": { + "type": "object", + "properties": { + "doChecksum": { + "type": "string" + }, + "fileTag": { + "type": "string" + }, + "remoteFileSystem": { + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_dbbackup.CnfLogicalBackup": { + "type": "object", + "properties": { + "chunkFilesize": { + "description": "ChunkFilesize Split tables into chunks of this output file size. This value is in MB", + "type": "integer" + }, + "defaultsFile": { + "type": "string" + }, + "disableCompress": { + "type": "boolean" + }, + "extraOpt": { + "description": "ExtraOpt other mydumper options string to be appended", + "type": "string" + }, + "flushRetryCount": { + "type": "integer" + }, + "regex": { + "type": "string" + }, + "threads": { + "type": "integer" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_dbbackup.CnfPhysicalBackup": { + "type": "object", + "required": [ + "defaultsFile" + ], + "properties": { + "defaultsFile": { + "type": "string" + }, + "extraOpt": { + "description": "ExtraOpt other xtrabackup options string to be appended", + "type": "string" + }, + "splitSpeed": { + "description": "SplitSpeed tar split limit in MB/s, default 300", + "type": "integer" + }, + "threads": { + "description": "Threads –parallel to copy files", + "type": "integer" + }, + "throttle": { + "description": "Throttle limits the number of chunks copied per second. The chunk size is 10 MB, 0 means no limit", + "type": "integer" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_dbbackup.CnfShared": { + "type": "object", + "required": [ + "backupDir", + "backupTimeOut", + "tarSizeThreshold" + ], + "properties": { + "backupDir": { + "type": "string" + }, + "backupId": { + "type": "string" + }, + "backupTimeOut": { + "type": "string" + }, + "backupType": { + "type": "string" + }, + "billId": { + "type": "string" + }, + "bkBizId": { + "type": "string" + }, + "bkCloudId": { + "type": "string" + }, + "clusterAddress": { + "type": "string" + }, + "clusterId": { + "type": "string" + }, + "dataSchemaGrant": { + "type": "string" + }, + "iolimitMBPerSec": { + "description": "IOLimitMBPerSec tar or split default io limit, mb/s. 0 means no limit", + "type": "integer" + }, + "mysqlCharset": { + "type": "string" + }, + "mysqlHost": { + "type": "string" + }, + "mysqlPasswd": { + "type": "string" + }, + "mysqlPort": { + "type": "string" + }, + "mysqlRole": { + "type": "string" + }, + "mysqlUser": { + "type": "string" + }, + "oldFileLeftDay": { + "type": "string" + }, + "resultReportPath": { + "type": "string" + }, + "statusReportPath": { + "type": "string" + }, + "tarSizeThreshold": { + "description": "TarSizeThreshold tar file will be split to this package size. MB", + "type": "integer", + "minimum": 128 + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_dbbackup.LogicBackupDataOption": { + "type": "object", + "properties": { + "DataSchemaGrant": { + "description": "\"grant,schema,data\"", + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_grant.GrantReplComp": { + "type": "object", + "properties": { + "db": { + "description": "本地db链接", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.DbWorker" + } + ] + }, + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_grant.GrantReplParam" + }, + "general": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_grant.GrantReplParam": { + "type": "object", + "properties": { + "host": { + "description": "当前实例的主机地址", + "type": "string" + }, + "port": { + "description": "当前实例的端口", + "type": "integer" + }, + "repl_hosts": { + "description": "slave host", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.MySQLBinlogUtil": { + "type": "object", + "properties": { + "databases": { + "description": "row event 解析指定 databases", + "type": "array", + "items": { + "type": "string" + } + }, + "databases_ignore": { + "description": "row event 解析指定 忽略 databases", + "type": "array", + "items": { + "type": "string" + } + }, + "filter_statement_match_error": { + "description": "匹配字符串成功,则解析 binlog 报错", + "type": "string" + }, + "filter_statement_match_ignore": { + "description": "匹配字符串成功,则忽略语句,加入注释中", + "type": "string" + }, + "filter_statement_match_ignore_force": { + "description": "匹配字符串成功,强制忽略语句,加入注释中。当与 filter_statement_match_error 都匹配时,ignore_force会优先生效\n默认 infodba_schema", + "type": "string" + }, + "flashback": { + "description": "是否启用 flashback", + "type": "boolean" + }, + "idempotent_mode": { + "description": "是否开启幂等模式, mysql --slave-exec-mode=idempotent or mysqlbinlog --idempotent", + "type": "boolean" + }, + "mysql_client_opt": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.MySQLClientOpt" + }, + "not_write_binlog": { + "description": "导入时是否记录 binlog, mysql sql_log_bin=0 or mysqlbinlog --disable-log-bin. true表示不写", + "type": "boolean" + }, + "query_event_handler": { + "description": "query event 默认处理策略。keep:保留解析出的query event 语句, ignore:注释(丢弃)该 query event, error:认为是不接受的语句,报错\n默认 keep", + "type": "string", + "enum": [ + "keep", + "ignore", + "safe", + "error" + ] + }, + "rewrite_db": { + "description": "--rewrite_db=\"db1-\u003exx_db1,db2-\u003exx_db2\"", + "type": "string" + }, + "start_pos": { + "description": "--start-position", + "type": "integer" + }, + "start_time": { + "description": "--start-datetime", + "type": "string" + }, + "stop_pos": { + "description": "--stop-position", + "type": "integer" + }, + "stop_time": { + "description": "--stop-datetime", + "type": "string" + }, + "tables": { + "description": "row event 解析指定 tables", + "type": "array", + "items": { + "type": "string" + } + }, + "tables_ignore": { + "description": "row event 解析指定 忽略 tables", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.MySQLClientOpt": { + "type": "object", + "properties": { + "binary_mode": { + "description": "是否启用 --binary-mode", + "type": "boolean" + }, + "max_allowed_packet": { + "type": "integer" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RecoverBinlog": { + "type": "object", + "required": [ + "binlog_dir", + "binlog_files", + "recover_opt", + "tgt_instance", + "work_dir" + ], + "properties": { + "binlog_dir": { + "description": "恢复时 binlog 存放目录,一般是下载目录", + "type": "string", + "example": "/data/dbbak/123456/binlog" + }, + "binlog_files": { + "description": "binlog列表", + "type": "array", + "items": { + "type": "string" + } + }, + "binlog_start_file": { + "description": "指定要开始应用的第 1 个 binlog。如果指定,一般要设置 start_pos,如果不指定则使用 start_time", + "type": "string" + }, + "parse_concurrency": { + "description": "解析的并发度,默认 1", + "type": "integer" + }, + "parse_only": { + "description": "仅解析 binlog,不做导入", + "type": "boolean" + }, + "quick_mode": { + "description": "如果启用 quick_mode,解析 binlog 时根据 filter databases 等选项过滤 row event,对 query event 会全部保留 。需要 mysqlbinlog 工具支持 --tables 选项,可以指定参数的 tools\n当 quick_mode=false 时,recover_opt 里的 databases 等选项无效,会应用全部 binlog", + "type": "boolean" + }, + "recover_opt": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.MySQLBinlogUtil" + }, + "source_binlog_format": { + "type": "string", + "enum": [ + "", + "ROW", + "STATEMENT", + "MIXED" + ] + }, + "tgt_instance": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.InsObject" + }, + "tools": { + "description": "外部指定工具路径", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "work_dir": { + "description": "binlog 解析所在目录,存放运行日志", + "type": "string", + "example": "/data/dbbak/" + }, + "work_id": { + "type": "string", + "example": "123456" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RecoverBinlogComp": { + "type": "object", + "properties": { + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RecoverBinlog" + }, + "general": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RestoreDRComp": { + "type": "object", + "properties": { + "extend": { + "description": "恢复参数,会复制给具体的 Restore 实现. 见 ChooseType 方法", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RestoreParam" + } + ] + }, + "general": { + "description": "通用参数", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam" + } + ] + }, + "resume": { + "description": "是否是中断后继续执行", + "type": "boolean" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RestoreOpt": { + "type": "object", + "properties": { + "databases": { + "description": "恢复哪些 db,当前只对 逻辑恢复有效", + "type": "array", + "items": { + "type": "string" + } + }, + "ignore_databases": { + "type": "array", + "items": { + "type": "string" + } + }, + "ignore_tables": { + "type": "array", + "items": { + "type": "string" + } + }, + "recover_binlog": { + "description": "在指定时间点回档场景才需要,是否恢复 binlog。在 doSlave 场景,是不需要 recover_binlog。这个选项是控制下一步恢复binlog的行为\n当 recover_binlog 时,要确保实例的所有库表结构都恢复。在逻辑回档场景,只回档部分库表数据时,依然要恢复所有表结构", + "type": "boolean" + }, + "recover_privs": { + "type": "boolean" + }, + "source_binlog_format": { + "description": "在库表级定点回档时有用,如果是 statement/mixed 格式,导入数据时需要全部导入;\n如果是 row,可只导入指定库表数据, 在 recover-binlog 时可指定 quick_mode=true 也恢复指定库表 binlog", + "type": "string", + "enum": [ + "", + "ROW", + "STATEMENT", + "MIXED" + ] + }, + "tables": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RestoreParam": { + "type": "object", + "required": [ + "backup_dir", + "backup_files", + "work_dir" + ], + "properties": { + "backup_dir": { + "description": "备份文件所在本地目录,理论上doDr不会对该目录写入,而是写入 targetDir", + "type": "string", + "example": "/data/dbbak" + }, + "backup_files": { + "description": "备份文件名列表,key 是 info|full|priv|index, value 是是相对于 backup_dir 的文件名列表", + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "change_master": { + "description": "恢复完成后是否执行 change master,会 change master 到 src_instance", + "type": "boolean" + }, + "restore_opts": { + "description": "恢复选项,比如恢复库表、是否导入binlog等。目前只对逻辑恢复有效", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RestoreOpt" + } + ] + }, + "src_instance": { + "description": "备份实例的 ip port,用于生产 change master 语句", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.Instance" + } + ] + }, + "tgt_instance": { + "description": "恢复本地的目标实例", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.InsObject" + } + ] + }, + "tools": { + "description": "恢复用到的客户端工具,不提供时会有默认值", + "allOf": [ + { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_tools.ToolSet" + } + ] + }, + "work_dir": { + "description": "备份恢复目录,工作目录", + "type": "string", + "example": "/data1/dbbak" + }, + "work_id": { + "description": "work_id 标识本次恢复,若为0则为当前时间戳", + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_rollback.Flashback": { + "type": "object", + "required": [ + "recover_opt", + "target_time", + "tgt_instance", + "work_dir" + ], + "properties": { + "binlog_dir": { + "description": "当 binlog_dir 不为空,表示 binlog 已下载;当为空时,目前只从本地软连接", + "type": "string" + }, + "binlog_files": { + "description": "binlog列表,如果不提供,则自动从本地查找符合时间范围的 binlog", + "type": "array", + "items": { + "type": "string" + } + }, + "parse_concurrency": { + "description": "解析binlog并发度", + "type": "integer" + }, + "recover_opt": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_rollback.RecoverOpt" + }, + "stop_time": { + "type": "string" + }, + "target_time": { + "description": "闪回的目标时间点,对应 recover-binlog 的 start_time, 精确到秒。目标实例的时区", + "type": "string" + }, + "tgt_instance": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.InsObject" + }, + "tools": { + "description": "外部指定工具路径", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "work_dir": { + "description": "binlog 解析所在目录,存放运行日志", + "type": "string" + }, + "work_id": { + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_rollback.FlashbackComp": { + "type": "object", + "properties": { + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_rollback.Flashback" + }, + "general": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_rollback.RecoverOpt": { + "type": "object", + "properties": { + "databases": { + "description": "row event 解析指定 databases", + "type": "array", + "items": { + "type": "string" + } + }, + "databases_ignore": { + "description": "row event 解析指定 忽略 databases", + "type": "array", + "items": { + "type": "string" + } + }, + "filter_rows": { + "description": "暂不支持行级闪回", + "type": "string" + }, + "tables": { + "description": "row event 解析指定 tables", + "type": "array", + "items": { + "type": "string" + } + }, + "tables_ignore": { + "description": "row event 解析指定 忽略 tables", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_spiderctl.InitClusterRoutingComp": { + "type": "object", + "properties": { + "extend": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_spiderctl.InitClusterRoutingParam" + }, + "general": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_spiderctl.InitClusterRoutingParam": { + "type": "object", + "required": [ + "ctl_instances", + "host", + "mysql_instances", + "port", + "spider_instances" + ], + "properties": { + "ctl_instances": { + "type": "array", + "items": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_spiderctl.Instance" + } + }, + "host": { + "type": "string" + }, + "mysql_instances": { + "type": "array", + "items": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_spiderctl.Instance" + } + }, + "port": { + "type": "integer", + "minimum": 3306 + }, + "spider_instances": { + "type": "array", + "items": { + "$ref": "#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_spiderctl.Instance" + } + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_components_spiderctl.Instance": { + "type": "object", + "properties": { + "host": { + "type": "string" + }, + "port": { + "type": "integer" + }, + "shard_id": { + "type": "integer" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_native.DbWorker": { + "type": "object", + "properties": { + "db": { + "$ref": "#/definitions/sql.DB" + }, + "dsn": { + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_native.InsObject": { + "type": "object", + "properties": { + "charset": { + "description": "连接字符集", + "type": "string" + }, + "host": { + "description": "当前实例的主机地址", + "type": "string" + }, + "options": { + "description": "其它选项", + "type": "string" + }, + "port": { + "description": "当前实例的端口", + "type": "integer" + }, + "pwd": { + "description": "连接当前实例的User Pwd", + "type": "string" + }, + "socket": { + "description": "连接socket", + "type": "string" + }, + "user": { + "description": "连接当前实例的User", + "type": "string" + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_native.Instance": { + "type": "object", + "properties": { + "host": { + "description": "当前实例的主机地址", + "type": "string", + "example": "127.0.0.1" + }, + "port": { + "description": "当前实例的端口", + "type": "integer", + "example": 33060 + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_tools.ToolSet": { + "type": "object", + "properties": { + "tools": { + "description": "外部指定工具路径", + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, + "dbm-services_mysql_db-tools_dbactuator_pkg_util_mysqlutil.ChangeMaster": { + "type": "object", + "required": [ + "master_host", + "master_password", + "master_port", + "master_user" + ], + "properties": { + "change_sql": { + "type": "string" + }, + "channel": { + "type": "string" + }, + "executed_gtid_set": { + "type": "string" + }, + "force": { + "description": "如果当前实例存在主从关系是否直接reset slave后,强制change master", + "type": "boolean" + }, + "is_gtid": { + "description": "是否启动GID方式进行建立主从", + "type": "boolean" + }, + "master_auto_position": { + "type": "integer" + }, + "master_host": { + "description": "主库ip", + "type": "string" + }, + "master_log_file": { + "description": "binlog 文件名称", + "type": "string" + }, + "master_log_pos": { + "description": "binlog 位点信息", + "type": "integer" + }, + "master_password": { + "type": "string" + }, + "master_port": { + "description": "主库端口", + "type": "integer", + "minimum": 3306 + }, + "master_user": { + "type": "string" + }, + "max_tolerate_delay": { + "description": "最大容忍延迟,即主从延迟小于该值,认为建立主从关系成功", + "type": "integer" + } + } + }, + "internal_subcmd_commoncmd.RMLargeFileParam": { + "type": "object", + "required": [ + "bw_limit_mb", + "filename" + ], + "properties": { + "bw_limit_mb": { + "description": "删除速度,MB/s,默认 30", + "type": "integer", + "default": 30, + "maximum": 1000, + "minimum": 1 + }, + "filename": { + "type": "string" + } + } + }, + "sql.DB": { + "type": "object" + } + } +} \ No newline at end of file diff --git a/dbm-services/riak/db-tools/dbactuator/docs/swagger.yaml b/dbm-services/riak/db-tools/dbactuator/docs/swagger.yaml new file mode 100644 index 0000000000..e6b8ef78c9 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/docs/swagger.yaml @@ -0,0 +1,2223 @@ +basePath: / +definitions: + dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam: + properties: + runtime_account: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.RuntimeAccountParam' + runtime_extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.RuntimeExtend' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components.RuntimeAccountParam: + properties: + admin_pwd: + description: mysql admin 密码,环境变量 GENERAL_ACCOUNT_admin_pwd + type: string + admin_user: + description: mysql admin 账户,环境变量 GENERAL_ACCOUNT_admin_user + type: string + backup_pwd: + description: dbbackup pwd + type: string + backup_user: + description: dbbackup user + type: string + monitor_access_all_pwd: + description: mysql monitor@% 密码 + type: string + monitor_access_all_user: + description: mysql monitor@% + type: string + monitor_pwd: + description: mysql monitor 密码,环境变量 GENERAL_ACCOUNT_monitor_pwd + type: string + monitor_user: + description: mysql monitor 账户,环境变量 GENERAL_ACCOUNT_monitor_user + type: string + proxy_admin_pwd: + description: proxy admin pwd + type: string + proxy_admin_user: + description: proxy admin user + type: string + repl_pwd: + description: repl pwd, 环境变量 GENERAL_ACCOUNT_repl_pwd + type: string + repl_user: + description: repl user, 环境变量 GENERAL_ACCOUNT_repl_user + type: string + tdbctl_pwd: + type: string + tdbctl_user: + type: string + yw_pwd: + description: yw pwd + type: string + yw_user: + description: yw user + type: string + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components.RuntimeExtend: + properties: + mysql_sys_users: + items: + type: string + type: array + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.DFHttpParam: + properties: + auth_pass: + description: http url basic auth pass + type: string + auth_user: + description: http url basic auth user + type: string + bk_biz_id: + type: integer + bwlimit_mb: + description: 单文件下载限速,单位 MB/s + type: integer + curl_options: + items: + type: string + type: array + curl_path: + description: curl 命令路径,默认留空. 目前只用于测试 url + type: string + file_list: + description: 下载哪些文件 + items: + type: string + type: array + max_concurrency: + description: 并发下载数 + type: integer + path_tgt: + description: 文件存放到本机哪个目录 + type: string + server: + description: 下载 url + type: string + required: + - file_list + - path_tgt + - server + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.DFScpParam: + properties: + bk_biz_id: + type: integer + bwlimit_mb: + description: 单文件下载限速,单位 MB/s + type: integer + file_src: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.FileSrc' + description: 下载源 + file_tgt: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.FileTgt' + description: 下载目标 + max_concurrency: + description: 并发下载数 + type: integer + required: + - file_src + - file_tgt + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.FileSrc: + properties: + file_list: + description: 源文件名列表,相对上面的 path + items: + type: string + type: array + match: + type: string + path: + description: 源文件所在目录 + type: string + ssh_host: + type: string + ssh_pass: + type: string + ssh_port: + type: string + ssh_user: + type: string + required: + - file_list + - path + - ssh_host + - ssh_port + - ssh_user + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.FileTgt: + properties: + path: + description: 文件下载目标目录 + type: string + required: + - path + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSBaseInfo: + properties: + key: + description: 16位字串,由备份系统分配,可从环境变量获取 IBS_INFO__key + type: string + sys_id: + description: application标识,亦即哪个系统需要访问本接口,可从环境变量获取 IBS_INFO_sys_id + type: string + ticket: + description: OA验证的ticket,一个长串,通常附加在访问内网应用的URL上,主要用来验证用户身份,可以留空 + type: string + url: + description: |- + ieg 备份系统 api url 地址,会在后面拼接 /query /recover 后缀进行请求 + 可从环境变量获取 IBS_INFO_url + example: http://127.0.0.1/backupApi + type: string + required: + - key + - sys_id + - url + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSQueryComp: + properties: + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSQueryParam' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSQueryForRecover: + properties: + begin_date: + description: 查询文件起始时间,备份系统以 file_last_mtime 为条件 + type: string + end_date: + description: 哪一天提交,结束时间,与begin_date形成一个时间范围,建议begin_date与end_date形成的时间范围不要超过3天 + type: string + source_ip: + description: 来源IP,即提交备份任务的机器IP + type: string + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSQueryParam: + properties: + begin_date: + description: 哪一天提交,起始时间 + type: string + end_date: + description: 哪一天提交,结束时间,与begin_date形成一个时间范围,建议begin_date与end_date形成的时间范围不要超过3天 + type: string + filename: + description: 文件名 + type: string + ibs_info: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSBaseInfo' + description: ieg backup system url and auth params + source_ip: + description: 来源IP,即提交备份任务的机器IP + type: string + required: + - begin_date + - end_date + - filename + - ibs_info + - source_ip + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSQueryResult: + properties: + bkstif: + description: '备份状态信息, ''done, success'', ''Fail: bad md5'' 等' + type: string + createTime: + description: 非备份系统字段,全备(截取文件名中的字段),binlog 打开文件读取 + type: string + expire_time: + type: string + expired: + type: string + file_last_mtime: + description: 文件最后修改时间 + type: string + file_name: + type: string + file_tag: + type: string + md5: + type: string + path: + description: 非备份系统字段 + type: string + size: + description: 文件大小 + type: string + source_ip: + description: 上报该备份任务的IP + type: string + source_port: + description: 非备份系统字段 + type: string + status: + description: 文件状态 + type: string + task_id: + description: 任务ID,用于下载 + type: string + uptime: + description: 备份任务上报时间 + type: string + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSRecoverComp: + properties: + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSRecoverParam' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSRecoverParam: + properties: + dest_ip: + description: 目标IP,文件恢复到哪一台机器上的 + example: 127.0.0.1 + type: string + diretory: + description: diretory 是备份系统参数错误拼写 + example: /data/dbbak + type: string + ibs_info: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSBaseInfo' + description: ieg backup system url and auth params + ibs_query: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSQueryForRecover' + description: 根据文件名下载,或者判断是否跳过下载时,需要提供 ibs_query 参数用于查询 + login_passwd: + description: 登录 dest_ip 的用户名的密码, ieg 传统scp 方式下载才需要。如果是 cos 下载则不需要 + type: string + login_user: + description: 登录 dest_ip 的用户名,下载后的文件属组是该用户 + type: string + reason: + description: 恢复原因(备注用途) + type: string + skip_local_exists: + description: 如果本地目标目录已经存在对应文件,是否保留(即跳过下载). 默认 false + example: false + type: boolean + task_files: + description: |- + 如果是精确文件名下载,用 task_files。提供需要下载的文件列表,提供 task_id 或者完整的 file_name 即可 + 如果顺便提供了 size 信息则不用请求备份系统获取大小 来决定文件是否需要重新下载 + items: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.TaskFilesExact' + type: array + task_files_wild: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.TaskFilesWild' + description: 如果是模糊匹配搜索并下载,用 task_files_wild + taskid_list: + description: taskid 列表,,逗号分隔。会根据 task_files 里的信息,追加到这里。这里一般不传值,在 task_files + 里提供 task_id 或者 file_name + example: 10000,100001 + type: string + required: + - dest_ip + - diretory + - ibs_info + - login_user + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSRecoverTask: + properties: + file_last_mtime: + description: 文件最后修改时间 + type: string + file_name: + type: string + file_tag: + type: string + md5: + type: string + size: + description: 文件大小 + type: string + source_ip: + description: 上报该备份任务的IP + type: string + status: + description: 文件状态 + type: string + task_id: + description: 任务ID,用于下载 + type: string + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.TaskFilesExact: + properties: + file_name: + type: string + md5: + type: string + size: + description: 文件大小 + type: string + task_id: + description: 任务ID,用于下载 + type: string + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.TaskFilesWild: + properties: + file_tag: + type: string + name_regex: + description: 在搜索的结果里面,应用该正则进行过滤 + type: string + name_search: + description: 搜索的模糊条件,不用带 * + type: string + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_fileserver.FileServer: + properties: + acls: + description: 访问来源限制,从前往后匹配。格式 `["allow 127.0.0.1/32", "deny all"]` + example: + - allow all + items: + type: string + type: array + auth_pass: + description: http basic auth pass,为空时会随机生成密码 + type: string + auth_user: + description: http basic auth user + type: string + bind_address: + description: http file-server 监听地址. 不提供端口,会在 12000-19999 之间随机选择一个端口,不提供 ip + 时默认 localhost + type: string + enable_tls: + description: 暂不支持 + type: boolean + max_connections: + description: 限制最大连接数,超过需要等待. 为 0 时表示不限制 + type: integer + mount_path: + description: 将本地哪个目录通过 http 分享 + type: string + path_prefix: + description: path_prefix 用在生成 url 时的路径前缀. 可留空 + type: string + print_download: + description: 输出 download http 的信息,方便使用 + type: boolean + proc_maxidle_duration: + description: 超过最大空闲时间,自动退出. 示例 3600s, 60m, 1h + example: 1h + type: string + required: + - auth_user + - bind_address + - mount_path + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_fileserver.FileServerComp: + properties: + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_fileserver.FileServer' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BackupOptions: + properties: + BackupType: + type: string + CrontabTime: + type: string + Logical: + properties: + ExcludeDatabases: + description: '"mysql,test,db_infobase,information_schema,performance_schema,sys"' + type: string + ExcludeTables: + type: string + type: object + Master: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_dbbackup.LogicBackupDataOption' + Slave: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_dbbackup.LogicBackupDataOption' + required: + - BackupType + - CrontabTime + - Master + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BinlogTimeComp: + properties: + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BinlogTimeParam' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BinlogTimeParam: + properties: + binlog_dir: + type: string + binlog_files: + items: + type: string + type: array + format: + enum: + - "" + - json + - dump + type: string + required: + - binlog_dir + - binlog_files + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BuildMSRelationComp: + properties: + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BuildMSRelationParam' + general: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam' + description: 本地使用 ADMIN, change master 使用 repl + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BuildMSRelationParam: + properties: + bin_file: + description: binlog 文件名称 + type: string + bin_position: + description: binlog 位点信息 + minimum: 0 + type: integer + force: + description: 如果当前实例存在主从关系是否直接reset slave后,强制change master + example: false + type: boolean + host: + description: 具体操作内容需要操作的参数 + type: string + is_gtid: + description: 是否启动GID方式进行建立主从 + type: boolean + master_host: + description: change master to 主库ip + type: string + master_port: + description: change master to 主库端口 + maximum: 65535 + minimum: 3306 + type: integer + max_tolerate_delay: + description: 最大容忍延迟, 当 主从延迟 小于 该值, 认为建立主从关系成功. 不传或者为 0 时,表示不检查 + type: integer + not_start_io_thread: + description: 不启动 io_thread。默认false 表示启动 io_thread + example: false + type: boolean + not_start_sql_thread: + description: 不启动 sql_thread。默认false 表示启动 sql_thread + example: false + type: boolean + port: + description: 当前实例的端口 + maximum: 65535 + minimum: 3306 + type: integer + required: + - bin_file + - bin_position + - host + - master_host + - master_port + - port + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.CleanMysqlComp: + properties: + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.CleanMysqlParam' + general: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.CleanMysqlParam: + properties: + check_interval_sec: + type: integer + drop_database: + description: 是否执行 drop database,这里是确认行为. 如果 false 则只把 drop 命令打印到输出 + type: boolean + force: + description: 当实例不空闲时是否强制清空 + type: boolean + reset_slave: + description: 是否执行 reset slave all + type: boolean + restart: + description: drop_database 之后是否重启实例 + type: boolean + stop_slave: + description: 是否执行 stop slave + type: boolean + tgt_instance: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.Instance' + description: 清空目标实例 + required: + - tgt_instance + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.ConfItemOp: + properties: + conf_value: + description: ConfName string `json:"conf_name" validate:"required"` + type: string + need_restart: + type: boolean + op_type: + description: 配置项修改动作,允许值 `upsert`,`remove` + enum: + - upsert + - remove + type: string + required: + - op_type + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.DBHAAccount: + properties: + access_hosts: + items: + type: string + type: array + pwd: + type: string + user: + type: string + required: + - pwd + - user + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.DumpSchemaParam: + properties: + backup_dir: + type: string + backup_file_name: + type: string + bk_cloud_id: + description: 所在的云区域 + type: integer + charset: + description: 字符集参数 + type: string + db_cloud_token: + description: 云区域token + type: string + fileserver: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.FileServer' + host: + description: 当前实例的主机地址 + type: string + port: + description: 当前实例的端口 + minimum: 3306 + type: integer + required: + - charset + - host + - port + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.ExcuteSQLFileObj: + properties: + dbnames: + description: 需要变更的DBNames,支持模糊匹配 + items: + type: string + type: array + ignore_dbnames: + description: 忽略的,需要排除变更的dbName,支持模糊匹配 + items: + type: string + type: array + sql_file: + description: 变更文件名称 + type: string + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.FileServer: + properties: + bucket: + description: 目标bucket + type: string + password: + description: 制品库 password + type: string + project: + description: 制品库 project + type: string + upload_path: + description: 上传路径 + type: string + url: + description: 制品库地址 + type: string + username: + description: 制品库 username + type: string + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.FindLocalBackupParam: + properties: + backup_dirs: + items: + type: string + type: array + cluster_id: + description: 指定查询哪个 cluster_id 的备份,如果不指定可能查询到其它非法的备份 + type: integer + file_server: + type: boolean + tgt_instance: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.Instance' + description: 查找哪个实例的备份 + required: + - backup_dirs + - tgt_instance + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.FindLocalBackupResp: + properties: + backups: + additionalProperties: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.LocalBackupObj' + description: backups key 是 .info 文件 + type: object + latest: + description: 记录上面 backups 最近的一次备份 + type: string + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallDBAToolkitComp: + properties: + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallDBAToolkitParam' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallDBAToolkitParam: + properties: + exec_user: + description: 发起执行actor的用户,仅用于审计 + type: string + pkg: + description: 安装包名 + type: string + pkg_md5: + description: 安装包MD5 + type: string + required: + - pkg + - pkg_md5 + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLChecksumComp: + properties: + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLChecksumParam' + general: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLChecksumParam: + properties: + api_url: + type: string + exec_user: + type: string + instances_info: + items: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstanceInfo' + type: array + pkg: + description: 安装包名 + type: string + pkg_md5: + description: 安装包MD5 + type: string + schedule: + type: string + system_dbs: + items: + type: string + type: array + required: + - pkg + - pkg_md5 + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLComp: + properties: + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLParams' + general: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam' + timeZone: + type: string + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLParams: + properties: + allowDiskFileSystemTypes: + items: + type: string + type: array + charset: + description: 字符集参数 + type: string + dbha_account: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.DBHAAccount' + host: + type: string + inst_mem: + description: 安装实例的内存大小,可以不指定,会自动计算 + type: integer + mycnf_configs: + description: map[port]my.cnf + items: + type: integer + type: array + mysql_version: + description: MySQLVerion 只需5.6 5.7 这样的大版本号 + type: string + pkg: + description: 安装包名 + type: string + pkg_md5: + description: 安装包MD5 + type: string + ports: + description: Ports + items: + type: integer + type: array + spider_auto_incr_mode_map: + items: + type: integer + type: array + super_account: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SuperAccount' + required: + - charset + - host + - mycnf_configs + - mysql_version + - pkg + - pkg_md5 + - ports + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallNewDbBackupComp: + properties: + generalParam: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam' + params: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallNewDbBackupParam' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallNewDbBackupParam: + properties: + bk_biz_id: + description: bkbizid + type: string + bk_cloud_id: + description: bk_cloud_id + type: string + cluster_address: + additionalProperties: + type: string + description: cluster addresss + type: object + cluster_id: + additionalProperties: + type: integer + description: cluster id + type: object + configs: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_dbbackup.Cnf' + description: 模板配置 + exec_user: + description: 执行Job的用户 + type: string + host: + description: 当前实例的主机地址 + type: string + options: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BackupOptions' + description: 选项参数配置 + pkg: + description: 安装包名 + type: string + pkg_md5: + description: 安装包MD5 + type: string + ports: + description: 被监控机器的上所有需要监控的端口 + items: + type: integer + type: array + role: + description: 当前主机安装的mysqld的角色 + type: string + required: + - bk_biz_id + - configs + - host + - options + - pkg + - pkg_md5 + - ports + - role + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstanceInfo: + properties: + bk_biz_id: + type: integer + bk_instance_id: + description: 0 被视为空, 不序列化 + type: integer + cluster_id: + type: integer + immute_domain: + type: string + ip: + type: string + port: + type: integer + role: + type: string + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.LocalBackupObj: + properties: + backup_dir: + description: 备份所在目录 + type: string + backup_id: + type: string + backup_time: + description: 备份时间,目前是备份开始时间 + type: string + backup_type: + type: string + bill_id: + type: string + bk_biz_id: + type: string + cluster_id: + type: integer + data_schema_grant: + type: string + db_role: + type: string + file_list: + description: |- + InfoFile common.InfoFileDetail `json:"info_file"` + 备份文件列表 + items: + type: string + type: array + index_file: + type: string + inst_host: + description: 备份所属 host + type: string + inst_port: + description: 备份所属 port + type: integer + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.MycnfChangeComp: + properties: + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.MycnfChangeParam' + general: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.MycnfChangeParam: + properties: + items: + additionalProperties: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.ConfItemOp' + type: object + persistent: + description: '是否持久化到 my.cnf 文件,-1: 不持久化,1: 持久化, 2: 仅持久化但不修改运行时' + enum: + - -1 + - 1 + - 2 + type: integer + restart: + description: '指定是否 允许重启, -1:不重启, 1: 重启, 2:根据 items need_restart 自动判断是否重启' + enum: + - -1 + - 1 + - 2 + type: integer + tgt_instance: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.InsObject' + required: + - items + - persistent + - restart + - tgt_instance + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.MycnfCloneComp: + properties: + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.MycnfCloneParam' + general: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.MycnfCloneParam: + properties: + items: + description: |- + 需要克隆哪些变量, 考虑到不同版本参数不一样,这里不要求指定一定存在; 只修改 mysqld 区。即失败忽略 + 有些参数是 readonly 的,只会保存到 my.cnf 中,如果与运行值不一样需要用户重启 + 默认值见 MycnfCloneItemsDefault + items: + type: string + type: array + persistent: + description: '是否持久化到 my.cnf 文件,0: 不持久化,1: 持久化, 2: 仅持久化但不修改运行时' + enum: + - 0 + - 1 + - 2 + example: 1 + type: integer + restart: + description: '指定是否 允许重启, 0:不重启, 1: 重启, 2:根据 items need_restart 自动判断是否重启' + enum: + - 0 + - 1 + - 2 + example: 2 + type: integer + src_instance: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.InsObject' + description: 参数克隆,获取源实例,可以提供 repl 账号权限 + tgt_instance: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.InsObject' + description: 应用到本地目标实例,需要有 ADMIN 权限 + required: + - persistent + - restart + - src_instance + - tgt_instance + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.PtTableChecksumComp: + properties: + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.PtTableChecksumParam' + general: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.PtTableChecksumParam: + properties: + bk_biz_id: + description: 业务 id + type: integer + cluster_id: + description: 集群 id + type: integer + db_patterns: + description: 库表过滤选项 + items: + type: string + type: array + ignore_dbs: + description: 库表过滤选项 + items: + type: string + type: array + ignore_tables: + description: 库表过滤选项 + items: + type: string + type: array + immute_domain: + description: 集群域名 + type: string + inner_role: + description: 执行校验的 db inner role, 应该是[master, repeater] + type: string + master_access_slave_password: + description: 从 db 访问 slave 的密码 + type: string + master_access_slave_user: + description: 从 db 访问 slave 的用户名 + type: string + master_ip: + description: 执行校验的 db ip + type: string + master_port: + description: 执行校验的 db port + type: integer + replicate_table: + description: 结果表, 带库前缀 + type: string + runtime_hour: + description: 校验运行时长 + type: integer + slaves: + description: slave 列表 + items: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SlaveInfo' + type: array + system_dbs: + description: 系统表 + items: + type: string + type: array + table_patterns: + description: 库表过滤选项 + items: + type: string + type: array + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SemanticCheckComp: + properties: + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SenmanticCheckParam' + general: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SemanticDumpSchemaComp: + properties: + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.DumpSchemaParam' + general: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SenmanticCheckParam: + properties: + execute_objects: + items: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.ExcuteSQLFileObj' + type: array + host: + description: 语义检查实例的主机地址 + type: string + port: + description: 语义检查实例的端口 + minimum: 3306 + type: integer + remote_host: + description: 用于获取目标实例的字符集,默认存储引擎 + type: string + remote_port: + description: 获取表结构的源实例Port + minimum: 3306 + type: integer + schemafile: + description: 表结构文件 + type: string + required: + - host + - port + - remote_host + - remote_port + - schemafile + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SlaveInfo: + properties: + id: + description: slave id + type: integer + ip: + description: slave ip + type: string + port: + description: slave port + type: integer + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SuperAccount: + properties: + access_hosts: + items: + type: string + type: array + pwd: + type: string + user: + type: string + required: + - pwd + - user + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_dbbackup.Cnf: + properties: + BackupClient: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_dbbackup.CnfBackupClient' + LogicalBackup: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_dbbackup.CnfLogicalBackup' + PhysicalBackup: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_dbbackup.CnfPhysicalBackup' + description: LogicalLoad CnfLogicalLoad `json:"LogicalLoad" + ini:"LogicalLoad"` + Public: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_dbbackup.CnfShared' + required: + - BackupClient + - LogicalBackup + - Public + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_dbbackup.CnfBackupClient: + properties: + doChecksum: + type: string + fileTag: + type: string + remoteFileSystem: + type: string + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_dbbackup.CnfLogicalBackup: + properties: + chunkFilesize: + description: ChunkFilesize Split tables into chunks of this output file size. + This value is in MB + type: integer + defaultsFile: + type: string + disableCompress: + type: boolean + extraOpt: + description: ExtraOpt other mydumper options string to be appended + type: string + flushRetryCount: + type: integer + regex: + type: string + threads: + type: integer + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_dbbackup.CnfPhysicalBackup: + properties: + defaultsFile: + type: string + extraOpt: + description: ExtraOpt other xtrabackup options string to be appended + type: string + splitSpeed: + description: SplitSpeed tar split limit in MB/s, default 300 + type: integer + threads: + description: Threads –parallel to copy files + type: integer + throttle: + description: Throttle limits the number of chunks copied per second. The chunk + size is 10 MB, 0 means no limit + type: integer + required: + - defaultsFile + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_dbbackup.CnfShared: + properties: + backupDir: + type: string + backupId: + type: string + backupTimeOut: + type: string + backupType: + type: string + billId: + type: string + bkBizId: + type: string + bkCloudId: + type: string + clusterAddress: + type: string + clusterId: + type: string + dataSchemaGrant: + type: string + iolimitMBPerSec: + description: IOLimitMBPerSec tar or split default io limit, mb/s. 0 means + no limit + type: integer + mysqlCharset: + type: string + mysqlHost: + type: string + mysqlPasswd: + type: string + mysqlPort: + type: string + mysqlRole: + type: string + mysqlUser: + type: string + oldFileLeftDay: + type: string + resultReportPath: + type: string + statusReportPath: + type: string + tarSizeThreshold: + description: TarSizeThreshold tar file will be split to this package size. + MB + minimum: 128 + type: integer + required: + - backupDir + - backupTimeOut + - tarSizeThreshold + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_dbbackup.LogicBackupDataOption: + properties: + DataSchemaGrant: + description: '"grant,schema,data"' + type: string + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_grant.GrantReplComp: + properties: + db: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.DbWorker' + description: 本地db链接 + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_grant.GrantReplParam' + general: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_grant.GrantReplParam: + properties: + host: + description: 当前实例的主机地址 + type: string + port: + description: 当前实例的端口 + type: integer + repl_hosts: + description: slave host + items: + type: string + type: array + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.MySQLBinlogUtil: + properties: + databases: + description: row event 解析指定 databases + items: + type: string + type: array + databases_ignore: + description: row event 解析指定 忽略 databases + items: + type: string + type: array + filter_statement_match_error: + description: 匹配字符串成功,则解析 binlog 报错 + type: string + filter_statement_match_ignore: + description: 匹配字符串成功,则忽略语句,加入注释中 + type: string + filter_statement_match_ignore_force: + description: |- + 匹配字符串成功,强制忽略语句,加入注释中。当与 filter_statement_match_error 都匹配时,ignore_force会优先生效 + 默认 infodba_schema + type: string + flashback: + description: 是否启用 flashback + type: boolean + idempotent_mode: + description: 是否开启幂等模式, mysql --slave-exec-mode=idempotent or mysqlbinlog --idempotent + type: boolean + mysql_client_opt: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.MySQLClientOpt' + not_write_binlog: + description: 导入时是否记录 binlog, mysql sql_log_bin=0 or mysqlbinlog --disable-log-bin. + true表示不写 + type: boolean + query_event_handler: + description: |- + query event 默认处理策略。keep:保留解析出的query event 语句, ignore:注释(丢弃)该 query event, error:认为是不接受的语句,报错 + 默认 keep + enum: + - keep + - ignore + - safe + - error + type: string + rewrite_db: + description: --rewrite_db="db1->xx_db1,db2->xx_db2" + type: string + start_pos: + description: --start-position + type: integer + start_time: + description: --start-datetime + type: string + stop_pos: + description: --stop-position + type: integer + stop_time: + description: --stop-datetime + type: string + tables: + description: row event 解析指定 tables + items: + type: string + type: array + tables_ignore: + description: row event 解析指定 忽略 tables + items: + type: string + type: array + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.MySQLClientOpt: + properties: + binary_mode: + description: 是否启用 --binary-mode + type: boolean + max_allowed_packet: + type: integer + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RecoverBinlog: + properties: + binlog_dir: + description: 恢复时 binlog 存放目录,一般是下载目录 + example: /data/dbbak/123456/binlog + type: string + binlog_files: + description: binlog列表 + items: + type: string + type: array + binlog_start_file: + description: 指定要开始应用的第 1 个 binlog。如果指定,一般要设置 start_pos,如果不指定则使用 start_time + type: string + parse_concurrency: + description: 解析的并发度,默认 1 + type: integer + parse_only: + description: 仅解析 binlog,不做导入 + type: boolean + quick_mode: + description: |- + 如果启用 quick_mode,解析 binlog 时根据 filter databases 等选项过滤 row event,对 query event 会全部保留 。需要 mysqlbinlog 工具支持 --tables 选项,可以指定参数的 tools + 当 quick_mode=false 时,recover_opt 里的 databases 等选项无效,会应用全部 binlog + type: boolean + recover_opt: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.MySQLBinlogUtil' + source_binlog_format: + enum: + - "" + - ROW + - STATEMENT + - MIXED + type: string + tgt_instance: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.InsObject' + tools: + additionalProperties: + type: string + description: 外部指定工具路径 + type: object + work_dir: + description: binlog 解析所在目录,存放运行日志 + example: /data/dbbak/ + type: string + work_id: + example: "123456" + type: string + required: + - binlog_dir + - binlog_files + - recover_opt + - tgt_instance + - work_dir + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RecoverBinlogComp: + properties: + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RecoverBinlog' + general: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RestoreDRComp: + properties: + extend: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RestoreParam' + description: 恢复参数,会复制给具体的 Restore 实现. 见 ChooseType 方法 + general: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam' + description: 通用参数 + resume: + description: 是否是中断后继续执行 + type: boolean + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RestoreOpt: + properties: + databases: + description: 恢复哪些 db,当前只对 逻辑恢复有效 + items: + type: string + type: array + ignore_databases: + items: + type: string + type: array + ignore_tables: + items: + type: string + type: array + recover_binlog: + description: |- + 在指定时间点回档场景才需要,是否恢复 binlog。在 doSlave 场景,是不需要 recover_binlog。这个选项是控制下一步恢复binlog的行为 + 当 recover_binlog 时,要确保实例的所有库表结构都恢复。在逻辑回档场景,只回档部分库表数据时,依然要恢复所有表结构 + type: boolean + recover_privs: + type: boolean + source_binlog_format: + description: |- + 在库表级定点回档时有用,如果是 statement/mixed 格式,导入数据时需要全部导入; + 如果是 row,可只导入指定库表数据, 在 recover-binlog 时可指定 quick_mode=true 也恢复指定库表 binlog + enum: + - "" + - ROW + - STATEMENT + - MIXED + type: string + tables: + items: + type: string + type: array + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RestoreParam: + properties: + backup_dir: + description: 备份文件所在本地目录,理论上doDr不会对该目录写入,而是写入 targetDir + example: /data/dbbak + type: string + backup_files: + additionalProperties: + items: + type: string + type: array + description: 备份文件名列表,key 是 info|full|priv|index, value 是是相对于 backup_dir 的文件名列表 + type: object + change_master: + description: 恢复完成后是否执行 change master,会 change master 到 src_instance + type: boolean + restore_opts: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RestoreOpt' + description: 恢复选项,比如恢复库表、是否导入binlog等。目前只对逻辑恢复有效 + src_instance: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.Instance' + description: 备份实例的 ip port,用于生产 change master 语句 + tgt_instance: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.InsObject' + description: 恢复本地的目标实例 + tools: + allOf: + - $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_tools.ToolSet' + description: 恢复用到的客户端工具,不提供时会有默认值 + work_dir: + description: 备份恢复目录,工作目录 + example: /data1/dbbak + type: string + work_id: + description: work_id 标识本次恢复,若为0则为当前时间戳 + type: string + required: + - backup_dir + - backup_files + - work_dir + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_rollback.Flashback: + properties: + binlog_dir: + description: 当 binlog_dir 不为空,表示 binlog 已下载;当为空时,目前只从本地软连接 + type: string + binlog_files: + description: binlog列表,如果不提供,则自动从本地查找符合时间范围的 binlog + items: + type: string + type: array + parse_concurrency: + description: 解析binlog并发度 + type: integer + recover_opt: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_rollback.RecoverOpt' + stop_time: + type: string + target_time: + description: 闪回的目标时间点,对应 recover-binlog 的 start_time, 精确到秒。目标实例的时区 + type: string + tgt_instance: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_native.InsObject' + tools: + additionalProperties: + type: string + description: 外部指定工具路径 + type: object + work_dir: + description: binlog 解析所在目录,存放运行日志 + type: string + work_id: + type: string + required: + - recover_opt + - target_time + - tgt_instance + - work_dir + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_rollback.FlashbackComp: + properties: + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_rollback.Flashback' + general: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_rollback.RecoverOpt: + properties: + databases: + description: row event 解析指定 databases + items: + type: string + type: array + databases_ignore: + description: row event 解析指定 忽略 databases + items: + type: string + type: array + filter_rows: + description: 暂不支持行级闪回 + type: string + tables: + description: row event 解析指定 tables + items: + type: string + type: array + tables_ignore: + description: row event 解析指定 忽略 tables + items: + type: string + type: array + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_spiderctl.InitClusterRoutingComp: + properties: + extend: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_spiderctl.InitClusterRoutingParam' + general: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components.GeneralParam' + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_spiderctl.InitClusterRoutingParam: + properties: + ctl_instances: + items: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_spiderctl.Instance' + type: array + host: + type: string + mysql_instances: + items: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_spiderctl.Instance' + type: array + port: + minimum: 3306 + type: integer + spider_instances: + items: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_spiderctl.Instance' + type: array + required: + - ctl_instances + - host + - mysql_instances + - port + - spider_instances + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_components_spiderctl.Instance: + properties: + host: + type: string + port: + type: integer + shard_id: + type: integer + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_native.DbWorker: + properties: + db: + $ref: '#/definitions/sql.DB' + dsn: + type: string + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_native.InsObject: + properties: + charset: + description: 连接字符集 + type: string + host: + description: 当前实例的主机地址 + type: string + options: + description: 其它选项 + type: string + port: + description: 当前实例的端口 + type: integer + pwd: + description: 连接当前实例的User Pwd + type: string + socket: + description: 连接socket + type: string + user: + description: 连接当前实例的User + type: string + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_native.Instance: + properties: + host: + description: 当前实例的主机地址 + example: 127.0.0.1 + type: string + port: + description: 当前实例的端口 + example: 33060 + type: integer + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_tools.ToolSet: + properties: + tools: + additionalProperties: + type: string + description: 外部指定工具路径 + type: object + type: object + dbm-services_mysql_db-tools_dbactuator_pkg_util_mysqlutil.ChangeMaster: + properties: + change_sql: + type: string + channel: + type: string + executed_gtid_set: + type: string + force: + description: 如果当前实例存在主从关系是否直接reset slave后,强制change master + type: boolean + is_gtid: + description: 是否启动GID方式进行建立主从 + type: boolean + master_auto_position: + type: integer + master_host: + description: 主库ip + type: string + master_log_file: + description: binlog 文件名称 + type: string + master_log_pos: + description: binlog 位点信息 + type: integer + master_password: + type: string + master_port: + description: 主库端口 + minimum: 3306 + type: integer + master_user: + type: string + max_tolerate_delay: + description: 最大容忍延迟,即主从延迟小于该值,认为建立主从关系成功 + type: integer + required: + - master_host + - master_password + - master_port + - master_user + type: object + internal_subcmd_commoncmd.RMLargeFileParam: + properties: + bw_limit_mb: + default: 30 + description: 删除速度,MB/s,默认 30 + maximum: 1000 + minimum: 1 + type: integer + filename: + type: string + required: + - bw_limit_mb + - filename + type: object + sql.DB: + type: object +host: ./dbactuator +info: + contact: + email: support@swagger.io + name: API Support + url: http://www.swagger.io/support + description: This is a dbactuator command collection. + license: + name: Apache 2.0 + url: http://www.apache.org/licenses/LICENSE-2.0.html + termsOfService: http://swagger.io/terms/ + title: dbactuator API + version: 0.0.1 +paths: + /common/file-server: + post: + consumes: + - application/json + description: |- + 通过 http 暴露指定目录可用于下载,可用于在重建备库时,从其它机器下载备份 + 在 OS 不允许 ssh 登录(scp/sftp)时,可以临时启动该服务来获取备份文件 + parameters: + - description: short description + in: body + name: body + required: true + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_fileserver.FileServerComp' + responses: {} + summary: 简单文件服务 + tags: + - common + /common/rm-file: + post: + consumes: + - application/json + parameters: + - description: short description + in: body + name: body + required: true + schema: + $ref: '#/definitions/internal_subcmd_commoncmd.RMLargeFileParam' + responses: {} + summary: 限速删除大文件 + tags: + - common + /download/http: + post: + consumes: + - application/json + description: |- + 支持限速、basicAuth 认证. 一般配合 common fileserver 使用 + # server1 + ./dbactuator common file-server \ + --payload-format raw \ + --payload '{"extend":{"bind_address":":8082","mount_path":"/data/dbbak","user":"xiaog","password":"xxxx","proc_maxidle_duration":"60s"}}' + + # server2 + curl -u 'xiaog:xxxx' 'http://server1:8082/datadbbak8082/dbactuator' -o dbactuator.bin --limit-rate 10k + parameters: + - description: short description + in: body + name: body + required: true + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.DFHttpParam' + responses: {} + summary: http下载文件 + tags: + - download + /download/ibs-query: + post: + consumes: + - application/json + description: filename 会进行模糊匹配,返回 task_id 用于下载 + parameters: + - description: short description + in: body + name: body + required: true + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSQueryComp' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSQueryResult' + summary: 从 ieg 备份系统查询文件 + tags: + - download + /download/ibs-recover: + post: + consumes: + - application/json + description: |- + 提供 task_id,从 ieg 备份系统下载文件 + task_files_wild: 模糊搜索文件并下载, task_files: 精确文件查询并下载 + task_files_wild, task_files 二选一 + 启用 skip_local_exists=true 时,如果目标目录已存在要下载的文件,会自动跳过 + parameters: + - description: short description + in: body + name: body + required: true + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSRecoverComp' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.IBSRecoverTask' + summary: 从 ieg 备份系统下载文件 + tags: + - download + /download/scp: + post: + consumes: + - application/json + description: 支持限速 + parameters: + - description: short description + in: body + name: body + required: true + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_backup_download.DFScpParam' + responses: {} + summary: scp下载文件 + tags: + - download + /mysql/change-master: + post: + consumes: + - application/json + description: 执行 change master to + parameters: + - description: short description + in: body + name: body + required: true + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BuildMSRelationComp' + responses: {} + summary: 建立主从关系 + tags: + - mysql + /mysql/clean-mysql: + post: + consumes: + - application/json + description: 清空本地实例,保留系统库 + parameters: + - description: description + in: body + name: body + required: true + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.CleanMysqlComp' + produces: + - application/json + responses: {} + summary: 清空实例,高危 + tags: + - mysql + /mysql/deploy: + post: + consumes: + - application/json + description: 部署 mysql 实例说明 + parameters: + - description: short description + in: body + name: body + required: true + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLComp' + responses: {} + summary: 部署 mysql 实例 + tags: + - mysql + /mysql/deploy-dbbackup: + post: + consumes: + - application/json + description: 部署GO版本备份程序 + parameters: + - description: short description + in: body + name: body + required: true + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallNewDbBackupComp' + responses: {} + summary: 部署备份程序 + tags: + - mysql + /mysql/find-local-backup: + post: + consumes: + - application/json + description: 查找本地备份 + parameters: + - description: short description + in: body + name: body + required: true + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.FindLocalBackupParam' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.FindLocalBackupResp' + summary: 查找本地备份 + tags: + - mysql + /mysql/flashback-binlog: + post: + consumes: + - application/json + description: 通过 `mysqlbinlog --flashback xxx | mysql` 导入 binlog + parameters: + - description: short description + in: body + name: body + required: true + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_rollback.FlashbackComp' + responses: {} + summary: 导入 binlog + tags: + - mysql + /mysql/grant-repl: + post: + consumes: + - application/json + description: 在目标机器新建 repl 账号 + parameters: + - description: short description + in: body + name: body + required: true + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_grant.GrantReplComp' + responses: {} + summary: 建立复制账号 + tags: + - mysql + /mysql/init-cluster-routing: + post: + consumes: + - application/json + description: 初始化tendb cluster 集群的路由关系说明 + parameters: + - description: short description + in: body + name: body + required: true + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_spiderctl.InitClusterRoutingComp' + responses: {} + summary: 初始化tendb cluster 集群的路由关系 + tags: + - spiderctl + /mysql/install-checksum: + post: + consumes: + - application/json + description: 安装mysql校验 + parameters: + - description: short description + in: body + name: body + required: true + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLChecksumComp' + responses: {} + summary: 安装mysql校验 + tags: + - mysql + /mysql/install-dbatoolkit: + post: + consumes: + - application/json + description: 部署 /home/mysql/dba_toolkit,覆盖 + parameters: + - description: short description + in: body + name: body + required: true + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallDBAToolkitComp' + responses: {} + summary: 部署DBA工具箱 + tags: + - mysql + /mysql/mycnf-change: + post: + consumes: + - application/json + description: 修改mysql配置 + parameters: + - description: description + in: body + name: body + required: true + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.MycnfChangeComp' + produces: + - application/json + responses: {} + summary: 修改mysql配置 + tags: + - mysql + /mysql/mycnf-clone: + post: + consumes: + - application/json + description: |- + 用于 slave 重建或迁移,保持新实例与 my.cnf 实例关键参数相同的场景 + 默认 clone 参数: + parameters: + - description: description + in: body + name: body + required: true + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.MycnfCloneComp' + produces: + - application/json + responses: {} + summary: 从源实例克隆 my.cnf 部分参数到目标实例 + tags: + - mysql + /mysql/parse-binlog-time: + post: + consumes: + - application/json + description: 获取 binlog FileDescriptionFormat 和 RotateEvent 事件 + parameters: + - description: short description + in: body + name: body + required: true + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.BinlogTimeComp' + responses: {} + summary: 获取 binlog 的开始和结束时间 + tags: + - mysql + /mysql/pt-table-checksum: + post: + consumes: + - application/json + description: 数据校验 + parameters: + - description: description + in: body + name: body + required: true + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.PtTableChecksumComp' + responses: {} + summary: 数据校验 + tags: + - mysql + /mysql/recover-binlog: + post: + consumes: + - application/json + description: 通过 `mysqlbinlog xxx | mysql` 导入 binlog + parameters: + - description: short description + in: body + name: body + required: true + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RecoverBinlogComp' + responses: {} + summary: 导入 binlog + tags: + - mysql + /mysql/restore-dr: + post: + consumes: + - application/json + description: 物理备份、逻辑备份恢复 + parameters: + - description: short description + in: body + name: body + required: true + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql_restore.RestoreDRComp' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_util_mysqlutil.ChangeMaster' + summary: 备份恢复 + tags: + - mysql + /mysql/semantic-check: + post: + consumes: + - application/json + description: 运行语义检查 + parameters: + - description: short description + in: body + name: body + required: true + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SemanticCheckComp' + produces: + - application/json + responses: {} + summary: 运行语义检查 + tags: + - mysql + /mysql/semantic-dumpschema: + post: + consumes: + - application/json + description: 运行语义检查 + parameters: + - description: short description + in: body + name: body + required: true + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.SemanticDumpSchemaComp' + produces: + - application/json + responses: {} + summary: 运行语义检查 + tags: + - mysql + /spdierctl/deploy: + post: + consumes: + - application/json + description: 部署 spider ctl 实例说明 + parameters: + - description: short description + in: body + name: body + required: true + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLComp' + responses: {} + summary: 部署 spider ctl 实例 + tags: + - spiderctl + /spider/deploy: + post: + consumes: + - application/json + description: 部署 spider 实例说明 + parameters: + - description: short description + in: body + name: body + required: true + schema: + $ref: '#/definitions/dbm-services_mysql_db-tools_dbactuator_pkg_components_mysql.InstallMySQLComp' + responses: {} + summary: 部署 spider 实例 + tags: + - spider +schemes: +- http +swagger: "2.0" diff --git a/dbm-services/riak/db-tools/dbactuator/go.mod b/dbm-services/riak/db-tools/dbactuator/go.mod new file mode 100644 index 0000000000..d0057aa1b1 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/go.mod @@ -0,0 +1,28 @@ +module dbm-services/riak/db-tools/dbactuator + +go 1.19 + +require ( + github.com/MakeNowJust/heredoc v1.0.0 + github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 + github.com/caarlos0/env/v6 v6.10.1 + github.com/dustin/go-humanize v1.0.1 + github.com/gofrs/flock v0.8.1 + github.com/golang-jwt/jwt/v4 v4.5.0 + github.com/pkg/errors v0.9.1 + github.com/spf13/cobra v1.7.0 + golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 + gopkg.in/ini.v1 v1.67.0 + gopkg.in/yaml.v2 v2.4.0 +) + +require ( + github.com/fatih/color v1.15.0 // indirect + github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/stretchr/testify v1.8.4 // indirect + golang.org/x/sys v0.6.0 // indirect +) diff --git a/dbm-services/riak/db-tools/dbactuator/go.sum b/dbm-services/riak/db-tools/dbactuator/go.sum new file mode 100644 index 0000000000..b072bde61d --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/go.sum @@ -0,0 +1,48 @@ +github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= +github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= +github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 h1:ZBbLwSJqkHBuFDA6DUhhse0IGJ7T5bemHyNILUjvOq4= +github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2/go.mod h1:VSw57q4QFiWDbRnjdX8Cb3Ow0SFncRw+bA/ofY6Q83w= +github.com/caarlos0/env/v6 v6.10.1 h1:t1mPSxNpei6M5yAeu1qtRdPAK29Nbcf/n3G7x+b3/II= +github.com/caarlos0/env/v6 v6.10.1/go.mod h1:hvp/ryKXKipEkcuYjs9mI4bBCg+UI0Yhgm5Zu0ddvwc= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f h1:7LYC+Yfkj3CTRcShK0KOL/w6iTiKyqqBA9a41Wnggw8= +github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f/go.mod h1:pFlLw2CfqZiIBOx6BuCeRLCrfxBJipTY0nIOF/VbGcI= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= +golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/check_connections.go b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/check_connections.go new file mode 100644 index 0000000000..87fde17e72 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/check_connections.go @@ -0,0 +1,67 @@ +package riakcmd + +import ( + "fmt" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/riak/db-tools/dbactuator/internal/subcmd" + "dbm-services/riak/db-tools/dbactuator/pkg/components/riak" + "dbm-services/riak/db-tools/dbactuator/pkg/util" + + "github.com/spf13/cobra" +) + +// CheckConnectionsAct 搬迁数据进度riak dbactor参数 +type CheckConnectionsAct struct { + *subcmd.BaseOptions + Payload riak.CheckConnectionsComp +} + +// NewCheckConnectionsCommand riak搬迁数据进度 +func NewCheckConnectionsCommand() *cobra.Command { + act := CheckConnectionsAct{ + BaseOptions: subcmd.GBaseOptions, + } + cmd := &cobra.Command{ + Use: "check-connections", + Short: "检查连接", + Example: fmt.Sprintf("dbactuator riak check-connections %s", subcmd.CmdBaseExampleStr), + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(act.Validator()) + util.CheckErr(act.Init()) + util.CheckErr(act.Run()) + }, + } + return cmd +} + +// Validator TODO +func (d *CheckConnectionsAct) Validator() error { + return d.BaseOptions.Validate() +} + +// Init 反序列化并检查 +func (d *CheckConnectionsAct) Init() error { + if err := d.DeserializeAndValidate(&d.Payload); err != nil { + logger.Error("DeserializeAndValidate err %s", err.Error()) + return err + } + return nil +} + +// Run 运行 +func (d *CheckConnectionsAct) Run() error { + steps := subcmd.Steps{ + { + FunName: "检查是否有业务连接", + Func: d.Payload.CheckConnections, + }, + } + // 有连接,检查失败 + if err := steps.Run(); err != nil { + return err + } + // 没有连接,检查成功 + logger.Info("no connections") + return nil +} diff --git a/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/cmd.go b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/cmd.go new file mode 100644 index 0000000000..7bcfebed51 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/cmd.go @@ -0,0 +1,44 @@ +// Package riakcmd TODO +/* + * @Description: proxy 相关操作的子命令集合 + */ +package riakcmd + +import ( + "dbm-services/riak/db-tools/dbactuator/internal/subcmd" + "dbm-services/riak/db-tools/dbactuator/pkg/util/templates" + + "github.com/spf13/cobra" +) + +// NewRiakCommand TODO +func NewRiakCommand() *cobra.Command { + cmds := &cobra.Command{ + Use: "riak [riak operation]", + Short: "Riak Operation Command Line Interface", + RunE: subcmd.ValidateSubCommand(), + } + groups := templates.CommandGroups{ + { + Message: "riak", + Commands: []*cobra.Command{ + NewDeployRiakCommand(), + NewJoinClusterCommand(), + NewCommitClusterChangeCommand(), + NewInitBucketCommand(), + NewGetConfigCommand(), + NewCheckConnectionsCommand(), + NewRemoveNodeCommand(), + NewTransferCommand(), + NewUninstallCommand(), + NewStopCommand(), + NewStartCommand(), + NewDeployMonitorCommand(), + NewStartMonitorCommand(), + NewStopMonitorCommand(), + }, + }, + } + groups.Add(cmds) + return cmds +} diff --git a/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/commit_cluster_change.go b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/commit_cluster_change.go new file mode 100644 index 0000000000..e748f52f11 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/commit_cluster_change.go @@ -0,0 +1,66 @@ +package riakcmd + +import ( + "fmt" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/riak/db-tools/dbactuator/internal/subcmd" + "dbm-services/riak/db-tools/dbactuator/pkg/components/riak" + "dbm-services/riak/db-tools/dbactuator/pkg/util" + + "github.com/spf13/cobra" +) + +// CommitClusterChangeAct 安装riak dbactor参数 +type CommitClusterChangeAct struct { + *subcmd.BaseOptions + Payload riak.CommitClusterChangeComp +} + +// NewCommitClusterChangeCommand 部署riak节点 +func NewCommitClusterChangeCommand() *cobra.Command { + act := CommitClusterChangeAct{ + BaseOptions: subcmd.GBaseOptions, + } + cmd := &cobra.Command{ + Use: "commit-cluster-change", + Short: "提交集群变化", + Example: fmt.Sprintf("dbactuator riak commit-cluster-change %s", subcmd.CmdBaseExampleStr), + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(act.Validator()) + util.CheckErr(act.Init()) + util.CheckErr(act.Run()) + }, + } + return cmd +} + +// Validator TODO +func (d *CommitClusterChangeAct) Validator() error { + return d.BaseOptions.Validate() +} + +// Init 反序列化并检查 +func (d *CommitClusterChangeAct) Init() error { + if err := d.DeserializeAndValidate(&d.Payload); err != nil { + logger.Error("DeserializeAndValidate err %s", err.Error()) + return err + } + return nil +} + +// Run 执行 +func (d *CommitClusterChangeAct) Run() error { + steps := subcmd.Steps{ + { + FunName: "提交集群变化", + Func: d.Payload.CommitClusterChange, + }, + } + + if err := steps.Run(); err != nil { + return err + } + logger.Info("commit cluster change success") + return nil +} diff --git a/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/deploy_monitor.go b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/deploy_monitor.go new file mode 100644 index 0000000000..123e139801 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/deploy_monitor.go @@ -0,0 +1,91 @@ +package riakcmd + +import ( + "fmt" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/riak/db-tools/dbactuator/internal/subcmd" + "dbm-services/riak/db-tools/dbactuator/pkg/components/riak" + "dbm-services/riak/db-tools/dbactuator/pkg/util" + + "github.com/spf13/cobra" +) + +// DeployMonitorAct 部署riak监控dbactor参数 +type DeployMonitorAct struct { + *subcmd.BaseOptions + Payload riak.DeployMonitorComp +} + +// NewDeployMonitorCommand riak搬迁数据进度 +func NewDeployMonitorCommand() *cobra.Command { + act := DeployMonitorAct{ + BaseOptions: subcmd.GBaseOptions, + } + cmd := &cobra.Command{ + Use: "deploy-monitor", + Short: "部署监控", + Example: fmt.Sprintf("dbactuator riak deploy-monitor %s", subcmd.CmdBaseExampleStr), + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(act.Validator()) + util.CheckErr(act.Init()) + util.CheckErr(act.Run()) + }, + } + return cmd +} + +// Validator TODO +func (d *DeployMonitorAct) Validator() error { + return d.BaseOptions.Validate() +} + +// Init 反序列化并检查 +func (d *DeployMonitorAct) Init() error { + if err := d.DeserializeAndValidate(&d.Payload); err != nil { + logger.Error("DeserializeAndValidate err %s", err.Error()) + return err + } + return nil +} + +// Run 运行 +func (d *DeployMonitorAct) Run() error { + steps := subcmd.Steps{ + { + FunName: "解压crond和monitor介质", + Func: d.Payload.DeployBinary, + }, + // riak使用mysql-crond实现定时功能 + // 生成mysql-crond与riak-monitor的配置文件,部分根据template生成 + // 部分直接生成yaml + { + FunName: "生成crond的runtime.yaml文件", + Func: d.Payload.GenerateCrondConfigYaml, + }, + { + FunName: "生成monitor的runtime.yaml文件", + Func: d.Payload.GenerateMonitorConfigYaml, + }, + { + FunName: "生成crond的jobs-config.yaml文件", + Func: d.Payload.GenerateJobsConfigYaml, + }, + { + FunName: "生成monitor的items-config.yaml文件", + Func: d.Payload.GenerateItemsConfigYaml, + }, + // 前台预执行便于发现执行报错,然后后台正式执行 + { + FunName: "部署监控", + Func: d.Payload.DeployMonitor, + }, + } + // 部署失败 + if err := steps.Run(); err != nil { + return err + } + // 部署成功 + logger.Info("deploy monitor success") + return nil +} diff --git a/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/get_config.go b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/get_config.go new file mode 100644 index 0000000000..8efe373a99 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/get_config.go @@ -0,0 +1,70 @@ +package riakcmd + +import ( + "fmt" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/riak/db-tools/dbactuator/internal/subcmd" + "dbm-services/riak/db-tools/dbactuator/pkg/components/riak" + "dbm-services/riak/db-tools/dbactuator/pkg/util" + + "github.com/spf13/cobra" +) + +// GetConfigAct 安装riak dbactor参数 +type GetConfigAct struct { + *subcmd.BaseOptions + Payload riak.GetConfigComp +} + +// NewGetConfigCommand 获取riak配置 +func NewGetConfigCommand() *cobra.Command { + act := GetConfigAct{ + BaseOptions: subcmd.GBaseOptions, + } + cmd := &cobra.Command{ + Use: "get-config", + Short: "获取riak配置", + Example: fmt.Sprintf("dbactuator riak get-config %s", subcmd.CmdBaseExampleStr), + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(act.Validator()) + util.CheckErr(act.Init()) + util.CheckErr(act.Run()) + }, + } + return cmd +} + +// Validator TODO +func (d *GetConfigAct) Validator() error { + return d.BaseOptions.Validate() +} + +// Init 反序列化并检查 +func (d *GetConfigAct) Init() error { + if err := d.DeserializeAndValidate(&d.Payload); err != nil { + logger.Error("DeserializeAndValidate err %s", err.Error()) + return err + } + return nil +} + +// Run 执行 +func (d *GetConfigAct) Run() error { + steps := subcmd.Steps{ + { + FunName: "获取配置", + Func: d.Payload.GetConfig, + }, + { + FunName: "输出配置", + Func: d.Payload.OutputConfigInfo, + }, + } + if err := steps.Run(); err != nil { + return err + } + // 获取riak配置参数成功 + logger.Info("get config success") + return nil +} diff --git a/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/init_bucket_type.go b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/init_bucket_type.go new file mode 100644 index 0000000000..8a54206630 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/init_bucket_type.go @@ -0,0 +1,66 @@ +package riakcmd + +import ( + "fmt" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/riak/db-tools/dbactuator/internal/subcmd" + "dbm-services/riak/db-tools/dbactuator/pkg/components/riak" + "dbm-services/riak/db-tools/dbactuator/pkg/util" + + "github.com/spf13/cobra" +) + +// InitBucketAct 安装riak dbactor参数 +type InitBucketAct struct { + *subcmd.BaseOptions + Payload riak.InitBucketComp +} + +// NewInitBucketCommand 部署riak节点 +func NewInitBucketCommand() *cobra.Command { + act := InitBucketAct{ + BaseOptions: subcmd.GBaseOptions, + } + cmd := &cobra.Command{ + Use: "init-bucket-type", + Short: "初始化bucket-type", + Example: fmt.Sprintf("dbactuator riak init-bucket-type %s", subcmd.CmdBaseExampleStr), + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(act.Validator()) + util.CheckErr(act.Init()) + util.CheckErr(act.Run()) + }, + } + return cmd +} + +// Validator TODO +func (d *InitBucketAct) Validator() error { + return d.BaseOptions.Validate() +} + +// Init 反序列化并检查 +func (d *InitBucketAct) Init() error { + if err := d.DeserializeAndValidate(&d.Payload); err != nil { + logger.Error("DeserializeAndValidate err %s", err.Error()) + return err + } + return nil +} + +// Run 运行 +func (d *InitBucketAct) Run() error { + steps := subcmd.Steps{ + { + FunName: "初始化bucket type", + Func: d.Payload.InitBucketType, + }, + } + + if err := steps.Run(); err != nil { + return err + } + logger.Info("init bucket type success") + return nil +} diff --git a/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/install_riak.go b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/install_riak.go new file mode 100644 index 0000000000..f2d6717f33 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/install_riak.go @@ -0,0 +1,86 @@ +package riakcmd + +import ( + "fmt" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/riak/db-tools/dbactuator/internal/subcmd" + "dbm-services/riak/db-tools/dbactuator/pkg/components/riak" + "dbm-services/riak/db-tools/dbactuator/pkg/util" + + "github.com/spf13/cobra" +) + +// InstallRiakAct 安装riak dbactor参数 +type InstallRiakAct struct { + *subcmd.BaseOptions + Payload riak.InstallRiakComp +} + +// NewDeployRiakCommand 部署riak节点 +func NewDeployRiakCommand() *cobra.Command { + act := InstallRiakAct{ + BaseOptions: subcmd.GBaseOptions, + } + cmd := &cobra.Command{ + Use: "deploy", + Short: "部署riak节点", + Example: fmt.Sprintf("dbactuator riak deploy %s", subcmd.CmdBaseExampleStr), + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(act.Validator()) + util.CheckErr(act.Init()) + util.CheckErr(act.Run()) + }, + } + return cmd +} + +// Validator TODO +func (d *InstallRiakAct) Validator() error { + return d.BaseOptions.Validate() +} + +// Init 反序列化并检查 +func (d *InstallRiakAct) Init() error { + if err := d.DeserializeAndValidate(&d.Payload); err != nil { + logger.Error("DeserializeAndValidate err %s", err.Error()) + return err + } + return nil +} + +// Run 运行 +func (d *InstallRiakAct) Run() error { + steps := subcmd.Steps{ + { + FunName: "初始化", + Func: d.Payload.Init, + }, + { + FunName: "环境预检查", + Func: d.Payload.PreCheck, + }, + { + FunName: "安装riak包", + Func: d.Payload.InstallRiakPackage, + }, + { + FunName: "生成riak.conf配置", + Func: d.Payload.CreateConfigFile, + }, + { + FunName: "启动Riak", + Func: d.Payload.Start, + }, + { + FunName: "检查状态", + Func: d.Payload.CheckRiakStatus, + }, + } + + if err := steps.Run(); err != nil { + return err + } + logger.Info("install riak success") + return nil +} diff --git a/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/join_cluster.go b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/join_cluster.go new file mode 100644 index 0000000000..ce7b69738a --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/join_cluster.go @@ -0,0 +1,70 @@ +package riakcmd + +import ( + "fmt" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/riak/db-tools/dbactuator/internal/subcmd" + "dbm-services/riak/db-tools/dbactuator/pkg/components/riak" + "dbm-services/riak/db-tools/dbactuator/pkg/util" + + "github.com/spf13/cobra" +) + +// JoinClusterAct 安装riak dbactor参数 +type JoinClusterAct struct { + *subcmd.BaseOptions + Payload riak.JoinClusterComp +} + +// NewJoinClusterCommand 部署riak节点 +func NewJoinClusterCommand() *cobra.Command { + act := JoinClusterAct{ + BaseOptions: subcmd.GBaseOptions, + } + cmd := &cobra.Command{ + Use: "join-cluster", + Short: "加入集群", + Example: fmt.Sprintf("dbactuator riak join-cluster %s", subcmd.CmdBaseExampleStr), + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(act.Validator()) + util.CheckErr(act.Init()) + util.CheckErr(act.Run()) + }, + } + return cmd +} + +// Validator TODO +func (d *JoinClusterAct) Validator() error { + return d.BaseOptions.Validate() +} + +// Init 反序列化并检查 +func (d *JoinClusterAct) Init() error { + if err := d.DeserializeAndValidate(&d.Payload); err != nil { + logger.Error("DeserializeAndValidate err %s", err.Error()) + return err + } + return nil +} + +// Run 运行 +func (d *JoinClusterAct) Run() error { + steps := subcmd.Steps{ + { + FunName: "环境预检查", + Func: d.Payload.PreCheck, + }, + { + FunName: "节点加入集群", + Func: d.Payload.JoinCluster, + }, + } + + if err := steps.Run(); err != nil { + return err + } + logger.Info("join cluster success") + return nil +} diff --git a/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/remove_node.go b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/remove_node.go new file mode 100644 index 0000000000..302a98f435 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/remove_node.go @@ -0,0 +1,70 @@ +package riakcmd + +import ( + "fmt" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/riak/db-tools/dbactuator/internal/subcmd" + "dbm-services/riak/db-tools/dbactuator/pkg/components/riak" + "dbm-services/riak/db-tools/dbactuator/pkg/util" + + "github.com/spf13/cobra" +) + +// RemoveNodeAct 剔除节点 riak dbactor参数 +type RemoveNodeAct struct { + *subcmd.BaseOptions + Payload riak.RemoveNodeComp +} + +// NewRemoveNodeCommand 剔除节点 riak节点 +func NewRemoveNodeCommand() *cobra.Command { + act := RemoveNodeAct{ + BaseOptions: subcmd.GBaseOptions, + } + cmd := &cobra.Command{ + Use: "remove-node", + Short: "集群剔除节点", + Example: fmt.Sprintf("dbactuator riak remove-node %s", subcmd.CmdBaseExampleStr), + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(act.Validator()) + util.CheckErr(act.Init()) + util.CheckErr(act.Run()) + }, + } + return cmd +} + +// Validator TODO +func (d *RemoveNodeAct) Validator() error { + return d.BaseOptions.Validate() +} + +// Init 反序列化并检查 +func (d *RemoveNodeAct) Init() error { + if err := d.DeserializeAndValidate(&d.Payload); err != nil { + logger.Error("DeserializeAndValidate err %s", err.Error()) + return err + } + return nil +} + +// Run 运行 +func (d *RemoveNodeAct) Run() error { + steps := subcmd.Steps{ + { + FunName: "环境预检查", + Func: d.Payload.PreCheck, + }, + { + FunName: "集群剔除节点", + Func: d.Payload.RemoveNode, + }, + } + + if err := steps.Run(); err != nil { + return err + } + logger.Info("remove node success") + return nil +} diff --git a/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/start.go b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/start.go new file mode 100644 index 0000000000..da1d116f5f --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/start.go @@ -0,0 +1,65 @@ +package riakcmd + +import ( + "fmt" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/riak/db-tools/dbactuator/internal/subcmd" + "dbm-services/riak/db-tools/dbactuator/pkg/components/riak" + "dbm-services/riak/db-tools/dbactuator/pkg/util" + + "github.com/spf13/cobra" +) + +// StartAct 搬迁数据进度riak dbactor参数 +type StartAct struct { + *subcmd.BaseOptions + Payload riak.StartComp +} + +// NewStartCommand riak搬迁数据进度 +func NewStartCommand() *cobra.Command { + act := StartAct{ + BaseOptions: subcmd.GBaseOptions, + } + cmd := &cobra.Command{ + Use: "start", + Short: "启动节点", + Example: fmt.Sprintf("dbactuator riak start %s", subcmd.CmdBaseExampleStr), + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(act.Validator()) + util.CheckErr(act.Init()) + util.CheckErr(act.Run()) + }, + } + return cmd +} + +// Validator TODO +func (d *StartAct) Validator() error { + return d.BaseOptions.Validate() +} + +// Init 反序列化并检查 +func (d *StartAct) Init() error { + if err := d.DeserializeAndValidate(&d.Payload); err != nil { + logger.Error("DeserializeAndValidate err %s", err.Error()) + return err + } + return nil +} + +// Run 运行 +func (d *StartAct) Run() error { + steps := subcmd.Steps{ + { + FunName: "启动节点", + Func: d.Payload.Start, + }, + } + if err := steps.Run(); err != nil { + return err + } + logger.Info("start success") + return nil +} diff --git a/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/start_monitor.go b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/start_monitor.go new file mode 100644 index 0000000000..f6b17b97ec --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/start_monitor.go @@ -0,0 +1,65 @@ +package riakcmd + +import ( + "fmt" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/riak/db-tools/dbactuator/internal/subcmd" + "dbm-services/riak/db-tools/dbactuator/pkg/components/riak" + "dbm-services/riak/db-tools/dbactuator/pkg/util" + + "github.com/spf13/cobra" +) + +// StartMonitorAct 启动监控riak dbactor参数 +type StartMonitorAct struct { + *subcmd.BaseOptions + Payload riak.StartMonitorComp +} + +// NewStartMonitorCommand riak搬迁数据进度 +func NewStartMonitorCommand() *cobra.Command { + act := StartMonitorAct{ + BaseOptions: subcmd.GBaseOptions, + } + cmd := &cobra.Command{ + Use: "start-monitor", + Short: "启动监控", + Example: fmt.Sprintf("dbactuator riak start-monitor %s", subcmd.CmdBaseExampleStr), + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(act.Validator()) + util.CheckErr(act.Init()) + util.CheckErr(act.Run()) + }, + } + return cmd +} + +// Validator TODO +func (d *StartMonitorAct) Validator() error { + return d.BaseOptions.Validate() +} + +// Init 反序列化并检查 +func (d *StartMonitorAct) Init() error { + if err := d.DeserializeAndValidate(&d.Payload); err != nil { + logger.Error("DeserializeAndValidate err %s", err.Error()) + return err + } + return nil +} + +// Run 运行 +func (d *StartMonitorAct) Run() error { + steps := subcmd.Steps{ + { + FunName: "启动监控", + Func: d.Payload.StartMonitor, + }, + } + if err := steps.Run(); err != nil { + return err + } + logger.Info("start monitor success") + return nil +} diff --git a/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/stop.go b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/stop.go new file mode 100644 index 0000000000..23df0eaf99 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/stop.go @@ -0,0 +1,65 @@ +package riakcmd + +import ( + "fmt" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/riak/db-tools/dbactuator/internal/subcmd" + "dbm-services/riak/db-tools/dbactuator/pkg/components/riak" + "dbm-services/riak/db-tools/dbactuator/pkg/util" + + "github.com/spf13/cobra" +) + +// StopAct 搬迁数据进度riak dbactor参数 +type StopAct struct { + *subcmd.BaseOptions + Payload riak.StopComp +} + +// NewStopCommand riak搬迁数据进度 +func NewStopCommand() *cobra.Command { + act := StopAct{ + BaseOptions: subcmd.GBaseOptions, + } + cmd := &cobra.Command{ + Use: "stop", + Short: "关闭节点", + Example: fmt.Sprintf("dbactuator riak stop %s", subcmd.CmdBaseExampleStr), + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(act.Validator()) + util.CheckErr(act.Init()) + util.CheckErr(act.Run()) + }, + } + return cmd +} + +// Validator TODO +func (d *StopAct) Validator() error { + return d.BaseOptions.Validate() +} + +// Init 反序列化并检查 +func (d *StopAct) Init() error { + if err := d.DeserializeAndValidate(&d.Payload); err != nil { + logger.Error("DeserializeAndValidate err %s", err.Error()) + return err + } + return nil +} + +// Run 运行 +func (d *StopAct) Run() error { + steps := subcmd.Steps{ + { + FunName: "关闭节点", + Func: d.Payload.Stop, + }, + } + if err := steps.Run(); err != nil { + return err + } + logger.Info("stop success") + return nil +} diff --git a/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/stop_monitor.go b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/stop_monitor.go new file mode 100644 index 0000000000..c8e7bc63ae --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/stop_monitor.go @@ -0,0 +1,65 @@ +package riakcmd + +import ( + "fmt" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/riak/db-tools/dbactuator/internal/subcmd" + "dbm-services/riak/db-tools/dbactuator/pkg/components/riak" + "dbm-services/riak/db-tools/dbactuator/pkg/util" + + "github.com/spf13/cobra" +) + +// StopMonitorAct 关闭监控riak dbactor参数 +type StopMonitorAct struct { + *subcmd.BaseOptions + Payload riak.StopMonitorComp +} + +// NewStopMonitorCommand riak关闭监控 +func NewStopMonitorCommand() *cobra.Command { + act := StopMonitorAct{ + BaseOptions: subcmd.GBaseOptions, + } + cmd := &cobra.Command{ + Use: "stop-monitor", + Short: "关闭监控", + Example: fmt.Sprintf("dbactuator riak stop-monitor %s", subcmd.CmdBaseExampleStr), + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(act.Validator()) + util.CheckErr(act.Init()) + util.CheckErr(act.Run()) + }, + } + return cmd +} + +// Validator TODO +func (d *StopMonitorAct) Validator() error { + return d.BaseOptions.Validate() +} + +// Init 反序列化并检查 +func (d *StopMonitorAct) Init() error { + if err := d.DeserializeAndValidate(&d.Payload); err != nil { + logger.Error("DeserializeAndValidate err %s", err.Error()) + return err + } + return nil +} + +// Run 运行 +func (d *StopMonitorAct) Run() error { + steps := subcmd.Steps{ + { + FunName: "关闭监控", + Func: d.Payload.StopMonitor, + }, + } + if err := steps.Run(); err != nil { + return err + } + logger.Info("stop monitor success") + return nil +} diff --git a/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/transfer.go b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/transfer.go new file mode 100644 index 0000000000..ff18ba5c53 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/transfer.go @@ -0,0 +1,65 @@ +package riakcmd + +import ( + "fmt" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/riak/db-tools/dbactuator/internal/subcmd" + "dbm-services/riak/db-tools/dbactuator/pkg/components/riak" + "dbm-services/riak/db-tools/dbactuator/pkg/util" + + "github.com/spf13/cobra" +) + +// TransferAct 搬迁数据进度riak dbactor参数 +type TransferAct struct { + *subcmd.BaseOptions + Payload riak.TransferComp +} + +// NewTransferCommand riak搬迁数据进度 +func NewTransferCommand() *cobra.Command { + act := TransferAct{ + BaseOptions: subcmd.GBaseOptions, + } + cmd := &cobra.Command{ + Use: "transfer", + Short: "数据搬迁进度", + Example: fmt.Sprintf("dbactuator riak transfer %s", subcmd.CmdBaseExampleStr), + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(act.Validator()) + util.CheckErr(act.Init()) + util.CheckErr(act.Run()) + }, + } + return cmd +} + +// Validator TODO +func (d *TransferAct) Validator() error { + return d.BaseOptions.Validate() +} + +// Init 反序列化并检查 +func (d *TransferAct) Init() error { + if err := d.DeserializeAndValidate(&d.Payload); err != nil { + logger.Error("DeserializeAndValidate err %s", err.Error()) + return err + } + return nil +} + +// Run 运行 +func (d *TransferAct) Run() error { + steps := subcmd.Steps{ + { + FunName: "搬迁数据进度", + Func: d.Payload.Transfer, + }, + } + if err := steps.Run(); err != nil { + return err + } + logger.Info("transfer data check success") + return nil +} diff --git a/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/uninstall.go b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/uninstall.go new file mode 100644 index 0000000000..e4255f6a32 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/riakcmd/uninstall.go @@ -0,0 +1,69 @@ +package riakcmd + +import ( + "fmt" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/riak/db-tools/dbactuator/internal/subcmd" + "dbm-services/riak/db-tools/dbactuator/pkg/components/riak" + "dbm-services/riak/db-tools/dbactuator/pkg/util" + + "github.com/spf13/cobra" +) + +// UninstallAct 搬迁数据进度riak dbactor参数 +type UninstallAct struct { + *subcmd.BaseOptions + Payload riak.UninstallComp +} + +// NewUninstallCommand riak搬迁数据进度 +func NewUninstallCommand() *cobra.Command { + act := UninstallAct{ + BaseOptions: subcmd.GBaseOptions, + } + cmd := &cobra.Command{ + Use: "uninstall", + Short: "下架节点", + Example: fmt.Sprintf("dbactuator riak uninstall %s", subcmd.CmdBaseExampleStr), + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(act.Validator()) + util.CheckErr(act.Init()) + util.CheckErr(act.Run()) + }, + } + return cmd +} + +// Validator TODO +func (d *UninstallAct) Validator() error { + return d.BaseOptions.Validate() +} + +// Init 反序列化并检查 +func (d *UninstallAct) Init() error { + if err := d.DeserializeAndValidate(&d.Payload); err != nil { + logger.Error("DeserializeAndValidate err %s", err.Error()) + return err + } + return nil +} + +// Run 运行 +func (d *UninstallAct) Run() error { + steps := subcmd.Steps{ + { + FunName: "环境预检查", + Func: d.Payload.PreCheck, + }, + { + FunName: "下架节点", + Func: d.Payload.Uninstall, + }, + } + if err := steps.Run(); err != nil { + return err + } + logger.Info("uninstall success") + return nil +} diff --git a/dbm-services/riak/db-tools/dbactuator/internal/subcmd/subcmd.go b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/subcmd.go new file mode 100644 index 0000000000..5aa6091024 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/subcmd.go @@ -0,0 +1,342 @@ +// Package subcmd TODO +package subcmd + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "os" + "path/filepath" + "strings" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/common/go-pubpkg/validate" + "dbm-services/riak/db-tools/dbactuator/pkg/components" + "dbm-services/riak/db-tools/dbactuator/pkg/util" + "dbm-services/riak/db-tools/dbactuator/pkg/util/templates" + + "github.com/caarlos0/env/v6" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +const ( + // CmdBaseExampleStr TODO + CmdBaseExampleStr = "-u {uid} -n {node_id} -p {base64}" + // PayloadFormatRaw TODO + PayloadFormatRaw = "raw" +) + +// GBaseOptions TODO +var GBaseOptions *BaseOptions + +// GeneralRuntimeParam TODO +var GeneralRuntimeParam *components.GeneralParam + +func init() { + GBaseOptions = &BaseOptions{} + GeneralRuntimeParam = &components.GeneralParam{} +} + +// BaseOptions TODO +/* + 此参数是json字符串的base64编码之后的字符串 +*/ +type BaseOptions struct { + Uid string + RootId string + NodeId string + VersionId string + Payload string + PayloadFormat string + RollBack bool + Helper bool + // 是否为外部版本 + // on ON + External string +} + +// IsExternal 是否编译成外部版本 +func (b *BaseOptions) IsExternal() bool { + return strings.ToUpper(b.External) == "ON" +} + +const ( + // StepStateDefault TODO + StepStateDefault = "default" + // StepStateRunning TODO + StepStateRunning = "running" + // StepStateSucc TODO + StepStateSucc = "success" + // StepStateSkip TODO + StepStateSkip = "skipped" // 用户主动跳过该 step + // StepStateStop TODO + StepStateStop = "stopped" // 用户主动暂停,特殊形式的 failed + // StepStateFail TODO + StepStateFail = "failed" +) + +// StepFunc TODO +type StepFunc struct { + FunName string + Func func() error + State string + FuncRetry func() error + FuncRollback func() error + FuncStop func() error + Retries int +} + +// Steps TODO +type Steps []StepFunc + +// Run TODO +func (s Steps) Run() (err error) { + for idx, step := range s { + logMessage := fmt.Sprintf("step <%d>, ready start run [%s]", idx, step.FunName) + logger.Info(logMessage) + if err = step.Func(); err != nil { + logger.Error("step<%d>: %s失败 , 错误: %s", idx, step.FunName, err) + // @todo + // 顺便输出接下来还有哪些 step 未允许 + return err + } + logger.Info("step <%d>, start run [%s] successfully", idx, step.FunName) + } + return nil +} + +// DeserializeAndValidate TODO +/* + 反序列化payload,并校验参数 + ps: 参数校验 from golang validate v10 +*/ +func (b *BaseOptions) DeserializeAndValidate(s interface{}) (err error) { + var bp []byte + if b.PayloadFormat == PayloadFormatRaw { + bp = []byte(b.Payload) + } else { + logger.Info("DeserializeAndValidate payload body: %s", b.Payload) + bp, err = base64.StdEncoding.DecodeString(b.Payload) + if err != nil { + return err + } + } + // 如果 s 里面的 sub struct 是 pointer,要初始化后再传进来才能解析到环境变量 + if err := env.Parse(s); err != nil { + logger.Warn("env parse error, ignore environment variables for payload:%s", err.Error()) + // env: expected a pointer to a Struct + } + defer logger.Info("payload parsed: %+v", s) + if err = json.Unmarshal(bp, s); err != nil { + logger.Error("json.Unmarshal failed, %v", s, err) + return + } + + if err = validate.GoValidateStruct(s, false, true); err != nil { + logger.Error("validate struct failed, %v", s, err) + return + } + return nil +} + +// Deserialize TODO +/* + { + "general":{} // + "extend":{} // 实际参数 + } + 反序列化payload,并校验参数 + ps: 参数校验 from golang validate v10 +*/ +func (b *BaseOptions) Deserialize(s interface{}) (err error) { + var bp []byte + if b.PayloadFormat == PayloadFormatRaw { + bp = []byte(b.Payload) + } else { + logger.Info("Deserialize payload body: %s", b.Payload) + bp, err = base64.StdEncoding.DecodeString(b.Payload) + if err != nil { + return err + } + } + if err := env.Parse(s); err != nil { + logger.Warn("env parse error, ignore environment variables for payload:%s", err.Error()) + } + logger.Info("params from env %+v", s) + g := components.RuntimeAccountParam{} + if err := env.Parse(&g); err != nil { + logger.Warn("env parse error, ignore environment variables for payload:%s", err.Error()) + } + // logger.Info("Account from env: %+v", g) + bip := components.BaseInputParam{ + ExtendParam: s, + GeneralParam: &components.GeneralParam{RuntimeAccountParam: g}, + } + defer logger.Info("payload parsed: %+v", bip) + if err = json.Unmarshal(bp, &bip); err != nil { + logger.Error("json.Unmarshal failed, %v", s, err) + err = errors.WithMessage(err, "参数解析错误") + return + } + // logger.Info("params after unmarshal %+v", bip) + if err = validate.GoValidateStruct(bip, false, true); err != nil { + logger.Error("validate struct failed, %v", s, err) + err = errors.WithMessage(err, "参数输入错误") + return + } + GeneralRuntimeParam = bip.GeneralParam + return nil +} + +// DeserializeSimple 简单 payload 不需要 {"extend":{body}},直接传入 body +func (b *BaseOptions) DeserializeSimple(s interface{}) (err error) { + var body []byte + if b.PayloadFormat == PayloadFormatRaw { + body = []byte(b.Payload) + } else { + logger.Info("DeserializeSimple payload body: %s", b.Payload) + body, err = base64.StdEncoding.DecodeString(b.Payload) + if err != nil { + return err + } + } + + // 如果 s 里面的 sub struct 是 pointer,要初始化后再传进来才能解析到环境变量 + if err := env.Parse(s); err != nil { + logger.Warn("env parse error, ignore environment variables for payload:%s", err.Error()) + } + + defer logger.Info("payload parsed: %+v", s) + if err = json.Unmarshal(body, &s); err != nil { + logger.Error("json.Unmarshal failed, %v", s, err) + err = errors.WithMessage(err, "参数解析错误") + return + } + if err = validate.GoValidateStruct(s, false, true); err != nil { + logger.Error("validate struct failed, %v", s, err) + err = errors.WithMessage(err, "参数输入错误") + return + } + return nil +} + +// Validate TODO +func (b BaseOptions) Validate() (err error) { + if len(b.Payload) == 0 { + return fmt.Errorf("payload need input") + } + // logger.Info("Validate payload body: %s", b.Payload) + + return nil +} + +// OutputCtx TODO +// +// @receiver b +func (b BaseOptions) OutputCtx(ctx string) { + fmt.Printf("%s", ctx) +} + +// SetLogger will mkdir logs/ +func SetLogger(cmd *cobra.Command, opt *BaseOptions) { + var file *os.File + var err error + var format = true + + executable, _ := os.Executable() + // executeName := filepath.Base(executable) + executeDir := filepath.Dir(executable) + if err = os.Chdir(executeDir); err != nil { + os.Stderr.WriteString(err.Error()) + os.Exit(1) + } + + mode := os.Getenv("MODE") + lgn := "" + if cmd != nil && cmd.Parent() != nil { + lgn = fmt.Sprintf("%s-%s", cmd.Parent().Name(), cmd.Name()) + } + switch mode { + case "dev": + file = os.Stdout + format = false + default: + logFileDir := filepath.Join(executeDir, "logs") + _ = os.MkdirAll(logFileDir, 0755) + fileName := filepath.Join(logFileDir, fmt.Sprintf("actuator_%s_%s_%s.log", opt.Uid, lgn, opt.NodeId)) + file, err = os.OpenFile(fileName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm) + if err != nil { + os.Stderr.WriteString(err.Error()) + os.Exit(1) + } + } + + extMap := map[string]string{ + "uid": opt.Uid, + "node_id": opt.NodeId, + "root_id": opt.RootId, + "version_id": opt.VersionId, + } + l := logger.New(file, format, logger.InfoLevel, extMap) + logger.ResetDefault(l) + defer logger.Sync() +} + +// ValidateSubCommand TODO +func ValidateSubCommand() func(cmd *cobra.Command, args []string) error { + return func(cmd *cobra.Command, args []string) error { + if len(args) <= 0 { + return fmt.Errorf( + "You must specify the type of Operation Describe. %s\n", + SuggestAPIResources(cmd.Parent().Name()), + ) + } + curName := args[0] + var subCommands []string + for _, c := range cmd.Commands() { + subCommands = append(subCommands, c.Name()) + } + if len(subCommands) <= 0 { + return nil + } + if !util.StringsHas(subCommands, curName) { + return fmt.Errorf("Unknown subcommand %s\n", curName) + } + return nil + } +} + +// PrintSubCommandHelper 返回是否成功打印 helper +// 如果打印,同时运行下 runHelp +func PrintSubCommandHelper(cmd *cobra.Command, opt *BaseOptions) bool { + if opt.Helper { + if cmd.Parent().Name() == "dbactuator" { + fmt.Println("--helper need sub-command to show payload parameter") + os.Exit(1) + } + if cmd.Name() != "" { + subcmdPath := fmt.Sprintf("%s %s", cmd.Parent().Name(), cmd.Name()) + if err := GetPathDefinitionHelper(subcmdPath); err != nil { + fmt.Println(err) + os.Exit(1) + } else { + return true + } + } else { + fmt.Println("--example need sub-command") + } + } + return false +} + +// SuggestAPIResources returns a suggestion to use the "api-resources" command +// to retrieve a supported list of resources +func SuggestAPIResources(parent string) string { + return templates.LongDesc( + fmt.Sprintf( + "Use \"%s {Operation Type}\" for a complete list of supported resources.", + parent, + ), + ) +} diff --git a/dbm-services/riak/db-tools/dbactuator/internal/subcmd/subcmd_helper.go b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/subcmd_helper.go new file mode 100644 index 0000000000..c405e79cdf --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/subcmd_helper.go @@ -0,0 +1,339 @@ +package subcmd + +import ( + "encoding/json" + "fmt" + "log" + "strings" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/riak/db-tools/dbactuator/docs" + "dbm-services/riak/db-tools/dbactuator/pkg/components" + "dbm-services/riak/db-tools/dbactuator/pkg/util" +) + +const ( + // DTString TODO + DTString = "string" + // DTInteger TODO + DTInteger = "integer" + // DTNumber TODO + DTNumber = "number" + // DTObject TODO + DTObject = "object" + // DTArray TODO + DTArray = "array" + // DTArrayObject TODO + DTArrayObject = "array object" + // DTBOOLEAN TODO + DTBOOLEAN = "boolean" + // DTUndefined TODO + DTUndefined = "undefined ref" + // RefMaxDepth TODO + RefMaxDepth = 9 +) + +const ( + // DefinitionPrefix TODO + DefinitionPrefix = "#/definitions/" + // RefKey TODO + RefKey = "$ref" + // IndentStep TODO + IndentStep = " " + // DefinitionKey TODO + DefinitionKey = "post" +) + +// PostPath TODO +type PostPath map[string]*Path // "post": {} +// Path TODO +type Path struct { + Description string `json:"description"` + Summary string `json:"summary"` + Parameters []Param `json:"parameters"` // parameters[0].schema.$ref + Responses map[string]Param `json:"responses"` +} + +// PrintDescription TODO +func (p *Path) PrintDescription() { + fmt.Printf("# Summary: %s\n", p.Summary) + if p.Description != "" { + fmt.Printf("# Description: %s\n", p.Description) + } +} + +// Param TODO +type Param struct { + Schema RefMap `json:"schema"` // {"$ref":""} + Name string `json:"name"` + Description string `json:"description"` +} + +// RefMap TODO +type RefMap map[string]string // "$ref":"#/definitions/xx" + +// RefMapObj TODO +type RefMapObj struct { + Ref string `json:"$ref"` +} + +// Parameter TODO +type Parameter struct { + Type string `json:"type"` + // Properties components.BaseInputParam `json:"properties"` + GeneralParam components.GeneralParam `json:"generalParam"` // generalParam.$ref + Params Definition `json:"params"` // params.$ref +} + +// Definition TODO +type Definition struct { + Type string `json:"type"` + Required []string `json:"required"` + Properties map[string]*Property `json:"properties"` + description string + depth int // 禁止无限套娃 + name string + expanded bool +} + +// PrintProperties TODO +func (d *Definition) PrintProperties(indent string, header string) { + if indent == "" { + fmt.Printf("%s: %s\n", header, d.description) + } + indent = IndentStep + indent + for _, prop := range d.Properties { + prop.Print(indent) + } +} + +// NestedRef TODO +type NestedRef struct { + Type string `json:"type"` + RefMapObj + Items *NestedRef `json:"items"` +} + +// Property TODO +type Property struct { + Type string `json:"type"` + Description string `json:"description"` + Example interface{} `json:"example"` + Default interface{} `json:"default"` + Enum []interface{} `json:"enum"` + AdditionalProperties *NestedRef `json:"additionalProperties"` // additionalProperties.$ref + Ref string `json:"$ref"` // $ref, RefKey + Items *NestedRef `json:"items"` // array: items.$ref + + additionalProperties map[string]*Definition + ref *Definition + required bool + name string + depth int // 禁止无限套娃 +} + +func wrapperBoolean(flag bool) string { + if flag { + return " Required," + } else { + return " " // Optional + } +} + +func wrapperType(t string) string { + if t == DTObject { + return "dict" + } else if t == DTNumber { + return "float" + } + return t +} + +func wrapperEnum(v []interface{}) string { + var enumStr = "" + if v != nil && len(v) > 0 { + enumStr = fmt.Sprintf(` Enum oneof%v,`, v) + } + return enumStr +} + +// Print TODO +func (p *Property) Print(indent string) { + leftMaxPad := "20" + left := fmt.Sprintf("%s%s:", indent, p.name) + + leftWithPad := fmt.Sprintf("%-"+leftMaxPad+"s", left) + ss := fmt.Sprintf( + "%s\t%s,%s%s %s", + leftWithPad, p.Type, wrapperBoolean(p.required), wrapperEnum(p.Enum), p.Description, + ) + if p.Example != nil { + ss += fmt.Sprintf(". 例: %v", p.Example) + } + if p.Default != nil { + ss += fmt.Sprintf(", 默认值: %v", p.Default) + } + if p.ref != nil { + fmt.Println(ss) + p.ref.PrintProperties(indent, p.ref.description) + } else { + fmt.Println(ss) + } +} + +// Definitions TODO +type Definitions map[string]*Definition + +// JsonSpec TODO +type JsonSpec struct { + Paths map[string]PostPath `json:"paths"` + Definitions Definitions `json:"definitions"` +} + +// GetOneDefinition TODO +func (ds *Definitions) GetOneDefinition(name string) *Definition { + name = strings.TrimPrefix(name, DefinitionPrefix) + if obj, ok := (*ds)[name]; ok { + return obj + } else { + // 未定义的 definition name + } + return nil +} + +// expandProperties 将 ref definition 展开 +func (ds *Definitions) expandProperties() { + for defName, d := range *ds { + d.name = defName + if !d.expanded { // 因为展开时,一直在操作同一个引用,不要重复展开 + d.ExpandProperties(ds) + } + } +} + +// ExpandProperties 展开 definition 的 property +// 因为 property 可能引用其它 definition +func (d *Definition) ExpandProperties(defs *Definitions) { + d.expanded = true + if d.Type != DTObject { + logger.Info("helper definition is no object %v", d) + return + } + for pname, prop := range d.Properties { + prop.depth = d.depth + prop.name = pname + if util.StringsHas(d.Required, pname) { + prop.required = true + } + + refObjName := prop.getRef() + if refObjName != "" { + prop.ref = defs.GetOneDefinition(refObjName) + if prop.ref == nil { + prop.Type = DTUndefined // 未知 definition, 置空 + prop.Ref = "" + continue + } + prop.ref.depth = prop.depth + 1 + d.depth = prop.ref.depth + if d.depth > RefMaxDepth { + fmt.Printf( + "ref max depth exceed, definition name:%v, depth:%d, depth def:%v\n", + d.name, d.depth, prop.ref, + ) + continue + } + prop.ref.ExpandProperties(defs) // 递归 + prop.ref.description = prop.Description + if prop.Type == "" { + prop.Type = DTObject + } + } + } +} + +// getRef 判断该 property 是否有下级嵌套 +// 如果有则存到 ref 中,且修改 Type 加上 嵌套类型 +func (p *Property) getRef() string { + if p.Ref != "" { + p.Type += " " + DTObject + return p.Ref + } else if p.AdditionalProperties != nil { + p.Type += ":map[string]" + " " + p.AdditionalProperties.Type // DTString + return p.getItemsNestedRef(p.AdditionalProperties) + } else if p.Items != nil { + p.Type += " " + p.Items.Type + return p.getItemsNestedRef(p.Items) + } + return "" +} + +func (p *Property) getItemsNestedRef(subRef *NestedRef) string { + if ref := subRef.RefMapObj.Ref; ref != "" { + p.Ref = ref + p.Type += " " + DTObject // DTArrayObject + return ref + } else if subRef.Items != nil { + if ref = subRef.Items.RefMapObj.Ref; ref != "" { + p.Ref = ref + p.Type += " " + DTObject // DTArrayObject + return ref + } + p.Type += " " + subRef.Items.Type + } + return "" +} + +// GetPathDefinitionHelper 结束命令字符串,打印描述 +// mysql mycnf-change +// /riak/mycnf-change +func GetPathDefinitionHelper(subcmd string) error { + defer func() { + if r := recover(); r != nil { + // logger.Error("get helper failed %s: %s", subcmd, r, string(debug.Stack())) + } + }() + if strings.Contains(subcmd, " ") { + tmp := strings.Split(strings.TrimSpace(subcmd), " ") + subcmd = "/" + strings.Join(tmp, "/") + } + f := docs.SwaggerDocs + doc := "swagger.json" + b, err := f.ReadFile(doc) + if err != nil { + return err + } + jsonSpec := JsonSpec{} + if err := json.Unmarshal(b, &jsonSpec); err != nil { + fmt.Println(err) + log.Fatalln("docs/swagger.json 解析失败") + } + if pathObj, ok := jsonSpec.Paths[subcmd]; !ok { + fmt.Printf("未找到参数定义 %s\n", subcmd) + } else { + if params, ok := pathObj[DefinitionKey]; !ok { + fmt.Printf("未找到参数定义post %s\n", subcmd) + } else if len(params.Parameters) == 0 { + fmt.Printf("未找到参数定义param %s\n", subcmd) + } + } + // jsonSpec.Definitions.ExpandProperties() + pathDefinition := jsonSpec.Paths[subcmd][DefinitionKey] + pathDefinition.PrintDescription() + // parameters + reqSchema := pathDefinition.Parameters[0].Schema + schemaName := strings.TrimPrefix(reqSchema[RefKey], DefinitionPrefix) + thisDef := jsonSpec.Definitions[schemaName] + thisDef.ExpandProperties(&jsonSpec.Definitions) + thisDef.PrintProperties("", "\n# Param") + + // responses + for code, resp := range pathDefinition.Responses { + respSchema := resp.Schema + schemaName = strings.TrimPrefix(respSchema[RefKey], DefinitionPrefix) + thisDef = jsonSpec.Definitions[schemaName] + thisDef.ExpandProperties(&jsonSpec.Definitions) // 如果 param 对象里面包含了 resp 的对象,这里可能重复展开。暂不处理 + thisDef.PrintProperties("", "\n# Response "+code) + } + return nil +} diff --git a/dbm-services/riak/db-tools/dbactuator/internal/subcmd/subcmd_util.go b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/subcmd_util.go new file mode 100644 index 0000000000..9cca425cd2 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/subcmd_util.go @@ -0,0 +1,69 @@ +package subcmd + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "encoding/base64" + "encoding/json" + "fmt" +) + +// PKCS5Padding TODO +func PKCS5Padding(ciphertext []byte, blockSize int) []byte { + padding := blockSize - len(ciphertext)%blockSize + padtext := bytes.Repeat([]byte{byte(padding)}, padding) + return append(ciphertext, padtext...) +} + +// PKCS5UnPadding TODO +func PKCS5UnPadding(origData []byte) []byte { + length := len(origData) + unpadding := int(origData[length-1]) + return origData[:(length - unpadding)] +} + +// AesEncrypt TODO +// +// 增加加密函数,加密的key必须是16,24,32位长 +func AesEncrypt(origData, key []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + blockSize := block.BlockSize() + origData = PKCS5Padding(origData, blockSize) + fmt.Println(origData) + blockMode := cipher.NewCBCEncrypter(block, key[:blockSize]) + crypted := make([]byte, len(origData)) + blockMode.CryptBlocks(crypted, origData) + return crypted, nil +} + +// AesDecrypt 增加解密函数 +func AesDecrypt(crypted string, key []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + data, err1 := base64.StdEncoding.DecodeString(crypted) + if err1 != nil { + return nil, err1 + } + blockSize := block.BlockSize() + blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) + origData := make([]byte, len(data)) + blockMode.CryptBlocks(origData, data) + origData = PKCS5UnPadding(origData) + return origData, nil +} + +// ToPrettyJson TODO +func ToPrettyJson(v interface{}) string { + if data, err := json.MarshalIndent(v, "", " "); err == nil { + // ss := "\n# use --helper to show explanations. example for payload:\n --payload-format raw --payload '%s'" + return string(data) + } + return "未找到合法的 example " +} diff --git a/dbm-services/riak/db-tools/dbactuator/internal/subcmd/sysinitcmd/sysinit.go b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/sysinitcmd/sysinit.go new file mode 100644 index 0000000000..cb1c25f858 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/sysinitcmd/sysinit.go @@ -0,0 +1,61 @@ +package sysinitcmd + +import ( + "dbm-services/common/go-pubpkg/logger" + "dbm-services/riak/db-tools/dbactuator/internal/subcmd" + "dbm-services/riak/db-tools/dbactuator/pkg/components/sysinit" + "dbm-services/riak/db-tools/dbactuator/pkg/util" + + "github.com/spf13/cobra" +) + +// SysInitAct TODO +type SysInitAct struct { + *subcmd.BaseOptions +} + +// NewSysInitCommand TODO +func NewSysInitCommand() *cobra.Command { + act := SysInitAct{ + BaseOptions: subcmd.GBaseOptions, + } + cmd := &cobra.Command{ + Use: "sysinit-riak", + Short: "Exec sysinit_riak.sh", + Example: `dbactuator sysinit-riak -p eyJ1c2VyIjoiIiwicHdkIjoiIn0=`, + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(act.Validate()) + util.CheckErr(act.Run()) + }, + } + return cmd +} + +// Run TODO +func (s *SysInitAct) Run() (err error) { + steps := []subcmd.StepFunc{ + { + FunName: "执行sysInit脚本", + Func: sysinit.ExecSysInitScript, + }, + } + if s.IsExternal() { + steps = append( + steps, subcmd.StepFunc{ + FunName: "安装Perl以及相关依赖", + Func: sysinit.InitExternal, + }, + ) + } + + logger.Info("start sysinit ...") + for idx, f := range steps { + if err = f.Func(); err != nil { + logger.Error("step <%d>, run [%s] occur %v", idx, f.FunName, err) + return err + } + logger.Info("step <%d>, run [%s] successfully", idx, f.FunName) + } + logger.Info("sysinit successfully") + return +} diff --git a/dbm-services/riak/db-tools/dbactuator/internal/subcmd/sysinitcmd/sysinitcmd.go b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/sysinitcmd/sysinitcmd.go new file mode 100644 index 0000000000..19eb9248ea --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/internal/subcmd/sysinitcmd/sysinitcmd.go @@ -0,0 +1,2 @@ +// Package sysinitcmd TODO +package sysinitcmd diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/components/components.go b/dbm-services/riak/db-tools/dbactuator/pkg/components/components.go new file mode 100644 index 0000000000..7fb03b9cf0 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/components/components.go @@ -0,0 +1,2 @@ +// Package components TODO +package components diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/components/medium.go b/dbm-services/riak/db-tools/dbactuator/pkg/components/medium.go new file mode 100644 index 0000000000..5c5afe6c83 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/components/medium.go @@ -0,0 +1,37 @@ +package components + +import ( + "dbm-services/common/go-pubpkg/cmutil" + "dbm-services/riak/db-tools/dbactuator/pkg/core/cst" + "dbm-services/riak/db-tools/dbactuator/pkg/util" + "fmt" + "path" +) + +// Medium 通用介质包处理 +type Medium struct { + Name string `json:"name" validate:"required"` // 安装包名 + Md5 string `json:"md5" validate:"required,md5"` // 安装包MD5 +} + +// Check TODO +func (m *Medium) Check() error { + // 判断安装包是否存在 + pkgAbPath := m.GetAbsolutePath() + if !cmutil.FileExists(pkgAbPath) { + return fmt.Errorf("%s不存在", pkgAbPath) + } + md5, err := util.GetFileMd5(pkgAbPath) + if err != nil { + return fmt.Errorf("获取[%s]md5失败, %v", m.Name, err.Error()) + } + if md5 != m.Md5 { + return fmt.Errorf("安装包[%s]的md5是[%s],与期望的md5[%s]不符", pkgAbPath, md5, m.Md5) + } + return nil +} + +// GetAbsolutePath 返回介质存放的绝对路径 +func (m *Medium) GetAbsolutePath() string { + return path.Join(cst.BK_PKG_INSTALL_PATH, m.Name) +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/components/output.go b/dbm-services/riak/db-tools/dbactuator/pkg/components/output.go new file mode 100644 index 0000000000..ff46190324 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/components/output.go @@ -0,0 +1,39 @@ +package components + +import ( + "encoding/json" + "fmt" +) + +// WrapperOutputString TODO +func WrapperOutputString(output string) string { + return fmt.Sprintf(`%s`, output) +} + +// WrapperOutput TODO +func WrapperOutput(v interface{}) (string, error) { + if b, e := json.Marshal(v); e != nil { + return "", e + } else { + return fmt.Sprintf(`%s`, string(b)), nil + } +} + +// PrintOutputCtx TODO +func PrintOutputCtx(v interface{}) error { + if ss, err := WrapperOutput(v); err != nil { + return err + } else { + fmt.Println(ss) + } + return nil +} + +// ToPrettyJson TODO +func ToPrettyJson(v interface{}) string { + if data, err := json.MarshalIndent(v, "", " "); err == nil { + // ss := "\n# use --helper to show explanations. example for payload:\n --payload-format raw --payload '%s'" + return string(data) + } + return "未找到合法的 example " +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/check_connections.go b/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/check_connections.go new file mode 100644 index 0000000000..fb2ce2f0f0 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/check_connections.go @@ -0,0 +1,42 @@ +// Package riak TODO +/* + * @Description: 安装 Riak + */ +package riak + +import ( + "dbm-services/common/go-pubpkg/logger" + "dbm-services/riak/db-tools/dbactuator/pkg/core/cst" + "dbm-services/riak/db-tools/dbactuator/pkg/util/osutil" + "fmt" + + "golang.org/x/exp/slog" +) + +// CheckConnectionsComp TODO +type CheckConnectionsComp struct { + Params *CheckConnectionsParam `json:"extend"` + CheckConnectionsRunTimeCtx `json:"-"` +} + +// CheckConnectionsParam TODO +type CheckConnectionsParam struct { +} + +// CheckConnectionsRunTimeCtx 运行时上下文 +type CheckConnectionsRunTimeCtx struct { +} + +// CheckConnections 集群数据搬迁进度检查 +func (i *CheckConnectionsComp) CheckConnections() error { + cmd := fmt.Sprintf(`netstat -anpl|grep %d | grep "beam.smp" | grep -E -v '0.0.0.0:*'`, cst.DefaultProtobufPort) + res, err := osutil.ExecShellCommand(false, cmd) + // 没有连接 + if err != nil { + slog.Info("check success, no connection") + return nil + } + logger.Error("execute shell [%s] connections: %s", cmd, res) + err = fmt.Errorf("execute shell [%s] connections: %s", cmd, res) + return err +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/commit_cluster_change.go b/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/commit_cluster_change.go new file mode 100644 index 0000000000..5dce780bfd --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/commit_cluster_change.go @@ -0,0 +1,45 @@ +// Package riak TODO +/* + * @Description: 安装 Riak + */ +package riak + +import ( + "dbm-services/common/go-pubpkg/logger" + "dbm-services/riak/db-tools/dbactuator/pkg/util/osutil" + "fmt" +) + +// CommitClusterChangeComp TODO +type CommitClusterChangeComp struct { + Params *CommitClusterChangeParam `json:"extend"` +} + +// CommitClusterChangeParam TODO +type CommitClusterChangeParam struct { + Nodes *[]string `json:"nodes" validate:"required"` +} + +// CommitClusterChange 提交集群变化 +func (i *CommitClusterChangeComp) CommitClusterChange() error { + cmds := []string{ + "riak-admin cluster plan", + "riak-admin cluster commit", + "riak-admin transfers", + "riak-admin transfer-limit", + "riak-admin ring-status", + "riak-admin cluster status", + } + for _, cmd := range cmds { + res, err := osutil.ExecShellCommand(false, cmd) + if err != nil { + logger.Error("execute shell [%s] error: %s", cmd, err.Error()) + err = fmt.Errorf("execute shell [%s] error: %s", cmd, err.Error()) + return err + } else { + logger.Info("execute shell [%s] output:\n %s", cmd, res) + } + } + logger.Info("commit cluster change success, begin to transfer data") + return nil +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/deploy_monitor.go b/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/deploy_monitor.go new file mode 100644 index 0000000000..dc5184f09e --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/deploy_monitor.go @@ -0,0 +1,342 @@ +// Package riak TODO +/* + * @Description: 安装 Riak + */ +package riak + +import ( + "bytes" + "dbm-services/common/go-pubpkg/logger" + "dbm-services/riak/db-tools/dbactuator/pkg/components" + "dbm-services/riak/db-tools/dbactuator/pkg/core/cst" + "dbm-services/riak/db-tools/dbactuator/pkg/util/osutil" + "fmt" + "io/ioutil" + "net/http" + "os" + "os/exec" + "path" + "text/template" + "time" + + "github.com/pkg/errors" + "gopkg.in/yaml.v2" +) + +// DeployMonitorComp TODO +type DeployMonitorComp struct { + Params *DeployMonitorParam `json:"extend"` + DeployMonitorRunTimeCtx `json:"-"` +} + +// DeployMonitorParam TODO +type DeployMonitorParam struct { + CrondPkg components.Medium `json:"crond_pkg" validate:"required"` + MonitorPkg components.Medium `json:"monitor_pkg" validate:"required"` + CrondConfig CrondConfigYaml `json:"crond_config" validate:"required"` + MonitorConfig MonitorConfigYaml `json:"monitor_config" validate:"required"` + MonitorItems []MonitorItem `json:"monitor_items" validate:"required"` + JobsConfig JobsConfig `json:"jobs_config" validate:"required"` +} + +type JobsConfig struct { + Jobs []Job `json:"jobs" yaml:"jobs"` + BkBizId int `json:"bk_biz_id" yaml:"bk_biz_id" validate:"required"` +} + +type Job struct { + Name string `json:"name" yaml:"name"` + Enable bool `json:"enable" yaml:"enable"` + Command string `json:"command" yaml:"command"` + Args []string `json:"args" yaml:"args"` + Schedule string `json:"schedule" yaml:"schedule"` + Creator string `json:"creator" yaml:"creator"` + WorkDir string `json:"work_dir" yaml:"work_dir"` +} + +// DeployMonitorRunTimeCtx 运行时上下文 +type DeployMonitorRunTimeCtx struct { + LocalIp string +} + +type CrondConfigYaml struct { + IP string `json:"ip" validate:"required,ipv4"` + BkCloudId *int `json:"bk_cloud_id" validate:"required,gte=0"` + EventDataId int `json:"event_data_id" validate:"required"` + EventDataToken string `json:"event_data_token" validate:"required"` + MetricsDataId int `json:"metrics_data_id" validate:"required"` + MetricsDataToken string `json:"metrics_data_token" validate:"required"` + LogPath string `json:"log_file_dir"` + PidPath string `json:"pid_path"` + InstallPath string `json:"install_path"` + BeatPath string `json:"beat_path" validate:"required"` + AgentAddress string `json:"agent_address" validate:"required"` +} + +type MonitorConfigYaml struct { + BkBizId int `json:"bk_biz_id" validate:"required"` + IP string `json:"ip" validate:"required,ipv4"` + Port int `json:"port" validate:"required,gt=1024,lte=65535"` + BkInstanceId int `json:"bk_instance_id" validate:"required,gt=0"` + ImmuteDomain string `json:"immute_domain" validate:"required"` + MachineType string `json:"machine_type" validate:"required"` + BkCloudId *int `json:"bk_cloud_id" validate:"required,gte=0"` + LogPath string `json:"log_path"` + ItemsConfigPath string `json:"items_config_file" validate:"required"` + InteractTimeout time.Duration `json:"interact_timeout" validate:"required"` +} + +type MonitorItem struct { + Name string `json:"name" yaml:"name"` + Enable *bool `json:"enable" yaml:"enable"` + Schedule *string `json:"schedule" yaml:"schedule"` + MachineType []string `json:"machine_type" yaml:"machine_type"` +} + +// DeployMonitor 启动监控 +func (i *DeployMonitorComp) DeployMonitor() error { + // riak-monitor启动指令存储到jobs-config.yaml,jobs-config.yaml作为mysql-crond的runtime.yaml中的jobs_config + // 启动mysql-crond时,会完成riak-monitor监控项注册。mysql-crond定时执行riak-monitor的指令,实现监控。 + + // 前台执行mysql-crond,方便获取报错 + errChan := make(chan error) + var dryRunErr error + go func() { + dryRunCmd := fmt.Sprintf("%s -c %s", path.Join(cst.CrondPath, "mysql-crond"), + path.Join(cst.CrondPath, "runtime.yaml")) + cmd := exec.Command("bash", "-c", dryRunCmd) + var stderr bytes.Buffer + cmd.Stderr = &stderr + err := cmd.Run() + if err != nil { + errChan <- errors.Wrap(err, stderr.String()) + return + } + errChan <- nil + return + }() + select { + case dryRunErr = <-errChan: + if dryRunErr != nil { + dryRunErr = fmt.Errorf("crond dry-run error: %s", dryRunErr.Error()) + logger.Error(dryRunErr.Error()) + return dryRunErr + } + case <-time.After(time.Second * 10): + // crond运行正常不会自动退出,10秒后没有报错,进一步检查是否启动正常 + logger.Info("crond dry-run running 10s without error") + } + + // 检查crond是否启动 + err := GetCrondEntries() + if err != nil { + logger.Error("crond dry-run check error: %s", err.Error()) + return err + } + + // 退出前台mysql-crond + err = QuitCrond() + if err != nil { + logger.Error("quit crond dry-run error: %s", err.Error()) + return err + } + // 后台执行mysql-crond + nohup := fmt.Sprintf("%s -c %s", path.Join(cst.CrondPath, "start.sh"), + path.Join(cst.CrondPath, "runtime.yaml")) + logger.Info("start crond cmd: %s", nohup) + cmd := exec.Command("bash", "-c", nohup) + err = cmd.Run() + if err != nil { + logger.Error("execute crond failed: %s", err.Error()) + return err + } + time.Sleep(10 * time.Second) + // 检查crond是否启动 + err = GetCrondEntries() + if err != nil { + logger.Error("crond check error: %s", err.Error()) + return err + } + logger.Info("crond started") + return nil +} + +// DeployBinary 部署二进制 +func (i *DeployMonitorComp) DeployBinary() (err error) { + // 解压crond介质 + err = Decompress(i.Params.CrondPkg.GetAbsolutePath(), cst.CrondPath) + if err != nil { + logger.Error("decompress: %s to %s failed: %s", i.Params.CrondPkg.GetAbsolutePath(), + cst.CrondPath, err.Error()) + return err + } + // 解压监控的介质 + err = Decompress(i.Params.MonitorPkg.GetAbsolutePath(), cst.RiakMonitorPath) + if err != nil { + logger.Error("decompress: %s to %s failed: %s", i.Params.MonitorPkg.GetAbsolutePath(), + cst.RiakMonitorPath, err.Error()) + return err + } + return nil +} + +// GenerateCrondConfigYaml 生成crond的runtime.yaml文件 +func (i *DeployMonitorComp) GenerateCrondConfigYaml() (err error) { + i.Params.CrondConfig.LogPath = path.Join(cst.CrondPath, i.Params.CrondConfig.LogPath) + i.Params.CrondConfig.PidPath = cst.CrondPath + i.Params.CrondConfig.InstallPath = cst.CrondPath + err = UseTemplate(cst.CrondPath, "mysql-crond.conf.go.tpl", "runtime.yaml", i.Params.CrondConfig) + if err != nil { + logger.Error("generate crond runtime.yaml error: %s", err.Error()) + return err + } + // todo mysql-crond 模版中jobs_user不是固定值 + cmd := fmt.Sprintf(`sed -i "s/jobs_user: mysql/jobs_user: root/g" %s`, path.Join(cst.CrondPath, "runtime.yaml")) + _, err = osutil.ExecShellCommand(false, cmd) + if err != nil { + logger.Error("execute %s error: %s", cmd, err.Error()) + return err + } + return nil +} + +// GenerateMonitorConfigYaml 生成runtime.yaml文件 +func (i *DeployMonitorComp) GenerateMonitorConfigYaml() (err error) { + i.Params.MonitorConfig.LogPath = path.Join(cst.RiakMonitorPath, i.Params.MonitorConfig.LogPath) + i.Params.MonitorConfig.ItemsConfigPath = path.Join(cst.RiakMonitorPath, i.Params.MonitorConfig.ItemsConfigPath) + err = UseTemplate(cst.RiakMonitorPath, "config.yaml.go.tpl", "runtime.yaml", i.Params.MonitorConfig) + if err != nil { + logger.Error("generate monitor runtime.yaml error: %s", err.Error()) + return err + } + return nil +} + +// GenerateJobsConfigYaml 生成jobs-config.yaml文件 +func (i *DeployMonitorComp) GenerateJobsConfigYaml() (err error) { + err = CreateYaml(cst.CrondPath, "jobs-config.yaml", i.Params.JobsConfig) + if err != nil { + logger.Error("create items-config.yaml file failed: %s", err.Error()) + return err + } + return nil +} + +// GenerateItemsConfigYaml 生成items-config.yaml文件 +func (i *DeployMonitorComp) GenerateItemsConfigYaml() (err error) { + err = CreateYaml(cst.RiakMonitorPath, "items-config.yaml", i.Params.MonitorItems) + if err != nil { + logger.Error("create items-config.yaml file failed: %s", err.Error()) + return err + } + return nil +} + +// Decompress 解压文件到目标路径 +func Decompress(source, target string) error { + err := os.MkdirAll(target, 0755) + if err != nil { + logger.Error("mkdir %s failed: %s", target, err.Error()) + return err + } + decompressCmd := fmt.Sprintf( + `tar zxf %s -C %s`, + source, target, + ) + _, err = osutil.ExecShellCommand(false, decompressCmd) + if err != nil { + logger.Error("decompress mysql-crond pkg failed: %s", err.Error()) + return err + } + return nil +} + +// UseTemplate 使用模版生成yaml文件 +func UseTemplate(vpath, tpl, target string, data interface{}) error { + t, err := template.ParseFiles(path.Join(vpath, tpl)) + if err != nil { + logger.Error("read config template failed: %s", err.Error()) + return err + } + + f, err := os.OpenFile( + path.Join(vpath, target), + os.O_CREATE|os.O_TRUNC|os.O_WRONLY, + 0644, + ) + if err != nil { + logger.Error("create yaml failed: %s", err.Error()) + return err + } + + err = t.Execute(f, data) + if err != nil { + logger.Error("execute template failed: %s", err.Error()) + return err + } + return nil +} + +// CreateYaml 根据结构体生成yaml文件 +func CreateYaml(vpath, target string, data interface{}) error { + content, err := yaml.Marshal(data) + if err != nil { + logger.Error("marshal data failed: %s", err.Error()) + return err + } + f, err := os.OpenFile(path.Join(vpath, target), + os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0755) + if err != nil { + logger.Error("create file failed: %s", err.Error()) + return err + } + _, err = f.Write(append(content, []byte("\n")...)) + if err != nil { + logger.Error("write file failed: %s", err.Error()) + return err + } + return nil +} + +func GetCrondEntries() error { + url := fmt.Sprintf("http://127.0.0.1:%d/entries", cst.CrondListenPort) + resp, err := http.Get(url) + defer resp.Body.Close() + if err != nil { + err = fmt.Errorf("connect crond failed: %s", err.Error()) + logger.Error(err.Error()) + return err + } + if resp.StatusCode != 200 { + err = fmt.Errorf("connect crond response status code is: %d not 200 ", resp.StatusCode) + logger.Error(err.Error()) + return err + } + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + err = fmt.Errorf("get crond response failed: %s", err.Error()) + logger.Info(err.Error()) + return err + } + logger.Info("monitor entries register in crond:\n%s", string(body)) + return nil +} + +func QuitCrond() error { + // 关闭启动的 mysql-crond + url := fmt.Sprintf("http://127.0.0.1:%d/quit", cst.CrondListenPort) + resp, err := http.Get(url) + defer resp.Body.Close() + if err != nil { + logger.Error("call quit failed: %s", err.Error()) + return err + } + if resp.StatusCode != 200 { + err = fmt.Errorf("quit crond response status code is: %d not 200 ", resp.StatusCode) + logger.Error(err.Error()) + return err + } + time.Sleep(15 * time.Second) + logger.Info("quit crond success") + return nil +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/get_config.go b/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/get_config.go new file mode 100644 index 0000000000..3b3cb2429f --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/get_config.go @@ -0,0 +1,103 @@ +// Package riak TODO +/* + * @Description: 安装 Riak + */ +package riak + +import ( + "dbm-services/common/go-pubpkg/logger" + "dbm-services/riak/db-tools/dbactuator/pkg/components" + "dbm-services/riak/db-tools/dbactuator/pkg/core/cst" + "dbm-services/riak/db-tools/dbactuator/pkg/util/osutil" + "fmt" + + "gopkg.in/ini.v1" +) + +// GetConfigComp TODO +type GetConfigComp struct { + Params *GetConfigParam `json:"extend"` + GetConfigRunTimeCtx `json:"-"` +} + +// GetConfigParam TODO +type GetConfigParam struct { +} + +// GetConfigRunTimeCtx 运行时上下文 +type GetConfigRunTimeCtx struct { + Configs map[string]string +} + +// Config Config +type Config struct { + DistributedCookie string `json:"distributed_cookie"` + RingSize string `json:"ring_size"` + + // riak legs战绩模块历史数据过期删除 + LeveldbExpiration string `json:"leveldb.expiration"` + LeveldbExpirationMode string `json:"leveldb.expiration.mode"` + LeveldbExpirationRetentionTime string `json:"leveldb.expiration.retention_time"` +} + +// GetConfig 获取配置 +func (i *GetConfigComp) GetConfig() error { + localIp, err := osutil.GetLocalIP() + if err != nil { + logger.Error("get local ip error: %s", err.Error()) + return err + } + checkStatus := fmt.Sprintf(`%s | grep " riak@%s " | grep 'valid'`, cst.ClusterStatusCmd, localIp) + _, err = osutil.ExecShellCommand(false, checkStatus) + if err != nil { + logger.Error("execute shell [%s] error: %s", checkStatus, err.Error()) + err = fmt.Errorf("execute shell [%s] error: %s", checkStatus, err.Error()) + return err + } + // riak实例的参数 + cmd := "riak config effective" + config, err := osutil.ExecShellCommand(false, cmd) + if err != nil { + logger.Error("execute shell [%s] error: %s", cmd, err.Error()) + err = fmt.Errorf("execute shell [%s] error: %s", cmd, err.Error()) + return err + } + // 生成ini文件 + file, err := ini.Load([]byte(config)) + if err != nil { + logger.Error("riak config template to ini file error: %s", err.Error()) + return err + } + // 配置项 + i.Configs = map[string]string{ + "ring_size": "", + "distributed_cookie": "", + "leveldb.expiration": "", + "leveldb.expiration.mode": "", + "leveldb.expiration.retention_time": "", + } + // 获取ini文件中的配置值 + for k, _ := range i.Configs { + key, err := file.Section(file.SectionStrings()[0]).GetKey(k) + if err != nil { + logger.Error("riak get config [%s] error: %s", k, err.Error()) + return err + } + if key.Value() == "" { + logger.Error("riak get config [%s] error: value is null", k) + return fmt.Errorf("riak get config [%s] error: value is null", k) + } + i.Configs[k] = key.Value() + } + return nil +} + +// OutputConfigInfo 输出config信息 +func (i *GetConfigComp) OutputConfigInfo() error { + err := components.PrintOutputCtx(&i.Configs) + if err != nil { + logger.Error("print config failed: %s.", err.Error()) + return err + } + return nil +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/init_bucket_type.go b/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/init_bucket_type.go new file mode 100644 index 0000000000..a28bfdb8d5 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/init_bucket_type.go @@ -0,0 +1,49 @@ +// Package riak TODO +/* + * @Description: 安装 Riak + */ +package riak + +import ( + "dbm-services/common/go-pubpkg/logger" + "dbm-services/riak/db-tools/dbactuator/pkg/util/osutil" + "fmt" +) + +// InitBucketComp TODO +type InitBucketComp struct { + Params *InitBucketParam `json:"extend"` +} + +// InitBucketParam TODO +type InitBucketParam struct { + BucketTypes *[]string `json:"bucket_types" validate:"required"` +} + +// InitBucketType 初始化 bucket type +func (i *InitBucketComp) InitBucketType() error { + basecmds := []string{ + "riak-admin bucket-type create ", + "riak-admin bucket-type activate ", + "riak-admin bucket-type status", + } + for _, bucket := range *i.Params.BucketTypes { + for _, basecmd := range basecmds { + cmd := fmt.Sprintf("%s %s", basecmd, bucket) + res, err := osutil.ExecShellCommand(false, cmd) + if err != nil { + logger.Error("execute shell [%s] error: %s", cmd, err.Error()) + err = fmt.Errorf("execute shell [%s] error: %s", cmd, err.Error()) + return err + } else { + logger.Info("execute shell [%s] output: %s", cmd, res) + } + } + } + if len(*i.Params.BucketTypes) > 0 { + logger.Info("bucket type create success") + } else { + logger.Info("bucket type not needed, do nothing") + } + return nil +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/install_riak.go b/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/install_riak.go new file mode 100644 index 0000000000..d2f4eca3c2 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/install_riak.go @@ -0,0 +1,243 @@ +// Package riak TODO +/* + * @Description: 安装 Riak + */ +package riak + +import ( + "dbm-services/common/go-pubpkg/logger" + "dbm-services/riak/db-tools/dbactuator/pkg/components" + "dbm-services/riak/db-tools/dbactuator/pkg/core/cst" + "dbm-services/riak/db-tools/dbactuator/pkg/core/staticembed" + "dbm-services/riak/db-tools/dbactuator/pkg/util/osutil" + "fmt" + "path" + "strings" + "time" + + "gopkg.in/ini.v1" +) + +// InstallRiakComp TODO +type InstallRiakComp struct { + Params *InstallRaikParam `json:"extend"` + InstallRiakRunTimeCtx `json:"-"` +} + +// InstallRaikParam TODO +type InstallRaikParam struct { + Pkg components.Medium `json:"pkg" validate:"required"` + DistributedCookie *string `json:"distributed_cookie" validate:"required"` + RingSize *string `json:"ring_size" validate:"required"` + LeveldbExpiration *string `json:"leveldb.expiration" validate:"required"` + LeveldbExpirationMode *string `json:"leveldb.expiration.mode" validate:"required"` + LeveldbExpirationRetentionTime *string `json:"leveldb.expiration.retention_time" validate:"required"` +} + +// InstallRiakRunTimeCtx 运行时上下文 +type InstallRiakRunTimeCtx struct { + DataDir string + Config map[string]string + LocalIp string +} + +// Init TODO +func (i *InstallRiakComp) Init() error { + mountpoint, err := osutil.FindFirstMountPoint( + cst.DefaultDataRootPath, + cst.AlterNativeDataRootPath, + ) + if err != nil { + logger.Error("not found mount point: %s", err.Error()) + return err + } + if osutil.IsDataDirOk(mountpoint) { + i.DataDir = mountpoint + } else { + logger.Error("%s could not be data dir", mountpoint) + return fmt.Errorf("%s could not be data dir", mountpoint) + } + platformDataDir := path.Join(mountpoint, cst.DataDir) + cmd := fmt.Sprintf("mkdir -p %s; chown -R riak:root %s", platformDataDir, platformDataDir) + _, err = osutil.ExecShellCommand(false, cmd) + if err != nil { + logger.Error("execute shell [%s] error: %s", cmd, err.Error()) + err = fmt.Errorf("execute shell [%s] error: %s", cmd, err.Error()) + return err + } + return nil +} + +// PreCheck 预检查 +func (i *InstallRiakComp) PreCheck() error { + // 空闲检查 + err := i.OsClearCheck() + if err != nil { + logger.Error("OsClearCheck failed: %s", err.Error()) + return err + } + // 校验介质 + err = i.Params.Pkg.Check() + if err != nil { + logger.Error("riak rpm package check failed: %s", err.Error()) + return err + } + return nil +} + +// OsClearCheck 空闲检查 +func (i *InstallRiakComp) OsClearCheck() error { + cmd := fmt.Sprintf(`%s | grep 'RESULT:' | cut -d':' -f2`, cst.OsClearScriptPath) + result, err := osutil.ExecShellCommand(false, cmd) + if err != nil { + logger.Error("[%s] error occurs while checking os clear %s", cmd, err.Error()) + return fmt.Errorf("[%s] error occurs while checking os clear %s", cmd, err.Error()) + } + ignoreDirtyFile := fmt.Sprintf("%s%s", result[0:3], result[4:5]) + if strings.Contains(ignoreDirtyFile, "1") { + cmd = fmt.Sprintf(`%s | grep 'dirty' | grep -v 'dirty FILE'`, cst.OsClearScriptPath) + dirty, err := osutil.ExecShellCommand(false, cmd) + if err != nil { + logger.Error("[%s] error occurs while checking os clear %s", cmd, err.Error()) + return fmt.Errorf("[%s] error occurs while checking os clear %s", cmd, err.Error()) + } + logger.Error("os not clear:\n %s", dirty) + return fmt.Errorf("os not clear:\n %s", dirty) + } + return nil +} + +// CreateConfigFile 创建riak配置文件 +func (i *InstallRiakComp) CreateConfigFile() error { + bytes, err := staticembed.RiakConfigTemplate.ReadFile(staticembed.RiakConfigTemplateFileName) + if err != nil { + logger.Error("read riak config template failed %s", err.Error()) + return err + } + file, err := ini.Load(bytes) + if err != nil { + logger.Error("riak config template to ini file error: %s", err.Error()) + return err + } + i.LocalIp, err = osutil.GetLocalIP() + if err != nil { + logger.Error("get local ip error: %s", err.Error()) + return err + } + i.Config = map[string]string{ + "nodename": fmt.Sprintf("riak@%s", i.LocalIp), + "platform_data_dir": path.Join(i.DataDir, cst.DataDir), + "platform_log_dir": cst.LogPath, + "listener.http.internal": fmt.Sprintf("%s:%d", i.LocalIp, cst.DefaultHttpPort), + "listener.protobuf.internal": fmt.Sprintf("%s:%d", i.LocalIp, cst.DefaultProtobufPort), + "ring_size": *i.Params.RingSize, + "distributed_cookie": *i.Params.DistributedCookie, + "leveldb.expiration": *i.Params.LeveldbExpiration, + "leveldb.expiration.mode": *i.Params.LeveldbExpirationMode, + "leveldb.expiration.retention_time": *i.Params.LeveldbExpirationRetentionTime, + } + for k, v := range i.Config { + file.Section(file.SectionStrings()[0]).DeleteKey(k) + _, err = file.Section(file.SectionStrings()[0]).NewKey(k, v) + if err != nil { + logger.Error("modify config file error: %s", err.Error()) + return err + } + } + err = file.SaveTo(cst.ConfigPath) + if err != nil { + logger.Error("config file save failed:%s", err.Error()) + return err + } + logger.Info("create config file success") + return nil +} + +// Start 启动riak +func (i *InstallRiakComp) Start() error { + return Start() +} + +func Start() error { + cmd := "riak start" + _, err := osutil.ExecShellCommand(false, cmd) + if err != nil { + logger.Error("execute shell [%s] error: %s", cmd, err.Error()) + err = fmt.Errorf("execute shell [%s] error: %s", cmd, err.Error()) + return err + } + time.Sleep(time.Minute) + logger.Info("start riak success") + return nil +} + +// CheckRiakStatus 检查riak状态 +func (i *InstallRiakComp) CheckRiakStatus() error { + err := CheckStatus(i.Config, i.LocalIp) + if err != nil { + logger.Info("CheckRiakStatus error: %s", err.Error()) + return err + } + logger.Info("riak node status check success") + return nil +} + +// CheckStatus 检查riak状态 +func CheckStatus(items map[string]string, localIp string) error { + checkStatus := fmt.Sprintf(` %s | grep "(C) riak@%s " | grep 'valid'`, cst.ClusterStatusCmd, localIp) + _, err := osutil.ExecShellCommand(false, checkStatus) + if err != nil { + logger.Error("execute shell [%s] error: %s", checkStatus, err.Error()) + err = fmt.Errorf("execute shell [%s] error: %s", checkStatus, err.Error()) + return err + } + cmd := "riak config effective" + config, err := osutil.ExecShellCommand(false, cmd) + if err != nil { + logger.Error("execute shell [%s] error: %s", cmd, err.Error()) + err = fmt.Errorf("execute shell [%s] error: %s", cmd, err.Error()) + return err + } + file, err := ini.Load([]byte(config)) + if err != nil { + logger.Error("riak config template to ini file error: %s", err.Error()) + return err + } + for k, v := range items { + key, err := file.Section(file.SectionStrings()[0]).GetKey(k) + if err != nil { + logger.Error("riak get config %s error: %s", k, err.Error()) + return err + } + if v != key.Value() { + logger.Error("riak %s value: %s not consistent with expected value %s", k, key.Value(), v) + return fmt.Errorf("riak %s value: %s not consistent with expected value %s", k, key.Value(), v) + } + logger.Info("key:%s values:%s", k, v) + } + return nil +} + +// InstallRiakPackage 安装riak包 +func (i *InstallRiakComp) InstallRiakPackage() error { + pkg := path.Join(cst.BK_PKG_INSTALL_PATH, cst.RiakPkgVersion) + res, err := osutil.ExecShellCommand(false, "rpm -q riak") + if err == nil { + if strings.Contains(res, cst.RiakPkgVersion) { + logger.Warn("riak already install") + return nil + } else { + logger.Error("riak already install, expected version: %s but get %s", cst.RiakPkgVersion, res) + return fmt.Errorf("riak already install, expected version: %s but get %s", cst.RiakPkgVersion, res) + } + } + cmd := fmt.Sprintf("rpm -Uvh %s.rpm", pkg) + _, err = osutil.ExecShellCommand(false, cmd) + if err != nil && !strings.Contains(err.Error(), "usermod: no changes") { + logger.Error("execute shell [%s] error: %s", cmd, err.Error()) + err = fmt.Errorf("execute shell [%s] error: %s", cmd, err.Error()) + return err + } + logger.Info("install riak package success") + return nil +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/join_cluster.go b/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/join_cluster.go new file mode 100644 index 0000000000..425312d81a --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/join_cluster.go @@ -0,0 +1,67 @@ +// Package riak TODO +/* + * @Description: 安装 Riak + */ +package riak + +import ( + "dbm-services/common/go-pubpkg/logger" + "dbm-services/riak/db-tools/dbactuator/pkg/core/cst" + "dbm-services/riak/db-tools/dbactuator/pkg/util/osutil" + "fmt" +) + +// JoinClusterComp TODO +type JoinClusterComp struct { + Params *JoinClusterParam `json:"extend"` + JoinClusterRunTimeCtx `json:"-"` +} + +// JoinClusterParam TODO +type JoinClusterParam struct { + DistributedCookie *string `json:"distributed_cookie" validate:"required"` + RingSize *string `json:"ring_size" validate:"required"` + BaseNode *string `json:"base_node" validate:"required"` +} + +// JoinClusterRunTimeCtx 运行时上下文 +type JoinClusterRunTimeCtx struct { + LocalIp string +} + +// PreCheck 预检查 +func (i *JoinClusterComp) PreCheck() error { + var err error + i.LocalIp, err = osutil.GetLocalIP() + if err != nil { + logger.Error("get local ip error: %s", err.Error()) + return err + } + config := map[string]string{ + "nodename": fmt.Sprintf("riak@%s", i.LocalIp), + "listener.http.internal": fmt.Sprintf("%s:%d", i.LocalIp, cst.DefaultHttpPort), + "listener.protobuf.internal": fmt.Sprintf("%s:%d", i.LocalIp, cst.DefaultProtobufPort), + "ring_size": *i.Params.RingSize, + "distributed_cookie": *i.Params.DistributedCookie, + } + err = CheckStatus(config, i.LocalIp) + if err != nil { + logger.Info("CheckRiakStatus error: %s", err.Error()) + return err + } + logger.Info("riak node status check success") + return nil +} + +// JoinCluster 节点加入集群 +func (i *JoinClusterComp) JoinCluster() error { + cmd := fmt.Sprintf("riak-admin cluster join riak@%s", *i.Params.BaseNode) + _, err := osutil.ExecShellCommand(false, cmd) + if err != nil { + logger.Error("execute shell [%s] error: %s", cmd, err.Error()) + err = fmt.Errorf("execute shell [%s] error: %s", cmd, err.Error()) + return err + } + logger.Info("join cluster success") + return nil +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/remove_node.go b/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/remove_node.go new file mode 100644 index 0000000000..ca9e2eef2f --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/remove_node.go @@ -0,0 +1,108 @@ +// Package riak TODO +/* + * @Description: 安装 Riak + */ +package riak + +import ( + "dbm-services/common/go-pubpkg/logger" + "dbm-services/riak/db-tools/dbactuator/pkg/core/cst" + "dbm-services/riak/db-tools/dbactuator/pkg/util/osutil" + "fmt" + "strings" +) + +// RemoveNodeComp TODO +type RemoveNodeComp struct { + Params *RemoveNodeParam `json:"extend"` + RemoveNodeRunTimeCtx `json:"-"` +} + +// RemoveNodeParam TODO +type RemoveNodeParam struct { + OperateNodes *[]string `json:"operate_nodes" validate:"required"` +} + +// RemoveNodeRunTimeCtx 运行时上下文 +type RemoveNodeRunTimeCtx struct { + LocalIp string + NodesStatus map[string]string +} + +// PreCheck 预检查 +func (i *RemoveNodeComp) PreCheck() error { + // 检查本节点的状态,是否正常启动 + _, err := osutil.ExecShellCommand(false, cst.ClusterStatusCmd) + if err != nil { + logger.Error("execute shell [%s] error: %s", cst.ClusterStatusCmd, err.Error()) + err = fmt.Errorf("execute shell [%s] error: %s", cst.ClusterStatusCmd, err.Error()) + return err + } + // 获取所有节点的状态 + cmd := fmt.Sprintf(`%s | grep riak | cut -d'@' -f2 | + awk '{print $1" "$4}' | sed 's/|//g'`, cst.ClusterStatusCmd) + res, err := osutil.ExecShellCommand(false, cmd) + if err != nil { + logger.Error("execute shell [%s] error: %s", cmd, err.Error()) + err = fmt.Errorf("execute shell [%s] error: %s", cmd, err.Error()) + return err + } + logger.Info("riak node status:\n%s", res) + lines := strings.Split(strings.TrimSuffix(strings.TrimPrefix(res, "\n"), "\n"), "\n") + i.NodesStatus = make(map[string]string) + for _, l := range lines { + node := strings.Split(l, " ") + ip := strings.Trim(node[0], " ") + status := strings.Trim(node[1], " ") + i.NodesStatus[ip] = status + // 集群中存在不在运行中的riak节点 + if strings.Contains(status, "down") { + logger.Warn("riak node %s status: %s", ip, status) + } + } + logger.Info("riak cluster status check success") + return nil +} + +// MarkInvalidNodeDown 标志集群中故障的节点为down,保障集群ring正常 +func (i *RemoveNodeComp) MarkInvalidNodeDown() error { + for k, v := range i.NodesStatus { + if v == "down!" { + // 标志故障的节点为down + cmd := fmt.Sprintf("riak-admin down riak@%s", k) + _, err := osutil.ExecShellCommand(false, cmd) + if err != nil { + logger.Error("execute shell [%s] error: %s", cmd, err.Error()) + return fmt.Errorf("execute shell [%s] error: %s", cmd, err.Error()) + } + logger.Info("execute shell [%s] success", cmd) + } + } + return nil +} + +// RemoveNode 集群剔除节点 +func (i *RemoveNodeComp) RemoveNode() error { + for _, ip := range *i.Params.OperateNodes { + // 剔除节点 + cmd := fmt.Sprintf("riak-admin cluster leave riak@%s", ip) + // 待剔除的节点,是否在集群中 + status := i.NodesStatus[ip] + if status == "" { + logger.Error("'riak@%s' is not a member of the cluster", ip) + return fmt.Errorf("'riak@%s' is not a member of the cluster", ip) + } + // 待剔除的节点没有运行,强制剔除,不搬迁节点上的数据 + if strings.Contains(status, "down") { + cmd = fmt.Sprintf("riak-admin cluster force-remove riak@%s", ip) + } + _, err := osutil.ExecShellCommand(false, cmd) + if err != nil { + logger.Error("execute shell [%s] error: %s", cmd, err.Error()) + err = fmt.Errorf("execute shell [%s] error: %s", cmd, err.Error()) + return err + } + } + logger.Info("remove node success") + return nil +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/start.go b/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/start.go new file mode 100644 index 0000000000..c1b518f027 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/start.go @@ -0,0 +1,24 @@ +// Package riak TODO +/* + * @Description: 安装 Riak + */ +package riak + +// StartComp TODO +type StartComp struct { + Params *StartParam `json:"extend"` + StartRunTimeCtx `json:"-"` +} + +// StartParam TODO +type StartParam struct { +} + +// StartRunTimeCtx 运行时上下文 +type StartRunTimeCtx struct { +} + +// Start 启动 +func (i *StartComp) Start() error { + return Start() +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/start_monitor.go b/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/start_monitor.go new file mode 100644 index 0000000000..c3edb64c93 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/start_monitor.go @@ -0,0 +1,25 @@ +// Package riak TODO +/* + * @Description: 安装 Riak + */ +package riak + +// StartMonitorComp TODO +type StartMonitorComp struct { + Params *StartMonitorParam `json:"extend"` + StartMonitorRunTimeCtx `json:"-"` +} + +// StartMonitorParam TODO +type StartMonitorParam struct { +} + +// StartMonitorRunTimeCtx 运行时上下文 +type StartMonitorRunTimeCtx struct { +} + +// StartMonitor 启动监控 +func (i *StartMonitorComp) StartMonitor() error { + var deploy *DeployMonitorComp + return deploy.DeployMonitor() +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/stop.go b/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/stop.go new file mode 100644 index 0000000000..5b3d25f873 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/stop.go @@ -0,0 +1,40 @@ +// Package riak TODO +/* + * @Description: 安装 Riak + */ +package riak + +import ( + "dbm-services/common/go-pubpkg/logger" + "dbm-services/riak/db-tools/dbactuator/pkg/util/osutil" + "fmt" + "time" +) + +// StopComp TODO +type StopComp struct { + Params *StopParam `json:"extend"` + StopRunTimeCtx `json:"-"` +} + +// StopParam TODO +type StopParam struct { +} + +// StopRunTimeCtx 运行时上下文 +type StopRunTimeCtx struct { +} + +// Stop 关闭 +func (i *StopComp) Stop() error { + // 关闭实例 + cmd := "riak stop" + _, err := osutil.ExecShellCommand(false, cmd) + if err != nil { + logger.Error("execute shell [%s] error: %s", cmd, err.Error()) + return fmt.Errorf("execute shell [%s] error: %s", cmd, err.Error()) + } + time.Sleep(time.Minute) + logger.Info("stop riak node complete") + return nil +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/stop_monitor.go b/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/stop_monitor.go new file mode 100644 index 0000000000..f8414640db --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/stop_monitor.go @@ -0,0 +1,33 @@ +// Package riak TODO +/* + * @Description: 安装 Riak + */ +package riak + +import "dbm-services/common/go-pubpkg/logger" + +// StopMonitorComp TODO +type StopMonitorComp struct { + Params *StopMonitorParam `json:"extend"` + StopMonitorRunTimeCtx `json:"-"` +} + +// StopMonitorParam TODO +type StopMonitorParam struct { +} + +// StopMonitorRunTimeCtx 运行时上下文 +type StopMonitorRunTimeCtx struct { +} + +// StopMonitor 关闭监控 +func (i *StopMonitorComp) StopMonitor() error { + // 退出mysql-crond + err := QuitCrond() + if err != nil { + logger.Error("quit crond error: %s", err.Error()) + return err + } + logger.Info("quit crond success") + return nil +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/transfer.go b/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/transfer.go new file mode 100644 index 0000000000..2c3db655bd --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/transfer.go @@ -0,0 +1,56 @@ +// Package riak TODO +/* + * @Description: 安装 Riak + */ +package riak + +import ( + "dbm-services/common/go-pubpkg/logger" + "dbm-services/riak/db-tools/dbactuator/pkg/util/osutil" + "fmt" + "strings" + "time" +) + +// TransferComp TODO +type TransferComp struct { + Params *TransferParam `json:"extend"` + TransferRunTimeCtx `json:"-"` +} + +// TransferParam TODO +type TransferParam struct { + AutoStop *bool `json:"auto_stop" validate:"required"` +} + +// TransferRunTimeCtx 运行时上下文 +type TransferRunTimeCtx struct { +} + +// Transfer 集群数据搬迁进度检查 +func (i *TransferComp) Transfer() error { + cmd := "riak-admin transfers" + cnt := 1 + logger.Info("check transfer status every 10s") + for true { + res, err := osutil.ExecShellCommand(false, cmd) + if err != nil { + if *i.Params.AutoStop == true && strings.Contains(err.Error(), "Node did not respond to ping!") { + logger.Info(err.Error()) + break + } else { + logger.Error("execute shell [%s] error: %s", cmd, err.Error()) + err = fmt.Errorf("execute shell [%s] error: %s", cmd, err.Error()) + return err + } + } + if strings.Contains(res, "No transfers active") { + break + } + logger.Info("%d check, transfering data...", cnt) + time.Sleep(10 * time.Second) + cnt++ + } + logger.Info("riak cluster transfer data complete") + return nil +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/uninstall.go b/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/uninstall.go new file mode 100644 index 0000000000..2e44ddc8cb --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/components/riak/uninstall.go @@ -0,0 +1,99 @@ +// Package riak TODO +/* + * @Description: 安装 Riak + */ +package riak + +import ( + "dbm-services/common/go-pubpkg/logger" + "dbm-services/riak/db-tools/dbactuator/pkg/core/cst" + "dbm-services/riak/db-tools/dbactuator/pkg/util/osutil" + "fmt" + "strings" + "time" +) + +// UninstallComp TODO +type UninstallComp struct { + Params *UninstallParam `json:"extend"` + UninstallRunTimeCtx `json:"-"` +} + +// UninstallParam TODO +type UninstallParam struct { + Stopped *bool `json:"stopped" validate:"required"` +} + +// UninstallRunTimeCtx 运行时上下文 +type UninstallRunTimeCtx struct { +} + +// PreCheck 下架预检查 +func (i *UninstallComp) PreCheck() error { + var cnt int + var res string + var err error + // 每10秒检查一次状态,集群剔除节点后,节点可能没有马上自动关闭,等待10分钟 + // 集群下架前,需要禁用集群,节点已关闭,检查一次即可 + max := 1 + if *i.Params.Stopped == false { + max = 60 + logger.Info("check riak status every 10s") + } + + for cnt = 1; cnt <= max; cnt++ { + res, err = osutil.ExecShellCommand(false, cst.ClusterStatusCmd) + if err != nil { + if strings.Contains(err.Error(), "Node did not respond to ping!") { + logger.Info(err.Error()) + break + } else { + logger.Error("execute shell [%s] error: %s", cst.ClusterStatusCmd, err.Error()) + err = fmt.Errorf("execute shell [%s] error: %s", cst.ClusterStatusCmd, err.Error()) + return err + } + } + logger.Info("%d check, riak still running", cnt) + time.Sleep(10 * time.Second) + } + // 本来应该自动关闭的实例一直未关闭,检查失败 + if cnt == max+1 { + logger.Error("riak still running. execute shell [%s], output:%s", cst.ClusterStatusCmd, res) + err = fmt.Errorf("riak still running. execute shell [%s], output:%s", cst.ClusterStatusCmd, res) + return err + } + // 检查是否有riak进程 + cmd := `ps -ef | grep riak | grep -v 'grep' | grep -v 'epmd -daemon' | grep -v dbactuator` + res, err = osutil.ExecShellCommand(false, cmd) + if err == nil { + logger.Error("riak still running. execute shell [%s], output:%s", cmd, res) + err = fmt.Errorf("riak still running. execute shell [%s], output:%s", cmd, res) + return err + } + logger.Info("uninstall riak node precheck complete") + return nil +} + +// Uninstall 下架 +func (i *UninstallComp) Uninstall() error { + // 关闭守护进程 + killDaemon := `ps -ef | grep 'epmd -daemon' | grep riak | grep -v grep | awk '{print "kill -9 "$2";"}' | sh` + vtime := time.Now().Local().Format(cst.TimeLayoutDir) + // 清理riak数据以及日志文件 + fileBak := fmt.Sprintf("mv %s/riak %s/riak.bak.%s", cst.DefaultDataRootPath, cst.DefaultDataRootPath, vtime) + fileBak2 := fmt.Sprintf("mv %s/riak %s/riak.bak.%s", cst.AlterNativeDataRootPath, cst.AlterNativeDataRootPath, vtime) + fileBak3 := fmt.Sprintf("mv %s %s.bak.%s", cst.MonitorPath, cst.MonitorPath, vtime) + cmds := []string{killDaemon, fileBak, fileBak2, fileBak3} + + for _, cmd := range cmds { + res, err := osutil.ExecShellCommand(false, cmd) + if err != nil && !strings.Contains(err.Error(), "No such file or directory") { + // 存在则清理,不存在,清理失败可忽略报错 + logger.Info("execute shell [%s] error: %s", cmd, err.Error()) + } else if err == nil { + logger.Info("execute shell [%s] output: %s", cmd, res) + } + } + logger.Info("uninstall riak node complete") + return nil +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/components/sysinit/sysinit.go b/dbm-services/riak/db-tools/dbactuator/pkg/components/sysinit/sysinit.go new file mode 100644 index 0000000000..db36d5be7a --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/components/sysinit/sysinit.go @@ -0,0 +1,53 @@ +// Package sysinit TODO +package sysinit + +import ( + "fmt" + "io/ioutil" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/riak/db-tools/dbactuator/pkg/core/staticembed" + "dbm-services/riak/db-tools/dbactuator/pkg/util/osutil" +) + +// ExecSysInitScript TODO +func ExecSysInitScript() (err error) { + data, err := staticembed.SysInitRiakScript.ReadFile(staticembed.SysInitRiakScriptFileName) + if err != nil { + logger.Error("read sysinit script failed %s", err.Error()) + return err + } + tmpScriptName := "/tmp/sysinit.sh" + if err = ioutil.WriteFile(tmpScriptName, data, 07555); err != nil { + logger.Error("write tmp script failed %s", err.Error()) + return err + } + command := fmt.Sprintf("/bin/bash -c \"%s\"", tmpScriptName) + _, err = osutil.StandardShellCommand(false, command) + if err != nil { + logger.Error("exec sysinit script failed %s", err.Error()) + return err + } + return nil +} + +// InitExternal TODO +func InitExternal() (err error) { + data, err := staticembed.ExternalScript.ReadFile(staticembed.ExternalScriptFileName) + if err != nil { + logger.Error("read sysinit script failed %s", err.Error()) + return err + } + tmpScriptName := "/tmp/yum_install_perl_dep.sh" + if err = ioutil.WriteFile(tmpScriptName, data, 07555); err != nil { + logger.Error("write tmp script failed %s", err.Error()) + return err + } + command := fmt.Sprintf("/bin/bash -c \"%s\"", tmpScriptName) + _, err = osutil.StandardShellCommand(false, command) + if err != nil { + logger.Error("yum install perl dep failed %s", err.Error()) + return err + } + return nil +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/core/cst/const.go b/dbm-services/riak/db-tools/dbactuator/pkg/core/cst/const.go new file mode 100644 index 0000000000..2f838de6bc --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/core/cst/const.go @@ -0,0 +1,29 @@ +package cst + +import "time" + +const ( + // Environment TODO + Environment = "enviroment" + // Test TODO + Test = "test" +) + +const ( + // TIMELAYOUT TODO + TIMELAYOUT = "2006-01-02 15:04:05" + // TIMELAYOUTSEQ TODO + TIMELAYOUTSEQ = "2006-01-02_15:04:05" + // TimeLayoutDir TODO + TimeLayoutDir = "20060102150405" +) + +const ( + // BK_PKG_INSTALL_PATH 默认文件下发路径 + BK_PKG_INSTALL_PATH = "/data/install" +) + +// GetNowTimeLayoutStr 20060102150405 +func GetNowTimeLayoutStr() string { + return time.Now().Format(TimeLayoutDir) +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/core/cst/cst.go b/dbm-services/riak/db-tools/dbactuator/pkg/core/cst/cst.go new file mode 100644 index 0000000000..1dab3e254a --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/core/cst/cst.go @@ -0,0 +1,2 @@ +// Package cst 常量 +package cst diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/core/cst/riak.go b/dbm-services/riak/db-tools/dbactuator/pkg/core/cst/riak.go new file mode 100644 index 0000000000..d651272880 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/core/cst/riak.go @@ -0,0 +1,29 @@ +package cst + +// proxy related +const ( + // DefaultDataRootPath 默认data路径 + DefaultDataRootPath = "/data" + // AlterNativeDataRootPath 备选data路径 + AlterNativeDataRootPath = "/data1" + // DefaultProtobufPort riak Protobuf监听接口 + DefaultProtobufPort = 8087 + // DefaultHttpPort riak http监听接口 + DefaultHttpPort = 8098 + // LogPath 日志路径 + LogPath = "/data/riak/log" + MonitorPath = "/data/monitor" + RiakMonitorPath = "/data/monitor/riak-monitor" + CrondPath = "/data/monitor/riak-crond" + CrondListenPort = 9999 + // DataDir data目录 + DataDir = "/riak/data" + // ConfigPath 配置文件路径 + ConfigPath = "/etc/riak/riak.conf" + // RiakPkgVersion riak rpm包的版本 + RiakPkgVersion = "riak-2.2.1-1.el6.x86_64" + // ClusterStatusCmd 命令行查询集群状态 + ClusterStatusCmd = "riak-admin cluster status" + // OsClearScriptPath sojob空闲检查的脚本 + OsClearScriptPath = "/usr/src/sojob/subjob/isclear/os_is_clear.pl" +) diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/core/staticembed/external.sh b/dbm-services/riak/db-tools/dbactuator/pkg/core/staticembed/external.sh new file mode 100644 index 0000000000..5494fca3ae --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/core/staticembed/external.sh @@ -0,0 +1,15 @@ +#/bin/bash + +FOUND=$(grep nofile /etc/security/limits.conf |grep -v "#") +if [ ! -z "$FOUND" ]; then + sed -i '/ nofile /s/^/#/' /etc/security/limits.conf +fi +PKGS=("perl" "perl-Digest-MD5" "perl-Test-Simple" "perl-DBI" "perl-DBD-MySQL" "perl-Data-Dumper" "perl-Encode" "perl-Time-HiRes" "perl-JSON") +for pkg in ${PKGS[@]} +do + if rpm -q ${pkg} &> /dev/null;then + echo "$pkg already install" + continue + fi + yum install -y ${pkg} +done diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/core/staticembed/riak.conf b/dbm-services/riak/db-tools/dbactuator/pkg/core/staticembed/riak.conf new file mode 100644 index 0000000000..1499143268 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/core/staticembed/riak.conf @@ -0,0 +1,500 @@ +## Where to emit the default log messages (typically at 'info' +## severity): +## off: disabled +## file: the file specified by log.console.file +## console: to standard output (seen when using `riak attach-direct`) +## both: log.console.file and standard out. +## +## Default: file +## +## Acceptable values: +## - one of: off, file, console, both +log.console = file + +## The severity level of the console log, default is 'info'. +## +## Default: info +## +## Acceptable values: +## - one of: debug, info, notice, warning, error, critical, alert, emergency, none +log.console.level = info + +## When 'log.console' is set to 'file' or 'both', the file where +## console messages will be logged. +## +## Default: $(platform_log_dir)/console.log +## +## Acceptable values: +## - the path to a file +log.console.file = $(platform_log_dir)/console.log + +## The file where error messages will be logged. +## +## Default: $(platform_log_dir)/error.log +## +## Acceptable values: +## - the path to a file +log.error.file = $(platform_log_dir)/error.log + +## When set to 'on', enables log output to syslog. +## +## Default: off +## +## Acceptable values: +## - on or off +log.syslog = off + +## Whether to enable the crash log. +## +## Default: on +## +## Acceptable values: +## - on or off +log.crash = on + +## If the crash log is enabled, the file where its messages will +## be written. +## +## Default: $(platform_log_dir)/crash.log +## +## Acceptable values: +## - the path to a file +log.crash.file = $(platform_log_dir)/crash.log + +## Maximum size in bytes of individual messages in the crash log +## +## Default: 64KB +## +## Acceptable values: +## - a byte size with units, e.g. 10GB +log.crash.maximum_message_size = 64KB + +## Maximum size of the crash log in bytes, before it is rotated +## +## Default: 10MB +## +## Acceptable values: +## - a byte size with units, e.g. 10GB +log.crash.size = 10MB + +## The schedule on which to rotate the crash log. For more +## information see: +## https://github.com/basho/lager/blob/master/README.md#internal-log-rotation +## +## Default: $D0 +## +## Acceptable values: +## - text +log.crash.rotation = $D0 + +## The number of rotated crash logs to keep. When set to +## 'current', only the current open log file is kept. +## +## Default: 5 +## +## Acceptable values: +## - an integer +## - the text "current" +log.crash.rotation.keep = 5 + +## Name of the Erlang node +## +## Default: riak@127.0.0.1 +## +## Acceptable values: +## - text +nodename = riak@127.0.0.1 + +## Cookie for distributed node communication. All nodes in the +## same cluster should use the same cookie or they will not be able to +## communicate. +## +## Default: riak +## +## Acceptable values: +## - text +distributed_cookie = riak + +## Sets the number of threads in async thread pool, valid range +## is 0-1024. If thread support is available, the default is 64. +## More information at: http://erlang.org/doc/man/erl.html +## +## Default: 64 +## +## Acceptable values: +## - an integer +erlang.async_threads = 64 + +## The number of concurrent ports/sockets +## Valid range is 1024-134217727 +## +## Default: 65536 +## +## Acceptable values: +## - an integer +erlang.max_ports = 65536 + +## Set scheduler forced wakeup interval. All run queues will be +## scanned each Interval milliseconds. While there are sleeping +## schedulers in the system, one scheduler will be woken for each +## non-empty run queue found. An Interval of zero disables this +## feature, which also is the default. +## This feature is a workaround for lengthy executing native code, and +## native code that do not bump reductions properly. +## More information: http://www.erlang.org/doc/man/erl.html#+sfwi +## +## Default: 500 +## +## Acceptable values: +## - an integer +## erlang.schedulers.force_wakeup_interval = 500 + +## Enable or disable scheduler compaction of load. By default +## scheduler compaction of load is enabled. When enabled, load +## balancing will strive for a load distribution which causes as many +## scheduler threads as possible to be fully loaded (i.e., not run out +## of work). This is accomplished by migrating load (e.g. runnable +## processes) into a smaller set of schedulers when schedulers +## frequently run out of work. When disabled, the frequency with which +## schedulers run out of work will not be taken into account by the +## load balancing logic. +## More information: http://www.erlang.org/doc/man/erl.html#+scl +## +## Default: false +## +## Acceptable values: +## - one of: true, false +## erlang.schedulers.compaction_of_load = false + +## Enable or disable scheduler utilization balancing of load. By +## default scheduler utilization balancing is disabled and instead +## scheduler compaction of load is enabled which will strive for a +## load distribution which causes as many scheduler threads as +## possible to be fully loaded (i.e., not run out of work). When +## scheduler utilization balancing is enabled the system will instead +## try to balance scheduler utilization between schedulers. That is, +## strive for equal scheduler utilization on all schedulers. +## More information: http://www.erlang.org/doc/man/erl.html#+sub +## +## Acceptable values: +## - one of: true, false +## erlang.schedulers.utilization_balancing = true + +## Number of partitions in the cluster (only valid when first +## creating the cluster). Must be a power of 2, minimum 8 and maximum +## 1024. +## +## Default: 64 +## +## Acceptable values: +## - an integer +## ring_size = 64 + +## Number of concurrent node-to-node transfers allowed. +## +## Default: 2 +## +## Acceptable values: +## - an integer +## transfer_limit = 2 + +## Default cert location for https can be overridden +## with the ssl config variable, for example: +## +## Acceptable values: +## - the path to a file +## ssl.certfile = $(platform_etc_dir)/cert.pem + +## Default key location for https can be overridden with the ssl +## config variable, for example: +## +## Acceptable values: +## - the path to a file +## ssl.keyfile = $(platform_etc_dir)/key.pem + +## Default signing authority location for https can be overridden +## with the ssl config variable, for example: +## +## Acceptable values: +## - the path to a file +## ssl.cacertfile = $(platform_etc_dir)/cacertfile.pem + +## DTrace support Do not enable 'dtrace' unless your Erlang/OTP +## runtime is compiled to support DTrace. DTrace is available in +## R15B01 (supported by the Erlang/OTP official source package) and in +## R14B04 via a custom source repository & branch. +## +## Default: off +## +## Acceptable values: +## - on or off +dtrace = off + +## Platform-specific installation paths (substituted by rebar) +## +## Default: /usr/sbin +## +## Acceptable values: +## - the path to a directory +platform_bin_dir = /usr/sbin + +## +## Default: /var/lib/riak +## +## Acceptable values: +## - the path to a directory +platform_data_dir = /data/riak/data + +## +## Default: /etc/riak +## +## Acceptable values: +## - the path to a directory +platform_etc_dir = /etc/riak + +## +## Default: /usr/lib64/riak/lib +## +## Acceptable values: +## - the path to a directory +platform_lib_dir = /usr/lib64/riak/lib + +## +## Default: /var/log/riak +## +## Acceptable values: +## - the path to a directory +platform_log_dir = /data/riak/log + +## Enable consensus subsystem. Set to 'on' to enable the +## consensus subsystem used for strongly consistent Riak operations. +## +## Default: off +## +## Acceptable values: +## - on or off +## strong_consistency = on + +## listener.http. is an IP address and TCP port that the Riak +## HTTP interface will bind. +## +## Default: 127.0.0.1:8098 +## +## Acceptable values: +## - an IP/port pair, e.g. 127.0.0.1:10011 +listener.http.internal = 127.0.0.1:8098 + +## listener.protobuf. is an IP address and TCP port that the Riak +## Protocol Buffers interface will bind. +## +## Default: 127.0.0.1:8087 +## +## Acceptable values: +## - an IP/port pair, e.g. 127.0.0.1:10011 +listener.protobuf.internal = 127.0.0.1:8087 + +## The maximum length to which the queue of pending connections +## may grow. If set, it must be an integer > 0. If you anticipate a +## huge number of connections being initialized *simultaneously*, set +## this number higher. +## +## Default: 128 +## +## Acceptable values: +## - an integer +## protobuf.backlog = 128 + +## listener.https. is an IP address and TCP port that the Riak +## HTTPS interface will bind. +## +## Acceptable values: +## - an IP/port pair, e.g. 127.0.0.1:10011 +## listener.https.internal = 127.0.0.1:8098 + +## How Riak will repair out-of-sync keys. Some features require +## this to be set to 'active', including search. +## * active: out-of-sync keys will be repaired in the background +## * passive: out-of-sync keys are only repaired on read +## * active-debug: like active, but outputs verbose debugging +## information +## +## Default: active +## +## Acceptable values: +## - one of: active, passive, active-debug +anti_entropy = active + +## Specifies the storage engine used for Riak's key-value data +## and secondary indexes (if supported). +## +## Default: bitcask +## +## Acceptable values: +## - one of: bitcask, leveldb, memory, multi, prefix_multi +storage_backend = leveldb + +## Simplify prefix_multi configuration for Riak CS. Keep this +## commented out unless Riak is configured for Riak CS. +## +## Acceptable values: +## - an integer +## cs_version = 20000 + +## Controls which binary representation of a riak value is stored +## on disk. +## * 0: Original erlang:term_to_binary format. Higher space overhead. +## * 1: New format for more compact storage of small values. +## +## Default: 1 +## +## Acceptable values: +## - the integer 1 +## - the integer 0 +object.format = 1 + +## Reading or writing objects bigger than this size will write a +## warning in the logs. +## +## Default: 5MB +## +## Acceptable values: +## - a byte size with units, e.g. 10GB +object.size.warning_threshold = 5MB + +## Writing an object bigger than this will send a failure to the +## client. +## +## Default: 50MB +## +## Acceptable values: +## - a byte size with units, e.g. 10GB +object.size.maximum = 50MB + +## Writing an object with more than this number of siblings will +## generate a warning in the logs. +## +## Default: 25 +## +## Acceptable values: +## - an integer +object.siblings.warning_threshold = 25 + +## Writing an object with more than this number of siblings will +## send a failure to the client. +## +## Default: 100 +## +## Acceptable values: +## - an integer +object.siblings.maximum = 100 + +## A path under which bitcask data files will be stored. +## +## Default: $(platform_data_dir)/bitcask +## +## Acceptable values: +## - the path to a directory +bitcask.data_root = $(platform_data_dir)/bitcask + +## Configure how Bitcask writes data to disk. +## erlang: Erlang's built-in file API +## nif: Direct calls to the POSIX C API +## The NIF mode provides higher throughput for certain +## workloads, but has the potential to negatively impact +## the Erlang VM, leading to higher worst-case latencies +## and possible throughput collapse. +## +## Default: erlang +## +## Acceptable values: +## - one of: erlang, nif +bitcask.io_mode = erlang + +## Set to 'off' to disable the admin panel. +## +## Default: off +## +## Acceptable values: +## - on or off +riak_control = off + +## Authentication mode used for access to the admin panel. +## +## Default: off +## +## Acceptable values: +## - one of: off, userlist +riak_control.auth.mode = off + +## If riak control's authentication mode (riak_control.auth.mode) +## is set to 'userlist' then this is the list of usernames and +## passwords for access to the admin panel. +## To create users with given names, add entries of the format: +## riak_control.auth.user.USERNAME.password = PASSWORD +## replacing USERNAME with the desired username and PASSWORD with the +## desired password for that user. +## +## Acceptable values: +## - text +## riak_control.auth.user.admin.password = pass + +## This parameter defines the percentage of total server memory +## to assign to LevelDB. LevelDB will dynamically adjust its internal +## cache sizes to stay within this size. The memory size can +## alternately be assigned as a byte count via leveldb.maximum_memory +## instead. +## +## Default: 70 +## +## Acceptable values: +## - an integer +leveldb.maximum_memory.percent = 70 + +## To enable Search set this 'on'. +## +## Default: off +## +## Acceptable values: +## - on or off +search = off + +## How long Riak will wait for Solr to start. The start sequence +## will be tried twice. If both attempts timeout, then the Riak node +## will be shutdown. This may need to be increased as more data is +## indexed and Solr takes longer to start. Values lower than 1s will +## be rounded up to the minimum 1s. +## +## Default: 30s +## +## Acceptable values: +## - a time duration with units, e.g. '10s' for 10 seconds +search.solr.start_timeout = 30s + +## The port number which Solr binds to. +## NOTE: Binds on every interface. +## +## Default: 8093 +## +## Acceptable values: +## - an integer +search.solr.port = 8093 + +## The port number which Solr JMX binds to. +## NOTE: Binds on every interface. +## +## Default: 8985 +## +## Acceptable values: +## - an integer +search.solr.jmx_port = 8985 + +## The options to pass to the Solr JVM. Non-standard options, +## i.e. -XX, may not be portable across JVM implementations. +## E.g. -XX:+UseCompressedStrings +## +## Default: -d64 -Xms1g -Xmx1g -XX:+UseStringCache -XX:+UseCompressedOops +## +## Acceptable values: +## - text +search.solr.jvm_options = -d64 -Xms1g -Xmx1g -XX:+UseStringCache -XX:+UseCompressedOops + diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/core/staticembed/staticembed.go b/dbm-services/riak/db-tools/dbactuator/pkg/core/staticembed/staticembed.go new file mode 100644 index 0000000000..9b14946fdb --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/core/staticembed/staticembed.go @@ -0,0 +1,2 @@ +// Package staticembed TODO +package staticembed diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/core/staticembed/sysinit_mysql.go b/dbm-services/riak/db-tools/dbactuator/pkg/core/staticembed/sysinit_mysql.go new file mode 100644 index 0000000000..243a5a63d5 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/core/staticembed/sysinit_mysql.go @@ -0,0 +1,27 @@ +package staticembed + +import "embed" + +// SysInitRiakScriptFileName TODO +var SysInitRiakScriptFileName = "sysinit_riak.sh" + +// ExternalScriptFileName TODO +var ExternalScriptFileName = "external.sh" + +// RiakConfigTemplateFileName TODO +var RiakConfigTemplateFileName = "riak.conf" + +// SysInitMySQLScript TODO +// +//go:embed sysinit_riak.sh +var SysInitRiakScript embed.FS + +// ExternalScript TODO +// +//go:embed external.sh +var ExternalScript embed.FS + +// RiakConfigTemplate TODO +// +//go:embed riak.conf +var RiakConfigTemplate embed.FS diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/core/staticembed/sysinit_riak.sh b/dbm-services/riak/db-tools/dbactuator/pkg/core/staticembed/sysinit_riak.sh new file mode 100644 index 0000000000..72b068f0da --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/core/staticembed/sysinit_riak.sh @@ -0,0 +1,58 @@ +#!/bin/sh +# 新建riak用户 +## +# riak scripts +## +# depends: ~/abs/ssh.exp ~/abs/scp.exp + +add_user () { + id riak >& /dev/null + if [ $? -ne 0 ] + then + useradd riak -u498 + else + echo "user riak exists" 1>&2 + rm $0 + exit 1 + fi +} + +change_os () { +echo "vm.dirty_background_ratio = 0 +vm.dirty_background_bytes = 104857600 +vm.dirty_ratio = 0 +vm.dirty_bytes = 209715200 +vm.dirty_writeback_centisecs = 100 +vm.dirty_expire_centisecs = 200 +vm.swappiness = 0 +net.ipv4.tcp_max_syn_backlog = 40000 +net.core.somaxconn = 40000 +net.core.wmem_default = 8388608 +net.core.rmem_default = 8388608 +net.ipv4.tcp_sack = 1 +net.ipv4.tcp_window_scaling = 1 +net.ipv4.tcp_fin_timeout = 15 +net.ipv4.tcp_keepalive_intvl = 30 +net.ipv4.tcp_tw_reuse = 1 +net.ipv4.tcp_moderate_rcvbuf = 1" >> /etc/sysctl.conf +/sbin/sysctl -p +} + +mk_riakdir () { + mkdir -p /data/install + chmod -R a+rwx /data/install + mkdir -p /data/riakenv + mkdir -p /data/riak/log + chown -R riak.root /data/riak/log + mkdir /etc/riak/ +} + +init_riak () { + add_user + change_os + mk_riakdir +} + +init_riak +rm $0 +exit \ No newline at end of file diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/rollback/rollback.go b/dbm-services/riak/db-tools/dbactuator/pkg/rollback/rollback.go new file mode 100644 index 0000000000..c0fd717906 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/rollback/rollback.go @@ -0,0 +1,233 @@ +// Package rollback TODO +package rollback + +import ( + "fmt" + "os" + "path" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/riak/db-tools/dbactuator/pkg/util" + "dbm-services/riak/db-tools/dbactuator/pkg/util/osutil" +) + +const ( + // OP_DEL TODO + OP_DEL = "DEL" + // OP_MOVE TODO + OP_MOVE = "MOVE" +) + +// RollBackObjects TODO +type RollBackObjects struct { + RollBackProcessList []RollBackProcess `json:"rollback_processlist"` + RollBackFiles []RollBackFile `json:"rollback_files"` +} + +// 这些目录无论如何都不能直接删除 +// 我们原子任务主要操作相关目录 +var safeDirs = map[string]struct{}{"/": {}, "/etc": {}, "/usr": {}, "/usr/local": {}, "/data": {}, "/data1": {}} + +// RollBackFile 文件包括 常规文件 目录 软连接等 +// 回滚操作不记录删除文件的操作 +// 因为删除文件没有源文件无法恢复 +type RollBackFile struct { + // 文件必须是绝对路径 + FileName string `json:"file_name"` // DEL,MOVE 后的文件名称 + OriginFileName string `json:"origin_file_name"` // DEL,MOVE 前的文件名称 + OriginOpera string `json:"origin_opera"` // 原始操作 DEL:新增文件 MOVE:文件重命名 +} + +// RollBackProcess 暂定回滚由任务拉起的新进程 +// 已经kill进程,暂不恢复 +type RollBackProcess struct { + StartOsUser string `json:"start_os_user"` // os启动用户 + ProcessId int `json:"process_id"` +} + +// AddDelFile TODO +func (r *RollBackObjects) AddDelFile(fileName string) { + r.RollBackFiles = append( + r.RollBackFiles, RollBackFile{ + FileName: fileName, + OriginOpera: OP_DEL, + }, + ) +} + +// AddMoveFile TODO +func (r *RollBackObjects) AddMoveFile(originFileName, fileName string) { + r.RollBackFiles = append( + r.RollBackFiles, RollBackFile{ + FileName: fileName, + OriginFileName: originFileName, + OriginOpera: OP_DEL, + }, + ) +} + +// AddKillProcess TODO +func (r *RollBackObjects) AddKillProcess(pid int) { + r.RollBackProcessList = append( + r.RollBackProcessList, RollBackProcess{ + ProcessId: pid, + }, + ) +} + +// RollBack TODO +func (r *RollBackObjects) RollBack() (err error) { + if r.RollBackProcessList != nil { + err = r.RollBack_Processlists() + } + if r.RollBackFiles != nil { + err = r.RollBack_Files() + } + return err +} + +// RollBack_Processlists TODO +func (r *RollBackObjects) RollBack_Processlists() (err error) { + if len(r.RollBackProcessList) <= 0 { + return nil + } + for _, rp := range r.RollBackProcessList { + if err = rp.Rollback(); err != nil { + return + } + } + return err +} + +// RollBack_Files TODO +func (r *RollBackObjects) RollBack_Files() (err error) { + if len(r.RollBackFiles) <= 0 { + return nil + } + for _, rfile := range r.RollBackFiles { + if err = rfile.RollBack(); err != nil { + return + } + } + return err +} + +// RollBack TODO +// os.Stat 和 os.Lstat 两个函数用来获取文件类型,但是os.Stat具有穿透连接能力,如果你去获取一个软链的 FileInfo,他会返回软链到的文件的信息,你既然想知道他的具体类型,就要使用 os.Lstat +func (r *RollBackFile) RollBack() (err error) { + f, err := os.Lstat(r.FileName) + if err != nil { + // 如果是删除文件的话,文件不存在,那就忽略错误 + if os.IsNotExist(err) && r.OriginOpera == OP_DEL { + return nil + } + return err + } + + switch mode := f.Mode().Type(); { + case mode.IsDir(): + return r.rollbackDir() + case mode.IsRegular(): + return r.rollbackRegularFile() + case mode&os.ModeSymlink != 0: + return r.rollbackLink() + default: + logger.Error("Not Define mode.String(): %v\n", mode.String()) + } + return nil +} + +func (r *RollBackFile) rollbackRegularFile() (err error) { + switch r.OriginOpera { + case OP_DEL: + return SafeRm(r.FileName) + case OP_MOVE: + return SafeMove(r.FileName, r.OriginFileName) + } + return fmt.Errorf("no define Operate %s", r.OriginOpera) +} + +func (r *RollBackFile) rollbackDir() (err error) { + switch r.OriginOpera { + case OP_DEL: + return SafeRmDir(r.FileName) + case OP_MOVE: + return SafeMove(r.FileName, r.OriginFileName) + } + return fmt.Errorf("no define Operate %s", r.OriginOpera) +} + +func (r *RollBackFile) rollbackLink() (err error) { + switch r.OriginOpera { + case OP_DEL: + return SafeUnlink(r.FileName) + case OP_MOVE: + return SafeRelink(r.FileName, r.OriginFileName) + } + return fmt.Errorf("no define Operate %s", r.OriginOpera) +} + +// SafeMove TODO +func SafeMove(file, destfile string) (err error) { + _, err = osutil.ExecShellCommand(false, fmt.Sprintf("mv %s %s", file, destfile)) + return +} + +// SafeRelink TODO +func SafeRelink(linkfile, destfile string) (err error) { + _, err = osutil.ExecShellCommand(false, fmt.Sprintf(" unlink %s && ln -s %s %s", linkfile, destfile, linkfile)) + return +} + +// SafeUnlink TODO +func SafeUnlink(file string) (err error) { + if IsSafe(file) { + _, err = osutil.ExecShellCommand(false, fmt.Sprintf("unlink %s", file)) + return + } + return fmt.Errorf("%s 不允许删除", file) +} + +// SafeRm TODO +func SafeRm(file string) (err error) { + if IsSafe(file) { + _, err = osutil.ExecShellCommand(false, fmt.Sprintf("rm %s", file)) + return + } + return fmt.Errorf("%s不允许删除", file) +} + +// SafeRmDir TODO +func SafeRmDir(file string) (err error) { + if IsSafe(file) { + _, err = osutil.ExecShellCommand(false, fmt.Sprintf("rm -rf %s", file)) + return + } + return fmt.Errorf("%s 不允许删除", file) +} + +// IsSafe TODO +func IsSafe(file string) bool { + // 如果存在 file 是不能直接删除的目录 + if _, ok := safeDirs[file]; ok { + return !ok + } + // 如果存在 file 是不能直接删除的目录,判断下base dir + if _, ok := safeDirs[path.Base(file)]; ok { + return !ok + } + return !util.StrIsEmpty(file) +} + +// Rollback TODO +func (r *RollBackProcess) Rollback() (err error) { + if r.ProcessId <= 0 { + return nil + } + p, err := os.FindProcess(r.ProcessId) + if err != nil { + // 找不到这个进程,可能吗没有 不需要回滚 + return nil + } + return p.Kill() +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/rollback/rollback_test.go b/dbm-services/riak/db-tools/dbactuator/pkg/rollback/rollback_test.go new file mode 100644 index 0000000000..232c2b2bd9 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/rollback/rollback_test.go @@ -0,0 +1,85 @@ +package rollback + +import ( + "testing" +) + +// 将删除/data1/11.txt +func TestRollBackFile(t *testing.T) { + t.Logf("start testing TestRollBackFile...") + rf := RollBackFile{ + FileName: "/data1/11.txt", + OriginOpera: OP_DEL, + } + if err := rf.RollBack(); err != nil { + t.Error("rollback", err) + } +} + +// 将会把/data1/1.txt mv /data1/2.txt +func TestMoveFile(t *testing.T) { + t.Logf("start testing TestRollBackFile...") + rf := RollBackFile{ + FileName: "/data1/1.txt", + OriginFileName: "/data1/2.txt", + OriginOpera: OP_MOVE, + } + if err := rf.RollBack(); err != nil { + t.Error("rollback", err) + } +} + +// 将会把/data1/d1 删除 +func TestDelDir(t *testing.T) { + t.Logf("start testing TestRollBackFile...") + rf := RollBackFile{ + FileName: "/data1/d1/", + OriginOpera: OP_DEL, + } + if err := rf.RollBack(); err != nil { + t.Errorf("rollback %s", err.Error()) + } +} + +// 将会把/data1/d1 删除 +func TestMoveDir(t *testing.T) { + t.Logf("start testing TestRollBackFile...") + rf := RollBackFile{ + FileName: "/data1/d1", + OriginFileName: "/data1/d", + OriginOpera: OP_MOVE, + } + if err := rf.RollBack(); err != nil { + t.Errorf("rollback %s", err.Error()) + } +} + +// 将会把/data1/f 软连接到 /data1/c 目录 +func TestRmLink(t *testing.T) { + t.Logf("start testing TestRollBackFile...") + rf := RollBackFile{ + FileName: "/data1/f", + OriginOpera: OP_DEL, + } + if err := rf.RollBack(); err != nil { + t.Errorf("rollback %s", err.Error()) + } +} + +// 将会把/data1/f 软连接到 /data1/c 目录 +func TestMoveLink(t *testing.T) { + t.Logf("start testing TestRollBackFile...") + rf := RollBackFile{ + FileName: "/data1/f", + OriginFileName: "/data1/c", + OriginOpera: OP_MOVE, + } + if err := rf.RollBack(); err != nil { + t.Errorf("rollback %s", err.Error()) + } +} + +func TestIsSafeDir(t *testing.T) { + t.Logf("start testing ...") + t.Log(IsSafe("/usr/local")) +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/tools/impls.go b/dbm-services/riak/db-tools/dbactuator/pkg/tools/impls.go new file mode 100644 index 0000000000..ea98e3634f --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/tools/impls.go @@ -0,0 +1,38 @@ +package tools + +import ( + "dbm-services/mysql/db-tools/dbactuator/pkg/util/osutil" + + "github.com/pkg/errors" +) + +func (s *ToolSet) validate() error { + for k, v := range s.maps { + if _, ok := defaultPath[k]; !ok { + return errors.Errorf("tool %s is not regiestered", k) + } + if !osutil.FileExist(v) { + err := errors.Errorf("%s: %s not found", k, v) + return err + } + } + return nil +} + +// Get 获得工具路径 +func (s *ToolSet) Get(tool ExternalTool) (string, error) { + if p, ok := s.maps[tool]; ok { + return p, nil + } + err := errors.Errorf("%s not registered or picked", tool) + return "", err +} + +// MustGet 必须获得 +func (s *ToolSet) MustGet(tool ExternalTool) string { + if p, ok := s.maps[tool]; ok { + return p + } + err := errors.Errorf("%s not registered or picked", tool) + panic(err) +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/tools/init.go b/dbm-services/riak/db-tools/dbactuator/pkg/tools/init.go new file mode 100644 index 0000000000..0014f563d4 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/tools/init.go @@ -0,0 +1,148 @@ +package tools + +import ( + "fmt" + "path" + "path/filepath" + + "dbm-services/mysql/db-tools/dbactuator/pkg/core/cst" +) + +// ExternalTool 外部工具类型 +type ExternalTool string + +const ( + // ToolMysqlbinlog mysqlbinlog + ToolMysqlbinlog ExternalTool = "mysqlbinlog" + // ToolMload mload + ToolMload ExternalTool = "mload" + // ToolMysqlclient mysql + ToolMysqlclient ExternalTool = "mysql" + // ToolXLoad xload + ToolXLoad ExternalTool = "xload" + // ToolQPress qpress + ToolQPress ExternalTool = "qpress" + // ToolPv TODO + ToolPv ExternalTool = "pv" + // ToolMysqlbinlogRollback mysqlbinlog_rollback + ToolMysqlbinlogRollback ExternalTool = "mysqlbinlog_rollback" + // ToolMysqlbinlogRollback80 mysqlbinlog_rollback80 + ToolMysqlbinlogRollback80 ExternalTool = "mysqlbinlog_rollback80" + // ToolMysqlTableChecksum mysql-table-checksum + ToolMysqlTableChecksum ExternalTool = "mysql-table-checksum" + // ToolPtTableChecksum pt-table-checksum + ToolPtTableChecksum ExternalTool = "pt-table-checksum" + // ToolPtTableSync pt-table-sync + ToolPtTableSync ExternalTool = "pt-table-sync" + // ToolDbbackupGo dbbackup + ToolDbbackupGo ExternalTool = "dbbackup" + // ToolRotatebinlog binlog 清理 + ToolRotatebinlog ExternalTool = "rotatebinlog" + // ToolMySQLCrond crond + ToolMySQLCrond ExternalTool = "mysql-crond" + // ToolMySQLMonitor mysql monitor + ToolMySQLMonitor ExternalTool = "mysql-monitor" +) + +// defaultPath defaults path +var defaultPath = map[ExternalTool]string{ + ToolMload: "/home/mysql/dbbackup/MLOAD/MLOAD.pl", + ToolXLoad: "/home/mysql/dbbackup/xtrabackup/xload.pl", + ToolQPress: "/home/mysql/dbbackup-go/bin/xtrabackup/qpress", + ToolPv: "/home/mysql/dbbackup-go/bin/pv", + ToolMysqlclient: "/usr/local/mysql/bin/mysql", + ToolMysqlbinlog: "/usr/local/mysql/bin/mysqlbinlog", + ToolMysqlbinlogRollback: filepath.Join(cst.DBAToolkitPath, string(ToolMysqlbinlogRollback)), + ToolMysqlbinlogRollback80: filepath.Join(cst.DBAToolkitPath, "mysqlbinlog_rollback_80"), + ToolMysqlTableChecksum: path.Join(cst.ChecksumInstallPath, string(ToolMysqlTableChecksum)), + ToolPtTableChecksum: path.Join(cst.ChecksumInstallPath, string(ToolPtTableChecksum)), + ToolPtTableSync: path.Join(cst.ChecksumInstallPath, string(ToolPtTableSync)), + ToolDbbackupGo: path.Join(cst.DbbackupGoInstallPath, string(ToolDbbackupGo)), + ToolMySQLCrond: path.Join(cst.MySQLCrondInstallPath, string(ToolMySQLCrond)), + ToolMySQLMonitor: path.Join(cst.MySQLMonitorInstallPath, string(ToolMySQLMonitor)), +} + +// ToolPath 基本结构 +type ToolPath struct { + Tools map[ExternalTool]string `json:"tools"` +} + +// ToolSet 外部工具 +type ToolSet struct { + // 外部指定工具路径 + Tools map[ExternalTool]string `json:"tools"` + maps map[ExternalTool]string +} + +// NewToolSetWithDefault 加载全部默认工具 +func NewToolSetWithDefault() (*ToolSet, error) { + res := &ToolSet{maps: defaultPath} + err := res.validate() + if err != nil { + return nil, err + } + return res, nil +} + +// NewToolsSetWithDefaultNoValidate 无验证 +func NewToolsSetWithDefaultNoValidate() *ToolSet { + return &ToolSet{maps: defaultPath} +} + +// NewToolSetWithPick 按需加载 +func NewToolSetWithPick(tools ...ExternalTool) (*ToolSet, error) { + maps := make(map[ExternalTool]string) + for _, tool := range tools { + if p, ok := defaultPath[tool]; ok { + maps[tool] = p + } else { + err := fmt.Errorf("%s not registered", tool) + return nil, err + } + } + res := &ToolSet{maps: maps} + if err := res.validate(); err != nil { + return nil, err + } + return res, nil +} + +// NewToolSetWithPickNoValidate 无验证 +func NewToolSetWithPickNoValidate(tools ...ExternalTool) *ToolSet { + maps := make(map[ExternalTool]string) + for _, tool := range tools { + if p, ok := defaultPath[tool]; ok { + maps[tool] = p + } else { + maps[tool] = "" + } + } + return &ToolSet{maps: maps} +} + +// Merge merge tools to left ToolSet +func (s *ToolSet) Merge(tools *ToolSet) error { + s.maps = s.Tools + if s.maps == nil { + s.maps = make(map[ExternalTool]string) + } + if err := s.validate(); err != nil { + return err + } + for toolName, toolPath := range tools.maps { + if _, ok := s.maps[toolName]; !ok { + s.maps[toolName] = toolPath + } + } + return nil +} + +// Set modify a tool path +// 没有校验 toolName 和 toolPath +func (s *ToolSet) Set(toolName ExternalTool, toolPath string) error { + if s.maps == nil { + s.maps = make(map[ExternalTool]string) + } + s.maps[toolName] = toolPath + return nil +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/tools/tools.go b/dbm-services/riak/db-tools/dbactuator/pkg/tools/tools.go new file mode 100644 index 0000000000..a8cb267c2c --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/tools/tools.go @@ -0,0 +1,2 @@ +// Package tools 外部工具 +package tools diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/util/auth/auth.go b/dbm-services/riak/db-tools/dbactuator/pkg/util/auth/auth.go new file mode 100644 index 0000000000..bde76f03e2 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/util/auth/auth.go @@ -0,0 +1,2 @@ +// Package auth 认证 +package auth diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/util/auth/jwt_token.go b/dbm-services/riak/db-tools/dbactuator/pkg/util/auth/jwt_token.go new file mode 100644 index 0000000000..77d96c955b --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/util/auth/jwt_token.go @@ -0,0 +1,21 @@ +package auth + +import ( + "time" + + "github.com/golang-jwt/jwt/v4" + // "github.com/dgrijalva/jwt-go" +) + +// Sign 签名加密 +func Sign(username string, secretId, secretKey string) (tokenString string, err error) { + // The token content. + token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ + "sub": secretId, + "user": username, + "iat": time.Now().Add(-1 * time.Minute).Unix(), + }) + // Sign the token with the specified secret. + tokenString, err = token.SignedString([]byte(secretKey)) + return +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/util/dbcnf.go b/dbm-services/riak/db-tools/dbactuator/pkg/util/dbcnf.go new file mode 100644 index 0000000000..56a7a4e48c --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/util/dbcnf.go @@ -0,0 +1,615 @@ +package util + +import ( + "fmt" + "os" + "path" + "path/filepath" + "reflect" + "regexp" + "sort" + "strings" + "sync" + + "dbm-services/common/go-pubpkg/cmutil" + "dbm-services/common/go-pubpkg/logger" + "dbm-services/mysql/db-tools/dbactuator/pkg/core/cst" + + "github.com/pkg/errors" + "gopkg.in/ini.v1" +) + +const ( + // SecTag TODO + SecTag = "sectag" + // KeyTag TODO + KeyTag = "keytag" +) + +const ( + // MysqldSec TODO + MysqldSec = "mysqld" +) + +// CnfFile TODO +type CnfFile struct { + FileName string + Cfg *ini.File + mu *sync.Mutex +} + +// CnfUint TODO +type CnfUint struct { + KvMap map[string]string + // 可重复的key + ShadowKvMap map[string]string + // skip_symbolic_links单key的配置 + BoolKey []string +} + +// MycnfIniObject TODO +type MycnfIniObject struct { + Section map[string]*CnfUint +} + +var iniLoadOption = ini.LoadOptions{ + PreserveSurroundedQuote: true, + IgnoreInlineComment: true, + AllowBooleanKeys: true, + AllowShadows: true, +} + +// NewMyCnfObject 渲染模板全量的配置文件 +// +// @receiver c +// @receiver myfileName +// @return nf +// @return err +func NewMyCnfObject(c interface{}, myfileName string) (nf *CnfFile, err error) { + nf = NewEmptyCnfObj(myfileName) + t := reflect.TypeOf(c) + if t.Kind() != reflect.Struct { + return nil, fmt.Errorf("mf reflect is not struct") + } + var isMysqldSectionExists bool // 要求 mysqld section 存在 + for i := 0; i < t.NumField(); i++ { // 这里遍历的是 [map{client} map{mysqld} ...] + var sectionName = t.Field(i).Tag.Get(SecTag) + m := reflect.ValueOf(c).FieldByName(t.Field(i).Name) + if _, err := nf.Cfg.NewSection(sectionName); err != nil { + return nil, err + } + for _, k := range m.MapKeys() { + if err = nf.RenderSection(sectionName, k.String(), m.MapIndex(k).String(), false); err != nil { + return nil, err + } + } + if sectionName == MysqldSec { + isMysqldSectionExists = true + } + } + if !isMysqldSectionExists { + return nil, fmt.Errorf("must Include Sections [mysqld]") + } + return +} + +// ReplaceMyconfigsObjects TODO +func ReplaceMyconfigsObjects(f *CnfFile, c interface{}) error { + t := reflect.TypeOf(c) + v := reflect.ValueOf(c) + if t.Kind() != reflect.Struct { + return fmt.Errorf("mycnf object reflect is not struct") + } + for i := 0; i < t.NumField(); i++ { + var sectionName = t.Field(i).Tag.Get(SecTag) + if v.Field(i).Type().Kind() == reflect.Struct { + structField := v.Field(i).Type() + for j := 0; j < structField.NumField(); j++ { + keyName := structField.Field(j).Tag.Get(KeyTag) + val := v.Field(i).Field(j).String() + f.ReplaceValue(sectionName, string(keyName), false, val) + } + } + } + return nil +} + +// NewEmptyCnfObj TODO +// NewCnfFile 生成ini empty 用于外部传参的配置中渲染新的my.cnf +// +// @receiver mycnf +// @return *CnfFile +// @return error +func NewEmptyCnfObj(mycnf string) *CnfFile { + return &CnfFile{ + FileName: mycnf, + mu: &sync.Mutex{}, + Cfg: ini.Empty(iniLoadOption), + } +} + +// LoadMyCnfForFile 读取一个已经存在的配置文件,将配置文件的内容解析,用于程序读取、修改my.cnf +// +// @receiver mycnf +// @return *CnfFile +// @return error +func LoadMyCnfForFile(mycnf string) (*CnfFile, error) { + if err := cmutil.FileExistsErr(mycnf); err != nil { + return nil, err + } + cfg, err := ini.LoadSources(iniLoadOption, mycnf) + if err != nil { + return nil, err + } + return &CnfFile{ + FileName: mycnf, + mu: &sync.Mutex{}, + Cfg: cfg, + }, nil +} + +func newMyCnfUint() *CnfUint { + return &CnfUint{ + KvMap: make(map[string]string), + ShadowKvMap: make(map[string]string), + BoolKey: make([]string, 0), + } +} + +// Load load m.FileName to CnfOj +func (m *CnfFile) Load() error { + if obj, err := LoadMyCnfForFile(m.FileName); err != nil { + return err + } else { + m.Cfg = obj.Cfg + if m.mu == nil { + m.mu = &sync.Mutex{} + } + } + return nil +} + +// GetMySQLDataDir 从my.cnf 获取datadir +// +// @receiver m +// @return datadir +// @return err +// +// e.g: datadir=/data1/mysqldata/20000/data +func (m *CnfFile) GetMySQLDataDir() (datadir string, err error) { + if m.Cfg.Section(MysqldSec).HasKey("datadir") { + return filepath.Dir(m.Cfg.Section(MysqldSec).Key("datadir").String()), nil + } + return "", fmt.Errorf("在配置中没找到datadir的配置项") +} + +// GetMySQLLogDir 从配置中获取mysql logdir +// +// @receiver m +// @return logdir +// @return err +func (m *CnfFile) GetMySQLLogDir() (logdir string, err error) { + // 先从 log_bin 配置项获取logdir + // 但是可能存在历史的实例并没有开始binlog + // log_bin = ON + // log_bin_basename = /data/mysqllog/20000/binlog/binlog20000 + // log_bin_index = /data/mysqllog/20000/binlog/binlog20000.index + // 或者 log_bin = /data/mysqllog/20000/binlog/binlog20000.bin + // 或者 slow_query_log_file = /data/mysqllog/20000/slow-query.log + keys := []string{"log_bin", "log_bin_basename", "slow_query_log_file"} + + for _, k := range keys { + if val, err := m.GetMySQLCnfByKey(MysqldSec, k); err == nil { + if filepath.IsAbs(val) { + return val, nil + } + } + } + return "", fmt.Errorf("在配置中没找到 log_bin 的配置项") +} + +// GetBinLogDir 获取 binlog dir +// 这里只从 my.cnf 获取,有可能没有设置选项,外部需要考虑再次从 global variables 获取 +// 返回 binlog 目录和 binlog 文件名前缀 +func (m *CnfFile) GetBinLogDir() (binlogDir, namePrefix string, err error) { + // log_bin = ON + // log_bin_basename = /data/mysqllog/20000/binlog/binlog20000 // binlog 在没有指定路径的情况下,默认存放在 datadir + // log_bin_index = /data/mysqllog/20000/binlog/binlog20000.index + // 或者 log_bin = /data/mysqllog/20000/binlog/binlog20000.bin + keys := []string{"log_bin", "log_bin_basename"} + for _, k := range keys { + if val, err := m.GetMySQLCnfByKey(MysqldSec, k); err == nil { + if filepath.IsAbs(val) { + if binlogDir, namePrefix, err = m.ParseLogBinBasename(val); err == nil { + return binlogDir, namePrefix, err + } + } + } + } + return "", "", fmt.Errorf("binlog dir not found or parse failed") +} + +// ParseLogBinBasename TODO +func (m *CnfFile) ParseLogBinBasename(val string) (binlogDir, namePrefix string, err error) { + binlogDir, namePrefix = path.Split(val) + if cmutil.IsDirectory(binlogDir) && !cmutil.IsDirectory(val) { + if strings.Contains(namePrefix, ".") { + binlogFilename := strings.Split(namePrefix, ".") + namePrefix = binlogFilename[0] + } + return binlogDir, namePrefix, nil + } else { + logger.Error("expect %s is a dir and % is not dir", binlogDir, val) + } + errStr := fmt.Sprintf("%s is not a valid log_bin_basename", val) + logger.Warn(errStr) + return "", "", errors.New(errStr) +} + +// GetRelayLogDir TODO +func (m *CnfFile) GetRelayLogDir() (string, error) { + // relay-log = /data1/mysqldata/20000/relay-log/relay-log.bin + // 或者 relay_log_basename = /data1/mysqldata/20000/relay-log/relay-bin + keys := []string{"relay_log", "relay_log_basename"} + for _, k := range keys { + if val, err := m.GetMySQLCnfByKey(MysqldSec, k); err == nil { + if filepath.IsAbs(val) { // 必须是绝对路径 + return val, nil + } + } + } + return "", fmt.Errorf("在配置中没找到 relay 的配置项") +} + +// GetMySQLSocket 从my.cnf中获取socket value +// +// @receiver m +// @return socket +// @return err +func (m *CnfFile) GetMySQLSocket() (socket string, err error) { + if m.Cfg.Section(MysqldSec).HasKey("socket") { + return m.Cfg.Section(MysqldSec).Key("socket").String(), nil + } + return "", fmt.Errorf("在配置中没找到socket的配置项") +} + +// GetMySQLCnfByKey 从 my.cnf 获取 key 对应的 value +// 允许替换 _, - +// 如果 section 为空,会尝试从 key 中以 . 切分 section +func (m *CnfFile) GetMySQLCnfByKey(section, key string) (string, error) { + if section == "" { + sk := GetSectionFromKey(key, false) + key = sk.Key + section = sk.Section + } + key = m.GetKeyFromFile(section, key) + if m.Cfg.Section(section).HasKey(key) { + } else { + return "", fmt.Errorf("在配置中没找到 %s 的配置项", key) + } + return m.Cfg.Section(section).Key(key).String(), nil +} + +// GetMyCnfByKeyWithDefault TODO +func (m *CnfFile) GetMyCnfByKeyWithDefault(section, key string, valueDefault string) string { + if val, err := m.GetMySQLCnfByKey(section, key); err != nil { + return valueDefault + } else { + return val + } +} + +// GetProxyLogFilePath 获取 Proxy log-file 的value +// +// @receiver m +// @return logFile +// @return err +func (m *CnfFile) GetProxyLogFilePath() (logFile string, err error) { + if m.Cfg.Section("mysql-proxy").HasKey("log-file") { + return m.Cfg.Section("mysql-proxy").Key("log-file").String(), nil + } + return "", fmt.Errorf("在配置中没找到log-file的配置项") +} + +// SaveMySQLConfig2Object 将 my.cnf 变成 key map +func (m *CnfFile) SaveMySQLConfig2Object() MycnfIniObject { + var object MycnfIniObject + object.Section = make(map[string]*CnfUint) + for _, section := range m.Cfg.SectionStrings() { + object.Section[section] = newMyCnfUint() + for _, keyName := range m.Cfg.Section(section).KeyStrings() { + if kv, err := m.Cfg.Section(section).GetKey(keyName); err == nil { + object.Section[section].KvMap[keyName] = kv.Value() + } + } + } + return object +} + +// FastSaveChange 快速修改一个配置项,并持久化到文件 +func (m *CnfFile) FastSaveChange(port int, section, key, value string) (err error) { + m.mu.Lock() + defer m.mu.Unlock() + // 假如删除一个不存在的Key,不会抛出异常 + m.Cfg.Section(section).DeleteKey(key) + if _, err = m.Cfg.Section(section).NewKey(key, value); err != nil { + return + } + err = m.Cfg.SaveTo(fmt.Sprintf("my.cnf.%d", port)) + return +} + +// SafeSaveFile 全量持久化配置文件 +func (m *CnfFile) SafeSaveFile(isProxy bool) (err error) { + m.mu.Lock() + defer m.mu.Unlock() + nf, err := m.sortAllkeys(isProxy) + if err != nil { + return err + } + err = nf.Cfg.SaveTo(m.FileName) + return +} + +// sortAllkeys 对写入的key进行排序 +// +// @receiver m +// @return *CnfFile +// @return error +func (m *CnfFile) sortAllkeys(isProxy bool) (*CnfFile, error) { + f := CnfFile{ + Cfg: ini.Empty(iniLoadOption), + mu: &sync.Mutex{}, + } + for _, sec := range m.Cfg.Sections() { + secName := sec.Name() + if _, err := f.Cfg.NewSection(secName); err != nil { + return nil, err + } + keys := m.Cfg.Section(secName).KeyStrings() + sort.Strings(keys) + for _, key := range keys { + if m.isShadowKey(key) { + for _, val := range m.Cfg.Section(secName).Key(key).ValueWithShadows() { + f.RenderSection(secName, key, val, isProxy) + } + } + f.RenderSection(secName, key, m.Cfg.Section(secName).Key(key).Value(), isProxy) + } + } + return &f, nil +} + +// isShadowKey TODO +// 表示下面的key,可以在配置文件重复出现 +func (m *CnfFile) isShadowKey(key string) bool { + key = strings.ReplaceAll(key, "-", "_") + sk := []string{ + "replicate_do_db", "replicate_ignore_db", "replicate_do_table", "replicate_wild_do_table", + "replicate_ignore_table", "replicate_wild_ignore_table", + } + return cmutil.HasElem(key, sk) +} + +// GetInitDirItemTpl TODO +func (m *CnfFile) GetInitDirItemTpl(initDirs map[string]string) (err error) { + mysqld, err := m.Cfg.GetSection(MysqldSec) + if err != nil { + return + } + for key := range initDirs { + initDirs[key] = mysqld.Key(key).String() + } + return +} + +// RenderSection 替换渲染配置,proxy keepalive=true 不能和mysql的bool一样进行渲染 +// +// @receiver f +// @receiver sectionName +// @receiver key +// @receiver val +// @receiver isProxy +// @return err +func (m *CnfFile) RenderSection(sectionName, key, val string, isProxy bool) (err error) { + if m.isShadowKey(key) { + for _, shadowv := range strings.Split(val, ",") { + if _, err := m.Cfg.Section(sectionName).NewKey(key, shadowv); err != nil { + return err + } + fmt.Println(",", "M") + } + return nil + } + // proxy.cnf 需要渲染 boolkey + // my.cnf 如果是空值,当做boolkey处理 + if !isProxy { + if strings.TrimSpace(val) == "true" { + if _, err = m.Cfg.Section(sectionName).NewBooleanKey(key); err != nil { + return err + } + return nil + } + } + // 如果是不是空值,当做boolkey处理 + if _, err = m.Cfg.Section(sectionName).NewKey(key, val); err != nil { + return err + } + return nil +} + +// ReplaceKeyName 存在oldkey,则替换为newkey; 不存在oldkey,则作任何处理 +// 比如用在把 default_charset_server 替换成 default-charset-server 的场景 +func (m *CnfFile) ReplaceKeyName(section string, oldKey string, newKey string) { + m.mu.Lock() + defer m.mu.Unlock() + + sel := m.Cfg.Section(section) + if sel.HasKey(oldKey) { + k, _ := sel.GetKey(oldKey) + sel.NewKey(newKey, k.Value()) + sel.DeleteKey(oldKey) + } +} + +// ReplaceMoreKv TODO +func (m *CnfFile) ReplaceMoreKv(pairs map[string]CnfUint) error { + if len(pairs) <= 0 { + return nil + } + for section, mu := range pairs { + for k, v := range mu.KvMap { + m.ReplaceValue(section, k, false, v) + } + } + return nil +} + +// GetKeyFromFile godoc +// my.cnf 里面允许 _, - 两种分隔符的变量,获取或者替换时,需要两种都尝试获取 +func (m *CnfFile) GetKeyFromFile(section string, key string) string { + if !m.Cfg.Section(section).HasKey(key) { + oldKey := key + key = strings.ReplaceAll(key, "_", "-") + if !m.Cfg.Section(section).HasKey(key) { + key = strings.ReplaceAll(key, "-", "_") + if !m.Cfg.Section(section).HasKey(key) { + key = oldKey // 始终没找到 key, 恢复输入值 + } + } + } + return key +} + +// ReplaceValue kv不存在则写入,k存在则更新 +// 会判断是否是 shadowKey +// 如果 key 不存在会尝试替换 _ 成 - +func (m *CnfFile) ReplaceValue(section string, key string, isBool bool, value string) { + m.mu.Lock() + defer m.mu.Unlock() + key = m.GetKeyFromFile(section, key) + if !m.isShadowKey(key) { + m.Cfg.Section(section).DeleteKey(key) + } + if isBool { + m.Cfg.Section(section).NewBooleanKey(key) + return + } + m.Cfg.Section(section).NewKey(key, value) +} + +// UpdateKeyValue 修改一个配置项,会判断是否是 shadowKey +func (m *CnfFile) UpdateKeyValue(section, key, value string) (err error) { + m.mu.Lock() + defer m.mu.Unlock() + if m.isShadowKey(key) { + // 如果这些key 是可重复的key + err = m.Cfg.Section(section).Key(key).AddShadow(value) + return + } + m.Cfg.Section(section).Key(key).SetValue(value) + return +} + +// GetMyCnfFileName 获取默认 my.cnf 的路径,不检查是否存在 +func GetMyCnfFileName(port int) string { + return fmt.Sprintf("%s.%d", cst.DefaultMyCnfName, port) +} + +// GetProxyCnfName TODO +/** + * @description: 计算proxy cnf name + * @receiver {int} port + * @return {*} + */ +func GetProxyCnfName(port int) string { + return fmt.Sprintf("%s.%d", cst.DefaultProxyCnfName, port) +} + +// ReplaceValuesToFile 文本替换 my.cnf 里面的 value,如果 key 不存在则插入 [mysqld] 后面 +func (m *CnfFile) ReplaceValuesToFile(newItems map[string]string) error { + f, err := os.ReadFile(m.FileName) + if err != nil { + return err + } + lines := strings.Split(string(f), "\n") + itemsNotFound := make(map[string]string) + for i, lineText := range lines { + for k, v := range newItems { + itemsNotFound[k] = v + reg := regexp.MustCompile(fmt.Sprintf(`^\s*%s\s*=(.*)`, k)) + if reg.MatchString(lineText) { + lines[i] = fmt.Sprintf(`%s = %s`, k, v) + delete(itemsNotFound, k) // found + } + } + } + for k, v := range itemsNotFound { + StringsInsertAfter(lines, "[mysqld]", fmt.Sprintf(`%s = %s`, k, v)) + } + if err = os.WriteFile(m.FileName, []byte(strings.Join(lines, "\n")), 0644); err != nil { + return err + } + return nil +} + +// GetMysqldKeyVaule 增加基础方法,获取myconf上面的某个配置参数值,用于做前置校验的对比 +func (m *CnfFile) GetMysqldKeyVaule(keyName string) (value string, err error) { + mysqld, err := m.Cfg.GetSection(MysqldSec) + if err != nil { + return "", err + } + return mysqld.Key(keyName).String(), nil +} + +// CnfKey my.cnf key格式 +type CnfKey struct { + Section string + Key string + IsBool bool + Separator string +} + +// GetSectionFromKey 从 . 分隔符里分离 section, key +// replace 控制是否将 - 替换为 _ (set global 用) +func GetSectionFromKey(key string, replace bool) *CnfKey { + sk := &CnfKey{Separator: "."} + ss := strings.Split(key, sk.Separator) + if len(ss) == 2 { + sk.Section = ss[0] + sk.Key = ss[1] + } else { + sk.Section = "" + sk.Key = key + } + if replace { + sk.Key = strings.ReplaceAll(sk.Key, "-", "_") + } + return sk +} + +// MycnfItemsMap mysqld变量映射到 my.cnf 中的配置名 +var MycnfItemsMap = map[string]string{ + "time_zone": "default-time-zone", + "character_set_system": "character_set_server", +} + +// CreateExporterConf 简单写一个根据端口号生成exporter文件的方法 +func CreateExporterConf(fileName string, host string, port string, user string, password string) (err error) { + cnfPath := fmt.Sprintf("%s", fileName) + cfg := ini.Empty() + + exporterSection, err := cfg.NewSection("client") + if err != nil { + return err + } + exporterSection.NewKey("user", user) + exporterSection.NewKey("password", password) + exporterSection.NewKey("host", host) + exporterSection.NewKey("port", port) + err = cfg.SaveTo(cnfPath) + if err != nil { + return err + } + return nil +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/util/dbcnf_test.go b/dbm-services/riak/db-tools/dbactuator/pkg/util/dbcnf_test.go new file mode 100644 index 0000000000..1f61917f5c --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/util/dbcnf_test.go @@ -0,0 +1,25 @@ +package util_test + +import ( + "testing" + + "dbm-services/mysql/db-tools/dbactuator/pkg/util" +) + +func TestGetMySQLDatadir(t *testing.T) { + t.Log("start") + c, err := util.LoadMyCnfForFile("/etc/my.cnf.20000") + if err != nil { + t.Fatal(err) + } + datadir, err := c.GetMySQLDataDir() + if err != nil { + t.Fatal(err) + } + t.Log("datadir path:", datadir) + logdir, err := c.GetMySQLLogDir() + if err != nil { + t.Fatal(err) + } + t.Log("logdir path:", logdir) +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/util/filelock.go b/dbm-services/riak/db-tools/dbactuator/pkg/util/filelock.go new file mode 100644 index 0000000000..3a44ed004a --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/util/filelock.go @@ -0,0 +1,199 @@ +package util + +import ( + "fmt" + "io/ioutil" + "os" + "strconv" + "strings" + "sync" + "time" + + "github.com/gofrs/flock" + "github.com/pkg/errors" +) + +// FLock TODO +type FLock struct { + fileName string + file *os.File + filedataName string + filedata *os.File + filedataMax int +} + +// NewFlock TODO +func NewFlock(filename string, maxConn int) (*FLock, error) { + // maxConn cannot be 0 + + // check filedataName lastModifyTime + // 如果 filedataName 文件最后修改时间在 1h 以前,删掉这个文件 + + if strings.ContainsAny(filename, " ;'\"") { + // 非法文件名 + return nil, fmt.Errorf("illegal filename:%s", filename) + } else if maxConn == 0 { + return nil, fmt.Errorf("illegal maxConn:%d", maxConn) + } + + filedataName := filename + ".data" + if ok, mtimeInt := GetFileModifyTime(filedataName); ok { + curTime := time.Now() + mtime := time.Unix(mtimeInt, 0) + timeDiff := curTime.Sub(mtime) + if timeDiff.Minutes() > 60 { + os.Remove(filedataName) + } + } + + fl := &FLock{ + fileName: filename, + filedataName: filedataName, + filedataMax: maxConn, + } + + return fl, nil +} + +// FileFlock TODO +func (fl *FLock) FileFlock() (locked bool, err error) { + if fl.fileName == "" { + return false, errors.New("fileLock filename canot be empty") + } + fileLock := flock.New(fl.fileName) + return fileLock.TryLock() +} + +// FileUnlock TODO +func (fl *FLock) FileUnlock() error { + fileLock := flock.New(fl.fileName) + return fileLock.Unlock() +} + +// SetFileLockIncr TODO +func (fl *FLock) SetFileLockIncr(incr int) (succ int, err error) { + f, err := os.OpenFile(fl.filedataName, os.O_CREATE|os.O_RDWR, 0644) + if err == nil { + defer f.Close() + } + content, err := ioutil.ReadAll(f) + contentStr := strings.Trim(strings.ReplaceAll(string(content), " ", ""), "\n") + if err != nil { + return -1, fmt.Errorf(`io error:%v`, err.Error()) + } else if contentStr == "" { + contentStr = fmt.Sprintf(`%d:0`, fl.filedataMax) + } + concurrent := strings.Split(contentStr, ":") + if len(concurrent) != 2 { + return -1, fmt.Errorf(`error:contentStr=%s`, contentStr) + } + maxNum, err1 := strconv.Atoi(concurrent[0]) + CurNum, err2 := strconv.Atoi(concurrent[1]) + if err1 == nil && err2 == nil { + CurNum += incr + if CurNum > maxNum && incr > 0 { + // lock fail + return 0, nil + } + if CurNum < 0 { + CurNum = 0 + } + contentStr = fmt.Sprintf(`%d:%d`, maxNum, CurNum) + f.Seek(0, 0) + f.Truncate(0) + f.WriteString(contentStr) + return 1, nil + } else { + return -1, fmt.Errorf(`error:contentStr=%s`, contentStr) + } +} + +// FileIncrSafe TODO +// retryInterval: 如果获取锁失败,下次重试间隔(秒)。为 0 时表示不重试,麻烦返回获取锁失败 +// retcode: +// 1: success incr +// 0: full +// -1: operation failed +func (fl *FLock) FileIncrSafe(incr int, retryInterval int) (succ int, err error) { + intvl := time.Duration(retryInterval) + + fileLock := flock.New(fl.fileName) + locked, err := fileLock.TryLock() + + if err != nil { + // handle locking error + return -1, errors.New(fmt.Sprintf("failed to get lock: %s", err.Error())) + } + if locked { + // open and incr 1 and close + succ, err2 := fl.SetFileLockIncr(incr) + fileLock.Unlock() + if succ == 1 { + /* + if err = fileLock.Unlock(); err != nil { + // handle unlock error + return false, errors.New(fmt.Sprintf(`failed to unlock: %s`, err.Error())) + } + */ + return 1, nil + } else if succ == 0 { + if retryInterval == 0 { + return 0, nil + } else { + time.Sleep(intvl * time.Second) + return fl.FileIncrSafe(incr, retryInterval) + } + } else { + return -1, errors.New(fmt.Sprintf("failed to incr: %s", err2.Error())) + } + + } else { + // wait and retry + if retryInterval == 0 { + return 0, nil + } else { + // lockWaitMs := IntnRange(500, 3000) + // time.Sleep(time.Duration(lockWaitMs) * time.Millisecond) + time.Sleep(time.Duration(IntnRange(500, 3000)) * time.Millisecond) + return fl.FileIncrSafe(incr, retryInterval) + } + } + return 1, nil +} + +// FileUnlockIncr TODO +func (fl *FLock) FileUnlockIncr(filename string) error { + fileLock := flock.New(filename) + return fileLock.Unlock() +} + +// Test TODO +func Test() { + filename := "flashback.lock" + maxConn := 4 + fl, err := NewFlock(filename, maxConn) + if err != nil { + fmt.Println(err) + return + } + + wg := &sync.WaitGroup{} + for i := 0; i <= 8; i++ { + fmt.Println(i) + wg.Add(1) + go func(i int) { + time.Sleep(time.Duration(IntnRange(100, 2000)) * time.Millisecond) + defer wg.Done() + // 这个 retryInterval 尽量跟单个任务处理时间接近 + if succ, err := fl.FileIncrSafe(1, 20); succ == 1 { + // do + fmt.Printf("id=%d\n", i) + time.Sleep(20 * time.Second) + fl.FileIncrSafe(-1, 1) + } else if err != nil { + fmt.Printf("id=%d err=%v\n", i, err.Error()) + } + }(i) + } + wg.Wait() +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/util/helpers.go b/dbm-services/riak/db-tools/dbactuator/pkg/util/helpers.go new file mode 100644 index 0000000000..bd477be25c --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/util/helpers.go @@ -0,0 +1,53 @@ +package util + +import ( + "fmt" + "os" + "strings" + + "dbm-services/common/go-pubpkg/logger" +) + +const ( + // DefaultErrorExitCode TODO + DefaultErrorExitCode = 1 +) + +// CheckErr TODO +func CheckErr(err error) { + if err == nil { + return + } + msg, ok := StandardErrorMessage(err) + if !ok { + msg = err.Error() + if !strings.HasPrefix(msg, "error: ") { + msg = fmt.Sprintf("error: %s", msg) + } + } + LoggerErrorStack(logger.Error, err) + fatal(msg, DefaultErrorExitCode) +} + +func fatal(msg string, code int) { + if len(msg) > 0 { + // add newline if needed + if !strings.HasSuffix(msg, "\n") { + msg += "\n" + } + fmt.Fprint(os.Stderr, msg) + } + os.Exit(code) +} + +type debugError interface { + DebugError() (msg string, args []interface{}) +} + +// StandardErrorMessage TODO +func StandardErrorMessage(err error) (string, bool) { + if debugErr, ok := err.(debugError); ok { + logger.Info(debugErr.DebugError()) + } + return "", false +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/util/logger.go b/dbm-services/riak/db-tools/dbactuator/pkg/util/logger.go new file mode 100644 index 0000000000..b76aad8a27 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/util/logger.go @@ -0,0 +1,10 @@ +package util + +// LoggerErrorStack 在最外层遇到 error 时打印 stack 信息到日志 +// err == nil 时不打印 +// output 是个 logger,避免在 util 里引入 logger导致循环 import +func LoggerErrorStack(output func(format string, args ...interface{}), err error) { + if err != nil { + output("%+v", err) + } +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/util/osutil/cmdexec.go b/dbm-services/riak/db-tools/dbactuator/pkg/util/osutil/cmdexec.go new file mode 100644 index 0000000000..933ed64d20 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/util/osutil/cmdexec.go @@ -0,0 +1,169 @@ +package osutil + +import ( + "bytes" + "fmt" + "os" + "os/exec" + "strings" + + "dbm-services/common/go-pubpkg/logger" + + "github.com/pkg/errors" +) + +// FileOutputCmd 封装exec.Command,用于执行命令并输出到文件的场景,支持自动将输出文件上传到文件服务器(尽可能上传,如果上传失败则返回原文件) +type FileOutputCmd struct { + exec.Cmd + StdOutFile string + StdErrFile string + + stdOutFile *os.File + stdErrFile *os.File + stdOutDownloadLink string + stdErrDownloadLink string +} + +// GetStdOutDownloadLink TODO +func (c *FileOutputCmd) GetStdOutDownloadLink() string { + return c.stdOutDownloadLink +} + +// GetStdErrDownloadLink TODO +func (c *FileOutputCmd) GetStdErrDownloadLink() string { + return c.stdErrDownloadLink +} + +func (c *FileOutputCmd) initOutputFile() error { + if c.StdErrFile == "" { + c.StdErrFile = c.StdOutFile + } + if c.StdOutFile != "" { + stdOutFile, err := os.OpenFile(c.StdOutFile, os.O_CREATE|os.O_WRONLY, os.ModePerm) + if err != nil { + return errors.Wrapf(err, "open std out log file %s failed", c.StdOutFile) + } + c.stdOutFile = stdOutFile + c.Cmd.Stdout = stdOutFile + } + + if c.StdOutFile == c.StdErrFile { + c.stdErrFile = nil + c.Cmd.Stderr = c.stdOutFile + return nil + } + + if c.StdErrFile != "" { + stdErrFile, err := os.OpenFile(c.StdErrFile, os.O_CREATE|os.O_WRONLY, os.ModePerm) + if err != nil { + return errors.Wrapf(err, "open std err log file %s failed", c.StdErrFile) + } + c.stdErrFile = stdErrFile + c.Cmd.Stderr = stdErrFile + } + return nil +} + +func (c *FileOutputCmd) closeOutputFile() { + if c.stdOutFile != nil { + if err := c.stdOutFile.Close(); err != nil { + logger.Warn("close %s failed, err:%s", c.StdOutFile, err.Error()) + } + } + if c.stdErrFile != nil { + if err := c.stdErrFile.Close(); err != nil { + logger.Warn("close %s failed, err:%s", c.StdErrFile, err.Error()) + } + } + // UploadPath? + return +} + +// Run TODO +func (c *FileOutputCmd) Run() error { + if err := c.initOutputFile(); err != nil { + return err + } + + defer func() { + c.closeOutputFile() + }() + + return c.Cmd.Run() +} + +// Start TODO +func (c *FileOutputCmd) Start() error { + if err := c.initOutputFile(); err != nil { + return err + } + + return c.Cmd.Start() +} + +// Wait TODO +func (c *FileOutputCmd) Wait() error { + defer func() { + c.closeOutputFile() + }() + + return c.Cmd.Wait() +} + +// RunInBG TODO +func RunInBG(isSudo bool, param string) (pid int, err error) { + if isSudo { + param = "sudo " + param + } + cmd := exec.Command("bash", "-c", param) + err = cmd.Start() + if err != nil { + return -1, err + } + return cmd.Process.Pid, nil +} + +// ExecShellCommand 执行 shell 命令 +// 如果有 err, 返回 stderr; 如果没有 err 返回的是 stdout +// 后续尽量不要用这个方法,因为通过标准错误来判断有点不靠谱 +func ExecShellCommand(isSudo bool, param string) (stdoutStr string, err error) { + if isSudo { + param = "sudo " + param + } + cmd := exec.Command("bash", "-c", param) + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + err = cmd.Run() + if err != nil { + return stderr.String(), errors.WithMessage(err, stderr.String()) + } + + if len(stderr.String()) > 0 { + err = fmt.Errorf("execute shell command(%s) error:%s", param, stderr.String()) + return stderr.String(), err + } + + return stdout.String(), nil +} + +// CleanExecShellOutput TODO +func CleanExecShellOutput(s string) string { + return strings.ReplaceAll(strings.TrimSpace(s), "\n", "") +} + +// StandardShellCommand TODO +func StandardShellCommand(isSudo bool, param string) (stdoutStr string, err error) { + var stdout, stderr bytes.Buffer + if isSudo { + param = "sudo " + param + } + cmd := exec.Command("bash", "-c", param) + cmd.Stdout = &stdout + cmd.Stderr = &stderr + err = cmd.Run() + if err != nil { + return stderr.String(), errors.WithMessage(err, stderr.String()) + } + return stdout.String(), nil +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/util/osutil/cmdexec_test.go b/dbm-services/riak/db-tools/dbactuator/pkg/util/osutil/cmdexec_test.go new file mode 100644 index 0000000000..68ccf696ce --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/util/osutil/cmdexec_test.go @@ -0,0 +1,16 @@ +package osutil_test + +import ( + "testing" + + "dbm-services/mysql/db-tools/dbactuator/pkg/util/osutil" +) + +func TestExecShellCommand(t *testing.T) { + t.Log("start..") + out, err := osutil.StandardShellCommand(false, "usermod -d /home/mysql mysql") + if err != nil { + t.Fatal(err) + } + t.Log(out) +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/util/osutil/crontab.go b/dbm-services/riak/db-tools/dbactuator/pkg/util/osutil/crontab.go new file mode 100644 index 0000000000..d681061494 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/util/osutil/crontab.go @@ -0,0 +1,247 @@ +/* + * @Description: 主机 crontab 操作 + */ + +package osutil + +import ( + "fmt" + "math/rand" + "os" + "regexp" + "strings" + "time" + + "dbm-services/common/go-pubpkg/logger" + "dbm-services/mysql/db-tools/dbactuator/pkg/core/cst" +) + +// CrontabLockFile TODO +const CrontabLockFile = "/home/mysql/.crontab_lock" + +// RemoveUserCrontab TODO +func RemoveUserCrontab(user string) error { + cmd := fmt.Sprintf("crontab -u %s -r ", user) + output, err := ExecShellCommand(false, cmd) + if err != nil { + logger.Info("%s. %s", output, err.Error()) + return err + } + return nil +} + +// GetStatusCrontab TODO +func GetStatusCrontab(user string) string { + newCrontab := make([]string, 0) + newCrontab = append( + newCrontab, fmt.Sprintf( + "#get_status.pl: mysql monitor and report status to tnm, distribute at %s by %s", + time.Now().Format(cst.TIMELAYOUT), + user, + ), + ) + newCrontab = append(newCrontab, "*/5 * * * * /home/mysql/monitor/get_status.pl 1>/dev/null 2>&1 \n") + return strings.Join(newCrontab, "\n") +} + +// RemoveSystemCrontab TODO +/** + * @description: 删除 mysql 用户下的crontab任务 + * @receiver {string} removeKey + * @return {*} + */ +func RemoveSystemCrontab(removeKey string) (err error) { + var ( + crontabs = make([]string, 0) + output string + crontabList string + ) + + output, err = ListCrontb("mysql") + if err != nil { + return err + } + + formerCrontab := strings.Split(output, "\n") + logger.Info("formerCrontab:%#v \n len(formerCrontab):%d", formerCrontab, len(formerCrontab)) + for _, crontab := range formerCrontab { + if regexp.MustCompile(`^\\s*$`).MatchString(crontab) || strings.Contains(crontab, `#.*DO NOT EDIT THIS FILE`) || + strings.Contains(crontab, "#.*cron installed") || strings.Contains(crontab, "#.*Cron version") || + strings.Contains(crontab, "#.*installed on") || strings.Contains(crontab, removeKey) { + continue + } + crontabs = append(crontabs, crontab) + } + // return crontabs, nil + crontabStr := strings.Join(crontabs, "\n") + err = ExecCrontab(crontabStr) + if err != nil { + return err + } + result, err := IsCrontabKeyExist(removeKey) + if err != nil { + return err + } + if result { + err = fmt.Errorf("remove %s failed ,pls execute %s to check it mannually", removeKey, crontabList) + return err + } + return nil +} + +// ListCrontb TODO +/** + * @description: 查看user下的crontab + * @receiver {string} user + * @return {*} + */ +func ListCrontb(user string) (output string, err error) { + crontabList := fmt.Sprintf("crontab -u %s -l|egrep -v ^$ || true", user) + // "crontab -u " + user + " -l" + output, err = ExecShellCommand(false, crontabList) + if err != nil { + err = fmt.Errorf("execute [%s] get an error:%w,%s", crontabList, err, output) + if strings.Contains(output, "no crontab for") { + return "", nil + } else { + return "", err + } + } + return output, err +} + +// AddCrontab TODO +/** + * @description: 追加添加crontab + * @receiver {string} crontab 表达式 + * @return {*} + */ +func AddCrontab(crontab string) error { + output, err := ListCrontb("mysql") + if err != nil { + return err + } + + crontab = output + "\n" + crontab + err = ExecCrontab(crontab) + if err != nil { + return err + } + return nil +} + +// ExecCrontab TODO +/** + * @description: 添加crontab + * @receiver {string} crontab 表达式 + * @return {*} + */ +func ExecCrontab(crontab string) error { + cmd := fmt.Sprintf("echo -e '%s' | crontab - -u mysql", crontab) + output, err := ExecShellCommand(false, cmd) + if err != nil { + logger.Info("%s. %s", output, err.Error()) + return err + } + return nil +} + +// IsCrontabKeyExist TODO +/** + * @description: grep crontab + * @receiver {string} key + * @return {*} + */ +func IsCrontabKeyExist(key string) (bool, error) { + var ( + output string + err error + ) + output, err = ListCrontb("mysql") + if err != nil { + return false, err + } + if strings.Contains(output, key) { + return true, nil + } + return false, nil +} + +// CrontabsExist 检查存在哪些Crontab +// +// @receiver crontabKeys +// @return existCrontabs +// @return err +func CrontabsExist(crontabKeys []string) (existCrontabs []string, err error) { + output, err := ListCrontb("mysql") + if err != nil { + return nil, err + } + for _, key := range crontabKeys { + if strings.Contains(output, key) { + existCrontabs = append(existCrontabs, key) + } + } + return +} + +// CleanLocalCrontab 通过 导出 -> grep -v -> 导入 的方式实现清理crontab任务 +func CleanLocalCrontab() error { + var ( + randnum = rand.Intn(10000) + tmpCronFile = fmt.Sprintf("/tmp/cron_%s_%d.crd", time.Now().Format(cst.TIMELAYOUTSEQ), randnum) + getStatusPL = "/home/mysql/monitor/get_status.pl" + dbBackupOld = "/home/mysql/dbbackup/dbbackup.sh" + dbBackupNew = "/home/mysql/dbbackup/dbbackup.pl" + dbBackupMulti = "/home/mysql/dbbackup/dbbackup_main.sh" + dbBackupMultiGo = "/home/mysql/dbbackup-go/dbbackup_main.sh" + dbBackupXtrabackup = "/home/mysql/dbbackup/xtrabackup/xtrabackup_main.sh" + rotateLog = "/home/mysql/rotate_logbin/rotate_logbin.pl" + rotateLogGo = "/home/mysql/rotate_binlog/rotatebinlog" + proxyStatus = "/home/mysql/proxy_monitor/get_proxy_status.pl" + slaveSync = "/home/mysql/monitor/master_slave_sync_check.pl" + tbinlodumperStatus = "tbinlogdumper_status.pl" + prometheus = "prometheus" + ) + cleanCrontabs := []string{ + getStatusPL, dbBackupOld, dbBackupNew, dbBackupMulti, dbBackupXtrabackup, rotateLog, + proxyStatus, slaveSync, tbinlodumperStatus, prometheus, rotateLogGo, dbBackupMultiGo, + } + + existCrontabs, err := CrontabsExist(cleanCrontabs) + if err != nil { + return err + } + // 如果不存在需要清理的crontab 直接返回成功 + if len(existCrontabs) <= 0 { + return nil + } + logger.Info("还存在的Crontabs %v", existCrontabs) + // 导出mysql用户的crontab任务,并过滤掉要清理的任务 + shellCMD := fmt.Sprintf("/usr/bin/crontab -u mysql -l") + for _, v := range existCrontabs { + shellCMD += fmt.Sprintf("|grep -v %s", v) + } + shellCMD += fmt.Sprintf(">%s 2>&1", tmpCronFile) + output, err := ExecShellCommand(false, shellCMD) + if err != nil { + err = fmt.Errorf("execute [%s] get an error:%s,%s", shellCMD, output, err.Error()) + logger.Warn(err.Error()) + // grep 没有找到结果也认为是失败的,这个地方不能当做错误返回。 + } + // 重新导入crontab文件 + shellCMD = fmt.Sprintf("/usr/bin/crontab -u mysql %s 2>&1", tmpCronFile) + output, err = ExecShellCommand(false, shellCMD) + if err != nil { + err = fmt.Errorf("execute [%s] get an error:%s", shellCMD, output) + logger.Error(err.Error()) + return err + } + // crontab延时1分钟,所以直接删掉监控执行文件,保证监控执行不成功,就不会有告警了 + if err := os.RemoveAll(getStatusPL); err != nil { + err = fmt.Errorf("rm %s failed, err:%s", getStatusPL, err.Error()) + logger.Error(err.Error()) + return err + } + return nil +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/util/osutil/crontab_test.go b/dbm-services/riak/db-tools/dbactuator/pkg/util/osutil/crontab_test.go new file mode 100644 index 0000000000..5bd2eea7da --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/util/osutil/crontab_test.go @@ -0,0 +1,6 @@ +package osutil + +import "testing" + +func TestAddCrontab(t *testing.T) { +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/util/osutil/mountpoint.go b/dbm-services/riak/db-tools/dbactuator/pkg/util/osutil/mountpoint.go new file mode 100644 index 0000000000..607c00bbe3 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/util/osutil/mountpoint.go @@ -0,0 +1,73 @@ +package osutil + +import ( + "dbm-services/common/go-pubpkg/logger" + "strconv" + "strings" +) + +// IsDataDirOk TODO +func IsDataDirOk(filepath string) bool { + mountPaths := GetMountPathInfo() + if m, ok := mountPaths[filepath]; ok { + // 大于150GB + if m.AvailSizeMB > 153600 { + return true + } else { + logger.Error("%s available disk size is less than 150GB", filepath) + return false + } + } + logger.Error("GetMountPathInfo not found mount point: %s", filepath) + return false +} + +// MountPath TODO +type MountPath struct { + Filesystem string + FileSystemType string + TotalSizeMB int64 + UsedSizeMB int64 + AvailSizeMB int64 + UsePct int + Path string +} + +// ParseDfOutput TODO +func ParseDfOutput(rawOutput string) map[string]*MountPath { + mountPaths := make(map[string]*MountPath) + lines := strings.Split(rawOutput, "\n") + for i, line := range lines { + // skip headers + if i == 0 { + continue + } + + fields := strings.Fields(line) + if len(fields) == 0 || len(fields) != 7 { + continue + } + mountPath := &MountPath{ + Path: fields[5], + Filesystem: fields[0], + } + mountPath.FileSystemType = fields[1] + mountPath.TotalSizeMB, _ = strconv.ParseInt(fields[2], 10, 64) + mountPath.UsedSizeMB, _ = strconv.ParseInt(fields[3], 10, 64) + mountPath.AvailSizeMB, _ = strconv.ParseInt(fields[4], 10, 64) + mountPath.UsePct, _ = strconv.Atoi(strings.TrimSuffix(fields[5], "%")) + + mountPaths[fields[6]] = mountPath + } + return mountPaths +} + +// GetMountPathInfo TODO +func GetMountPathInfo() map[string]*MountPath { + cmdDfm, err := ExecShellCommand(false, "df -Thm") + mountPaths := ParseDfOutput(cmdDfm) + if err != nil { + return nil + } + return mountPaths +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/util/osutil/netutil.go b/dbm-services/riak/db-tools/dbactuator/pkg/util/osutil/netutil.go new file mode 100644 index 0000000000..7587cd442e --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/util/osutil/netutil.go @@ -0,0 +1,57 @@ +package osutil + +import ( + "net" + "strconv" + "time" + + "github.com/pkg/errors" +) + +// IsPortUp 判断端口是否开启监听 +func IsPortUp(host string, ports ...int) bool { + for _, port := range ports { + timeout := time.Second + hostPort := net.JoinHostPort(host, strconv.Itoa(port)) + conn, err := net.DialTimeout("tcp", hostPort, timeout) + if err != nil { + // fmt.Println("Connecting error:", err) + return false + } + if conn != nil { + defer conn.Close() + return true + // fmt.Println("Opened", net.JoinHostPort(host, port)) + } + } + return false +} + +// GetLocalIPAddrs TODO +func GetLocalIPAddrs() ([]string, error) { + ifaces, err := net.Interfaces() + if err != nil { + return nil, errors.Wrap(err, "get local ipaddrs") + } + ipAddrs := []string{} + for _, i := range ifaces { + addrs, err := i.Addrs() + if err != nil { + return nil, errors.Wrap(err, "get local ipaddr") + } + for _, addr := range addrs { + var ip net.IP + switch v := addr.(type) { + case *net.IPNet: + ip = v.IP + case *net.IPAddr: + ip = v.IP + } + ipAddrs = append(ipAddrs, ip.String()) + } + } + if len(ipAddrs) == 0 { + return nil, errors.New("failed to get any local ipaddr") + } + return ipAddrs, nil +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/util/osutil/osutil.go b/dbm-services/riak/db-tools/dbactuator/pkg/util/osutil/osutil.go new file mode 100644 index 0000000000..f65b7657d9 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/util/osutil/osutil.go @@ -0,0 +1,671 @@ +// Package osutil TODO +package osutil + +import ( + "bufio" + "bytes" + "fmt" + "io" + "math" + "math/rand" + "net" + "os" + "os/exec" + "os/user" + "path" + "path/filepath" + "reflect" + "regexp" + "strconv" + "strings" + "time" + "unicode" + + "dbm-services/common/go-pubpkg/logger" + + "github.com/dustin/go-humanize" + "github.com/pkg/errors" +) + +// Ucfirst TODO +func Ucfirst(str string) string { + for i, v := range str { + return string(unicode.ToUpper(v)) + str[i+1:] + } + return "" +} + +// HasElem TODO +func HasElem(elem interface{}, slice interface{}) bool { + defer func() { + if err := recover(); err != nil { + logger.Error("HasElem error %s", err) + } + }() + arrV := reflect.ValueOf(slice) + if arrV.Kind() == reflect.Slice || arrV.Kind() == reflect.Array { + for i := 0; i < arrV.Len(); i++ { + // XXX - panics if slice element points to an unexported struct field + // see https://golang.org/pkg/reflect/#Value.Interface + if reflect.DeepEqual(arrV.Index(i).Interface(), elem) { + return true + } + } + } + return false +} + +// 描述: +// 把任何类型的值转换成字符串类型 +// 目前暂时支持的类型为:string,int,int64,float64,bool + +// ChangeValueToString TODO +func ChangeValueToString(value interface{}) (string, error) { + var result string + if item, ok := value.(string); ok { + result = item + } else if item1, ok := value.(int); ok { + result = strconv.Itoa(item1) + } else if item2, ok := value.(int64); ok { + result = strconv.FormatInt(item2, 10) + } else if item3, ok := value.(float64); ok { + result = strconv.FormatFloat(item3, 'f', -1, 64) + } else if item4, ok := value.(bool); ok { + result = strconv.FormatBool(item4) + } else { + return result, errors.New("[ChangeValueToString]value type unknow,not in (string,int,int64,float64,bool)") + } + return result, nil +} + +// GetLocalIP 获得本地IP +func GetLocalIP() (string, error) { + var localIP string + var err error + addrs, err := net.InterfaceAddrs() + if err != nil { + return localIP, err + } + for _, addr := range addrs { + if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { + if ipnet.IP.To4() != nil { + localIP = ipnet.IP.String() + return localIP, nil + } + } + } + err = fmt.Errorf("can't find local ip") + return localIP, err +} + +// StringToMap 字符串 TO map +// 如db1,,db2,db3,db2 ,等去重并转换成db1,db2,db3 +func StringToMap(srcStr string, seq string) map[string]struct{} { + splitReg := regexp.MustCompile(seq) + strList := splitReg.Split(srcStr, -1) + strMap := make(map[string]struct{}) + for _, str := range strList { + if len(strings.TrimSpace(str)) == 0 { + continue + } + strMap[strings.TrimSpace(str)] = struct{}{} + } + return strMap +} + +// StrSliceToMap 字符串slice to map,目标是去重 +func StrSliceToMap(srcStrSlice []string) map[string]struct{} { + strMap := make(map[string]struct{}) + for _, str := range srcStrSlice { + if len(strings.TrimSpace(str)) == 0 { + continue + } + strMap[strings.TrimSpace(str)] = struct{}{} + } + return strMap +} + +// MapKeysToSlice TODO +func MapKeysToSlice(mapObj map[string]struct{}) []string { + keys := make([]string, len(mapObj)) + + i := 0 + for k := range mapObj { + keys[i] = k + i++ + } + return keys +} + +// IntnRange TODO +func IntnRange(min, max int) int { + rand.Seed(time.Now().Unix()) + return rand.Intn(max-min) + min +} + +// GetFileModifyTime TODO +func GetFileModifyTime(filename string) (bool, int64) { + if _, err := os.Stat(filename); !os.IsNotExist(err) { + f, err1 := os.Open(filename) + if err1 != nil { + return true, 0 + } + fi, err2 := f.Stat() + if err2 != nil { + return true, 0 + } + return true, fi.ModTime().Unix() + } + return false, 0 +} + +// GetMySQLBaseDir TODO +func GetMySQLBaseDir(grepstr string) (string, error) { + strCmd := fmt.Sprintf(`ps -ef | grep 'mysqld '|grep basedir | grep %s| grep -v grep`, grepstr) + data, err := ExecShellCommand(false, strCmd) + reg := regexp.MustCompile(`--basedir=[/A-Za-z_]*`) + tmparr := reg.FindAllString(data, -1) + if len(tmparr) != 1 { + return "", errors.New("get basedir unexpected") + } + basedir := strings.Split(strings.TrimSpace(tmparr[0]), "=") + if len(basedir) != 2 || strings.TrimSpace(basedir[1]) == "" { + return "", fmt.Errorf("get base dir error:%v", basedir) + } + return strings.TrimSpace(basedir[1]), err +} + +// GetMySQLBinDir TODO +func GetMySQLBinDir(getstr string) (string, error) { + basedir, err := GetMySQLBaseDir(getstr) + if err != nil { + return "", err + } + if !strings.HasPrefix(basedir, "/") { + return "", fmt.Errorf("basedir must start at /") + } + return strings.TrimRight(basedir, "/") + "/bin", nil +} + +// MakeSoftLink src and dest are absolute path with filename +func MakeSoftLink(src string, dest string, force bool) error { + if !FileExist(src) { + return errors.Errorf("src file does not exists: %s", src) + } + if src == dest { + return nil + } + if FileExist(dest) { + if !force { + return errors.Errorf("dest file exists: %s", dest) + } + if err := os.Remove(dest); err != nil { + logger.Warn("remove file %s failed, err:%s", dest, err.Error()) + } + } + // os.Symlink(src, dest) + cmd := exec.Command("ln", "-s", src, dest) + out, err := cmd.CombinedOutput() + if err != nil { + logger.Error("ln -s failed, output:%s, err:%s", string(out), err.Error()) + } + return err +} + +// MakeHardLink TODO +func MakeHardLink(src string, dest string) error { + if !FileExist(src) { + return errors.New("src file does not exists") + } else if FileExist(dest) { + return errors.New("dest file already exists") + } + if err := os.Link(src, dest); err != nil { + return err + } + return nil +} + +// CheckFileExistWithPath TODO +func CheckFileExistWithPath(filename, dirname string) bool { + var destFile string + if strings.HasPrefix(filename, "/") { + destFile = filename + } else { + destFile = fmt.Sprintf(`%s/%s`, dirname, filename) // app_149/ulog/xxxx.ulog + } + + if _, err := os.Stat(destFile); err != nil { + if os.IsNotExist(err) { + return false + } + return false + } + return true +} + +// CheckAndMkdir mkdir ppathname/pathname +func CheckAndMkdir(pathname, ppathname string) error { + if !CheckFileExistWithPath(pathname, ppathname) { + return os.MkdirAll(ppathname+"/"+pathname, 0755) + } + return nil +} + +// ParsePsOutput TODO +// for ps command, output should skip first line, which +// refer to cmd string itself(catch by ps after bash -c) +func ParsePsOutput(rawOutput string) string { + var output []string + lines := strings.Split(rawOutput, "\n") + for i, line := range lines { + // skip headers + if i == 0 { + continue + } + + fields := strings.Fields(line) + if len(fields) == 0 { + continue + } + output = append(output, fields[0]) + } + return strings.Join(output, "\n") +} + +// FileExist TODO +func FileExist(fileName string) bool { + _, err := os.Stat(fileName) + if err != nil { + if os.IsExist(err) { + return true + } + return false + } + return true +} + +// GetFileMd5 TODO +func GetFileMd5(file string) (string, error) { + cmd := "md5sum " + file + data, err := exec.Command("/bin/bash", "-c", cmd).CombinedOutput() + if err != nil { + return "", err + } + reg, err := regexp.Compile(`\s+`) + if err != nil { + return "", err + } + array := reg.Split(string(data), -1) + if len(array) != 3 { + return "", errors.New("data result len wrong ,not 3,is " + strconv.Itoa(len(array))) + } + return array[0], nil +} + +// GetLinuxDisksInfo TODO +func GetLinuxDisksInfo() ([]DiskInfo, error) { + var res []DiskInfo + cmd := "df -l|grep -vE 'Filesystem|overlay|tmpfs'" + data, err := exec.Command("/bin/bash", "-c", cmd).CombinedOutput() + if err != nil { + return res, err + } + reg, err := regexp.Compile(`\n+`) + if err != nil { + return res, err + } + strs := reg.Split(string(data), -1) + + for _, row := range strs { + if strings.TrimSpace(row) == "" { + continue + } + result := DiskInfo{} + reg, err := regexp.Compile(`\s+`) + if err != nil { + return res, err + } + array := reg.Split(row, -1) + if len(array) == 6 { + result.Filesystem = array[0] + result.Blocks_1K = array[1] + result.Used, err = strconv.Atoi(array[2]) + if err != nil { + return res, err + } + result.Available, err = strconv.ParseInt(array[3], 10, 64) + if err != nil { + return res, err + } + result.UsedRate = array[4] + result.MountedOn = array[5] + + res = append(res, result) + } else { + return res, errors.New("data result len wrong ,not 6,is " + strconv.Itoa(len(array))) + } + } + + return res, nil +} + +// GetCurrentUser TODO +func GetCurrentUser() (string, error) { + var currentUser = "" + cmd := `whoami` + data, err := exec.Command("/bin/bash", "-c", cmd).CombinedOutput() + if err != nil { + return currentUser, fmt.Errorf(err.Error() + ",cmd:" + cmd) + } + reg, err := regexp.Compile(`\n+`) + if err != nil { + return currentUser, err + } + array := reg.Split(string(data), -1) + if len(array) == 2 { + currentUser = array[0] + } else { + return currentUser, fmt.Errorf("get currentUser fail,len not 2,array:%s", strings.Join(array, ";")) + } + + return currentUser, nil +} + +// GetLinuxDirDiskInfo TODO +func GetLinuxDirDiskInfo(dir string) (DiskInfo, error) { + result := DiskInfo{} + cmd := fmt.Sprintf("df -l %s|grep -v Filesystem", dir) + data, err := exec.Command("/bin/bash", "-c", cmd).CombinedOutput() + if err != nil { + return result, err + } + reg, err := regexp.Compile(`\s+`) + if err != nil { + return result, err + } + array := reg.Split(string(data), -1) + if len(array) == 7 { + result.Filesystem = array[0] + result.Blocks_1K = array[1] + result.Used, err = strconv.Atoi(array[2]) + if err != nil { + return result, err + } + result.Available, err = strconv.ParseInt(array[3], 10, 64) + if err != nil { + return result, err + } + result.UsedRate = array[4] + result.MountedOn = array[5] + } else { + return result, errors.New("data result len wrong ,not 7,is " + strconv.Itoa(len(array))) + } + + return result, nil +} + +// DiskInfo TODO +type DiskInfo struct { + Filesystem string `json:"filesystem"` + Blocks_1K string `json:"blocks_1K"` + Used int `json:"used"` + Available int64 `json:"available"` + UsedRate string `json:"usedRate"` + MountedOn string `json:"MountedOn"` +} + +// SplitName 切分用户传过来的IP字符串列表等 +// 切分规则: +// 把\r+|\s+|;+|\n+|,+这些分隔符,转成字符串数组 +// 返回字符串数组 +func SplitName(input string) ([]string, error) { + if reg, err := regexp.Compile(`\r+|\s+|;+|\n+`); err != nil { + return nil, err + } else { + input = reg.ReplaceAllString(input, ",") + } + if reg, err := regexp.Compile(`^,+|,+$`); err != nil { + return nil, err + } else { + input = reg.ReplaceAllString(input, "") + } + if reg, err := regexp.Compile(`,+`); err != nil { + return nil, err + } else { + input = reg.ReplaceAllString(input, ",") + } + result := strings.Split(input, ",") + return result, nil +} + +// Uniq 对字符串数组进行去重 +func Uniq(input []string) []string { + var newData []string + if len(input) > 0 { + temp := map[string]bool{} + for _, value := range input { + temp[value] = true + } + for k := range temp { + newData = append(newData, k) + } + } + return newData +} + +// GetUidGid TODO +func GetUidGid(osuser string) (int, int, error) { + group, err := user.Lookup(osuser) + if err != nil { + logger.Info("Failed to lookup user %s", osuser) + return 0, 0, err + } + + uid, err := strconv.Atoi(group.Uid) + if err != nil { + logger.Info("Convert Uid for %s : `%s` failed", osuser, group.Uid) + return 0, 0, err + } + + gid, err := strconv.Atoi(group.Gid) + if err != nil { + logger.Info("Convert Gid for %s : `%s` failed", osuser, group.Gid) + return 0, 0, err + } + + return uid, gid, err +} + +// FileLineCounter 计算文件行数 +// 参考: https://stackoverflow.com/questions/24562942/golang-how-do-i-determine-the-number-of-lines-in-a-file-efficiently +func FileLineCounter(filename string) (lineCnt uint64, err error) { + _, err = os.Stat(filename) + if err != nil && os.IsNotExist(err) { + return 0, fmt.Errorf("file:%s not exists", filename) + } + file, err := os.Open(filename) + if err != nil { + return 0, fmt.Errorf("file:%s open fail,err:%w", filename, err) + } + defer func() { + if err := file.Close(); err != nil { + logger.Warn("close file %s failed, err:%s", filename, err.Error()) + } + }() + reader01 := bufio.NewReader(file) + buf := make([]byte, 32*1024) + lineCnt = 0 + lineSep := []byte{'\n'} + + for { + c, err := reader01.Read(buf) + lineCnt += uint64(bytes.Count(buf[:c], lineSep)) + + switch { + case err == io.EOF: + return lineCnt, nil + + case err != nil: + return lineCnt, fmt.Errorf("file:%s read fail,err:%w", filename, err) + } + } +} + +// WrapFileLink TODO +func WrapFileLink(link string) string { + name := filepath.Base(link) + return fmt.Sprintf(`%s`, link, name) +} + +// SetOSUserPassword run set user password by chpasswd +func SetOSUserPassword(user, password string) error { + exec.Command("/bin/bash", "-c", "") + cmd := exec.Command("chpasswd") + stdin, err := cmd.StdinPipe() + if err != nil { + return fmt.Errorf("new pipe failed, err:%w", err) + } + go func() { + _, err := io.WriteString(stdin, fmt.Sprintf("%s:%s", user, password)) + if err != nil { + logger.Warn("write into pipe failed, err:%s", err.Error()) + } + if err := stdin.Close(); err != nil { + logger.Warn("colse stdin failed, err:%s", err.Error()) + } + }() + if output, err := cmd.CombinedOutput(); err != nil { + return fmt.Errorf("run chpasswd failed, output:%s, err:%w", string(output), err) + } + return nil +} + +// GetNumaStr TODO +func GetNumaStr() string { + numaCmd := "numactl --show | grep policy" + output, err := ExecShellCommand(false, numaCmd) + if err != nil { + logger.Error(err.Error()) + return "" + } + if len(output) > 0 { + return "numactl --interleave=all " + } + return "" +} + +// SafeRmDir TODO +func SafeRmDir(dir string) (err error) { + if strings.TrimSpace(dir) == "/" { + return fmt.Errorf("禁止删除系统根目录") + } + return os.RemoveAll(dir) +} + +func getFileSize(f string) (int64, error) { + fd, err := os.Stat(f) + if err != nil { + return 0, err + } + return fd.Size(), nil +} + +// CalcFileSizeIncr TODO +func CalcFileSizeIncr(f string, secs uint64) string { + var err error + var t1Size, t2Size int64 + if t1Size, err = getFileSize(f); err != nil { + return "0" + } + time.Sleep(time.Duration(secs) * time.Second) + if t2Size, err = getFileSize(f); err != nil { + return "0" + } + + bytesIncr := uint64(math.Abs(float64(t2Size-t1Size))) / secs + return humanize.Bytes(bytesIncr) +} + +// PrintFileSizeIncr 后台计算文件变化 +// ch 通知退出,外层需要 close(ch) +// 2 hour 超时 +func PrintFileSizeIncr( + f string, secs uint64, printInterval uint64, + output func(format string, args ...interface{}), ch chan int, +) { + for true { + speed := CalcFileSizeIncr(f, secs) + if speed != "0" { + output("file %s change speed %s", f, speed) + } else { + break + } + select { + case _, beforeClosed := <-ch: + if !beforeClosed { + return + } + case <-time.After(2 * time.Hour): + return + default: + time.Sleep(time.Duration(printInterval) * time.Second) + } + /* + ch <- 1 // 这里为了不阻塞,我们只关注外面的 close 信号 + if _, beforeClosed := <-ch; !beforeClosed { + return + } + */ + } +} + +// CapturingPassThroughWriter is a writer that remembers +// data written to it and passes it to w +type CapturingPassThroughWriter struct { + buf bytes.Buffer + w io.Writer +} + +// NewCapturingPassThroughWriter creates new CapturingPassThroughWriter +func NewCapturingPassThroughWriter(w io.Writer) *CapturingPassThroughWriter { + return &CapturingPassThroughWriter{ + w: w, + } +} + +// Write 用于常见IO +func (w *CapturingPassThroughWriter) Write(d []byte) (int, error) { + w.buf.Write(d) + return w.w.Write(d) +} + +// Bytes returns bytes written to the writer +func (w *CapturingPassThroughWriter) Bytes() []byte { + return w.buf.Bytes() +} + +// ReadFileString TODO +func ReadFileString(filename string) (string, error) { + if body, err := os.ReadFile(filename); err != nil { + return "", err + } else { + return string(body), nil + } +} + +// CreateSoftLink TODO +// sourceFile : 绝对路径 +// linkName: 觉得路径 +func CreateSoftLink(sourceFile string, linkFile string) (err error) { + if !(path.IsAbs(sourceFile) && path.IsAbs(linkFile)) { + return fmt.Errorf("源文件和目标链接文件传参必须是绝对路径") + } + // try del origin link file + if FileExist(linkFile) { + if err := os.Remove(linkFile); err != nil { + logger.Error("del %s failed", linkFile) + return err + } + } + return os.Symlink(sourceFile, linkFile) +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/util/osutil/osutil_test.go b/dbm-services/riak/db-tools/dbactuator/pkg/util/osutil/osutil_test.go new file mode 100644 index 0000000000..bc0f16b641 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/util/osutil/osutil_test.go @@ -0,0 +1,27 @@ +package osutil_test + +import ( + "testing" + + "dbm-services/mysql/db-tools/dbactuator/pkg/util/osutil" +) + +func TestIsFileExist(t *testing.T) { + f := "/tmp/1.txt" + d := "/tmp/asdad/" + exist_f := osutil.FileExist(f) + exist_d := osutil.FileExist(d) + t.Log("f exist", exist_f) + t.Log("d exist", exist_d) + return +} + +func TestCreateLink(t *testing.T) { + t.Log("start..") + err := osutil.CreateSoftLink("/data/mysql/3306/mysql.sock", "/tmp/mysql.sock") + if err != nil { + t.Log(err.Error()) + return + } + t.Log("end..") +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/util/osutil/sysctl.go b/dbm-services/riak/db-tools/dbactuator/pkg/util/osutil/sysctl.go new file mode 100644 index 0000000000..25526475c4 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/util/osutil/sysctl.go @@ -0,0 +1,60 @@ +// Package osutil TODO +/* + * @Author: your name + * @Date: 2022-04-21 15:07:16 + * @LastEditTime: 2022-04-21 15:07:16 + * @LastEditors: your name + * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE + * @FilePath: /bk-dbactuator/pkg/util/osutil/sysctl.go + */ +package osutil + +import ( + "fmt" + + "dbm-services/common/go-pubpkg/logger" +) + +// ClearTcpRecycle TODO +// +// logger.Warn +func ClearTcpRecycle() error { + twRecycleCmd := "grep 'net.ipv4.tcp_tw_recycle=1' /etc/sysctl.conf" + result, err := ExecShellCommand(false, twRecycleCmd) + if err != nil { + err = fmt.Errorf("execute [%s] get an error:%w", twRecycleCmd, err) + logger.Warn(err.Error()) + } + if len(result) > 0 { + insertTwRecycle := "sed -i -e 's/net.ipv4.tcp_tw_recycle=1/net.ipv4.tcp_tw_recycle=0/g' /etc/sysctl.conf" + _, err := ExecShellCommand(false, insertTwRecycle) + if err != nil { + err = fmt.Errorf("execute [%s] get an error:%w", insertTwRecycle, err) + logger.Info(err.Error()) + return err + } + } + twReuseCmd := "grep 'net.ipv4.tcp_tw_reuse=1' /etc/sysctl.conf" + result, err = ExecShellCommand(false, twReuseCmd) + if err != nil { + err = fmt.Errorf("execute [%s] get an error:%w", twReuseCmd, err) + logger.Warn(err.Error()) + } + if len(result) > 0 { + insertTwReuse := "sed -i -e 's/net.ipv4.tcp_tw_reuse=1/net.ipv4.tcp_tw_reuse=0/g' /etc/sysctl.conf" + _, err := ExecShellCommand(false, insertTwReuse) + if err != nil { + err = fmt.Errorf("execute [%s] get an error:%w", insertTwReuse, err) + logger.Info(err.Error()) + return err + } + } + + // Linux kernel 那边只有 t-linux2-0044 开始才支持上面的 2 个参数,下面执行报错的话,给出一个warning。 + _, err = ExecShellCommand(false, "/sbin/sysctl -p") + if err != nil { + err = fmt.Errorf("execute [/sbin/sysctl -p] get an error:%w", err) + logger.Warn(err.Error()) + } + return nil +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/util/osutil/unix_only.go b/dbm-services/riak/db-tools/dbactuator/pkg/util/osutil/unix_only.go new file mode 100644 index 0000000000..3c1a7f62b0 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/util/osutil/unix_only.go @@ -0,0 +1,204 @@ +//go:build !windows +// +build !windows + +package osutil + +import ( + "bytes" + "fmt" + "math/rand" + "os" + "os/exec" + "strings" + "syscall" + "time" + + "dbm-services/common/go-pubpkg/logger" +) + +// IsMountPoint TODO +// Determine if a directory is a mountpoint, by comparing the device for the directory +// with the device for it's parent. If they are the same, it's not a mountpoint, if they're +// different, it is. +// reference: https://github.com/cnaize/kubernetes/blob/master/pkg/util/mount/mountpoint_unix.go#L29 +func IsMountPoint(file string) (bool, error) { + stat, err := os.Stat(file) + if err != nil { + return false, err + } + rootStat, err := os.Lstat(file + "/..") + if err != nil { + return false, err + } + // If the directory has the same device as parent, then it's not a mountpoint. + return stat.Sys().(*syscall.Stat_t).Dev != rootStat.Sys().(*syscall.Stat_t).Dev, nil +} + +// FindFirstMountPoint find first mountpoint in prefer order +func FindFirstMountPoint(paths ...string) (string, error) { + for _, path := range paths { + if _, err := os.Stat(path); err != nil { + if os.IsNotExist(err) { + continue + } + } + isMountPoint, err := IsMountPoint(path) + if err != nil { + return "", fmt.Errorf("check whether mountpoint failed, path: %s, err: %w", path, err) + } + if isMountPoint { + return path, nil + } + } + return "", fmt.Errorf("no available mountpoint found, choices: %#v", paths) +} + +// RunShellCmdAsUser a simple wrapper of Cmd +// NOTE(wangqingping) len(strings.Join(args, " ")) cannot +// exceed MAX_ARG_STRLEN, checkout: +// https://www.linuxjournal.com/article/6060 +func RunShellCmdAsUser(args []string, osuser string) (string, error) { + cmd := exec.Command("bash", "-c", strings.Join(args, " ")) + var outbuff, errbuff bytes.Buffer + cmd.Stdout = &outbuff + cmd.Stderr = &errbuff + uid, gid, err := GetUidGid(osuser) + if err != nil { + return "", err + } + cmd.SysProcAttr = &syscall.SysProcAttr{} + cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)} + if err := cmd.Run(); err != nil { + logger.Info( + "Run command failed, cmd `%s` error %s, %s", + strings.Join(args, " "), errbuff.String(), err, + ) + return "", err + } else { + logger.Info("Run command `%s` successfully", strings.Join(args, " ")) + } + return outbuff.String(), nil +} + +// RunShellCmdNoWaitAsUser TODO +// starts the specified command but does not wait for it to complete. +func RunShellCmdNoWaitAsUser(args []string, osuser string) (string, error) { + cmd := exec.Command("bash", "-c", strings.Join(args, " ")) + var outbuff, errbuff bytes.Buffer + cmd.Stdout = &outbuff + cmd.Stderr = &errbuff + uid, gid, err := GetUidGid(osuser) + if err != nil { + return "", err + } + cmd.SysProcAttr = &syscall.SysProcAttr{} + cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)} + if err := cmd.Start(); err != nil { + logger.Info( + "Run command failed, cmd `%s` error %s, %s", + strings.Join(args, " "), errbuff.String(), err, + ) + return "", err + } else { + logger.Info("Run command `%s` successfully", strings.Join(args, " ")) + } + + return outbuff.String(), nil +} + +// Lock TODO +func (l *DirLock) Lock() error { + f, err := os.Open(l.dir) + if err != nil { + return err + } + l.f = f + err = syscall.Flock(int(l.f.Fd()), syscall.LOCK_EX|syscall.LOCK_NB) + if err != nil { + return fmt.Errorf("cannot flock directory %s - %w", l.dir, err) + } + return nil +} + +// Unlock TODO +func (l *DirLock) Unlock() error { + defer func() { + if err := l.f.Close(); err != nil { + logger.Warn("close lock file failed, err:%s", err.Error()) + } + }() + return syscall.Flock(int(l.f.Fd()), syscall.LOCK_UN) +} + +// GetDirLock TODO +/* + GetDirLock 获取 crontab lock. + set waitTime = 0 if you don't want to wait crontab lock +*/ +func GetDirLock(waitTime time.Duration, l *DirLock) error { + var ( + flockErr = make(chan error, 1) + timeoutChan = make(chan struct{}) + err error + ) + + if waitTime == 0 { + return l.Lock() + } + + go func() { + var deadline = time.Now().Add(waitTime) + for { + err := l.Lock() + if err == nil { + flockErr <- err + return + } + logger.Error("get file lock error:%s,continue to wait", err) + if time.Until(deadline) < 0 { + timeoutChan <- struct{}{} + return + } + time.Sleep(time.Duration(7+rand.Intn(7)) * time.Second) + } + }() + + select { + case err := <-flockErr: + return err + case <-timeoutChan: + err = fmt.Errorf("lock file(%s) timeout", l.GetDirName()) + return err + } +} + +// ReleaseDirLock TODO +func ReleaseDirLock(l *DirLock) error { + return l.Unlock() +} + +// DirLock TODO +// from https://github.com/nsqio/nsq/blob/master/internal/dirlock/dirlock.go +type DirLock struct { + dir string + f *os.File +} + +// NewDirLock TODO +func NewDirLock(dir string) *DirLock { + isExist := FileExist(dir) + if !isExist { + _, err := os.OpenFile(dir, os.O_RDWR|os.O_CREATE, 0755) + if err != nil { + logger.Warn("openFile(%s) error:%s", dir, err) + } + } + return &DirLock{ + dir: dir, + } +} + +// GetDirName TODO +func (l *DirLock) GetDirName() string { + return l.dir +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/util/osutil/windows_only.go b/dbm-services/riak/db-tools/dbactuator/pkg/util/osutil/windows_only.go new file mode 100644 index 0000000000..3a8de4e525 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/util/osutil/windows_only.go @@ -0,0 +1,16 @@ +//go:build windows +// +build windows + +package osutil + +// 这里只是为了能在 windows 编译成功,不一定可以使用 + +// FindFirstMountPoint find first mountpoint in prefer order +func FindFirstMountPoint(paths ...string) (string, error) { + return "/data", nil +} + +// FindFirstMountPointProxy TODO +func FindFirstMountPointProxy(paths ...string) (string, error) { + return "/data", nil +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/util/slice.go b/dbm-services/riak/db-tools/dbactuator/pkg/util/slice.go new file mode 100644 index 0000000000..32e9926704 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/util/slice.go @@ -0,0 +1,273 @@ +package util + +import ( + "fmt" + "reflect" + "sort" + "strconv" + "strings" + + "github.com/pkg/errors" +) + +// IntsHas check the []int contains the given value +func IntsHas(ints []int, val int) bool { + for _, ele := range ints { + if ele == val { + return true + } + } + return false +} + +// Int64sHas check the []int64 contains the given value +func Int64sHas(ints []int64, val int64) bool { + for _, ele := range ints { + if ele == val { + return true + } + } + return false +} + +// StringsHas check the []string contains the given element +func StringsHas(ss []string, val string) bool { + for _, ele := range ss { + if ele == val { + return true + } + } + return false +} + +// StringsHasICase check the []string contains the given element. insensitive case +func StringsHasICase(ss []string, val string) bool { + val = strings.ToLower(val) + for _, ele := range ss { + if strings.ToLower(ele) == val { + return true + } + } + return false +} + +// UniqueStrings Returns unique items in a slice +func UniqueStrings(slice []string) []string { + // create a map with all the values as key + uniqMap := make(map[string]struct{}) + for _, v := range slice { + uniqMap[v] = struct{}{} + } + + // turn the map keys into a slice + uniqSlice := make([]string, 0, len(uniqMap)) + for v := range uniqMap { + uniqSlice = append(uniqSlice, v) + } + return uniqSlice +} + +// UniqueInts Returns unique items in a slice +func UniqueInts(slice []int) []int { + // create a map with all the values as key + uniqMap := make(map[int]struct{}) + for _, v := range slice { + uniqMap[v] = struct{}{} + } + + // turn the map keys into a slice + uniqSlice := make([]int, 0, len(uniqMap)) + for v := range uniqMap { + uniqSlice = append(uniqSlice, v) + } + return uniqSlice +} + +// IsConsecutiveStrings 是否是连续数字 +// 如果存在 空元素 则报错 +// 如果 isNumber=false, 则当做字符比较是否连续 +func IsConsecutiveStrings(strList []string, isNumber bool) error { + err := errors.New("not consecutive numbers") + intList := make([]int, len(strList)) + if !isNumber { + // string to ascii + // .aa .ab .ac => 469797 469798 469799 + for i, s := range strList { + ss := "" + for _, si := range []rune(s) { + ss += strconv.FormatInt(int64(si), 10) + } + // todo ss 不能超过20位 + strList[i] = ss + } + } + for i, s := range strList { + if d, e := strconv.Atoi(s); e != nil { + return errors.Errorf("illegal number %s", s) + } else { + intList[i] = d + } + } + intList = UniqueInts(intList) + sort.Ints(intList) + count := len(intList) + if (intList[count-1] - intList[0] + 1) != count { + return err + } + return nil +} + +// RemoveEmpty 过滤掉空字符串 +func RemoveEmpty(input []string) []string { + var result []string + for _, item := range input { + if strings.TrimSpace(item) != "" { + result = append(result, item) + } + } + return result +} + +// StringSliceToInterfaceSlice 把字符串数组转换为interface{}数组 +func StringSliceToInterfaceSlice(ids []string) []interface{} { + var result []interface{} + if len(ids) == 1 { + result = append(result, ids[0]) + } else { + for i := 0; i < len(ids); i++ { + result = append(result, ids[i]) + } + } + return result +} + +// StringsRemove an value form an string slice +func StringsRemove(ss []string, s string) []string { + var ns []string + for _, v := range ss { + if v != s { + ns = append(ns, v) + } + } + + return ns +} + +// StringsInsertAfter 在 slice 里插入某个元素之后,仅匹配一次 +// 如果没有找到元素,忽略 +func StringsInsertAfter(ss []string, old string, new string) []string { + var ssNew = make([]string, len(ss)+1) + var found bool + for i, v := range ss { + if found { + ssNew[i+1] = v + } else if v == old { + ssNew[i] = v + ssNew[i+1] = new + found = true + } else { + ssNew[i] = v + } + } + if !found { + return ssNew[:len(ss)] + } + return ssNew +} + +// StringsInsertIndex 在 slice index 当前位置,插入一个元素 +// 如果 index 非法,则忽略 +func StringsInsertIndex(ss []string, index int, new string) []string { + if index < 0 || index > len(ss)-1 { + return ss + } + var ssNew = make([]string, len(ss)+1) + for i, v := range ss { + if i > index { + ssNew[i+1] = v + } else if i < index { + ssNew[i] = v + } else { + ssNew[i] = new + ssNew[i+1] = v + } + } + return ssNew +} + +// FilterOutStringSlice 滤除scr中含有filters 里面元素的数组 +// +// @receiver src +// @receiver filters +// @return dst +func FilterOutStringSlice(src []string, filters []string) (dst []string) { + for _, v := range src { + if !StringsHas(filters, v) { + dst = append(dst, v) + } + } + return +} + +// RemoveNilElements TODO +func RemoveNilElements(v []interface{}) []interface{} { + newSlice := make([]interface{}, 0, len(v)) + for _, i := range v { + if i != nil { + newSlice = append(newSlice, i) + } + } + return newSlice +} + +// StrVal TODO +func StrVal(v interface{}) string { + switch v := v.(type) { + case string: + return v + case []byte: + return string(v) + case error: + return v.Error() + case fmt.Stringer: + return v.String() + default: + return fmt.Sprintf("%v", v) + } +} + +// StrSlice TODO +func StrSlice(v interface{}) []string { + switch v := v.(type) { + case []string: + return v + case []interface{}: + b := make([]string, 0, len(v)) + for _, s := range v { + if s != nil { + b = append(b, StrVal(s)) + } + } + return b + default: + val := reflect.ValueOf(v) + switch val.Kind() { + case reflect.Array, reflect.Slice: + l := val.Len() + b := make([]string, 0, l) + for i := 0; i < l; i++ { + value := val.Index(i).Interface() + if value != nil { + b = append(b, StrVal(value)) + } + } + return b + default: + if v == nil { + return []string{} + } + + return []string{StrVal(v)} + } + } +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/util/str.go b/dbm-services/riak/db-tools/dbactuator/pkg/util/str.go new file mode 100644 index 0000000000..f306057802 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/util/str.go @@ -0,0 +1,45 @@ +package util + +import ( + "regexp" + "strings" +) + +// SplitAny TODO +// util.SplitAny("ab##cd$$ef", "(##|\$\$)") +func SplitAny(s string, delimiters string) []string { + // seps := fmt.Sprintf() + // splitRegex := regexp.MustCompile(`[;,\n\t ]+`) + // delimiters=[;,\t\s ]+ + splitRegex := regexp.MustCompile(delimiters) + splitResults := splitRegex.Split(s, -1) + results := make([]string, 0) + for _, s := range splitResults { + if strings.TrimSpace(s) != "" { + results = append(results, strings.TrimSpace(s)) + } + } + return results +} + +// SplitAnyRune TODO +// util.SplitAnyRune("a,b c", ", ") +// if s is empty, return [], not [""] +func SplitAnyRune(s string, seps string) []string { + splitter := func(r rune) bool { + return strings.ContainsRune(seps, r) + } + return strings.FieldsFunc(s, splitter) +} + +// SplitAnyRuneTrim 分隔字符串,并去除空字符 +func SplitAnyRuneTrim(s string, seps string) []string { + ss := SplitAnyRune(s, seps) + for i, el := range ss { + if sss := strings.TrimSpace(el); sss != "" { + ss[i] = sss + } + // 忽略空字符 + } + return ss +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/util/templates/cmd_groups.go b/dbm-services/riak/db-tools/dbactuator/pkg/util/templates/cmd_groups.go new file mode 100644 index 0000000000..e153e2ee69 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/util/templates/cmd_groups.go @@ -0,0 +1,21 @@ +package templates + +import ( + "github.com/spf13/cobra" +) + +// CommandGroup TODO +type CommandGroup struct { + Message string + Commands []*cobra.Command +} + +// CommandGroups TODO +type CommandGroups []CommandGroup + +// Add TODO +func (g CommandGroups) Add(c *cobra.Command) { + for _, group := range g { + c.AddCommand(group.Commands...) + } +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/util/templates/normallizers.go b/dbm-services/riak/db-tools/dbactuator/pkg/util/templates/normallizers.go new file mode 100644 index 0000000000..b1ec2db878 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/util/templates/normallizers.go @@ -0,0 +1,32 @@ +package templates + +import ( + "strings" + + "github.com/MakeNowJust/heredoc" +) + +// Indentation TODO +const Indentation = ` ` + +// LongDesc TODO +func LongDesc(s string) string { + if len(s) == 0 { + return s + } + return normalizer{s}.heredoc().trim().string +} + +type normalizer struct { + string +} + +func (s normalizer) heredoc() normalizer { + s.string = heredoc.Doc(s.string) + return s +} + +func (s normalizer) trim() normalizer { + s.string = strings.TrimSpace(s.string) + return s +} diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/util/templates/templates.go b/dbm-services/riak/db-tools/dbactuator/pkg/util/templates/templates.go new file mode 100644 index 0000000000..08870591a3 --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/util/templates/templates.go @@ -0,0 +1,2 @@ +// Package templates TODO +package templates diff --git a/dbm-services/riak/db-tools/dbactuator/pkg/util/util.go b/dbm-services/riak/db-tools/dbactuator/pkg/util/util.go new file mode 100644 index 0000000000..f78e1abfbe --- /dev/null +++ b/dbm-services/riak/db-tools/dbactuator/pkg/util/util.go @@ -0,0 +1,336 @@ +// Package util TODO +package util + +import ( + "crypto/md5" + "encoding/json" + "fmt" + "io" + "math/rand" + "net" + "net/url" + "os" + "path" + "reflect" + "regexp" + "runtime" + "strings" + "time" + + "dbm-services/common/go-pubpkg/logger" + + "github.com/TylerBrock/colorjson" + "github.com/pkg/errors" +) + +// RetryConfig TODO +type RetryConfig struct { + Times int // 重试次数 + DelayTime time.Duration // 每次重试间隔 +} + +// Retry 重试 +// 第 0 次也需要 delay 再运行 +func Retry(r RetryConfig, f func() error) (err error) { + for i := 0; i < r.Times; i++ { + time.Sleep(r.DelayTime) + if err = f(); err == nil { + return nil + } + logger.Warn("第%d次重试,函数错误:%s", i, err.Error(), err.Error()) + } + return +} + +// AtWhere TODO +func AtWhere() string { + pc, _, _, ok := runtime.Caller(1) + if ok { + fileName, line := runtime.FuncForPC(pc).FileLine(pc) + result := strings.Index(fileName, "/bk-dbactuator/") + if result > 1 { + preStr := fileName[0:result] + fileName = strings.Replace(fileName, preStr, "", 1) + } + return fmt.Sprintf("%s:%d", fileName, line) + } else { + return "Method not Found!" + } +} + +const ( + tcpDialTimeout = 3 * time.Second +) + +// HostCheck TODO +func HostCheck(host string) bool { + _, err := net.DialTimeout("tcp", host, time.Duration(tcpDialTimeout)) + if err != nil { + logger.Info(err.Error()) + return false + } + return true +} + +// GetFileMd5 TODO +func GetFileMd5(fileAbPath string) (md5sum string, err error) { + rFile, err := os.Open(fileAbPath) + if err != nil { + return "", err + } + defer rFile.Close() + h := md5.New() + if _, err := io.Copy(h, rFile); err != nil { + return "", err + } + return fmt.Sprintf("%x", h.Sum(nil)), nil +} + +// Struct2Map TODO +func Struct2Map(s interface{}, tag string) (map[string]interface{}, error) { + out := make(map[string]interface{}) + v := reflect.ValueOf(s) + if v.Kind() == reflect.Ptr { + v = v.Elem() + } + if v.Kind() != reflect.Struct { + return nil, fmt.Errorf("only accept struct or pointer, got %T", v) + } + t := v.Type() + for i := 0; i < v.NumField(); i++ { + f := t.Field(i) + if tagValue := f.Tag.Get(tag); tagValue != "" { + out[tagValue] = v.Field(i).Interface() + } + } + return out, nil +} + +// SetField TODO +func SetField(obj interface{}, name string, value interface{}) error { + structValue := reflect.ValueOf(obj).Elem() + structFieldValue := structValue.FieldByName(name) + + if !structFieldValue.IsValid() { + return fmt.Errorf("no such field: %s in obj", name) + } + + if !structFieldValue.CanSet() { + return fmt.Errorf("cannot set %s field value", name) + } + + structFieldType := structFieldValue.Type() + val := reflect.ValueOf(value) + if structFieldType != val.Type() { + return errors.New("provided value type didn't match obj field type") + } + + structFieldValue.Set(val) + return nil +} + +// Convert2Map TODO +func Convert2Map(m interface{}) map[string]string { + ret := make(map[string]string) + v := reflect.ValueOf(m) + if v.Kind() == reflect.Ptr { + v = v.Elem() + } + var fd string + for i := 0; i < v.NumField(); i++ { + f := v.Field(i) + switch f.Kind() { + case reflect.Struct: + fallthrough + case reflect.Ptr: + Convert2Map(f.Interface()) + default: + fd = f.String() + } + ret[v.Type().Field(i).Tag.Get("json")] = fd + } + return ret +} + +// StrIsEmpty TODO +func StrIsEmpty(str string) bool { + return strings.TrimSpace(str) == "" +} + +// OutputPrettyJson 直接传一个空结构体过来 +func OutputPrettyJson(p interface{}) { + var inInterface map[string]interface{} + inrec, _ := json.Marshal(p) + json.Unmarshal(inrec, &inInterface) + // Make a custom formatter with indent set + f := colorjson.NewFormatter() + f.Indent = 4 + pp, err := f.Marshal(inInterface) + if err != nil { + fmt.Println(err) + return + } + fmt.Println("Payload Example: ") + fmt.Println("") + fmt.Println(string(pp)) + fmt.Println("") +} + +// IntSlice2String 效果:[]int{1,2,3,4} -> "1,2,3,4" +func IntSlice2String(elements []int, sep string) string { + elemStr := "" + if len(elements) > 0 { + for i, elem := range elements { + if i == (len(elements) - 1) { + elemStr += fmt.Sprintf("%d", elem) + break + } + elemStr += fmt.Sprintf("%d%s", elem, sep) + } + } + return elemStr +} + +// ConverMapInterface2MapString TODO +func ConverMapInterface2MapString(mi map[string]interface{}) (ms map[string]string, err error) { + ms = make(map[string]string) + for key, v := range mi { + dv, ok := v.(string) + if !ok { + return nil, fmt.Errorf("key:%s 断言string 失败", key) + } + ms[key] = dv + } + return +} + +// RegexReplaceSubString TODO +func RegexReplaceSubString(str, old, new string) string { + re := regexp.MustCompile(fmt.Sprintf(`(%s)`, old)) + return re.ReplaceAllString(str, new) +} + +// GetSuffixWithLenAndSep 获取后缀 +// 先截取后面 maxlen 长度字符串,再根据 separator 分隔取后缀 +func GetSuffixWithLenAndSep(strList []string, separator string, maxlen int) []string { + if maxlen > 0 { + for i, s := range strList { + l := len(s) + if l-maxlen > 0 { + strList[i] = s[l-maxlen:] + } + } + } + seqList := make([]string, len(strList)) + for i, s := range strList { + seqList[i] = LastElement(strings.Split(s, separator)) + } + return seqList +} + +// LastElement TODO +func LastElement(arr []string) string { + return arr[len(arr)-1] +} + +// ReverseRead · 逆序读取文件,类型tail -n 10 +// +// @receiver name +// @receiver lineNum 读取最后多少上内容 +// @return []string 返回逆序读取的文件内容 +// @return error +func ReverseRead(name string, lineNum uint) ([]string, error) { + // 打开文件 + file, err := os.Open(name) + if err != nil { + return nil, err + } + defer file.Close() + // 获取文件大小 + fs, err := file.Stat() + if err != nil { + return nil, err + } + fileSize := fs.Size() + + var offset int64 = -1 // 偏移量,初始化为-1,若为0则会读到EOF + char := make([]byte, 1) // 用于读取单个字节 + lineStr := "" // 存放一行的数据 + buff := make([]string, 0, 100) + for (-offset) <= fileSize { + // 通过Seek函数从末尾移动游标然后每次读取一个字节 + file.Seek(offset, io.SeekEnd) + _, err := file.Read(char) + if err != nil { + return buff, err + } + if char[0] == '\n' { + offset-- // windows跳过'\r' + lineNum-- // 到此读取完一行 + buff = append(buff, lineStr) + lineStr = "" + if lineNum == 0 { + return buff, nil + } + } else { + lineStr = string(char) + lineStr + } + offset-- + } + buff = append(buff, lineStr) + return buff, nil +} + +// SliceErrorsToError TODO +func SliceErrorsToError(errs []error) error { + var errStrs []string + for _, e := range errs { + errStrs = append(errStrs, e.Error()) + } + errString := strings.Join(errStrs, "\n") + return errors.New(errString) +} + +// IntnRange TODO +func IntnRange(min, max int) int { + rand.Seed(time.Now().Unix()) + return rand.Intn(max-min) + min +} + +// GetFileModifyTime TODO +func GetFileModifyTime(filename string) (bool, int64) { + if _, err := os.Stat(filename); !os.IsNotExist(err) { + f, err1 := os.Open(filename) + if err1 != nil { + return true, 0 + } + fi, err2 := f.Stat() + if err2 != nil { + return true, 0 + } + return true, fi.ModTime().Unix() + } + return false, 0 +} + +// UrlJoinPath utl.JoinPath go1.919 +func UrlJoinPath(p, subPath string) (string, error) { + u, err := url.Parse(p) + if err != nil { + return "", err + } + u.Path = path.Join(u.Path, subPath) + return u.String(), nil +} + +// FileIsEmpty TODO +func FileIsEmpty(path string) error { + fileInfo, err := os.Stat(path) + if err != nil { + return err + } + if fileInfo.Size() <= 0 { + return fmt.Errorf("文件为空") + } + return nil +} diff --git a/dbm-services/riak/db-tools/riak-monitor/.gitignore b/dbm-services/riak/db-tools/riak-monitor/.gitignore new file mode 100644 index 0000000000..12520a1502 --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/.gitignore @@ -0,0 +1,32 @@ +build +.idea +logs +.DS_Store +cmd/generator +!.gitkeep +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib +# Test binary, built with `go test -c` +*.test +# Output of the go coverage tool, specifically when used with LiteIDE +*.out +# Dependency directories (remove the comment below to include it) +vendor/ +# Go workspace file +go.work +configs/* +log/ +build/ +conf/ +*exe +*.log +.idea/ +sync_test.sh +.vscode/ +scripts/upload_media.sh +scripts/upload.sh +.ci \ No newline at end of file diff --git a/dbm-services/riak/db-tools/riak-monitor/Makefile b/dbm-services/riak/db-tools/riak-monitor/Makefile new file mode 100644 index 0000000000..19af8d1544 --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/Makefile @@ -0,0 +1,26 @@ +PROJ="riak-monitor" +MODULE="dbm-services/riak/db-tools/riak-monitor" +VERSION = $(error please set VERSION flag) +PKG = ${PROJ}.tar.gz +OUTPUT_DIR = build +RELEASE_BUILD_FLAG = "-X ${MODULE}/cmd.version=${VERSION} -X ${MODULE}/cmd.buildStamp=`date -u '+%Y-%m-%d_%I:%M:%S%p'` -X ${MODULE}/cmd.gitHash=`git rev-parse HEAD` " +DEV_BUILD_FLAG = "-X ${MODULE}/cmd.version="develop" -X ${MODULE}/cmd.buildStamp=`date -u '+%Y-%m-%d_%I:%M:%S%p'` -X ${MODULE}/cmd.gitHash="" " + + +.PHONY: release-bin +release-bin: + @CGO_ENABLE=0 GOARCH=amd64 GOOS=linux go build -ldflags ${RELEASE_BUILD_FLAG} -o ${OUTPUT_DIR}/${$PROJ} + @cp config.yaml.go.tpl ${OUTPUT_DIR}/config.yaml.go.tpl + @tar -C ${OUTPUT_DIR} -zcf ${OUTPUT_DIR}/${PKG} riak-monitor config.yaml.go.tpl + +.PHONY: dev-bin +dev-bin: + @go build -ldflags ${DEV_BUILD_FLAG} -o ${OUTPUT_DIR}/${PROJ} + @cp config.yaml.go.tpl ${OUTPUT_DIR}/config.yaml.go.tpl + @tar -C ${OUTPUT_DIR} -zcf ${OUTPUT_DIR}/${PKG} riak-monitor config.yaml.go.tpl + +.PHONY: clean +clean: + @rm -rf $(OUTPUT_DIR) + + diff --git a/dbm-services/riak/db-tools/riak-monitor/README.md b/dbm-services/riak/db-tools/riak-monitor/README.md new file mode 100644 index 0000000000..8ab7225a78 --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/README.md @@ -0,0 +1,194 @@ +# 使用 +* riak-monitor被mysql-crond调起 +* 必须先部署 `mysql-crond` +* 配置文件分为 _runtime_ 配置 和 监控项配置 +* _runtime_ 配置需要作为命令行参数传入, 如 `mysql-crond -c runtime.yaml` +* 监控项配置在 _runtime_ 配置中指定,相关配置文件示例见”示例文件“ +* 前台启动 + * 示例: + * /data/monitor/riak-crond/mysql-crond -c /data/monitor/riak-crond/runtime.yaml +* 后台执行 + * 示例: + * /data/monitor/riak-crond/start.sh -c /data/monitor/riak-crond/runtime.yaml + * cat start.sh + * SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + cd $SCRIPT_DIR && nohup ./mysql-crond ${@:1} & + +* 查看定时任务: + * curl http://xxx:xxx/entries +* 关闭定时任务: + * curl http://xxx:xxx/quit + +## 硬编码项 +目前有两个硬编码项 +1. 执行心跳 +2. 连接 _DB_ 失败 +* 如果监控项配置中缺少这两项, 会被自动添加 +* 以 `hardcode-run` 子命令运行 + + +# 监控项配置 +```go +type MonitorItem struct { + Name string `yaml:"name" validate:"required"` + Enable *bool `yaml:"enable" validate:"required"` + Schedule *string `yaml:"schedule"` + MachineType string `yaml:"machine_type"` +} +``` + +* `name`: 监控项名称, 对应蓝鲸监控平台的事件 +* `enable`: 是否启用 +* `schedule`: 可选, 在 _runtime_ 配置中有默认值, 不建议修改 +* `machine_type`: 基于机器类型的过滤 + +## 分组 +在注册 `mysql-crond entry` 时, 会按照 _schedule_ 把所有监控项分组注册 + +# 开发 +1. 在 `items_collect` 中添加监控项目录, 如 _some_new_item_ +2. 在 _some_new_item_ 中实现 `monitor_item_interface.MonitorItemInterface` +3. 同时还要提供 + * `func New(cc *monitor_item_interface.ConnectionCollect) monitor_item_interface.MonitorItemInterface` + * `func Register() (string, monitor_item_interface.MonitorItemConstructorFuncType)` +4. 在 `items_collect.init` 中注册新增的监控项 +5. 把新增监控项的相关配置已经添加到 _items-config.yaml_ 中 + + +# 监控项 + +| 监控项 |调度计划| 机器类型 | 实例角色 |级别| 说明 |自定义| +|--------------------------|-----|----------------|-----------------|-----|-----------------------------|-----| +| riak-err-notice |@every 1m| riak | | 预警 | 预警错误日志 |schedule, enable +| db-up |@every 10s| backend, proxy | | 致命 | db 连通性. 硬编码, 不可配置, 无需录入配置系统 |enable +| riak_monitor_heart_beat |@every 10s| riak | | 致命 | 监控心跳. 硬编码, 不可配置, 无需录入配置系统 |enable +| riak-load-health |@every 1m| riak | | 致命 | 检查负载与响应情况 |enable +| riak-ring-status |@every 10s| riak | | 致命 | 检查ring status, 发现集群中所有的故障节点 |enable + + +## 示例文件: +请在Editor模式下查看 +1. mysql-crond的runtime.yaml示例: +ip: xxx +port: xxx +bk_cloud_id: 0 +bk_monitor_beat: +custom_event: +bk_data_id: xxx +access_token: xxx +report_type: agent +message_kind: event +custom_metrics: +bk_data_id: xxx +access_token: xxx +report_type: agent +message_kind: timeseries +beat_path: xxx +agent_address: xxx +log: +console: false +log_file_dir: /data/monitor/riak-crond +debug: false +source: true +json: true +pid_path: /data/monitor/riak-crond +jobs_user: root +jobs_config: /data/monitor/riak-crond/jobs-config.yaml + + +2. mysql-crond的jobs-config.yaml示例: +- name: riak-err-notice@every 1m + enable: true + command: /data/monitor/riak-monitor/riak-monitor + args: + - run + - --items + - riak-err-notice + - -c + - /data/monitor/riak-monitor/runtime.yaml + schedule: '@every 1m' + creator: admin + work_dir: "" +- name: riak-load-health@every 1m + enable: true + command: /data/monitor/riak-monitor/riak-monitor + args: + - run + - --items + - riak-load-health + - -c + - /data/monitor/riak-monitor/runtime.yaml + schedule: '@every 1m' + creator: admin + work_dir: "" +- name: riak-ring-status@every 10s + enable: true + command: /data/monitor/riak-monitor/riak-monitor + args: + - run + - --items + - riak-ring-status + - -c + - /data/monitor/riak-monitor/runtime.yaml + schedule: '@every 10s' + creator: admin + work_dir: "" +- name: riak-monitor-hardcode@every 10s + enable: true + command: /data/monitor/riak-monitor/riak-monitor + args: + - hardcode-run + - --items + - db-up,riak_monitor_heart_beat + - -c + - /data/monitor/riak-monitor/runtime.yaml + schedule: '@every 10s' + creator: admin + work_dir: "" + bk_biz_id: xxx + +3. riak-monitor的runtime.yaml示例: +bk_biz_id: xxx +ip: xxx +port: xxx +bk_instance_id: xxx +immute_domain: xxx +machine_type: riak +bk_cloud_id: 0 +log: +console: true +log_file_dir: /data/monitor/riak-monitor/logs +debug: true +source: true +json: false +api_url: xxx +items_config_file: /data/monitor/riak-monitor/items-config.yaml +interact_timeout: 2s +default_schedule: '@every 1m' + +4. riak-monitor的items-config.yaml示例: +- name: riak-err-notice + enable: true + schedule: '@every 1m' + machine_type: + - riak +- name: riak-load-health + enable: true + schedule: '@every 1m' + machine_type: + - riak +- name: riak-ring-status + enable: true + schedule: '@every 10s' + machine_type: + - riak +- name: db-up + enable: true + schedule: '@every 10s' + machine_type: + - riak +- name: riak_monitor_heart_beat + enable: true + schedule: '@every 10s' + machine_type: + - riak diff --git a/dbm-services/riak/db-tools/riak-monitor/cmd/cmd.go b/dbm-services/riak/db-tools/riak-monitor/cmd/cmd.go new file mode 100644 index 0000000000..5b729f1814 --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/cmd/cmd.go @@ -0,0 +1,2 @@ +// Package cmd TODO +package cmd diff --git a/dbm-services/riak/db-tools/riak-monitor/cmd/init.go b/dbm-services/riak/db-tools/riak-monitor/cmd/init.go new file mode 100644 index 0000000000..e1055154ef --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/cmd/init.go @@ -0,0 +1,79 @@ +package cmd + +import ( + "fmt" + "io" + "os" + "path/filepath" + + "dbm-services/riak/db-tools/riak-monitor/pkg/config" + + "github.com/natefinch/lumberjack" + "golang.org/x/exp/slog" +) + +var executable string +var executableName string +var executableDir string + +func init() { + executable, _ = os.Executable() + // 获取可执行文件的名称 + executableName = filepath.Base(executable) + // 获取可执行文件的路径 + executableDir = filepath.Dir(executable) +} + +// initLogger 初始化日志格式 +func initLogger(cfg *config.LogConfig) { + var ioWriters []io.Writer + + // console打印日志 + if cfg.Console { + ioWriters = append(ioWriters, os.Stdout) + } + + // 日志文件记录日志 + if cfg.LogFileDir != nil { + if !filepath.IsAbs(*cfg.LogFileDir) { + *cfg.LogFileDir = filepath.Join(executableDir, *cfg.LogFileDir) + } + + err := os.MkdirAll(*cfg.LogFileDir, 0755) + if err != nil { + panic(err) + } + + logFile := filepath.Join(*cfg.LogFileDir, fmt.Sprintf("%s.log", executableName)) + _, err = os.Stat(logFile) + if err != nil { + // 目录不存在创建目录 + if os.IsNotExist(err) { + _, err := os.Create(logFile) + if err != nil { + panic(err) + } + } else { + panic(err) + } + } + ioWriters = append(ioWriters, &lumberjack.Logger{Filename: logFile}) + } + // 日志中添加源头信息,方便定位 + handleOpt := slog.HandlerOptions{AddSource: cfg.Source} + if cfg.Debug { + handleOpt.Level = slog.LevelDebug + } else { + handleOpt.Level = slog.LevelInfo + } + + var logger *slog.Logger + // 设置日志格式 + if cfg.Json { + logger = slog.New(slog.NewJSONHandler(io.MultiWriter(ioWriters...), &handleOpt)) + } else { + logger = slog.New(slog.NewTextHandler(io.MultiWriter(ioWriters...), &handleOpt)) + } + + slog.SetDefault(logger) +} diff --git a/dbm-services/riak/db-tools/riak-monitor/cmd/root.go b/dbm-services/riak/db-tools/riak-monitor/cmd/root.go new file mode 100644 index 0000000000..b8912b4643 --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/cmd/root.go @@ -0,0 +1,27 @@ +package cmd + +import ( + "os" + + "github.com/spf13/cobra" + "golang.org/x/exp/slog" +) + +var rootCmd = &cobra.Command{ + Use: "riak-monitor", + Short: "riak-monitor", +} + +func init() { + // rootCmd.PersistentFlags().StringP("config", "c", "", "config file") + // _ = viper.BindPFlag("config", rootCmd.PersistentFlags().Lookup("config")) +} + +// Execute TODO +func Execute() { + err := rootCmd.Execute() + if err != nil { + slog.Error("start", err) + os.Exit(1) + } +} diff --git a/dbm-services/riak/db-tools/riak-monitor/cmd/subcmd_clean.go b/dbm-services/riak/db-tools/riak-monitor/cmd/subcmd_clean.go new file mode 100644 index 0000000000..9ea91c7bd9 --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/cmd/subcmd_clean.go @@ -0,0 +1,63 @@ +package cmd + +import ( + "strings" + + ma "dbm-services/mysql/db-tools/mysql-crond/api" + "dbm-services/riak/db-tools/riak-monitor/pkg/config" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + "golang.org/x/exp/slog" +) + +// 清理mysql-crond中riak监控entry,但不包括硬编码。如果要退出监控,可访问mysql-crond的quit接口 +var subCmdClean = &cobra.Command{ + Use: "clean", + Short: "clean all mysql-crond entry", + Long: "clean all mysql-crond entry", + RunE: func(cmd *cobra.Command, args []string) error { + err := config.InitConfig(viper.GetString("clean-config")) + if err != nil { + return err + } + initLogger(config.MonitorConfig.Log) + // 通过mysql-crond的接口删除entry + manager := ma.NewManager(config.MonitorConfig.ApiUrl) + // 获取mysql-crond注册的所有entry + entries, err := manager.Entries() + if err != nil { + slog.Error("clean list entries", err) + return err + } + for _, entry := range entries { + // riak监控的entry,排除硬编码的entry,注意此接口会持久化到crond的配置文件jobs-config.yaml + if strings.HasPrefix(entry.Job.Name, "riak-") && + !strings.Contains(entry.Job.Name, "hardcode") { + eid, err := manager.Delete(entry.Job.Name, true) + if err != nil { + slog.Error( + "delete entry", err, + slog.String("name", entry.Job.Name), + ) + return err + } + slog.Info( + "delete entry", + slog.String("name", entry.Job.Name), + slog.Int("ID", eid), + ) + } + } + return nil + }, +} + +func init() { + subCmdClean.PersistentFlags().StringP("config", "c", "", "config file") + _ = subCmdClean.MarkPersistentFlagRequired("config") + _ = viper.BindPFlag("clean-config", subCmdClean.PersistentFlags().Lookup("config")) + + rootCmd.AddCommand(subCmdClean) + +} diff --git a/dbm-services/riak/db-tools/riak-monitor/cmd/subcmd_hardcode_run.go b/dbm-services/riak/db-tools/riak-monitor/cmd/subcmd_hardcode_run.go new file mode 100644 index 0000000000..b11bbbf210 --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/cmd/subcmd_hardcode_run.go @@ -0,0 +1,49 @@ +package cmd + +import ( + "dbm-services/riak/db-tools/riak-monitor/pkg/config" + "dbm-services/riak/db-tools/riak-monitor/pkg/mainloop" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + "golang.org/x/exp/slog" +) + +// 硬编码,监控启动后基础的定时任务,包括数据库是否启动、心跳上报 +var subCmdHardCodeRun = &cobra.Command{ + Use: "hardcode-run", + Short: "run hardcode monitor items", + Long: "run hardcode monitor items", + RunE: func(cmd *cobra.Command, args []string) error { + err := config.InitConfig(viper.GetString("hard-run-config")) + if err != nil { + return err + } + initLogger(config.MonitorConfig.Log) + // 加载监控items-config.yaml配置文件 + err = config.LoadMonitorItemsConfig() + if err != nil { + slog.Error("run hardcode monitor load items", err) + return err + } + // 执行监控项对应的函数 + err = mainloop.Run(true) + if err != nil { + slog.Error("run monitor hardcode items", err) + return err + } + return nil + }, +} + +func init() { + subCmdHardCodeRun.PersistentFlags().StringP("config", "c", "", "config file") + _ = subCmdHardCodeRun.MarkPersistentFlagRequired("config") + _ = viper.BindPFlag("hard-run-config", subCmdHardCodeRun.PersistentFlags().Lookup("config")) + + subCmdHardCodeRun.PersistentFlags().StringSliceP("items", "", nil, "run items") + _ = subCmdHardCodeRun.MarkPersistentFlagRequired("items") + _ = viper.BindPFlag("hardcode-items", subCmdHardCodeRun.PersistentFlags().Lookup("items")) + + rootCmd.AddCommand(subCmdHardCodeRun) +} diff --git a/dbm-services/riak/db-tools/riak-monitor/cmd/subcmd_run.go b/dbm-services/riak/db-tools/riak-monitor/cmd/subcmd_run.go new file mode 100644 index 0000000000..7b6f7c581a --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/cmd/subcmd_run.go @@ -0,0 +1,52 @@ +package cmd + +import ( + "dbm-services/riak/db-tools/riak-monitor/pkg/config" + "dbm-services/riak/db-tools/riak-monitor/pkg/mainloop" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + "golang.org/x/exp/slog" +) + +// 根据配置文件以及指定的item,运行监控 +var subCmdRun = &cobra.Command{ + Use: "run", + Short: "run monitor items", + Long: "run monitor items", + RunE: func(cmd *cobra.Command, args []string) error { + // 配置初始化 + err := config.InitConfig(viper.GetString("run-config")) + if err != nil { + return err + } + // 初始化日志配置 + initLogger(config.MonitorConfig.Log) + // 加载监控items-config.yaml配置文件 + err = config.LoadMonitorItemsConfig() + if err != nil { + slog.Error("run monitor load items", err) + return err + } + // 执行监控项 + err = mainloop.Run(false) + if err != nil { + slog.Error("msg", "run monitor items", err) + return err + } + return nil + }, +} + +func init() { + // 配置文件 + subCmdRun.PersistentFlags().StringP("config", "c", "", "config file") + _ = subCmdRun.MarkPersistentFlagRequired("config") + _ = viper.BindPFlag("run-config", subCmdRun.PersistentFlags().Lookup("config")) + // 指定items + subCmdRun.PersistentFlags().StringSliceP("items", "", nil, "run items") + _ = subCmdRun.MarkPersistentFlagRequired("items") + _ = viper.BindPFlag("run-items", subCmdRun.PersistentFlags().Lookup("items")) + + rootCmd.AddCommand(subCmdRun) +} diff --git a/dbm-services/riak/db-tools/riak-monitor/cmd/subcmd_version.go b/dbm-services/riak/db-tools/riak-monitor/cmd/subcmd_version.go new file mode 100644 index 0000000000..eeb516b6df --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/cmd/subcmd_version.go @@ -0,0 +1,42 @@ +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +// versionCmd represents the version command +var subCmdVersion = &cobra.Command{ + Use: "version", + Short: "A brief description of your command", + Long: `A longer description that spans multiple lines and likely contains examples +and usage of using your command. For example: + +Cobra is a CLI library for Go that empowers applications. +This application is a tool to generate the needed files +to quickly create a Cobra application.`, + Run: func(cmd *cobra.Command, args []string) { + printVersion() + }, +} +var version = "" +var buildStamp = "" +var gitHash = "" + +func init() { + rootCmd.AddCommand(subCmdVersion) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // versionCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // versionCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} +func printVersion() { + fmt.Printf("Version: %s, GitHash: %s, BuildAt: %s\n", version, gitHash, buildStamp) +} diff --git a/dbm-services/riak/db-tools/riak-monitor/config.yaml b/dbm-services/riak/db-tools/riak-monitor/config.yaml new file mode 100644 index 0000000000..5347f41f93 --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/config.yaml @@ -0,0 +1,17 @@ +bk_biz_id: xxx +ip: xxx +port: xxx +bk_instance_id: xxx +immute_domain: xxx +machine_type: riak +bk_cloud_id: xxx +log: + console: true + log_file_dir: /data/monitor/riak-monitor/logs + debug: true + source: true + json: false +api_url: http://127.0.0.1:9999 +items_config_file: /data/monitor/riak-monitor/items-config.yaml +interact_timeout: 2s +default_schedule: '@every 1m' diff --git a/dbm-services/riak/db-tools/riak-monitor/config.yaml.go.tpl b/dbm-services/riak/db-tools/riak-monitor/config.yaml.go.tpl new file mode 100644 index 0000000000..b0488e6e7f --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/config.yaml.go.tpl @@ -0,0 +1,17 @@ +bk_biz_id: {{ .BkBizId }} +ip: {{ .IP }} +port: {{ .Port }} +bk_instance_id: {{ .BkInstanceId }} +immute_domain: {{ .ImmuteDomain }} +machine_type: {{ .MachineType }} +bk_cloud_id: {{ .BkCloudId }} +log: + console: true + log_file_dir: {{ .LogPath }} + debug: true + source: true + json: false +api_url: http://127.0.0.1:9999 +items_config_file: {{ .ItemsConfigPath }} +interact_timeout: 2s +default_schedule: '@every 1m' diff --git a/dbm-services/riak/db-tools/riak-monitor/go.mod b/dbm-services/riak/db-tools/riak-monitor/go.mod new file mode 100644 index 0000000000..ed8b8eb031 --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/go.mod @@ -0,0 +1,38 @@ +module dbm-services/riak/db-tools/riak-monitor + +go 1.19 + +require ( + github.com/dlclark/regexp2 v1.10.0 + github.com/go-playground/validator v9.31.0+incompatible + github.com/go-sql-driver/mysql v1.7.1 + github.com/natefinch/lumberjack v2.0.0+incompatible + github.com/pkg/errors v0.9.1 + github.com/spf13/cobra v1.7.0 + github.com/spf13/viper v1.16.0 + golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 + gopkg.in/yaml.v2 v2.4.0 +) + +require ( + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/leodido/go-urn v1.2.4 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/spf13/afero v1.9.5 // indirect + github.com/spf13/cast v1.5.1 // indirect + github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/subosito/gotenv v1.4.2 // indirect + golang.org/x/sys v0.8.0 // indirect + golang.org/x/text v0.9.0 // indirect + gopkg.in/go-playground/assert.v1 v1.2.1 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/dbm-services/riak/db-tools/riak-monitor/go.sum b/dbm-services/riak/db-tools/riak-monitor/go.sum new file mode 100644 index 0000000000..cd57c03fda --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/go.sum @@ -0,0 +1,512 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0= +github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator v9.31.0+incompatible h1:UA72EPEogEnq76ehGdEDp4Mit+3FDh548oRqwVgNsHA= +github.com/go-playground/validator v9.31.0+incompatible/go.mod h1:yrEkQXlcI+PugkyDjY2bRrL/UBU4f3rvrgkN3V8JEig= +github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= +github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM= +github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk= +github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= +github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= +github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= +github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= +github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= +github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= +github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 h1:Di6/M8l0O2lCLc6VVRWhgCiApHV8MnQurBnFSHsQtNY= +golang.org/x/exp v0.0.0-20230725093048-515e97ebf090/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= +gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/dbm-services/riak/db-tools/riak-monitor/items-config.yaml b/dbm-services/riak/db-tools/riak-monitor/items-config.yaml new file mode 100644 index 0000000000..fe03fedc04 --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/items-config.yaml @@ -0,0 +1,25 @@ +- name: riak-err-notice + enable: true + schedule: '@every 1m' + machine_type: + - riak +- name: riak-load-health + enable: true + schedule: '@every 1m' + machine_type: + - riak +- name: riak-ring-status + enable: true + schedule: '@every 10s' + machine_type: + - riak +- name: db-up + enable: true + schedule: '@every 10s' + machine_type: + - riak +- name: riak_monitor_heart_beat + enable: true + schedule: '@every 10s' + machine_type: + - riak diff --git a/dbm-services/riak/db-tools/riak-monitor/main.go b/dbm-services/riak/db-tools/riak-monitor/main.go new file mode 100644 index 0000000000..17178c7c87 --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/main.go @@ -0,0 +1,8 @@ +package main + +import "dbm-services/riak/db-tools/riak-monitor/cmd" + +func main() { + // 执行命令行指令 + cmd.Execute() +} diff --git a/dbm-services/riak/db-tools/riak-monitor/pkg/config/config.go b/dbm-services/riak/db-tools/riak-monitor/pkg/config/config.go new file mode 100644 index 0000000000..f0e566976e --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/pkg/config/config.go @@ -0,0 +1,2 @@ +// Package config 配置 +package config diff --git a/dbm-services/riak/db-tools/riak-monitor/pkg/config/init.go b/dbm-services/riak/db-tools/riak-monitor/pkg/config/init.go new file mode 100644 index 0000000000..f358a64660 --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/pkg/config/init.go @@ -0,0 +1,142 @@ +package config + +import ( + "os" + "path/filepath" + + "github.com/go-playground/validator" + "golang.org/x/exp/slog" + "gopkg.in/yaml.v2" +) + +var HeartBeatName = "riak_monitor_heart_beat" +var MonitorConfig *monitorConfig +var ItemsConfig []*MonitorItem +var HardCodeSchedule = "@every 10s" + +// InitConfig 配置初始化 +func InitConfig(configPath string) error { + slog.Info("msg", "config path", configPath) + if !filepath.IsAbs(configPath) { + cwd, err := os.Getwd() + if err != nil { + slog.Error("init config", err) + return err + } + + configPath = filepath.Join(cwd, configPath) + } + slog.Info("msg", "absolute config path", configPath) + + content, err := os.ReadFile(configPath) + if err != nil { + slog.Error("init config", err) + return err + } + + slog.Info("msg", "content", string(content)) + MonitorConfig = &monitorConfig{} + slog.Info("msg", "MonitorConfig", MonitorConfig) + err = yaml.UnmarshalStrict(content, MonitorConfig) + if err != nil { + slog.Error("config file content", string(content)) + slog.Error("init config", err) + return err + } + validate := validator.New() + err = validate.Struct(MonitorConfig) + if err != nil { + slog.Error("validate monitor config", err) + return err + } + + return nil +} + +// LoadMonitorItemsConfig 加载监控项配置 +func LoadMonitorItemsConfig() error { + ItemsConfig = make([]*MonitorItem, 0) + + content, err := os.ReadFile(MonitorConfig.ItemsConfigFile) + if err != nil { + slog.Error("load monitor items config", err) + return err + } + + err = yaml.UnmarshalStrict(content, &ItemsConfig) + if err != nil { + slog.Error("unmarshal monitor items config", err) + return err + } + + validate := validator.New() + for _, ele := range ItemsConfig { + err := validate.Struct(ele) + if err != nil { + slog.Error("validate monitor items config", err) + return err + } + } + + return nil +} + +// InjectHardCodeItem 注入硬编码的心跳和db-up监控 +func InjectHardCodeItem() { + enable := true + dbUpItem := &MonitorItem{ + Name: "db-up", + Enable: &enable, + Schedule: &HardCodeSchedule, //&MonitorConfig.DefaultSchedule, + MachineType: []string{MonitorConfig.MachineType}, + } + heartBeatItem := &MonitorItem{ + Name: HeartBeatName, + Enable: &enable, + Schedule: &HardCodeSchedule, //&MonitorConfig.DefaultSchedule, + MachineType: []string{MonitorConfig.MachineType}, + } + slog.Debug("load monitor item", slog.Any("items", ItemsConfig)) + + ItemsConfig = injectItem(dbUpItem, ItemsConfig) + slog.Debug("inject hardcode", slog.Any("items", ItemsConfig)) + + ItemsConfig = injectItem(heartBeatItem, ItemsConfig) + slog.Debug("inject hardcode", slog.Any("items", ItemsConfig)) +} + +func injectItem(item *MonitorItem, collect []*MonitorItem) (res []*MonitorItem) { + for i, ele := range collect { + if ele.Name == item.Name { + // 如果已经在配置文件, 保留 enable 配置, 其他覆盖为默认配置 + res = append(collect[:i], collect[i+1:]...) + item.Enable = ele.Enable + return append(res, item) + } + } + + return append(collect, item) +} + +// WriteMonitorItemsBack 回写监控项到文件 +func WriteMonitorItemsBack() error { + // 注入硬编码监控项后回写items文件 + content, err := yaml.Marshal(ItemsConfig) + if err != nil { + slog.Error("marshal items config", err) + return err + } + + f, err := os.OpenFile(MonitorConfig.ItemsConfigFile, os.O_TRUNC|os.O_WRONLY, 0755) + if err != nil { + slog.Error("open items config file", err) + return err + } + + _, err = f.Write(content) + if err != nil { + slog.Error("write items config file", err) + return err + } + return nil +} diff --git a/dbm-services/riak/db-tools/riak-monitor/pkg/config/items_config.go b/dbm-services/riak/db-tools/riak-monitor/pkg/config/items_config.go new file mode 100644 index 0000000000..98db2ccccc --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/pkg/config/items_config.go @@ -0,0 +1,21 @@ +package config + +import "golang.org/x/exp/slices" + +// MonitorItem 监控项 +type MonitorItem struct { + Name string `yaml:"name" validate:"required"` + Enable *bool `yaml:"enable" validate:"required"` + Schedule *string `yaml:"schedule"` + MachineType []string `yaml:"machine_type"` +} + +// IsEnable 监控项启用 +func (c *MonitorItem) IsEnable() bool { + return c.Enable != nil && *c.Enable +} + +// IsMatchMachineType 机器类型匹配 +func (c *MonitorItem) IsMatchMachineType() bool { + return slices.Index(c.MachineType, MonitorConfig.MachineType) >= 0 +} diff --git a/dbm-services/riak/db-tools/riak-monitor/pkg/config/log_config.go b/dbm-services/riak/db-tools/riak-monitor/pkg/config/log_config.go new file mode 100644 index 0000000000..f5ca53f03d --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/pkg/config/log_config.go @@ -0,0 +1,13 @@ +package config + +// LogConfig 日志配置 +type LogConfig struct { + // 是否console输出 + Console bool `yaml:"console"` + // 位置 + LogFileDir *string `yaml:"log_file_dir"` + Debug bool `yaml:"debug"` + // 是否打印日志源头信息 + Source bool `yaml:"source"` + Json bool `yaml:"json"` +} diff --git a/dbm-services/riak/db-tools/riak-monitor/pkg/config/monitor_config.go b/dbm-services/riak/db-tools/riak-monitor/pkg/config/monitor_config.go new file mode 100644 index 0000000000..dec2af5b20 --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/pkg/config/monitor_config.go @@ -0,0 +1,25 @@ +package config + +import ( + "time" +) + +type monitorConfig struct { + BkBizId int `yaml:"bk_biz_id"` + Ip string `yaml:"ip" validate:"required,ipv4"` + Port int `yaml:"port" validate:"required,gt=1024,lte=65535"` + BkInstanceId int64 `yaml:"bk_instance_id" validate:"required,gt=0"` + ImmuteDomain string `yaml:"immute_domain"` + MachineType string `yaml:"machine_type"` + BkCloudID *int `yaml:"bk_cloud_id" validate:"required,gte=0"` + // 日志配置项 + Log *LogConfig `yaml:"log"` + // items-config.yaml 路径 + ItemsConfigFile string `yaml:"items_config_file" validate:"required"` + // crond的访问url + ApiUrl string `yaml:"api_url" validate:"required"` + // 超时时间 + InteractTimeout time.Duration `yaml:"interact_timeout" validate:"required"` + // 调度频率 + DefaultSchedule string `yaml:"default_schedule" validate:"required"` +} diff --git a/dbm-services/riak/db-tools/riak-monitor/pkg/internal/cst/const.go b/dbm-services/riak/db-tools/riak-monitor/pkg/internal/cst/const.go new file mode 100644 index 0000000000..67c05d39c1 --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/pkg/internal/cst/const.go @@ -0,0 +1,10 @@ +package cst + +const ( + // RiakMachineType 机器类型 + RiakMachineType = "riak" + // RiakHttpPort http端口 + RiakHttpPort = 8098 + // RiakAdminPath 管理指令 + RiakAdminPath = "/usr/sbin/riak-admin" +) diff --git a/dbm-services/riak/db-tools/riak-monitor/pkg/internal/cst/cst.go b/dbm-services/riak/db-tools/riak-monitor/pkg/internal/cst/cst.go new file mode 100644 index 0000000000..f558488b06 --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/pkg/internal/cst/cst.go @@ -0,0 +1,2 @@ +// Package cst TODO +package cst diff --git a/dbm-services/riak/db-tools/riak-monitor/pkg/itemscollect/checkload/check_load.go b/dbm-services/riak/db-tools/riak-monitor/pkg/itemscollect/checkload/check_load.go new file mode 100644 index 0000000000..df8f131a4a --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/pkg/itemscollect/checkload/check_load.go @@ -0,0 +1,57 @@ +package checkload + +import ( + "dbm-services/riak/db-tools/riak-monitor/pkg/internal/cst" + "dbm-services/riak/db-tools/riak-monitor/pkg/utils" + "encoding/json" + "fmt" + "strings" + + "golang.org/x/exp/slog" +) + +func CheckResponseTime() (string, error) { + item := `^node_put_fsm_time_mean|^node_get_fsm_time_mean|^node_put_fsm_rejected|^node_get_fsm_rejected` + format := `awk '{print "\""$1"\" "$3}' | awk '{printf("%s: %s,",$1,$2)}' | sed "{s/,$/\}/g}" | sed "{s/^/{/g}"` + cmd := fmt.Sprintf(`%s status | grep -E '%s' | grep -E -v '_60s|_total' | %s`, cst.RiakAdminPath, item, format) + resp, err := utils.ExecShellCommand(false, cmd) + if err != nil { + // 这个检查项是在此riak节点运行时,执行集群ring检查,发现其他异常节点;如果此节点异常,检查联通性时可探测到 + if strings.Contains(err.Error(), "Node did not respond to ping!") { + slog.Warn(fmt.Sprintf("check load. execute [ %s ] error: %s.", cmd, err.Error())) + return "", nil + } else { + errInfo := fmt.Sprintf("check load. execute [ %s ] error: %s", cmd, err.Error()) + return "", fmt.Errorf(errInfo) + } + } + type CheckItems struct { + GetTime int `json:"node_get_fsm_time_mean"` // 客户端发起GET请求到收到响应时间间隔的均值,微妙 + PutTime int `json:"node_put_fsm_time_mean"` // 客户端发起PUT请求到收到响应时间间隔的均值,微妙 + GetRejectedNum int `json:"node_get_fsm_rejected"` // 被过载保护主动拒绝的GET FSM数量 + PutRejectedNum int `json:"node_put_fsm_rejected"` // 被过载保护主动拒绝的PUT FSM数量 + } + var items CheckItems + if err = json.Unmarshal([]byte(resp), &items); err != nil { + err = fmt.Errorf("unmarshall %s to %+v get an error:%s", resp, items, err.Error()) + slog.Error(err.Error()) + return "", err + } + var errList []string + if items.GetTime > 300000 { + errList = append(errList, fmt.Sprintf("get response time over than 0.3s")) + } + if items.PutTime > 500000 { + errList = append(errList, fmt.Sprintf("put response time over than 0.5s")) + } + if items.GetRejectedNum > 0 { + errList = append(errList, fmt.Sprintf("overload protection, get was rejected")) + } + if items.PutRejectedNum > 0 { + errList = append(errList, fmt.Sprintf("overload protection, put was rejected")) + } + if len(errList) > 0 { + return "", fmt.Errorf(strings.Join(errList, ",")) + } + return "", nil +} diff --git a/dbm-services/riak/db-tools/riak-monitor/pkg/itemscollect/checkload/init.go b/dbm-services/riak/db-tools/riak-monitor/pkg/itemscollect/checkload/init.go new file mode 100644 index 0000000000..c3326c51f5 --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/pkg/itemscollect/checkload/init.go @@ -0,0 +1,45 @@ +package checkload + +import ( + "dbm-services/riak/db-tools/riak-monitor/pkg/monitoriteminterface" + "fmt" + + "github.com/pkg/errors" +) + +var NameCheckLoadHealth = "riak-load-health" + +func init() {} + +// Checker TODO +type Checker struct { + name string + f func() (string, error) +} + +// Run TODO +func (c *Checker) Run() (msg string, err error) { + msg, err = c.f() + if err != nil { + return "", errors.Wrap(err, fmt.Sprintf("run %s", c.name)) + } + return msg, nil +} + +// Name TODO +func (c *Checker) Name() string { + return c.name +} + +// NewCheckLoadHealth TODO +func NewCheckLoadHealth(cc *monitoriteminterface.ConnectionCollect) monitoriteminterface.MonitorItemInterface { + return &Checker{ + name: NameCheckLoadHealth, + f: CheckResponseTime, + } +} + +// RegisterCheckLoadHealth TODO +func RegisterCheckLoadHealth() (string, monitoriteminterface.MonitorItemConstructorFuncType) { + return NameCheckLoadHealth, NewCheckLoadHealth +} diff --git a/dbm-services/riak/db-tools/riak-monitor/pkg/itemscollect/checkringstatus/init.go b/dbm-services/riak/db-tools/riak-monitor/pkg/itemscollect/checkringstatus/init.go new file mode 100644 index 0000000000..d2f3e45974 --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/pkg/itemscollect/checkringstatus/init.go @@ -0,0 +1,45 @@ +package checkringstatus + +import ( + "dbm-services/riak/db-tools/riak-monitor/pkg/monitoriteminterface" + "fmt" + + "github.com/pkg/errors" +) + +var NameCheckRingStatus = "riak-ring-status" + +func init() {} + +// Checker TODO +type Checker struct { + name string + f func() (string, error) +} + +// Run TODO +func (c *Checker) Run() (msg string, err error) { + msg, err = c.f() + if err != nil { + return "", errors.Wrap(err, fmt.Sprintf("run %s", c.name)) + } + return msg, nil +} + +// Name TODO +func (c *Checker) Name() string { + return c.name +} + +// NewCheckRingStatus TODO +func NewCheckRingStatus(cc *monitoriteminterface.ConnectionCollect) monitoriteminterface.MonitorItemInterface { + return &Checker{ + name: NameCheckRingStatus, + f: CheckRingStatus, + } +} + +// RegisterCheckRingStatus TODO +func RegisterCheckRingStatus() (string, monitoriteminterface.MonitorItemConstructorFuncType) { + return NameCheckRingStatus, NewCheckRingStatus +} diff --git a/dbm-services/riak/db-tools/riak-monitor/pkg/itemscollect/checkringstatus/riak_ring_status.go b/dbm-services/riak/db-tools/riak-monitor/pkg/itemscollect/checkringstatus/riak_ring_status.go new file mode 100644 index 0000000000..cfbbbb9854 --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/pkg/itemscollect/checkringstatus/riak_ring_status.go @@ -0,0 +1,37 @@ +package checkringstatus + +import ( + "dbm-services/riak/db-tools/riak-monitor/pkg/internal/cst" + "dbm-services/riak/db-tools/riak-monitor/pkg/utils" + "fmt" + "regexp" + "strings" + + "golang.org/x/exp/slog" +) + +func CheckRingStatus() (string, error) { + cmd := fmt.Sprintf("%s ringready", cst.RiakAdminPath) + resp, err := utils.ExecShellCommand(false, cmd) + if err != nil { + // 这个检查项是在此riak节点运行时,执行集群ring检查,发现其他异常节点;如果此节点异常,检查联通性时可探测到 + if strings.Contains(err.Error(), "Node did not respond to ping!") { + slog.Warn(fmt.Sprintf("execute [ %s ] error: %s.", cmd, err.Error())) + } else if strings.Contains(resp, "FALSE") { + // FALSE ['riak@xxx','riak@xxx'] down. All nodes need to be up to check. + slog.Error(resp) + re := regexp.MustCompile(`\[[^[]*\] down`) + matchArr := re.FindStringSubmatch(resp) + if len(matchArr) == 1 { + return "", fmt.Errorf(matchArr[0]) + } else { + return "", fmt.Errorf(resp) + } + } else { + errInfo := fmt.Sprintf("execute [ %s ] error: %s", cmd, err.Error()) + slog.Error(errInfo) + return "", fmt.Errorf(errInfo) + } + } + return "", nil +} diff --git a/dbm-services/riak/db-tools/riak-monitor/pkg/itemscollect/items_collect.go b/dbm-services/riak/db-tools/riak-monitor/pkg/itemscollect/items_collect.go new file mode 100644 index 0000000000..d7f7b69f8a --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/pkg/itemscollect/items_collect.go @@ -0,0 +1,50 @@ +// TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +// Copyright (C) 2017-2023 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 https://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. + +// Package itemscollect 监控项 +package itemscollect + +import ( + "dbm-services/riak/db-tools/riak-monitor/pkg/itemscollect/checkload" + "dbm-services/riak/db-tools/riak-monitor/pkg/itemscollect/checkringstatus" + "dbm-services/riak/db-tools/riak-monitor/pkg/itemscollect/riakconsolelog" + "fmt" + + mi "dbm-services/riak/db-tools/riak-monitor/pkg/monitoriteminterface" + + "golang.org/x/exp/slog" +) + +var registeredItemConstructor map[string]func(*mi.ConnectionCollect) mi.MonitorItemInterface + +func registerItemConstructor( + name string, f func(*mi.ConnectionCollect) mi.MonitorItemInterface, +) error { + if _, ok := registeredItemConstructor[name]; ok { + err := fmt.Errorf("%s already registered", name) + slog.Error("register item creator", err) + return err + } + registeredItemConstructor[name] = f + return nil +} + +// RegisteredItemConstructor 返回注册列表 +func RegisteredItemConstructor() map[string]func(*mi.ConnectionCollect) mi.MonitorItemInterface { + return registeredItemConstructor +} + +func init() { + registeredItemConstructor = make(map[string]func(*mi.ConnectionCollect) mi.MonitorItemInterface) + /* + 注册监控项 + */ + _ = registerItemConstructor(riakconsolelog.RegisterRiakErrNotice()) + _ = registerItemConstructor(checkringstatus.RegisterCheckRingStatus()) + _ = registerItemConstructor(checkload.RegisterCheckLoadHealth()) +} diff --git a/dbm-services/riak/db-tools/riak-monitor/pkg/itemscollect/riakconsolelog/init.go b/dbm-services/riak/db-tools/riak-monitor/pkg/itemscollect/riakconsolelog/init.go new file mode 100644 index 0000000000..5d3564b9a3 --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/pkg/itemscollect/riakconsolelog/init.go @@ -0,0 +1,44 @@ +// TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +// Copyright (C) 2017-2023 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 https://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. + +package riakconsolelog + +import ( + "dbm-services/riak/db-tools/riak-monitor/pkg/monitoriteminterface" +) + +var nameRiakErrNotice = "riak-err-notice" + +// Checker TODO +type Checker struct { + name string + f func() (string, error) +} + +// Run TODO +func (c *Checker) Run() (msg string, err error) { + return c.f() +} + +// Name TODO +func (c *Checker) Name() string { + return c.name +} + +// NewRiakErrNotice TODO +func NewRiakErrNotice(cc *monitoriteminterface.ConnectionCollect) monitoriteminterface.MonitorItemInterface { + return &Checker{ + name: nameRiakErrNotice, + f: riakNotice, + } +} + +// RegisterRiakErrNotice TODO +func RegisterRiakErrNotice() (string, monitoriteminterface.MonitorItemConstructorFuncType) { + return nameRiakErrNotice, NewRiakErrNotice +} diff --git a/dbm-services/riak/db-tools/riak-monitor/pkg/itemscollect/riakconsolelog/riak_notice.go b/dbm-services/riak/db-tools/riak-monitor/pkg/itemscollect/riakconsolelog/riak_notice.go new file mode 100644 index 0000000000..62bd373133 --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/pkg/itemscollect/riakconsolelog/riak_notice.go @@ -0,0 +1,200 @@ +// TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +// Copyright (C) 2017-2023 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 https://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. + +package riakconsolelog + +import ( + "bufio" + "dbm-services/riak/db-tools/riak-monitor/pkg/utils" + "fmt" + "os" + "path" + "path/filepath" + "strconv" + "strings" + "time" + + "golang.org/x/exp/slog" + + "github.com/dlclark/regexp2" +) + +var executable string +var offsetRegFile string +var rowStartPattern *regexp2.Regexp +var riakNoticePattern *regexp2.Regexp +var riakNoticeIgnorePattern *regexp2.Regexp + +func riakNotice() (string, error) { + return ScanLog() +} + +func init() { + executable, _ = os.Executable() + offsetRegFile = filepath.Join(filepath.Dir(executable), "console_log_offset.reg") + now := time.Now() + rowStartPattern = regexp2.MustCompile( + fmt.Sprintf( + `^(?=(?:(%s|%s|%s)))`, + now.Format("2006-01-02"), + now.Format("060102"), + now.Format("20060102"), + ), + regexp2.None, + ) + riakNoticePattern = regexp2.MustCompile( + fmt.Sprintf( + `(?=(?:(%s)))`, + strings.Join( + []string{"error", "fatal"}, + "|", + ), + ), + regexp2.IgnoreCase, + ) + riakNoticeIgnorePattern = regexp2.MustCompile( + fmt.Sprintf( + `(?=(?:(%s)))`, + strings.Join( + []string{"Unrecognized message", + "no function clause matching webmachine_request", + "too many siblings for object"}, + "|", + ), + ), + regexp2.IgnoreCase, + ) +} + +// ScanLog 扫描日志 +func ScanLog() (string, error) { + consoleLogPath, err := findConsoleLogFile() + if err != nil { + return "", err + } + file, offset, err := newScanner(consoleLogPath) + scanner := bufio.NewScanner(file) + var lines, infos []string + // 逐行扫描日志文件 + for scanner.Scan() { + content := scanner.Bytes() + line := scanner.Text() + lines = append(lines, line) + offset += int64(len(content)) + 1 + } + file.Close() + for _, line := range lines { + match, err := rowStartPattern.MatchString(line) + if err != nil { + slog.Error( + "apply row pattern", err, slog.String("pattern", rowStartPattern.String()), + ) + continue + } + // 非完整一行的读取不做判断 + if !match { + continue + } + // 匹配报错信息 + match, err = riakNoticePattern.MatchString(line) + if err != nil { + slog.Error( + "apply pattern", err, slog.String("pattern", riakNoticePattern.String()), + ) + } + if match { + // 应该忽略的报错信息 + matchignore, err := riakNoticeIgnorePattern.MatchString(line) + if err != nil { + slog.Error( + "apply ignore pattern", err, slog.String("ignore pattern", riakNoticeIgnorePattern.String()), + ) + } + if !matchignore { + infos = append(infos, line) + } + } + } + // 更新offsetRegFile文件中,下次读取开始的位置 + f, err := os.OpenFile(offsetRegFile, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0755) + if err != nil { + slog.Error("open offset reg", err) + return "", err + } + _, err = f.WriteString(strconv.FormatInt(offset, 10)) + if err != nil { + slog.Error("update offset reg", err) + return "", err + } + if len(infos) > 0 { + return strings.Join(infos, "\n"), nil + } + return "", nil +} + +func findConsoleLogFile() (string, error) { + cmd := `/usr/sbin/riak config effective | grep '^platform_log_dir' | cut -d '=' -f2 | awk '{print $1}'` + LogPath, err := utils.ExecShellCommand(false, cmd) + if err != nil { + slog.Error("get riak log error", err) + return LogPath, err + } + LogPath = strings.ReplaceAll(LogPath, "\n", "") + return path.Join(LogPath, "console.log"), nil +} + +func newScanner(logPath string) (*os.File, int64, error) { + f, err := os.Open(logPath) + if err != nil { + slog.Error("open console log", err) + return nil, 0, err + } + + st, err := f.Stat() + if err != nil { + slog.Error("stat of console log", err) + return nil, 0, err + } + consoleLogSize := st.Size() + + lastOffset, err := lastRoundOffset() + if err != nil { + return nil, 0, err + } + + // errlog 应该是被 rotate 了 + if consoleLogSize < lastOffset { + lastOffset = 0 + } + + // 从lastOffset开始读取文件 + offset, err := f.Seek(lastOffset, 0) + if err != nil { + slog.Error("seek err log", err) + return nil, 0, err + } + return f, offset, nil +} + +func lastRoundOffset() (int64, error) { + content, err := os.ReadFile(offsetRegFile) + if err != nil { + if os.IsNotExist(err) { + return 0, nil + } + slog.Error("read offset reg", err, slog.String("file", offsetRegFile)) + return 0, err + } + + r, err := strconv.ParseInt(string(content), 10, 64) + if err != nil { + slog.Error("parse last offset", err) + return 0, err + } + return r, nil +} diff --git a/dbm-services/riak/db-tools/riak-monitor/pkg/mainloop/main_loop.go b/dbm-services/riak/db-tools/riak-monitor/pkg/mainloop/main_loop.go new file mode 100644 index 0000000000..5293223685 --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/pkg/mainloop/main_loop.go @@ -0,0 +1,87 @@ +// TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +// Copyright (C) 2017-2023 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 https://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. + +// Package mainloop 主循环 +package mainloop + +import ( + "fmt" + "strings" + + "dbm-services/riak/db-tools/riak-monitor/pkg/config" + "dbm-services/riak/db-tools/riak-monitor/pkg/itemscollect" + "dbm-services/riak/db-tools/riak-monitor/pkg/monitoriteminterface" + "dbm-services/riak/db-tools/riak-monitor/pkg/utils" + + _ "github.com/go-sql-driver/mysql" // mysql TODO + "github.com/pkg/errors" + "github.com/spf13/viper" + "golang.org/x/exp/slices" + "golang.org/x/exp/slog" +) + +// Run TODO +func Run(hardcode bool) error { + var iNames []string + if hardcode { + iNames = viper.GetStringSlice("hardcode-items") + } else { + iNames = viper.GetStringSlice("run-items") + } + slog.Info("main loop", slog.String("items", strings.Join(iNames, ","))) + slog.Info("main loop", slog.Bool("hardcode", hardcode)) + + if hardcode && slices.Index(iNames, config.HeartBeatName) >= 0 { + utils.SendMonitorMetrics(config.HeartBeatName, 1, nil) + } + + cc, err := monitoriteminterface.NewConnectionCollect() + if err != nil { + if hardcode && slices.Index(iNames, "db-up") >= 0 { + utils.SendMonitorEvent("db-up", err.Error()) + } + return nil + } + + if hardcode { + return nil + } + + for _, iName := range iNames { + + if constructor, ok := itemscollect.RegisteredItemConstructor()[iName]; ok { + msg, err := constructor(cc).Run() + if err != nil { + slog.Error("run monitor item", err, slog.String("name", iName)) + utils.SendMonitorEvent( + "monitor-internal-error", + fmt.Sprintf("run monitor item %s failed: %s", iName, err.Error()), + ) + continue + } + + if msg != "" { + slog.Info( + "run monitor items", + slog.String("name", iName), + slog.String("msg", msg), + ) + utils.SendMonitorEvent(iName, msg) + continue + } + + slog.Info("run monitor item pass", slog.String("name", iName)) + + } else { + err := errors.Errorf("%s not registered", iName) + slog.Error("run monitor item", err) + continue + } + } + return nil +} diff --git a/dbm-services/riak/db-tools/riak-monitor/pkg/monitoriteminterface/connection_collect.go b/dbm-services/riak/db-tools/riak-monitor/pkg/monitoriteminterface/connection_collect.go new file mode 100644 index 0000000000..c88e2fe36c --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/pkg/monitoriteminterface/connection_collect.go @@ -0,0 +1,95 @@ +// TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +// Copyright (C) 2017-2023 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 https://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. + +package monitoriteminterface + +import ( + "dbm-services/riak/db-tools/riak-monitor/pkg/config" + "dbm-services/riak/db-tools/riak-monitor/pkg/internal/cst" + "dbm-services/riak/db-tools/riak-monitor/pkg/utils" + "fmt" + "strings" + "time" + + "golang.org/x/exp/slog" +) + +// ConnectionCollect DB连接对象 +type ConnectionCollect struct { +} + +// NewConnectionCollect 新建连接 +func NewConnectionCollect() (*ConnectionCollect, error) { + if config.MonitorConfig.MachineType == cst.RiakMachineType { + err := ConnectDB( + config.MonitorConfig.Ip, + ) + if err != nil { + slog.Error( + fmt.Sprintf("connect error: %s", config.MonitorConfig.MachineType), err, + slog.String("ip", config.MonitorConfig.Ip), + slog.Int("port", config.MonitorConfig.Port), + ) + return nil, err + } + } + return nil, nil +} + +func ConnectDB(ip string) error { + recheck := 1 + var riakErr error + for i := 0; i <= recheck; i++ { + // 设置缓冲为1防止没有接收者导致阻塞,即Detection已经超时返回 + errChan := make(chan error, 2) + // 这里存在资源泄露的可能,因为不能主动kill掉协程,所以如果这个协程依然阻塞在连接riak,但是 + // 这个函数已经超时返回了,那么这个协程因为被阻塞一直没被释放,直到Riak连接超时,如果阻塞的时间 + // 大于下次探测该实例的时间间隔,则创建协程频率大于释放协程频率,可能会导致oom。可以考虑在Riak + // 客户端连接设置超时时间来防止。 + go CheckRiak(ip, int(config.MonitorConfig.InteractTimeout.Seconds()), errChan) + select { + case riakErr = <-errChan: + if riakErr != nil { + slog.Error(fmt.Sprintf("The Node is out of service:%s.", riakErr.Error())) + } else { + return nil + } + case <-time.After(config.MonitorConfig.InteractTimeout): + slog.Error(fmt.Sprintf("Connect Riak timeout recheck:%d", recheck)) + riakErr = fmt.Errorf(`['riak@%s'] down`, ip) + } + } + return riakErr +} + +// CheckRiak check whether riak alive +func CheckRiak(ip string, timeout int, errChan chan error) { + foundNothing := "riak down, query heartbeat nothing return" + down := fmt.Errorf(`['riak@%s'] down`, ip) + query := fmt.Sprintf(`curl -s --connect-timeout %d -m %d http://%s:%d/types/default/buckets/test/keys/1000`, + timeout, timeout, ip, cst.RiakHttpPort) + insert := fmt.Sprintf( + `%s -X PUT -H 'Content-Type: application/json' -d '{name: "DBATeam", members: 31}'`, query) + _, err := utils.ExecShellCommand(false, insert) + if err != nil { + slog.Warn(fmt.Sprintf("Execute [ %s ] error: %s.", insert, err.Error())) + errChan <- down + return + } + stdout, err := utils.ExecShellCommand(false, query) + if err != nil { + slog.Warn(fmt.Sprintf(" Execute [ %s ] error: %s", query, err.Error())) + errChan <- down + return + } else if strings.Contains(stdout, "not found") { + slog.Warn(fmt.Sprintf("%s. Execute [ %s ]", foundNothing, query)) + errChan <- down + return + } + errChan <- nil +} diff --git a/dbm-services/riak/db-tools/riak-monitor/pkg/monitoriteminterface/monitor_item_interface.go b/dbm-services/riak/db-tools/riak-monitor/pkg/monitoriteminterface/monitor_item_interface.go new file mode 100644 index 0000000000..d96fe3d03b --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/pkg/monitoriteminterface/monitor_item_interface.go @@ -0,0 +1,11 @@ +// Package monitoriteminterface 监控项接口 +package monitoriteminterface + +// MonitorItemInterface TODO +type MonitorItemInterface interface { + Run() (msg string, err error) + Name() string +} + +// MonitorItemConstructorFuncType TODO +type MonitorItemConstructorFuncType func(cc *ConnectionCollect) MonitorItemInterface diff --git a/dbm-services/riak/db-tools/riak-monitor/pkg/utils/send_monitor_event.go b/dbm-services/riak/db-tools/riak-monitor/pkg/utils/send_monitor_event.go new file mode 100644 index 0000000000..bead1335da --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/pkg/utils/send_monitor_event.go @@ -0,0 +1,43 @@ +package utils + +import ( + "strconv" + + ma "dbm-services/mysql/db-tools/mysql-crond/api" + "dbm-services/riak/db-tools/riak-monitor/pkg/config" + + "golang.org/x/exp/slog" +) + +// SendMonitorEvent 向蓝鲸监控发送监控事件 +func SendMonitorEvent(name string, msg string) { + // 借助crond发送监控的信息 + crondManager := ma.NewManager(config.MonitorConfig.ApiUrl) + + // 事件的维度信息 + additionDimension := map[string]interface{}{ + "immute_domain": config.MonitorConfig.ImmuteDomain, + "machine_type": config.MonitorConfig.MachineType, + "bk_cloud_id": *config.MonitorConfig.BkCloudID, + "port": config.MonitorConfig.Port, + // 实例id + "bk_target_service_instance_id": strconv.FormatInt(config.MonitorConfig.BkInstanceId, 10), + } + + err := crondManager.SendEvent( + name, + msg, + additionDimension, + ) + if err != nil { + slog.Error( + "send event", err, + slog.String("name", name), slog.String("msg", msg), + ) + } + + slog.Info( + "send event", + slog.String("name", name), slog.String("msg", msg), + ) +} diff --git a/dbm-services/riak/db-tools/riak-monitor/pkg/utils/send_monitor_metrics.go b/dbm-services/riak/db-tools/riak-monitor/pkg/utils/send_monitor_metrics.go new file mode 100644 index 0000000000..190e88d97f --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/pkg/utils/send_monitor_metrics.go @@ -0,0 +1,46 @@ +package utils + +import ( + "strconv" + + ma "dbm-services/mysql/db-tools/mysql-crond/api" + "dbm-services/riak/db-tools/riak-monitor/pkg/config" + + "golang.org/x/exp/maps" + "golang.org/x/exp/slog" +) + +// SendMonitorMetrics 向蓝鲸监控发送监控指标,用于发送心跳 +func SendMonitorMetrics(name string, value int64, customDimension map[string]interface{}) { + // 借助crond发送监控的信息 + crondManager := ma.NewManager(config.MonitorConfig.ApiUrl) + // 指标的维度信息 + additionDimension := map[string]interface{}{ + "immute_domain": config.MonitorConfig.ImmuteDomain, + "machine_type": config.MonitorConfig.MachineType, + "bk_cloud_id": strconv.Itoa(*config.MonitorConfig.BkCloudID), + "port": strconv.Itoa(config.MonitorConfig.Port), + "bk_target_service_instance_id": strconv.FormatInt(config.MonitorConfig.BkInstanceId, 10), + } + + if customDimension != nil { + maps.Copy(additionDimension, customDimension) + } + + err := crondManager.SendMetrics( + name, + value, + additionDimension, + ) + if err != nil { + slog.Error( + "send metrics", err, + slog.String("name", name), slog.Int64("value", value), + ) + } + + slog.Info( + "send metrics", + slog.String("name", name), slog.Int64("msg", value), + ) +} diff --git a/dbm-services/riak/db-tools/riak-monitor/pkg/utils/utils.go b/dbm-services/riak/db-tools/riak-monitor/pkg/utils/utils.go new file mode 100644 index 0000000000..f76e345e74 --- /dev/null +++ b/dbm-services/riak/db-tools/riak-monitor/pkg/utils/utils.go @@ -0,0 +1,38 @@ +// Package utils TODO +package utils + +import ( + "bytes" + "fmt" + "os/exec" + + "github.com/pkg/errors" +) + +// ExecShellCommand 执行 shell 命令 +// 如果有 err, 返回 stderr; 如果没有 err 返回的是 stdout +// 后续尽量不要用这个方法,因为通过标准错误来判断有点不靠谱 +func ExecShellCommand(isSudo bool, param string) (stdoutStr string, err error) { + if isSudo { + param = "sudo " + param + } + cmd := exec.Command("bash", "-c", param) + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + err = cmd.Run() + if err != nil { + if len(stderr.String()) > 0 { + return stderr.String(), errors.WithMessage(err, stderr.String()) + } else { + return stdout.String(), errors.WithMessage(err, stderr.String()) + } + } + + if len(stderr.String()) > 0 { + err = fmt.Errorf("execute shell command(%s) error:%s", param, stderr.String()) + return stderr.String(), err + } + + return stdout.String(), nil +} diff --git a/dbm-ui/.gitignore b/dbm-ui/.gitignore index 1c7362a7c5..3756a19d9f 100644 --- a/dbm-ui/.gitignore +++ b/dbm-ui/.gitignore @@ -130,6 +130,8 @@ test.py # 忽略ssl backend/components/conf backend/dbm_init/tmp/ +backend/tmp* scripts/ssls/ .codecc -.vscode \ No newline at end of file +.vscode +.build.yml \ No newline at end of file diff --git a/dbm-ui/Dockerfile b/dbm-ui/Dockerfile index 4120a6d870..3cfd11776b 100644 --- a/dbm-ui/Dockerfile +++ b/dbm-ui/Dockerfile @@ -10,7 +10,6 @@ RUN yarn install ENV NODE_OPTIONS="--max_old_space_size=8192" RUN yarn build - FROM python:3.6.12-slim-buster AS base ENV LC_ALL=C.UTF-8 \ @@ -77,9 +76,6 @@ COPY --from=static-builder /frontend/dist /app/static/ ENV APP_ID=bk-dbm ENV APP_TOKEN=xxxx -# 创建 celery 的migrations -RUN python manage.py makemigrations django_celery_beat - # 收集静态文件 RUN python manage.py collectstatic --settings=config.prod --noinput diff --git a/dbm-ui/backend/bk_dataview/grafana/provisioning.py b/dbm-ui/backend/bk_dataview/grafana/provisioning.py index 06d397430f..51097f8e4c 100644 --- a/dbm-ui/backend/bk_dataview/grafana/provisioning.py +++ b/dbm-ui/backend/bk_dataview/grafana/provisioning.py @@ -17,8 +17,9 @@ import yaml -from ...configuration.constants import BKM_DBM_TOKEN -from ...configuration.models import SystemSettings +from backend.configuration.constants import SystemSettingsEnum +from backend.configuration.models import SystemSettings + from .settings import grafana_settings from .utils import os_env @@ -82,7 +83,7 @@ def read_conf(self, name, suffix): def datasources(self, request, org_name: str, org_id: int) -> List[Datasource]: """不注入数据源""" # 从db中获取监控token,并补充到环境变量 - bkm_dbm_token = SystemSettings.get_setting_value(key=BKM_DBM_TOKEN) + bkm_dbm_token = SystemSettings.get_setting_value(key=SystemSettingsEnum.BKM_DBM_TOKEN.value) with os_env(ORG_NAME=org_name, ORG_ID=org_id, BKM_DBM_TOKEN=bkm_dbm_token): for suffix in self.file_suffix: for conf in self.read_conf("datasources", suffix): diff --git a/dbm-ui/backend/bk_dataview/grafana/views.py b/dbm-ui/backend/bk_dataview/grafana/views.py index 0c722442d2..7e71913ba1 100644 --- a/dbm-ui/backend/bk_dataview/grafana/views.py +++ b/dbm-ui/backend/bk_dataview/grafana/views.py @@ -19,10 +19,10 @@ from django.views.decorators.csrf import csrf_exempt from django.views.generic import View +from backend.configuration.constants import SystemSettingsEnum +from backend.configuration.models import SystemSettings from backend.db_monitor.models import Dashboard as MonitorDash -from ...configuration.constants import BKM_DBM_TOKEN -from ...configuration.models import SystemSettings from . import client from .provisioning import Dashboard, Datasource from .settings import grafana_settings @@ -121,7 +121,7 @@ def perform_provisioning(self, request): provisioning = provisioning_cls() # 仅当db中配置了监控的token,才进行数据源初始化 - if SystemSettings.objects.filter(key=BKM_DBM_TOKEN).exists(): + if SystemSettings.objects.filter(key=SystemSettingsEnum.BKM_DBM_TOKEN.value).exists(): # 注入数据源 ds_list = [] logger.info("create datasource monitor for grafana") diff --git a/dbm-ui/backend/bk_web/handlers.py b/dbm-ui/backend/bk_web/handlers.py index 1d3a11cf31..ba9e89ecc3 100644 --- a/dbm-ui/backend/bk_web/handlers.py +++ b/dbm-ui/backend/bk_web/handlers.py @@ -22,30 +22,6 @@ logger = logging.getLogger("root") -def format_validation_message(detail): - """格式化drf校验错误信息""" - - if isinstance(detail, list): - message = "; ".join(["{}: {}".format(k, v) for k, v in enumerate(detail)]) - elif isinstance(detail, dict): - messages = [] - for k, v in detail.items(): - if isinstance(v, list): - try: - messages.append("{}: {}".format(k, ",".join(v)).replace("non_field_errors:", "")) - except TypeError: - messages.append("{}: {}".format(k, _("部分列表元素的参数不合法,请检查"))) - elif isinstance(v, dict): - messages.append(format_validation_message(v)) - else: - messages.append("{}: {}".format(k, v)) - message = ";".join(messages) - else: - message = detail - - return message - - def drf_exception_handler(exc, context): """ 自定义错误处理方式 @@ -74,7 +50,8 @@ def drf_exception_handler(exc, context): # 特殊处理 rest_framework ValidationError if isinstance(exc, exceptions.ValidationError): - return JsonResponse(_error(100, format_validation_message(exc.detail))) + validation_msg = json.dumps(exc.detail, ensure_ascii=False) + return JsonResponse(_error(100, validation_msg)) if isinstance(exc, (exceptions.NotAuthenticated, exceptions.AuthenticationFailed)): return JsonResponse( diff --git a/dbm-ui/backend/bk_web/middleware.py b/dbm-ui/backend/bk_web/middleware.py index f9ad6094ef..2d43bdc091 100644 --- a/dbm-ui/backend/bk_web/middleware.py +++ b/dbm-ui/backend/bk_web/middleware.py @@ -8,12 +8,18 @@ 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. """ +import json +import logging + from blueapps.account.middlewares import LoginRequiredMiddleware from blueapps.account.models import User +from django.contrib.auth.models import AnonymousUser from backend import env from backend.utils.local import local +logger = logging.getLogger("root") + class DisableCSRFCheckMiddleware: """本地开发,去掉 django rest framework 强制的 csrf 检查""" @@ -52,16 +58,33 @@ class DBMLoginRequiredMiddleware(LoginRequiredMiddleware): """DBM自定义的登录中间件,主要用于增加额外的鉴权格式""" def process_view(self, request, view, args, kwargs): - # 如果传入了bk_app_code和bk_app_secret,则认为是内部调用,鉴权通过 - bk_app_code = request.COOKIES.get("bk_app_code") - bk_app_secret = request.COOKIES.get("bk_app_secret") - if bk_app_code == env.APP_CODE and bk_app_secret == env.SECRET_KEY: - # 为request授权一个超级用户,并标记为内部调用 + """ + 对于这里的登录用户,分为以下情况: + 1. 如果是已认证用户,则直接返回 + 2. 如果用的dbm的APP CODE和APP TOKEN,则认为是服务内调用,授予超级用户 + 3. 如果是apigw认证通过,则授予请求头中X-Bkapi-Apigw的用户 + """ + + def authorize_admin_user(): request.user = User(username="admin", is_superuser=True) request.internal_call = True - # 超级用户跳过csrf认证 setattr(request, "_dont_enforce_csrf_checks", True) + def authorize_valid_user(): + username = request.jwt.payload.get("user", {}).get("username", None) + request.user = User(username=username) if username else AnonymousUser() + setattr(request, "_dont_enforce_csrf_checks", True) + + bk_app_code = request.COOKIES.get("bk_app_code") + bk_app_secret = request.COOKIES.get("bk_app_secret") + + if request.user and request.user.is_authenticated: + return super().process_view(request, view, args, kwargs) + elif bk_app_code == env.APP_CODE and bk_app_secret == env.SECRET_KEY: + authorize_admin_user() return None + elif request.is_bk_jwt(): + authorize_valid_user() + # TODO: 考虑特殊平台开发admin账号,比如通过X-Bkapi-JWT查看app_code是否为特殊加白(eg: 作业平台,bcs等) return super().process_view(request, view, args, kwargs) diff --git a/dbm-ui/backend/bk_web/viewsets.py b/dbm-ui/backend/bk_web/viewsets.py index dc5dda1405..b88b7dc6b8 100644 --- a/dbm-ui/backend/bk_web/viewsets.py +++ b/dbm-ui/backend/bk_web/viewsets.py @@ -13,7 +13,7 @@ from blueapps.account.decorators import login_exempt from django.utils.decorators import classonlymethod -from rest_framework import viewsets +from rest_framework import serializers, viewsets from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet @@ -117,6 +117,8 @@ def as_view(cls, actions=None, **initkwargs): class SystemViewSet(GenericMixin, viewsets.ViewSet, viewsets.GenericViewSet): """SaaS app 使用的 API ViewSet""" + serializer_class = serializers.Serializer + class AuditedModelViewSet(GenericMixin, ModelViewSet): """记录数据插入信息的ModelViewSet类""" diff --git a/dbm-ui/backend/components/base.py b/dbm-ui/backend/components/base.py index f3dfbcb03b..8a2c989890 100644 --- a/dbm-ui/backend/components/base.py +++ b/dbm-ui/backend/components/base.py @@ -88,6 +88,7 @@ def __init__( module: str, ssl: bool = False, description: str = "", + freeze_params: bool = False, default_return_value: Any = None, before_request: Callable = None, after_request: Callable = None, @@ -106,6 +107,7 @@ def __init__( @param {string} module 对应的模块,用于后续查询标记 @param {bool} ssl 是否采用https链接 @param {string} description 中文描述 + @param {bool} freeze_params 是否冻结参数(参数不允许修改) @param {object} default_return_value 默认返回值,在请求失败情形下将返回该值 @param {function} before_request 请求前的回调 @param {function} after_request 请求后的回调 @@ -123,6 +125,7 @@ def __init__( self.method = method self.ssl = ssl self.default_return_value = default_return_value + self.freeze_params = freeze_params self.before_request = before_request self.after_request = after_request @@ -196,7 +199,7 @@ def __call__( except (ApiResultError, ApiRequestError, Exception) as error: # pylint: disable=broad-except # 捕获ApiResultError, ApiRequestError和其他未知异常 error_message = getattr(error, "error_message", None) or getattr( - error, "message", _("{}-接口调用异常").format(self.module) + error, "message", _("{}-接口调用异常: {}").format(self.module, str(error)) ) logger.exception(f"{error_message}, url => {self.url}, params => {params}, headers => {headers}") @@ -222,15 +225,16 @@ def get_error_message(self, error_message, request_id=None): return message def _send_request(self, params, headers, use_admin=False): - # 请求前的参数清洗处理 - if self.before_request is not None: - params = self.before_request(params) + # 请求前的参数清洗处理 如果参数冻结了,则无需修改 + if not self.freeze_params: + if self.before_request is not None: + params = self.before_request(params) - params = add_esb_info_before_request(params) - # 使用管理员账户请求时,设置用户名为管理员用户名,移除bk_token等认证信息 - if use_admin: - params["bk_username"] = env.DEFAULT_USERNAME - params = remove_auth_args(params) + params = add_esb_info_before_request(params) + # 使用管理员账户请求时,设置用户名为管理员用户名,移除bk_token等认证信息 + if use_admin: + params["bk_username"] = env.DEFAULT_USERNAME + params = remove_auth_args(params) # 是否有默认返回,调试阶段可用 if self.default_return_value is not None: @@ -297,6 +301,9 @@ def _send_request(self, params, headers, use_admin=False): finally: # 最后记录时间 end_time = time.time() + # 如果param是一个非dict,则手动变成dict来记录流水日志 + if not isinstance(params, dict): + params = {"params_data": params} # 判断是否需要记录,及其最大返回值 bk_username = params.get("bk_username", "") @@ -373,7 +380,7 @@ def _set_cache(self, cache_key, data): """ cache.set(cache_key, data, self.cache_time) - def _send(self, params: Dict, headers: Dict, use_admin: bool = False): + def _send(self, params: Any, headers: Dict, use_admin: bool = False): """ 发送和接受返回请求的包装 @param params: 请求的参数,预期是一个字典 @@ -386,16 +393,22 @@ def _send(self, params: Dict, headers: Dict, use_admin: bool = False): session.headers.update( { "X-Bkapi-Request-Id": self.request_id, - "X-Bkapi-Authorization": json.dumps( - { - "bk_app_code": params.pop("bk_app_code"), - "bk_app_secret": params.pop("bk_app_secret"), - "bk_username": params.pop("bk_username", ""), - } - ), "blueking-language": translation.get_language(), } ) + # 增加鉴权信息 + if isinstance(params, dict): + session.headers.update( + { + "X-Bkapi-Authorization": json.dumps( + { + "bk_app_code": params.pop("bk_app_code"), + "bk_app_secret": params.pop("bk_app_secret"), + "bk_username": params.pop("bk_username", ""), + } + ), + } + ) # 设置cookies try: @@ -451,7 +464,7 @@ def _fetch_client_crt(self): client_crt, client_key = f"{CLIENT_CRT_PATH}/{SSLEnum.CLIENT_CRT}", f"{CLIENT_CRT_PATH}/{SSLEnum.CLIENT_KEY}" # 如何证书已存在,则直接返回即可 ssl = SystemSettings.get_setting_value(key=SSL_KEY, default={}) - if ssl and ssl.get("local"): + if ssl and ssl.get("local") and os.path.exists(CLIENT_CRT_PATH): return client_crt, client_key # 本地写入crt和key文件,防止每次都需要write IO @@ -470,7 +483,9 @@ def _fetch_client_crt(self): def build_actual_url(self, params): # url中包含变量的场景,用参数渲染 - return self.url.format(**params) + if isinstance(params, dict): + return self.url.format(**params) + return self.url def safe_response(self, response_result): if "result" not in response_result: @@ -487,6 +502,10 @@ def safe_response(self, response_result): def _split_file_data(data): file_data = {} non_file_data = {} + + if not isinstance(data, dict): + return file_data, non_file_data + for key, value in list(data.items()): if hasattr(value, "read"): # 一般认为含有read属性的为文件类型 diff --git a/dbm-ui/backend/components/bkmonitorv3/client.py b/dbm-ui/backend/components/bkmonitorv3/client.py index 701db672ec..730acb7117 100644 --- a/dbm-ui/backend/components/bkmonitorv3/client.py +++ b/dbm-ui/backend/components/bkmonitorv3/client.py @@ -86,6 +86,13 @@ def __init__(self): module=self.MODULE, description=_("保存告警策略"), ) + self.delete_alarm_strategy_v3 = DataAPI( + method="POST", + base=BKMONITORV3_APIGW_DOMAIN, + url="delete_alarm_strategy_v3/", + module=self.MODULE, + description=_("删除告警策略"), + ) self.search_alarm_strategy_v3 = DataAPI( method="POST", base=BKMONITORV3_APIGW_DOMAIN, @@ -121,6 +128,34 @@ def __init__(self): module=self.MODULE, description=_("查询采集策略详情"), ) + self.search_user_groups = DataAPI( + method="POST", + base=BKMONITORV3_APIGW_DOMAIN, + url="search_user_groups/", + module=self.MODULE, + description=_("查询用户组列表"), + ) + self.search_user_group_detail = DataAPI( + method="POST", + base=BKMONITORV3_APIGW_DOMAIN, + url="search_user_group_detail/", + module=self.MODULE, + description=_("查询用户组详情"), + ) + self.delete_user_groups = DataAPI( + method="POST", + base=BKMONITORV3_APIGW_DOMAIN, + url="delete_user_groups/", + module=self.MODULE, + description=_("删除用户组"), + ) + self.save_user_group = DataAPI( + method="POST", + base=BKMONITORV3_APIGW_DOMAIN, + url="save_user_group/", + module=self.MODULE, + description=_("保存用户组"), + ) BKMonitorV3Api = _BKMonitorV3Api() diff --git a/dbm-ui/backend/components/cc/client.py b/dbm-ui/backend/components/cc/client.py index 4bebd183d5..d0c038fe8f 100644 --- a/dbm-ui/backend/components/cc/client.py +++ b/dbm-ui/backend/components/cc/client.py @@ -261,5 +261,13 @@ def __init__(self): description=_("查询主机业务关系信息"), ) + self.search_object_attribute = DataAPI( + method="POST", + base=CC_APIGW_DOMAIN, + url="search_object_attribute/", + module=self.MODULE, + description=_("查询对象属性"), + ) + CCApi = _CCApi() diff --git a/dbm-ui/backend/db_services/redis_dts/__init__.py b/dbm-ui/backend/components/celery_service/__init__.py similarity index 100% rename from dbm-ui/backend/db_services/redis_dts/__init__.py rename to dbm-ui/backend/components/celery_service/__init__.py diff --git a/dbm-ui/backend/components/celery_service/client.py b/dbm-ui/backend/components/celery_service/client.py new file mode 100644 index 0000000000..b533f9b656 --- /dev/null +++ b/dbm-ui/backend/components/celery_service/client.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 ..base import DataAPI +from ..domains import CELERY_SERVICE_APIGW_DOMAIN + + +class _CeleryServiceApi(object): + MODULE = _("周期任务服务") + + def __init__(self): + self.list = DataAPI( + method="GET", + base=CELERY_SERVICE_APIGW_DOMAIN, + freeze_params=True, + url="/list", + module=self.MODULE, + description=_("获取API列表"), + ) + self.async_list = DataAPI( + method="GET", + base=CELERY_SERVICE_APIGW_DOMAIN, + url="/discovery", + module=self.MODULE, + description=_("获取周期任务的注册列表"), + ) + self.async_query = DataAPI( + method="POST", + base=CELERY_SERVICE_APIGW_DOMAIN, + url="/async/query", + module=self.MODULE, + description=_("查询异步会话"), + ) + self.async_kill = DataAPI( + method="POST", + base=CELERY_SERVICE_APIGW_DOMAIN, + url="/async/kill", + module=self.MODULE, + description=_("结束异步会话"), + ) + + +CeleryServiceApi = _CeleryServiceApi() diff --git a/dbm-ui/backend/components/cmsi/client.py b/dbm-ui/backend/components/cmsi/client.py index 04fa55a32e..416c8be57a 100644 --- a/dbm-ui/backend/components/cmsi/client.py +++ b/dbm-ui/backend/components/cmsi/client.py @@ -26,6 +26,13 @@ def __init__(self): module=self.MODULE, description=_("通用消息发送"), ) + self.get_msg_type = DataAPI( + method="GET", + base=CMSI_APIGW_DOMAIN, + url="get_msg_type/", + module=self.MODULE, + description=_("查询通知类型"), + ) CmsiApi = _CmsiApi() diff --git a/dbm-ui/backend/components/db_name_service/client.py b/dbm-ui/backend/components/db_name_service/client.py index 2659efcf9e..7b64a944e4 100644 --- a/dbm-ui/backend/components/db_name_service/client.py +++ b/dbm-ui/backend/components/db_name_service/client.py @@ -22,77 +22,77 @@ def __init__(self): self.clb_create_lb_and_register_target = DataAPI( method="POST", base=NAMESERVICE_APIGW_DOMAIN, - url="/clb/create_lb_and_register_target", + url="/api/nameservice/clb/create_lb_and_register_target", module=self.MODULE, description=_("创建clb并绑定后端主机"), ) self.clb_deregister_part_target = DataAPI( method="POST", base=NAMESERVICE_APIGW_DOMAIN, - url="/clb/deregister_part_target", + url="/api/nameservice/clb/deregister_part_target", module=self.MODULE, description=_("clb解绑部分后端主机"), ) self.clb_register_part_target = DataAPI( method="POST", base=NAMESERVICE_APIGW_DOMAIN, - url="/clb/register_part_target", + url="/api/nameservice/clb/register_part_target", module=self.MODULE, description=_("clb新增绑定部分后端主机"), ) self.clb_get_target_private_ips = DataAPI( method="POST", base=NAMESERVICE_APIGW_DOMAIN, - url="/clb/get_target_private_ips", + url="/api/nameservice/clb/get_target_private_ips", module=self.MODULE, description=_("获取已绑定clb的后端主机私网IP"), ) self.clb_check_clb_register_target_by_ip = DataAPI( method="POST", base=NAMESERVICE_APIGW_DOMAIN, - url="/clb/check_clb_register_target_by_ip", + url="/api/nameservice/clb/check_clb_register_target_by_ip", module=self.MODULE, description=_("通过IP查询该IP是否已经被clb绑定了"), ) self.clb_deregister_target_and_del_lb = DataAPI( method="POST", base=NAMESERVICE_APIGW_DOMAIN, - url="/clb/deregister_target_and_del_lb", + url="/api/nameservice/clb/deregister_target_and_del_lb", module=self.MODULE, description=_("解绑后端主机并删除clb"), ) self.polaris_create_service_alias_and_bind_targets = DataAPI( method="POST", base=NAMESERVICE_APIGW_DOMAIN, - url="/polaris/create_service_alias_and_bind_targets", + url="/api/nameservice/polaris/create_service_alias_and_bind_targets", module=self.MODULE, description=_("创建北极星服务和别名并绑定后端主机"), ) self.polaris_unbind_part_targets = DataAPI( method="POST", base=NAMESERVICE_APIGW_DOMAIN, - url="/polaris/unbind_part_targets", + url="/api/nameservice/polaris/unbind_part_targets", module=self.MODULE, description=_("北极星解绑部分后端主机"), ) self.polaris_bind_part_targets = DataAPI( method="POST", base=NAMESERVICE_APIGW_DOMAIN, - url="/polaris/bind_part_targets", + url="/api/nameservice/polaris/bind_part_targets", module=self.MODULE, description=_("北极星新增绑定部分后端主机"), ) self.polaris_describe_targets = DataAPI( method="POST", base=NAMESERVICE_APIGW_DOMAIN, - url="/polaris/describe_targets", + url="/api/nameservice/polaris/describe_targets", module=self.MODULE, description=_("获取北极星已绑定的后端主机信息"), ) self.polaris_unbind_targets_and_delete_alias_service = DataAPI( method="POST", base=NAMESERVICE_APIGW_DOMAIN, - url="/polaris/unbind_targets_and_delete_alias_service", + url="/api/nameservice/polaris/unbind_targets_and_delete_alias_service", module=self.MODULE, description=_("解绑后端主机并删除别名和北极星服务"), ) diff --git a/dbm-ui/backend/components/db_remote_service/client.py b/dbm-ui/backend/components/db_remote_service/client.py index c23f1ba792..345438919a 100644 --- a/dbm-ui/backend/components/db_remote_service/client.py +++ b/dbm-ui/backend/components/db_remote_service/client.py @@ -51,7 +51,7 @@ def __init__(self): base=self.BASE_DOMAIN, url="redis/rpc", module=self.MODULE, - ssl=True, + ssl=ssl_flag, description=_("redis 远程执行"), ) @@ -60,7 +60,7 @@ def __init__(self): base=self.BASE_DOMAIN, url="twemproxy/rpc", module=self.MODULE, - ssl=True, + ssl=ssl_flag, description=_("twemproxy 远程执行"), ) diff --git a/dbm-ui/backend/components/dbresource/client.py b/dbm-ui/backend/components/dbresource/client.py index 9e1c3e798f..f8937d49c4 100644 --- a/dbm-ui/backend/components/dbresource/client.py +++ b/dbm-ui/backend/components/dbresource/client.py @@ -33,6 +33,13 @@ def __init__(self): module=self.MODULE, description=_("资源池资源列表"), ) + self.resource_list_all = DataAPI( + method="POST", + base=DBRESOURCE_APIGW_DOMAIN, + url="resource/list/all", + module=self.MODULE, + description=_("资源池全部资源列表"), + ) self.resource_apply = DataAPI( method="POST", base=DBRESOURCE_APIGW_DOMAIN, @@ -89,6 +96,13 @@ def __init__(self): module=self.MODULE, description=_("资源更新"), ) + self.resource_batch_update = DataAPI( + method="POST", + base=DBRESOURCE_APIGW_DOMAIN, + url="resource/batch/update", + module=self.MODULE, + description=_("资源批量更新"), + ) self.get_device_class = DataAPI( method="POST", base=DBRESOURCE_APIGW_DOMAIN, @@ -103,6 +117,13 @@ def __init__(self): module=self.MODULE, description=_("获取操作记录"), ) + self.apply_count = DataAPI( + method="POST", + base=DBRESOURCE_APIGW_DOMAIN, + url="/resource/spec/sum", + module=self.MODULE, + description=_("预申请获取资源数量"), + ) DBResourceApi = _DBResourceApi() diff --git a/dbm-ui/backend/components/domains.py b/dbm-ui/backend/components/domains.py index edda897583..96530f03ef 100644 --- a/dbm-ui/backend/components/domains.py +++ b/dbm-ui/backend/components/domains.py @@ -38,8 +38,9 @@ BKMONITORV3_APIGW_DOMAIN = env.BKMONITORV3_APIGW_DOMAIN or APIGW_DOMAIN_PREFIX_FORMAT.format( "monitor_v3" if settings.RUN_VER == "open" else "bkmonitorv3" ) -MYSQL_SIMULATION_DONAIN = env.MYSQL_SIMULATION_DONAIN or APIGW_DOMAIN_PREFIX_FORMAT.format("db_simulation") +MYSQL_SIMULATION_DOMAIN = env.MYSQL_SIMULATION_DOMAIN or APIGW_DOMAIN_PREFIX_FORMAT.format("db_simulation") NAMESERVICE_APIGW_DOMAIN = env.NAMESERVICE_APIGW_DOMAIN or APIGW_DOMAIN_PREFIX_FORMAT.format("nameservice") HADB_APIGW_DOMAIN = env.HADB_APIGW_DOMAIN or APIGW_DOMAIN_PREFIX_FORMAT.format("hadb") DBRESOURCE_APIGW_DOMAIN = env.DBRESOURCE_APIGW_DOMAIN or APIGW_DOMAIN_PREFIX_FORMAT.format("dbresource") BACKUP_APIGW_DOMAIN = env.BACKUP_APIGW_DOMAIN or APIGW_DOMAIN_PREFIX_FORMAT.format("backup") +CELERY_SERVICE_APIGW_DOMAIN = env.CELERY_SERVICE_APIGW_DOMAIN or APIGW_DOMAIN_PREFIX_FORMAT.format("celery_service") diff --git a/dbm-ui/backend/components/gse/client.py b/dbm-ui/backend/components/gse/client.py index b867f210b7..25ccb8158e 100644 --- a/dbm-ui/backend/components/gse/client.py +++ b/dbm-ui/backend/components/gse/client.py @@ -24,7 +24,15 @@ def __init__(self): base=GSE_APIGW_DOMAIN, url="get_agent_status/", module=self.MODULE, - description=_("Agent在线状态查询"), + description=_("Agent在线状态查询 - 1.0"), + ) + + self.list_agent_state = DataAPI( + method="POST", + base=GSE_APIGW_DOMAIN, + url="list_agent_state/", + module=self.MODULE, + description=_("查询Agent状态列表信息 - 2.0"), ) diff --git a/dbm-ui/backend/components/mysql_backup/client.py b/dbm-ui/backend/components/mysql_backup/client.py index a6fa6c8994..f518f58e03 100644 --- a/dbm-ui/backend/components/mysql_backup/client.py +++ b/dbm-ui/backend/components/mysql_backup/client.py @@ -11,7 +11,7 @@ from django.utils.translation import ugettext_lazy as _ from ..base import DataAPI -from ..domains import BACKUP_APIGW_DOMAIN, PARTITION_APIGW_DOMAIN +from ..domains import BACKUP_APIGW_DOMAIN class _BackupApi(object): @@ -28,7 +28,7 @@ def __init__(self): self.download = DataAPI( method="POST", - base=PARTITION_APIGW_DOMAIN, + base=BACKUP_APIGW_DOMAIN, url="backupapi/recover", module=self.MODULE, description=_("备份文件下载"), @@ -36,11 +36,19 @@ def __init__(self): self.download_result = DataAPI( method="GET", - base=PARTITION_APIGW_DOMAIN, + base=BACKUP_APIGW_DOMAIN, url="backupapi/get_recover_result", module=self.MODULE, description=_("查询单据状态"), ) + self.download_backup_client = DataAPI( + method="POST", + base=BACKUP_APIGW_DOMAIN, + url="backupapi/client/install", + module=self.MODULE, + description=_("backup_client下载,同步任务"), + default_timeout=300, + ) MysqlBackupApi = _BackupApi() diff --git a/dbm-ui/backend/components/mysql_partition/client.py b/dbm-ui/backend/components/mysql_partition/client.py index 06463c5cd3..da3fa29683 100644 --- a/dbm-ui/backend/components/mysql_partition/client.py +++ b/dbm-ui/backend/components/mysql_partition/client.py @@ -35,7 +35,7 @@ def __init__(self): ) self.del_conf = DataAPI( - method="DELETE", + method="POST", base=PARTITION_APIGW_DOMAIN, url="partition/del_conf", module=self.MODULE, @@ -82,5 +82,13 @@ def __init__(self): description=_("查询分区日志"), ) + self.create_log = DataAPI( + method="POST", + base=PARTITION_APIGW_DOMAIN, + url="partition/create_log", + module=self.MODULE, + description=_("创建分区操作日志"), + ) + DBPartitionApi = _PartitionApi() diff --git a/dbm-ui/backend/components/sql_import/client.py b/dbm-ui/backend/components/sql_import/client.py index db5db5a14b..b912ebcde6 100644 --- a/dbm-ui/backend/components/sql_import/client.py +++ b/dbm-ui/backend/components/sql_import/client.py @@ -12,7 +12,7 @@ from django.utils.translation import ugettext_lazy as _ from ..base import DataAPI -from ..domains import MYSQL_SIMULATION_DONAIN +from ..domains import MYSQL_SIMULATION_DOMAIN class _SQLImportApi(object): @@ -22,7 +22,7 @@ class _SQLImportApi(object): def __init__(self): self.grammar_check = DataAPI( method="POST", - base=MYSQL_SIMULATION_DONAIN, + base=MYSQL_SIMULATION_DOMAIN, url="/syntax/check/file", module=self.MODULE, description=_("sql语法检查"), @@ -36,7 +36,7 @@ class _SQLSimulation(object): def __init__(self): self.mysql_simulation = DataAPI( method="POST", - base=MYSQL_SIMULATION_DONAIN, + base=MYSQL_SIMULATION_DOMAIN, url="/mysql/simulation", module=self.MODULE, description=_("容器化SQL模拟执行"), @@ -44,7 +44,7 @@ def __init__(self): self.query_simulation_task = DataAPI( method="POST", - base=MYSQL_SIMULATION_DONAIN, + base=MYSQL_SIMULATION_DOMAIN, url="/mysql/task", module=self.MODULE, description=_("查询模拟执行任务状态也"), @@ -52,7 +52,7 @@ def __init__(self): self.spider_simulation = DataAPI( method="POST", - base=MYSQL_SIMULATION_DONAIN, + base=MYSQL_SIMULATION_DOMAIN, url="/spider/simulation", module=self.MODULE, description=_("容器化SQL模拟执行"), diff --git a/dbm-ui/backend/configuration/admin.py b/dbm-ui/backend/configuration/admin.py index cb303c5890..58f61348b2 100644 --- a/dbm-ui/backend/configuration/admin.py +++ b/dbm-ui/backend/configuration/admin.py @@ -40,3 +40,10 @@ class ProfileAdmin(admin.ModelAdmin): class PasswordPolicyAdmin(admin.ModelAdmin): list_display = ("account_type", "policy") search_fields = ("account_type",) + + +@admin.register(models.FunctionController) +class FunctionControllerAdmin(admin.ModelAdmin): + list_display = ("name", "is_enabled", "is_frozen", "parent_name") + list_filter = ("is_enabled", "is_frozen") + search_fields = ("name",) diff --git a/dbm-ui/backend/configuration/apps.py b/dbm-ui/backend/configuration/apps.py index 4500e5e597..c652e2ae49 100644 --- a/dbm-ui/backend/configuration/apps.py +++ b/dbm-ui/backend/configuration/apps.py @@ -21,11 +21,13 @@ def register_system_settings(sender, **kwargs): """初始化配置""" + from .models.function_controller import FunctionController from .models.system import SystemSettings try: SystemSettings.init_default_settings() SystemSettings.register_system_settings() + FunctionController.init_function_controller() except Exception as e: # pylint: disable=broad-except logger.error(_("初始化配置异常,错误信息:{}").format(e)) diff --git a/dbm-ui/backend/configuration/constants.py b/dbm-ui/backend/configuration/constants.py index 68f24ba55e..e458f907b1 100644 --- a/dbm-ui/backend/configuration/constants.py +++ b/dbm-ui/backend/configuration/constants.py @@ -19,8 +19,9 @@ class DBType(str, StructuredEnum): MySQL = EnumField("mysql", _("MySQL")) - Tendb = EnumField("tendbcluster", _("TendbCluster")) + TenDBCluster = EnumField("tendbcluster", _("TendbCluster")) Redis = EnumField("redis", _("Redis")) + MongoDB = EnumField("mongodb", _("MongoDB")) Kafka = EnumField("kafka", _("Kafka")) Hdfs = EnumField("hdfs", _("HDFS")) Es = EnumField("es", _("ElasticSearch")) @@ -32,6 +33,20 @@ class DBType(str, StructuredEnum): Cloud = EnumField("cloud", _("Cloud")) +class SystemSettingsEnum(str, StructuredEnum): + """配置的枚举项,建议将系统配置都录入到这里方便统一管理""" + + BK_ITSM_SERVICE_ID = EnumField("BK_ITSM_SERVICE_ID", _("DBM的流程服务ID")) + MANAGE_TOPO = EnumField("MANAGE_TOPO", _("DBM系统的管理集群拓扑")) + DBM_SSL = EnumField("DBM_SSL", _("DBM_SSL")) + BKM_DBM_TOKEN = EnumField("BKM_DBM_TOKEN", _("监控数据源token")) + BKM_DBM_REPORT = EnumField("BKM_DBM_REPORT", _("mysql/redis-监控自定义上报: dataid/token")) + FREE_BK_MODULE_ID = EnumField("FREE_BK_MODULE_ID", _("业务空闲模块ID")) + # 主机默认统一转移到 DBM 业务下托管,若业务 ID 属于这个列表,则转移到对应的业务下 + INDEPENDENT_HOSTING_BIZS = EnumField("INDEPENDENT_HOSTING_BIZS", _("独立托管机器的业务列表")) + SPEC_OFFSET = EnumField("SPEC_OFFSET", _("默认的规格参数偏移量")) + + DEFAULT_DB_ADMINISTRATORS = ["admin"] # TODO 域名模板是否可配置,调整后会带来额外的管理成本? @@ -56,14 +71,6 @@ class DBType(str, StructuredEnum): "min_length": 8, } -DBM_SSL = "DBM_SSL" -# 监控数据源token -BKM_DBM_TOKEN = "BKM_DBM_TOKEN" -# mysql/redis-监控自定义上报: dataid/token -BKM_DBM_REPORT = "BKM_DBM_REPORT" - -# 业务空闲模块ID -FREE_BK_MODULE_ID = "FREE_BK_MODULE_ID" # 监控数据自定义上报配置 DBM_REPORT_INITIAL_VALUE = { @@ -78,11 +85,16 @@ class DBType(str, StructuredEnum): }, } +# 默认的规格参数偏移量,磁盘为0,内存偏移1G +SPEC_OFFSET_VALUE = {"mem": 1024, "disk": 0} + DEFAULT_SETTINGS = [ # [key, 类型,初始值, 描述] - [BKM_DBM_TOKEN, "str", "", _("监控数据源token")], - [BKM_DBM_REPORT, "dict", DBM_REPORT_INITIAL_VALUE, _("监控数据源上报配置")], - [FREE_BK_MODULE_ID, "str", "0", _("业务空闲模块ID")], + [SystemSettingsEnum.BKM_DBM_TOKEN.value, "str", "", _("监控数据源token")], + [SystemSettingsEnum.BKM_DBM_REPORT.value, "dict", DBM_REPORT_INITIAL_VALUE, _("监控数据源上报配置")], + [SystemSettingsEnum.FREE_BK_MODULE_ID.value, "str", "0", _("业务空闲模块ID")], + [SystemSettingsEnum.INDEPENDENT_HOSTING_BIZS.value, "list", [], _("独立托管机器的业务列表")], + [SystemSettingsEnum.SPEC_OFFSET.value, "dict", SPEC_OFFSET_VALUE, _("默认的规格参数偏移量")], ] # 环境配置项 是否支持DNS解析 pulsar flow used diff --git a/dbm-ui/backend/configuration/migrations/0001_initial.py b/dbm-ui/backend/configuration/migrations/0001_initial.py index fa569f8b0e..6ea7a1f357 100644 --- a/dbm-ui/backend/configuration/migrations/0001_initial.py +++ b/dbm-ui/backend/configuration/migrations/0001_initial.py @@ -3,6 +3,7 @@ from django.db import migrations, models from backend.configuration.constants import DBType +from backend.db_services.mysql.permission.constants import AccountType class Migration(migrations.Migration): @@ -35,7 +36,7 @@ class Migration(migrations.Migration): ( "account_type", models.CharField( - choices=DBType.get_choices(), + choices=AccountType.get_choices(), max_length=32, primary_key=True, serialize=False, diff --git a/dbm-ui/backend/configuration/migrations/0002_auto_20230605_2027.py b/dbm-ui/backend/configuration/migrations/0002_auto_20230605_2027.py new file mode 100644 index 0000000000..29ddea31fe --- /dev/null +++ b/dbm-ui/backend/configuration/migrations/0002_auto_20230605_2027.py @@ -0,0 +1,26 @@ +# Generated by Django 3.2.19 on 2023-06-05 12:27 + +from django.db import migrations, models + +from backend.configuration.constants import DBType + + +class Migration(migrations.Migration): + + dependencies = [ + ("configuration", "0001_initial"), + ] + + operations = [ + migrations.AddField( + model_name="ipwhitelist", + name="db_type", + field=models.CharField( + choices=DBType.get_choices(), + default="mysql", + max_length=32, + verbose_name="数据库类型", + ), + preserve_default=False, + ) + ] diff --git a/dbm-ui/backend/configuration/migrations/0002_functioncontroller.py b/dbm-ui/backend/configuration/migrations/0002_functioncontroller.py new file mode 100644 index 0000000000..7509e9bca3 --- /dev/null +++ b/dbm-ui/backend/configuration/migrations/0002_functioncontroller.py @@ -0,0 +1,28 @@ +# Generated by Django 3.2.19 on 2023-06-30 09:04 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("configuration", "0001_initial"), + ] + + operations = [ + migrations.CreateModel( + name="FunctionController", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("name", models.CharField(max_length=255, verbose_name="功能名称")), + ("is_enabled", models.BooleanField(default=False, verbose_name="是否开启")), + ("is_frozen", models.BooleanField(default=False, help_text="人工冻结此开关,将不受更新影响", verbose_name="是否冻结")), + ("parent_name", models.CharField(blank=True, max_length=255, null=True, verbose_name="父功能名称")), + ], + options={ + "verbose_name": "功能控制器", + "verbose_name_plural": "功能控制器", + "unique_together": {("name", "parent_name")}, + }, + ), + ] diff --git a/dbm-ui/backend/configuration/migrations/0003_merge_0002_auto_20230605_2027_0002_functioncontroller.py b/dbm-ui/backend/configuration/migrations/0003_merge_0002_auto_20230605_2027_0002_functioncontroller.py new file mode 100644 index 0000000000..8efb3ee021 --- /dev/null +++ b/dbm-ui/backend/configuration/migrations/0003_merge_0002_auto_20230605_2027_0002_functioncontroller.py @@ -0,0 +1,13 @@ +# Generated by Django 3.2.19 on 2023-07-14 04:06 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("configuration", "0002_auto_20230605_2027"), + ("configuration", "0002_functioncontroller"), + ] + + operations = [] diff --git a/dbm-ui/backend/configuration/migrations/0004_remove_ipwhitelist_db_type.py b/dbm-ui/backend/configuration/migrations/0004_remove_ipwhitelist_db_type.py new file mode 100644 index 0000000000..7a004d6df6 --- /dev/null +++ b/dbm-ui/backend/configuration/migrations/0004_remove_ipwhitelist_db_type.py @@ -0,0 +1,17 @@ +# Generated by Django 3.2.19 on 2023-08-24 07:17 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("configuration", "0003_merge_0002_auto_20230605_2027_0002_functioncontroller"), + ] + + operations = [ + migrations.RemoveField( + model_name="ipwhitelist", + name="db_type", + ), + ] diff --git a/dbm-ui/backend/configuration/models/__init__.py b/dbm-ui/backend/configuration/models/__init__.py index 77f45b1a1a..68a0fe8466 100644 --- a/dbm-ui/backend/configuration/models/__init__.py +++ b/dbm-ui/backend/configuration/models/__init__.py @@ -9,6 +9,7 @@ specific language governing permissions and limitations under the License. """ from .dba import DBAdministrator +from .function_controller import FunctionController from .password_policy import PasswordPolicy from .profile import Profile from .system import SystemSettings diff --git a/dbm-ui/backend/configuration/models/dba.py b/dbm-ui/backend/configuration/models/dba.py index 389c04c821..d25fa90f9c 100644 --- a/dbm-ui/backend/configuration/models/dba.py +++ b/dbm-ui/backend/configuration/models/dba.py @@ -51,8 +51,22 @@ def list_biz_admins(cls, bk_biz_id: int) -> List[Dict[str, Union[str, List[str]] @classmethod def upsert_biz_admins(cls, bk_biz_id: int, db_admins: List[Dict[str, Union[str, List[str]]]]): + # 平台管理员 + db_type_platform_dba = {dba.db_type: dba.users for dba in cls.objects.filter(bk_biz_id=0)} + + # 业务管理员 + db_type_biz_dba = {dba.db_type: dba.users for dba in cls.objects.filter(bk_biz_id=bk_biz_id)} + + # 更新或创建业务管理员 for dba in db_admins: - cls.objects.update_or_create(bk_biz_id=bk_biz_id, db_type=dba["db_type"], defaults={"users": dba["users"]}) + db_type = dba["db_type"] + new_dba = dba["users"] + platform_dba = db_type_platform_dba.get(db_type, []) + biz_dba = db_type_biz_dba.get(db_type, []) + if set(new_dba) == set(platform_dba) and not biz_dba: + # 业务新设置的与平台人员一致,则无需新建 + continue + cls.objects.update_or_create(bk_biz_id=bk_biz_id, db_type=db_type, defaults={"users": new_dba}) @classmethod def get_biz_db_type_admins(cls, bk_biz_id: int, db_type: str) -> List[str]: diff --git a/dbm-ui/backend/configuration/models/function_controller.py b/dbm-ui/backend/configuration/models/function_controller.py new file mode 100644 index 0000000000..a038b1b74c --- /dev/null +++ b/dbm-ui/backend/configuration/models/function_controller.py @@ -0,0 +1,139 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 blue_krill.data_types.enum import EnumField, StructuredEnum +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +from backend.configuration.constants import DBType +from backend.db_meta.enums import ClusterType + + +class CustomFuncNameEnum(str, StructuredEnum): + BigData = EnumField("bigdata", _("大数据")) + ToolBox = EnumField("toolbox", _("工具箱")) + TenDBClusterToolBox = EnumField("tendbcluster_toolbox", _("TenDBCluster 工具箱")) + AddOnServicePlugin = EnumField("addons", _("插件服务")) + + +# 用于初始化功能开关,注意调整 key 后需跟前端对齐 +# 为降低复杂度,这里仅设计两层功能开关 +# 以 DBType、ClusterType、TicketType 为主,按需补充功能开关,需跟前端进行约定 +FUNCTION_CONTROLLER_INIT_MAP = { + DBType.MySQL.value: { + "is_enabled": True, + "children": { + CustomFuncNameEnum.ToolBox.value: {"is_enabled": True}, + ClusterType.TenDBSingle.value: {"is_enabled": True}, + ClusterType.TenDBHA.value: {"is_enabled": True}, + ClusterType.TenDBCluster.value: {"is_enabled": False}, + CustomFuncNameEnum.TenDBClusterToolBox.value: {"is_enabled": False}, + }, + }, + DBType.Redis.value: { + "is_enabled": True, + "children": { + ClusterType.TendisPredixyTendisplusCluster.value: {"is_enabled": True}, + ClusterType.TendisTwemproxyRedisInstance.value: {"is_enabled": True}, + ClusterType.TwemproxyTendisSSDInstance.value: {"is_enabled": True}, + CustomFuncNameEnum.ToolBox.value: {"is_enabled": False}, + }, + }, + CustomFuncNameEnum.BigData.value: { + "is_enabled": True, + "children": { + DBType.Es.value: {"is_enabled": True}, + DBType.Kafka.value: {"is_enabled": True}, + DBType.Hdfs.value: {"is_enabled": True}, + DBType.InfluxDB.value: {"is_enabled": True}, + DBType.Pulsar.value: {"is_enabled": True}, + }, + }, + CustomFuncNameEnum.AddOnServicePlugin.value: { + "is_enabled": False, + "children": { + "redis_nameservice": {"is_enabled": True}, + }, + }, +} + + +class FunctionController(models.Model): + """ + 功能开启控制器,主要为了解决开发中未完成的功能,不暴露给到用户使用 + TODO: 目前仅用作前端访问入口开关,后续需考虑用于限制后台功能和 API 返回的数据 + """ + + name = models.CharField(_("功能名称"), max_length=255) + is_enabled = models.BooleanField(_("是否开启"), default=False) + is_frozen = models.BooleanField(_("是否冻结"), help_text=_("人工冻结此开关,将不受更新影响"), default=False) + parent_name = models.CharField(_("父功能名称"), max_length=255, null=True, blank=True) + + class Meta: + verbose_name = _("功能控制器") + verbose_name_plural = _("功能控制器") + unique_together = ("name", "parent_name") + + @classmethod + def init_function_controller(cls): + """ + 功能开关初始化原则: + - 不存在的功能开关,进行创建 + - 已存在、非冻结的功能开关,进行更新 + - 已存在、已冻结的功能开关,忽略 + - 如果父开关是关闭的,那么子开关也必须是关闭的 + """ + exist_functions = { + (func.name, func.parent_name): {"is_enabled": func.is_enabled, "is_frozen": func.is_frozen, "id": func.id} + for func in FunctionController.objects.all() + } + to_be_created_functions = [] + to_be_updated_functions = [] + + def calculate_functions(data, parent_name=None): + for func_name, func_data in data.items(): + name = func_data.get("name", func_name) + is_enabled = func_data.get("is_enabled", False) + is_frozen = exist_functions.get((name, parent_name), {}).get("is_frozen", False) + if (name, parent_name) not in exist_functions: + to_be_created_functions.append( + FunctionController( + name=name, is_enabled=is_enabled, is_frozen=is_frozen, parent_name=parent_name + ) + ) + elif not is_frozen: + func_id = exist_functions[(name, parent_name)]["id"] + to_be_updated_functions.append( + FunctionController(id=func_id, name=name, parent_name=parent_name, is_enabled=is_enabled) + ) + if "children" in func_data: + calculate_functions(func_data["children"], parent_name=name) + + calculate_functions(FUNCTION_CONTROLLER_INIT_MAP) + FunctionController.objects.bulk_create(to_be_created_functions) + FunctionController.objects.bulk_update(to_be_updated_functions, fields=["is_enabled"]) + + @classmethod + def get_all_function_controllers(cls, parent_name: str = None, all_fc_ctl: dict = None): + """ + 递归获取所有 FunctionController 对象及其子对象 + """ + function_controllers = {} + # 避免在递归中进行 orm 查询,这里查询一次后传入即可 + if all_fc_ctl is None: + all_fc_ctl = FunctionController.objects.all() + for fc_ctl in all_fc_ctl: + if fc_ctl.parent_name != parent_name: + continue + function_controllers[fc_ctl.name] = { + "is_enabled": fc_ctl.is_enabled, + "children": cls.get_all_function_controllers(parent_name=fc_ctl.name, all_fc_ctl=all_fc_ctl), + } + return function_controllers diff --git a/dbm-ui/backend/configuration/models/ip_whitelist.py b/dbm-ui/backend/configuration/models/ip_whitelist.py index adde3d7351..c845901fed 100644 --- a/dbm-ui/backend/configuration/models/ip_whitelist.py +++ b/dbm-ui/backend/configuration/models/ip_whitelist.py @@ -9,16 +9,16 @@ specific language governing permissions and limitations under the License. """ -from typing import Any, Dict, List +from typing import Any, Dict, List, Tuple from django.db import models from django.db.models import Q from django.forms.models import model_to_dict from django.utils.translation import ugettext_lazy as _ -from backend.bk_web.constants import LEN_LONG +from backend.bk_web.constants import LEN_LONG, LEN_SHORT from backend.bk_web.models import AuditedModel -from backend.configuration.constants import PLAT_BIZ_ID +from backend.configuration.constants import PLAT_BIZ_ID, DBType class IPWhitelist(AuditedModel): @@ -31,10 +31,14 @@ class Meta: verbose_name_plural = _("IP白名单") @classmethod - def list_ip_whitelist(cls, filter_data: Dict[str, Any]) -> List[Dict[str, Any]]: + def list_ip_whitelist( + cls, filter_data: Dict[str, Any], limit: int, offset: int + ) -> Tuple[int, List[Dict[str, Any]]]: """ 根据业务ID获取平台以及该业务下的白名单 - :param filter_data: 过滤的字段 + @param filter_data: 过滤的字段 + @param limit: 分页限制 + @param offset: 分页起始 """ ips_filters = Q(bk_biz_id=PLAT_BIZ_ID) | Q(bk_biz_id=filter_data["bk_biz_id"]) if filter_data.get("ip"): @@ -46,7 +50,8 @@ def list_ip_whitelist(cls, filter_data: Dict[str, Any]) -> List[Dict[str, Any]]: # IP模糊匹配 # select_sts = "f'SELECT * FROM `bk-dbm`.configuration_ipwhitelist where ips->"$[*]" LIKE "%{filter}%"'" # IPWhitelist.objects.raw(select_sts) - + iplist = cls.objects.filter(ips_filters) + count = iplist.count() ip_whitelist = [ # model_to_dict没有带上create_at和update_at { @@ -55,10 +60,10 @@ def list_ip_whitelist(cls, filter_data: Dict[str, Any]) -> List[Dict[str, Any]]: "update_at": ip.update_at, **model_to_dict(ip), } - for ip in list(cls.objects.filter(ips_filters)) + for ip in list(iplist[offset : limit + offset]) ] - return ip_whitelist + return count, ip_whitelist @classmethod def batch_delete(cls, ids: List[int]): diff --git a/dbm-ui/backend/configuration/models/password_policy.py b/dbm-ui/backend/configuration/models/password_policy.py index 1d119229df..2c0912632c 100644 --- a/dbm-ui/backend/configuration/models/password_policy.py +++ b/dbm-ui/backend/configuration/models/password_policy.py @@ -16,10 +16,13 @@ from backend.bk_web.constants import LEN_SHORT from backend.configuration.constants import INIT_PASSWORD_POLICY, DBType +from backend.db_services.mysql.permission.constants import AccountType class PasswordPolicy(models.Model): - account_type = models.CharField(_("账号类型"), choices=DBType.get_choices(), max_length=LEN_SHORT, primary_key=True) + account_type = models.CharField( + _("账号类型"), choices=AccountType.get_choices(), max_length=LEN_SHORT, primary_key=True + ) policy = models.JSONField(_("密码安全策略")) class Meta: diff --git a/dbm-ui/backend/configuration/models/system.py b/dbm-ui/backend/configuration/models/system.py index 17c69c5cb5..52d50ccb97 100644 --- a/dbm-ui/backend/configuration/models/system.py +++ b/dbm-ui/backend/configuration/models/system.py @@ -11,24 +11,18 @@ import logging from typing import Any, Dict, Optional, Union -from blue_krill.data_types.enum import EnumField, StructuredEnum from django.conf import settings from django.db import connection, models from django.utils.translation import ugettext_lazy as _ +from backend import env from backend.bk_web.constants import LEN_LONG, LEN_NORMAL from backend.bk_web.models import AuditedModel -from backend.configuration.constants import DEFAULT_SETTINGS +from backend.configuration import constants logger = logging.getLogger("root") -class SystemSettingsEnum(str, StructuredEnum): - """配置的枚举项,建议将系统配置都录入到这里方便统一管理""" - - BK_ITSM_SERVICE_ID = EnumField("BK_ITSM_SERVICE_ID", _("DBM的流程服务ID")) - - class SystemSettings(AuditedModel): type = models.CharField(_("类型"), max_length=LEN_NORMAL) key = models.CharField(_("关键字唯一标识"), max_length=LEN_NORMAL, unique=True) @@ -43,7 +37,7 @@ class Meta: @classmethod def init_default_settings(cls, *args, **kwargs): """初始化system的默认配置""" - for setting in DEFAULT_SETTINGS: + for setting in constants.DEFAULT_SETTINGS: # logger.info("init_default_settings get_or_create_setting: {0}".format(setting)) cls.objects.get_or_create( defaults={ @@ -77,7 +71,10 @@ def get_setting_value(cls, key: str, default: Optional[Any] = None) -> Union[str try: setting_value = cls.objects.get(key=key).value except cls.DoesNotExist: - setting_value = default or "" + if default is None: + setting_value = "" + else: + setting_value = default return setting_value @classmethod @@ -87,8 +84,21 @@ def insert_setting_value(cls, key: str, value: Any, value_type: str = "str", use defaults={ "type": value_type, "value": value, - "desc": SystemSettingsEnum.get_choice_label(key), + "desc": constants.SystemSettingsEnum.get_choice_label(key), "updater": user, }, key=key, ) + + @classmethod + def get_exact_hosting_biz(cls, bk_biz_id: int) -> int: + """ + 查询业务在 CMDB 准确托管的业务 + DBM 管理的机器托管有两类 + 1. 全部托管到 DBA 业务下(env.DBA_APP_BK_BIZ_ID) + 2. 全部托管到业务下 + 不支持混合的情况 + """ + if bk_biz_id in cls.get_setting_value(constants.SystemSettingsEnum.INDEPENDENT_HOSTING_BIZS.value, default=[]): + return bk_biz_id + return env.DBA_APP_BK_BIZ_ID diff --git a/dbm-ui/backend/configuration/serializers.py b/dbm-ui/backend/configuration/serializers.py index b2a34f6256..9e6577968b 100644 --- a/dbm-ui/backend/configuration/serializers.py +++ b/dbm-ui/backend/configuration/serializers.py @@ -17,8 +17,10 @@ from backend.configuration import mock_data from backend.configuration.constants import DBType from backend.configuration.mock_data import PASSWORD_POLICY +from backend.configuration.models.function_controller import FunctionController from backend.configuration.models.ip_whitelist import IPWhitelist from backend.configuration.models.system import SystemSettings +from backend.db_services.mysql.permission.constants import AccountType class SystemSettingsSerializer(serializers.ModelSerializer): @@ -52,7 +54,7 @@ class UpsertDBAdminSerializer(serializers.Serializer): class PasswordPolicySerializer(serializers.Serializer): - account_type = serializers.ChoiceField(help_text=_("账号类型"), choices=DBType.get_choices()) + account_type = serializers.ChoiceField(help_text=_("账号类型"), choices=AccountType.get_choices()) policy = serializers.JSONField(help_text=_("密码安全策略")) class Meta: @@ -69,7 +71,7 @@ def validate(self, attrs): class GetPasswordPolicySerializer(serializers.Serializer): - account_type = serializers.ChoiceField(help_text=_("账号类型"), choices=DBType.get_choices()) + account_type = serializers.ChoiceField(help_text=_("账号类型"), choices=AccountType.get_choices()) class IPWhitelistSerializer(AuditedSerializer, serializers.ModelSerializer): @@ -92,3 +94,12 @@ class ListIPWhitelistSerializer(serializers.Serializer): bk_biz_id = serializers.IntegerField(help_text=_("业务ID")) ip = serializers.CharField(help_text=_("代过滤IP"), required=False, allow_null=True, allow_blank=True) ids = serializers.ListField(child=serializers.IntegerField(help_text=_("待过滤白名单ID")), required=False) + + limit = serializers.IntegerField(help_text=_("分页限制"), default=10, required=False) + offset = serializers.IntegerField(help_text=_("分页起始"), default=0, required=False) + + +class FunctionControllerSerializer(serializers.Serializer): + class Meta: + model = FunctionController + fields = "__all__" diff --git a/dbm-ui/backend/configuration/urls.py b/dbm-ui/backend/configuration/urls.py index 08c50a2f93..8e44d00bbf 100644 --- a/dbm-ui/backend/configuration/urls.py +++ b/dbm-ui/backend/configuration/urls.py @@ -12,6 +12,7 @@ from rest_framework.routers import DefaultRouter from backend.configuration.views.dba import DBAdminViewSet +from backend.configuration.views.function_controller import FunctionControllerViewSet from backend.configuration.views.ip_whitelist import IPWhitelistViewSet from backend.configuration.views.password_policy import PasswordPolicyViewSet from backend.configuration.views.profile import ProfileViewSet @@ -24,5 +25,6 @@ routers.register(r"profile", ProfileViewSet, basename="profile") routers.register(r"password_policy", PasswordPolicyViewSet, basename="password_policy") routers.register(r"ip_whitelist", IPWhitelistViewSet, basename="ip_whitelist") +routers.register(r"function_controller", FunctionControllerViewSet, basename="function_controller") urlpatterns = routers.urls diff --git a/dbm-ui/backend/configuration/views/__init__.py b/dbm-ui/backend/configuration/views/__init__.py index aa5085c628..c9ee38dc85 100644 --- a/dbm-ui/backend/configuration/views/__init__.py +++ b/dbm-ui/backend/configuration/views/__init__.py @@ -8,3 +8,4 @@ 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. """ +default_app_config = "backend.configuration.apps.ConfigurationConfig" diff --git a/dbm-ui/backend/configuration/views/function_controller.py b/dbm-ui/backend/configuration/views/function_controller.py new file mode 100644 index 0000000000..2c973816ac --- /dev/null +++ b/dbm-ui/backend/configuration/views/function_controller.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 rest_framework import status +from rest_framework.response import Response + +from backend.bk_web import viewsets +from backend.bk_web.swagger import common_swagger_auto_schema +from backend.configuration.models.function_controller import FunctionController +from backend.configuration.serializers import FunctionControllerSerializer + +tags = [_("功能开关")] + + +class FunctionControllerViewSet(viewsets.AuditedModelViewSet): + """功能开关视图""" + + serializer_class = FunctionControllerSerializer + queryset = FunctionController.objects.all() + + def _get_custom_permissions(self): + return [] + + @common_swagger_auto_schema( + operation_summary=_("功能开关列表"), + responses={status.HTTP_200_OK: FunctionControllerSerializer(_("功能开关"), many=True)}, + tags=tags, + ) + def list(self, request, *args, **kwargs): + return Response(FunctionController.get_all_function_controllers()) diff --git a/dbm-ui/backend/configuration/views/ip_whitelist.py b/dbm-ui/backend/configuration/views/ip_whitelist.py index bca6fa42ae..2bcdb9d2cb 100644 --- a/dbm-ui/backend/configuration/views/ip_whitelist.py +++ b/dbm-ui/backend/configuration/views/ip_whitelist.py @@ -41,7 +41,7 @@ class IPWhitelistViewSet(viewsets.AuditedModelViewSet): pagination_class = AuditedLimitOffsetPagination def _get_custom_permissions(self): - bk_biz_id = self.request.query_params.get("bk_biz_id", 0) or self.request.data.get("bk_biz_id", 0) + bk_biz_id = self.request.query_params.get("bk_biz_id") or self.request.data.get("bk_biz_id") if bk_biz_id: return [DBManageIAMPermission()] @@ -71,8 +71,9 @@ def retrieve(self, request, *args, **kwargs): @action(methods=["POST"], detail=False, serializer_class=ListIPWhitelistSerializer) def iplist(self, request, *args, **kwargs): validated_data = self.params_validate(self.get_serializer_class()) - page = self.paginate_queryset(IPWhitelist.list_ip_whitelist(validated_data)) - return self.get_paginated_response(page) + limit, offset = validated_data["limit"], validated_data["offset"] + count, iplist = IPWhitelist.list_ip_whitelist(validated_data, limit, offset) + return Response({"count": count, "results": iplist}) @common_swagger_auto_schema( operation_summary=_("创建IP白名单"), diff --git a/dbm-ui/backend/constants.py b/dbm-ui/backend/constants.py index 68ccbdc5ab..2506898671 100644 --- a/dbm-ui/backend/constants.py +++ b/dbm-ui/backend/constants.py @@ -12,8 +12,12 @@ # 默认云区域ID from dataclasses import dataclass +# 默认云区域ID DEFAULT_BK_CLOUD_ID = 0 +# 一些常量值 +INT_MAX = 2 ** 31 - 1 + # IP 端口分隔符 IP_PORT_DIVIDER = ":" @@ -47,6 +51,7 @@ class CommonInstanceLabels: instance: str instance_role: str instance_host: str + instance_port: str # 定义添加host的公共固定标签结构 diff --git a/dbm-ui/backend/core/storages/apps.py b/dbm-ui/backend/core/storages/apps.py index 37551e8c03..41cfb499a3 100644 --- a/dbm-ui/backend/core/storages/apps.py +++ b/dbm-ui/backend/core/storages/apps.py @@ -14,4 +14,5 @@ class CoreFilesConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" name = "backend.core.storages" diff --git a/dbm-ui/backend/ticket/builders/spider/__init__.py b/dbm-ui/backend/db_dirty/__init__.py similarity index 100% rename from dbm-ui/backend/ticket/builders/spider/__init__.py rename to dbm-ui/backend/db_dirty/__init__.py diff --git a/dbm-ui/backend/db_dirty/admin.py b/dbm-ui/backend/db_dirty/admin.py new file mode 100644 index 0000000000..86f0e6ac8e --- /dev/null +++ b/dbm-ui/backend/db_dirty/admin.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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.contrib import admin + +from . import models + + +@admin.register(models.DirtyMachine) +class DirtyMachineAdmin(admin.ModelAdmin): + list_display = ("ip", "bk_biz_id", "bk_host_id", "flow", "ticket") + list_filter = ("ip", "bk_biz_id", "bk_host_id", "flow", "ticket") + search_fields = ("ip", "bk_biz_id", "bk_host_id", "flow", "ticket") diff --git a/dbm-ui/backend/db_dirty/apps.py b/dbm-ui/backend/db_dirty/apps.py new file mode 100644 index 0000000000..30b9fd7bb6 --- /dev/null +++ b/dbm-ui/backend/db_dirty/apps.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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.apps import AppConfig + + +class DbDirtyConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "backend.db_dirty" diff --git a/dbm-ui/backend/db_dirty/constants.py b/dbm-ui/backend/db_dirty/constants.py new file mode 100644 index 0000000000..08bd76fb1c --- /dev/null +++ b/dbm-ui/backend/db_dirty/constants.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 backend.ticket.constants import TicketType + +SWAGGER_TAG = _("污点池") diff --git a/dbm-ui/backend/db_dirty/filters.py b/dbm-ui/backend/db_dirty/filters.py new file mode 100644 index 0000000000..3e6305d562 --- /dev/null +++ b/dbm-ui/backend/db_dirty/filters.py @@ -0,0 +1,46 @@ +# -*- coding:utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 django_filters import rest_framework as filters + +from backend.db_dirty.models import DirtyMachine +from backend.ticket.constants import TicketType + + +class DirtyMachineFilter(filters.FilterSet): + ticket_types = filters.CharFilter(field_name="ticket__ticket_type", method="filter_ticket_types", label=_("单据类型")) + ticket_ids = filters.CharFilter(field_name="ticket__id", method="filter_ticket_ids", label=_("单据ID")) + task_ids = filters.CharFilter(field_name="flow__flow_obj_id", method="filter_task_ids", label=_("任务ID")) + operator = filters.CharFilter(field_name="ticket__creator", lookup_expr="icontains", label=_("操作者")) + ip_list = filters.CharFilter(field_name="ip_list", method="filter_ip_list", label=_("过滤IP")) + + def _split_int(self, value): + try: + return list(map(int, value.split(","))) + except ValueError: + return [] + + def filter_ip_list(self, queryset, name, value): + return queryset.filter(ip__in=value.split(",")) + + def filter_ticket_ids(self, queryset, name, value): + return queryset.filter(ticket__id__in=self._split_int(value)) + + def filter_ticket_types(self, queryset, name, value): + return queryset.filter(ticket__ticket_type__in=value.split(",")) + + def filter_task_ids(self, queryset, name, value): + return queryset.filter(flow__flow_obj_id__in=value.split(",")) + + class Meta: + model = DirtyMachine + fields = ["ticket_types", "ticket_ids", "task_ids", "operator", "ip_list"] diff --git a/dbm-ui/backend/db_dirty/handlers.py b/dbm-ui/backend/db_dirty/handlers.py new file mode 100644 index 0000000000..424d732006 --- /dev/null +++ b/dbm-ui/backend/db_dirty/handlers.py @@ -0,0 +1,112 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 collections import defaultdict +from typing import Any, Dict, List + +from backend import env +from backend.components import CCApi +from backend.components.dbresource.client import DBResourceApi +from backend.configuration.constants import SystemSettingsEnum +from backend.configuration.models import SystemSettings +from backend.db_dirty.models import DirtyMachine +from backend.db_meta.models import Machine +from backend.flow.utils.cc_manage import CcManage +from backend.ticket.models import Flow, Ticket + + +class DBDirtyMachineHandler(object): + """ + 污点池处理接口的逻辑处理 + """ + + @classmethod + def transfer_dirty_machines(cls, bk_host_ids: List[int]): + """ + 将污点主机转移待回收模块,并从资源池移除 + @param bk_host_ids: 主机列表 + """ + # 将主机移动到待回收模块 + dirty_machines = DirtyMachine.objects.filter(bk_host_id__in=bk_host_ids) + bk_biz_id__host_ids = defaultdict(list) + for machine in dirty_machines: + bk_biz_id__host_ids[machine.bk_biz_id].append(machine.bk_host_id) + + for bk_biz_id, bk_host_ids in bk_biz_id__host_ids.items(): + CcManage(bk_biz_id).recycle_host(bk_host_ids) + + # 删除污点池记录,并从资源池移除(忽略删除错误,因为机器可能不来自资源池) + dirty_machines.delete() + DBResourceApi.resource_delete(params={"bk_host_ids": bk_host_ids}, raise_exception=False) + + @classmethod + def insert_dirty_machines(cls, bk_biz_id: int, bk_host_ids: List[Dict[str, Any]], ticket: Ticket, flow: Flow): + """ + 将机器导入到污点池中 + @param bk_biz_id: 业务ID + @param bk_host_ids: 主机列表 + @param ticket: 关联的单据 + @param flow: 关联的flow任务 + """ + # 排除已经录入到Machine表的机器(因为是成功消费不算污点机器) + db_machines = list(Machine.objects.filter(bk_host_id__in=bk_host_ids).values_list("bk_host_id", flat=True)) + dirty_host_ids = list(set(bk_host_ids) - set(db_machines)) + + # 查询污点机器信息 + host_property_filter = { + "condition": "AND", + "rules": [{"field": "bk_host_id", "operator": "in", "value": dirty_host_ids}], + } + dirty_host_infos = CCApi.list_biz_hosts( + { + "bk_biz_id": bk_biz_id, + # 默认一次性录入的机器不会超过500 + "page": {"start": 0, "limit": 500, "sort": "bk_host_id"}, + "host_property_filter": host_property_filter, + "fields": ["bk_host_id", "bk_cloud_id", "bk_host_innerip"], + } + )["info"] + + # 将污点机器信息转移至污点池模块 + dirty_module = SystemSettings.get_setting_value(key=SystemSettingsEnum.MANAGE_TOPO.value)["dirty_module_id"] + CcManage(bk_biz_id=env.DBA_APP_BK_BIZ_ID).transfer_host_module( + bk_host_ids=dirty_host_ids, target_module_ids=[dirty_module] + ) + + # 录入污点池表中 + exist_dirty_machine_ids = list( + DirtyMachine.objects.filter(bk_host_id__in=dirty_host_ids).values_list("bk_host_id", flat=True) + ) + DirtyMachine.objects.bulk_create( + [ + DirtyMachine( + ticket=ticket, + flow=flow, + ip=host["bk_host_innerip"], + bk_biz_id=bk_biz_id, + bk_host_id=host["bk_host_id"], + bk_cloud_id=host["bk_cloud_id"], + ) + for host in dirty_host_infos + if host["bk_host_id"] not in exist_dirty_machine_ids + ] + ) + + @classmethod + def remove_dirty_machines(cls, bk_host_ids: List[Dict[str, Any]]): + """ + 将机器从污点池挪走,一般是重试后会调用此函数。 + 这里只用删除记录,无需做其他挪模块的操作,原因如下: + 1. 如果重试依然失败,则机器会重新回归污点池,模块不变 + 2. 如果重试成功,则机器已经由flow挪到了对应的DB模块 + 3. 如果手动处理,则机器会被挪到待回收模块 + @param bk_host_ids: 主机列表 + """ + DirtyMachine.objects.filter(bk_host_id__in=bk_host_ids).delete() diff --git a/dbm-ui/backend/db_dirty/migrations/0001_initial.py b/dbm-ui/backend/db_dirty/migrations/0001_initial.py new file mode 100644 index 0000000000..5d9216b55b --- /dev/null +++ b/dbm-ui/backend/db_dirty/migrations/0001_initial.py @@ -0,0 +1,45 @@ +# Generated by Django 3.2.19 on 2023-07-11 09:19 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ("ticket", "0001_initial"), + ] + + operations = [ + migrations.CreateModel( + name="DirtyMachine", + fields=[ + ("creator", models.CharField(max_length=64, verbose_name="创建人")), + ("create_at", models.DateTimeField(auto_now_add=True, verbose_name="创建时间")), + ("updater", models.CharField(max_length=64, verbose_name="修改人")), + ("update_at", models.DateTimeField(auto_now=True, verbose_name="更新时间")), + ("bk_biz_id", models.IntegerField(default=0, help_text="业务ID")), + ( + "bk_host_id", + models.PositiveBigIntegerField(default=0, help_text="主机ID", primary_key=True, serialize=False), + ), + ("bk_cloud_id", models.IntegerField(default=0, help_text="主机云区域")), + ("ip", models.CharField(help_text="主机IP", max_length=128)), + ( + "flow", + models.ForeignKey(help_text="关联任务", on_delete=django.db.models.deletion.CASCADE, to="ticket.flow"), + ), + ( + "ticket", + models.ForeignKey( + help_text="关联单据", on_delete=django.db.models.deletion.CASCADE, to="ticket.ticket" + ), + ), + ], + options={ + "abstract": False, + }, + ), + ] diff --git a/dbm-ui/backend/db_services/redis_dts/yasg_slz.py b/dbm-ui/backend/db_dirty/migrations/__init__.py similarity index 100% rename from dbm-ui/backend/db_services/redis_dts/yasg_slz.py rename to dbm-ui/backend/db_dirty/migrations/__init__.py diff --git a/dbm-ui/backend/db_dirty/mock.py b/dbm-ui/backend/db_dirty/mock.py new file mode 100644 index 0000000000..4dbd38149c --- /dev/null +++ b/dbm-ui/backend/db_dirty/mock.py @@ -0,0 +1,25 @@ +# -*- coding:utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + + +DIRTY_MACHINE_LIST = [ + { + "ip": "127.0.0.4", + "bk_host_id": 4, + "bk_cloud_name": "direct area", + "bk_cloud_id": 0, + "bk_biz_name": "DBA", + "bk_biz_id": 3, + "ticket_type": "REDIS_CLUSTER_APPLY", + "ticket_id": 1, + "task_id": "", + } +] diff --git a/dbm-ui/backend/db_dirty/models.py b/dbm-ui/backend/db_dirty/models.py new file mode 100644 index 0000000000..4d94d0d217 --- /dev/null +++ b/dbm-ui/backend/db_dirty/models.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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.db import models +from django.utils.translation import ugettext_lazy as _ + +from backend.bk_web.constants import LEN_MIDDLE +from backend.bk_web.models import AuditedModel +from backend.ticket.models import Flow, Ticket + + +class DirtyMachine(AuditedModel): + """ + 污点机器记录:从资源池申请成功后,但是部署失败未处理的机器记录 + """ + + bk_biz_id = models.IntegerField(default=0, help_text=_("业务ID")) + bk_host_id = models.PositiveBigIntegerField(primary_key=True, default=0, help_text=_("主机ID")) + bk_cloud_id = models.IntegerField(default=0, help_text=_("主机云区域")) + ip = models.CharField(max_length=LEN_MIDDLE, help_text=_("主机IP")) + flow = models.ForeignKey(Flow, on_delete=models.CASCADE, help_text=_("关联任务")) + ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE, help_text=_("关联单据")) diff --git a/dbm-ui/backend/db_dirty/serializers.py b/dbm-ui/backend/db_dirty/serializers.py new file mode 100644 index 0000000000..6d9bcab601 --- /dev/null +++ b/dbm-ui/backend/db_dirty/serializers.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 rest_framework import serializers + +from backend.db_dirty.mock import DIRTY_MACHINE_LIST +from backend.ticket.constants import TicketType + + +class QueryDirtyMachineSerializer(serializers.Serializer): + ip_list = serializers.CharField(help_text=_("过滤的主机IP列表,以逗号分隔"), required=False) + ticket_id = serializers.IntegerField(help_text=_("过滤的单据ID"), required=False) + task_id = serializers.CharField(help_text=_("过滤的任务ID"), required=False) + ticket_type = serializers.ChoiceField(help_text=_("过滤的单据类型"), choices=TicketType.get_choices(), required=False) + operator = serializers.CharField(help_text=_("操作人"), required=False) + + limit = serializers.IntegerField(help_text=_("分页限制"), required=False, default=10) + offset = serializers.IntegerField(help_text=_("分页起始"), required=False, default=0) + + def validate(self, attrs): + if "ip_list" in attrs: + attrs["ip_list"] = attrs["ip_list"].split(",") + + return attrs + + +class QueryDirtyMachineResponseSerializer(serializers.Serializer): + class Meta: + swagger_schema_fields = {"example": DIRTY_MACHINE_LIST} + + +class DeleteDirtyMachineSerializer(serializers.Serializer): + bk_host_ids = serializers.ListField(child=serializers.IntegerField(), help_text=_("待转移的主机ID列表")) diff --git a/dbm-ui/backend/db_dirty/urls.py b/dbm-ui/backend/db_dirty/urls.py new file mode 100644 index 0000000000..59ffa385dd --- /dev/null +++ b/dbm-ui/backend/db_dirty/urls.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 rest_framework.routers import DefaultRouter + +from backend.db_dirty.views import DBDirtyMachineViewSet + +routers = DefaultRouter(trailing_slash=True) + +routers.register(r"", DBDirtyMachineViewSet, basename="db_dirty") + +urlpatterns = routers.urls diff --git a/dbm-ui/backend/db_dirty/views.py b/dbm-ui/backend/db_dirty/views.py new file mode 100644 index 0000000000..a72e5d3a46 --- /dev/null +++ b/dbm-ui/backend/db_dirty/views.py @@ -0,0 +1,101 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 collections import defaultdict + +from django.utils.translation import ugettext_lazy as _ +from django_filters import rest_framework +from rest_framework import status +from rest_framework.decorators import action +from rest_framework.response import Response + +from backend.bk_web import viewsets +from backend.bk_web.pagination import AuditedLimitOffsetPagination +from backend.bk_web.swagger import common_swagger_auto_schema +from backend.components import CCApi +from backend.db_dirty.constants import SWAGGER_TAG +from backend.db_dirty.filters import DirtyMachineFilter +from backend.db_dirty.handlers import DBDirtyMachineHandler +from backend.db_dirty.models import DirtyMachine +from backend.db_dirty.serializers import ( + DeleteDirtyMachineSerializer, + QueryDirtyMachineResponseSerializer, + QueryDirtyMachineSerializer, +) +from backend.db_meta.models import AppCache +from backend.db_services.ipchooser.query.resource import ResourceQueryHelper +from backend.iam_app.handlers.drf_perm import GlobalManageIAMPermission + + +class DBDirtyMachineViewSet(viewsets.SystemViewSet): + pagination_class = None + filter_class = None + + def _get_custom_permissions(self): + return [GlobalManageIAMPermission()] + + @common_swagger_auto_schema( + operation_summary=_("查询污点池列表"), + responses={status.HTTP_200_OK: QueryDirtyMachineResponseSerializer()}, + tags=[SWAGGER_TAG], + ) + @action( + detail=False, + methods=["GET"], + url_path="query_dirty_machines", + serializer_class=QueryDirtyMachineSerializer, + pagination_class=AuditedLimitOffsetPagination, + filter_class=DirtyMachineFilter, + filter_backends=(rest_framework.DjangoFilterBackend,), + queryset=DirtyMachine.objects.all(), + ) + def query_operation_list(self, request): + dirty_machines = self.filter_queryset(self.get_queryset()) + page_dirty_machines = self.paginate_queryset(dirty_machines) + + # 提前缓存云区域和业务信息 + bk_biz_ids = [dirty_machine.bk_biz_id for dirty_machine in page_dirty_machines] + for_biz_infos = AppCache.batch_get_app_attr(bk_biz_ids=bk_biz_ids, attr_name="bk_biz_name") + cloud_info = ResourceQueryHelper.search_cc_cloud(get_cache=True) + + # 获取污点池列表 + page_dirty_machine_list = [ + { + "ip": dirty.ip, + "bk_host_id": dirty.bk_host_id, + "bk_cloud_name": cloud_info[str(dirty.bk_cloud_id)]["bk_cloud_name"], + "bk_cloud_id": dirty.bk_cloud_id, + "bk_biz_name": for_biz_infos[int(dirty.bk_biz_id)], + "bk_biz_id": dirty.bk_biz_id, + "ticket_type": dirty.ticket.ticket_type, + "ticket_id": dirty.ticket.id, + "ticket_type_display": dirty.ticket.get_ticket_type_display(), + "task_id": dirty.flow.flow_obj_id, + "operator": dirty.ticket.creator, + } + for dirty in page_dirty_machines + ] + return self.paginator.get_paginated_response(data=page_dirty_machine_list) + + @common_swagger_auto_schema( + operation_summary=_("将污点池主机转移至待回收模块"), + request_body=DeleteDirtyMachineSerializer(), + tags=[SWAGGER_TAG], + ) + @action( + detail=False, + methods=["POST"], + url_path="transfer_dirty_machines", + serializer_class=DeleteDirtyMachineSerializer, + ) + def transfer_dirty_machines(self, request): + bk_host_ids = self.params_validate(self.get_serializer_class())["bk_host_ids"] + DBDirtyMachineHandler.transfer_dirty_machines(bk_host_ids) + return Response() diff --git a/dbm-ui/backend/db_meta/admin.py b/dbm-ui/backend/db_meta/admin.py index 20fdb8074b..2645482ea5 100644 --- a/dbm-ui/backend/db_meta/admin.py +++ b/dbm-ui/backend/db_meta/admin.py @@ -135,3 +135,17 @@ class StorageInstanceTupleAdmin(admin.ModelAdmin): "receiver", ) search_fields = ("ejector__machine__ip", "receiver__machine__ip") + + +@admin.register(models.spec.Spec) +class SpecAdmin(admin.ModelAdmin): + list_display = ( + "spec_name", + "spec_cluster_type", + "spec_machine_type", + "cpu", + "mem", + "device_class", + "storage_spec", + ) + search_fields = ("spec_name", "spec_cluster_type", "spec_machine_type") diff --git a/dbm-ui/backend/db_meta/api/cluster/__init__.py b/dbm-ui/backend/db_meta/api/cluster/__init__.py index 915b0f6ec5..37c0dbddb7 100644 --- a/dbm-ui/backend/db_meta/api/cluster/__init__.py +++ b/dbm-ui/backend/db_meta/api/cluster/__init__.py @@ -8,7 +8,7 @@ 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 .apis import * + from . import ( es, hdfs, diff --git a/dbm-ui/backend/db_meta/api/cluster/base/graph.py b/dbm-ui/backend/db_meta/api/cluster/base/graph.py index c3aeff3227..0fd9a7f0c5 100644 --- a/dbm-ui/backend/db_meta/api/cluster/base/graph.py +++ b/dbm-ui/backend/db_meta/api/cluster/base/graph.py @@ -213,6 +213,9 @@ def add_instance_nodes( instances = StorageInstance.objects.filter( reduce(operator.or_, [Q(instance_role=role) for role in roles]), cluster=cluster ) + if not instances: + return None, None + group = self.get_or_create_group(group_id=Node.generate_node_type(instances.first()), group_name=group_name) for inst in instances: self.add_node(inst, group) diff --git a/dbm-ui/backend/db_meta/api/cluster/es/create.py b/dbm-ui/backend/db_meta/api/cluster/es/create.py index 3e4061aeb9..5ec850ac29 100644 --- a/dbm-ui/backend/db_meta/api/cluster/es/create.py +++ b/dbm-ui/backend/db_meta/api/cluster/es/create.py @@ -18,11 +18,7 @@ from backend.db_meta.enums import ClusterEntryType, ClusterPhase, ClusterStatus, ClusterType, InstanceRole, MachineType from backend.db_meta.models import Cluster, ClusterEntry, ClusterMonitorTopo, StorageInstance from backend.flow.consts import InstanceFuncAliasEnum -from backend.flow.utils.es.es_module_operate import ( - create_bk_module_for_cluster_id, - init_instance_service, - transfer_host_in_cluster_module, -) +from backend.flow.utils.es.es_module_operate import EsCCTopoOperator logger = logging.getLogger("root") @@ -104,29 +100,5 @@ def create( ins.save() m.save() - # 生成域名模块 - create_bk_module_for_cluster_id(cluster_id=cluster.id) - - # es主机转移模块、添加对应的服务实例 - for machine_type in [MachineType.ES_DATANODE.value, MachineType.ES_CLIENT.value, MachineType.ES_MASTER.value]: - ip_set = set([ins.machine.ip for ins in storage_objs.filter(machine__machine_type=machine_type)]) - transfer_host_in_cluster_module( - cluster_id=cluster.id, - ip_list=list(ip_set), - machine_type=machine_type, - bk_cloud_id=bk_cloud_id, - ) - - # 写入监控信息 - cluster = Cluster.objects.get(id=cluster.id) - bk_module_id = ClusterMonitorTopo.objects.get( - bk_biz_id=cluster.bk_biz_id, cluster_id=cluster.id, machine_type=MachineType.ES_DATANODE.value - ).bk_module_id - instance = storage_objs.filter(machine__machine_type=MachineType.ES_DATANODE.value).first() - init_instance_service( - cluster=cluster, - ins=instance, - bk_module_id=bk_module_id, - instance_role=instance.instance_role, - func_name=InstanceFuncAliasEnum.ES_FUNC_ALIAS.value, - ) + # 生成域名模块、es主机转移模块、添加对应的服务实例 + EsCCTopoOperator(cluster).transfer_instances_to_cluster_module(storage_objs) diff --git a/dbm-ui/backend/db_meta/api/cluster/es/destroy.py b/dbm-ui/backend/db_meta/api/cluster/es/destroy.py index 5e0abb87bb..0db9152f6d 100644 --- a/dbm-ui/backend/db_meta/api/cluster/es/destroy.py +++ b/dbm-ui/backend/db_meta/api/cluster/es/destroy.py @@ -12,11 +12,9 @@ from django.db import transaction -from backend import env -from backend.components import CCApi from backend.configuration.constants import DBType -from backend.db_meta.api.db_module import delete_cluster_modules from backend.db_meta.models import Cluster, ClusterEntry +from backend.flow.utils.cc_manage import CcManage logger = logging.getLogger("root") @@ -28,15 +26,14 @@ def destroy(cluster_id: int): """ cluster = Cluster.objects.get(id=cluster_id) + cc_manage = CcManage(bk_biz_id=cluster.bk_biz_id) # 删除storage instance for storage in cluster.storageinstance_set.all(): storage.delete(keep_parents=True) if not storage.machine.storageinstance_set.exists(): # 这个 api 不需要检查返回值, 转移主机到待回收模块,转移模块这里会把服务实例删除 - CCApi.transfer_host_to_recyclemodule( - {"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "bk_host_id": [storage.machine.bk_host_id]} - ) + cc_manage.recycle_host([storage.machine.bk_host_id]) storage.machine.delete(keep_parents=True) # 删除entry @@ -44,7 +41,7 @@ def destroy(cluster_id: int): ce.delete(keep_parents=True) # 删除cmdb中的模块 - delete_cluster_modules(db_type=DBType.Es.value, del_cluster_id=cluster.id) + cc_manage.delete_cluster_modules(db_type=DBType.Es.value, cluster=cluster) # 删除集群 cluster.delete(keep_parents=True) diff --git a/dbm-ui/backend/db_meta/api/cluster/es/detail.py b/dbm-ui/backend/db_meta/api/cluster/es/detail.py index 7eeab2ecc7..f6ecabfc27 100644 --- a/dbm-ui/backend/db_meta/api/cluster/es/detail.py +++ b/dbm-ui/backend/db_meta/api/cluster/es/detail.py @@ -42,12 +42,14 @@ def scan_cluster(cluster: Cluster) -> Graphic: entry = client_insts.first().bind_entry.first() _dummy, entry_group = graph.add_node(entry) - # 访问入口 ---> Client节点,关系为:访问 - graph.add_line(source=entry_group, target=client_group, label=LineLabel.Access) - - # Client节点 ---> Master/冷/热节点,关系为:访问 - graph.add_line(source=client_group, target=master_group, label=LineLabel.Access) - graph.add_line(source=client_group, target=hot_node_group, label=LineLabel.Access) - graph.add_line(source=client_group, target=cold_node_group, label=LineLabel.Access) + if client_group: + # 访问入口 ---> Client节点,关系为:访问 + graph.add_line(source=entry_group, target=client_group, label=LineLabel.Access) + # Client节点 ---> Master/冷/热节点,关系为:访问 + graph.add_line(source=client_group, target=master_group, label=LineLabel.Access) + if hot_node_group: + graph.add_line(source=client_group, target=hot_node_group, label=LineLabel.Access) + if cold_node_group: + graph.add_line(source=client_group, target=cold_node_group, label=LineLabel.Access) return graph diff --git a/dbm-ui/backend/db_meta/api/cluster/es/scale_up.py b/dbm-ui/backend/db_meta/api/cluster/es/scale_up.py index 894963b5bc..97dbc27be9 100644 --- a/dbm-ui/backend/db_meta/api/cluster/es/scale_up.py +++ b/dbm-ui/backend/db_meta/api/cluster/es/scale_up.py @@ -17,7 +17,7 @@ from backend.db_meta.api import common from backend.db_meta.enums import InstanceRole, MachineType from backend.db_meta.models import Cluster, ClusterEntry, StorageInstance -from backend.flow.utils.es.es_module_operate import transfer_host_in_cluster_module +from backend.flow.utils.es.es_module_operate import EsCCTopoOperator logger = logging.getLogger("root") @@ -82,13 +82,5 @@ def scale_up( ins.save() m.save() - # es主机转移模块、添加对应的服务实例 - for machine_type in [MachineType.ES_DATANODE.value, MachineType.ES_CLIENT.value, MachineType.ES_MASTER]: - ip_set = set([ins.machine.ip for ins in storage_objs.filter(machine__machine_type=machine_type)]) - if ip_set: - transfer_host_in_cluster_module( - cluster_id=cluster.id, - ip_list=list(ip_set), - machine_type=machine_type, - bk_cloud_id=cluster.bk_cloud_id, - ) + # 生成域名模块、es主机转移模块、添加对应的服务实例 + EsCCTopoOperator(cluster).transfer_instances_to_cluster_module(storage_objs) diff --git a/dbm-ui/backend/db_meta/api/cluster/es/shrink.py b/dbm-ui/backend/db_meta/api/cluster/es/shrink.py index 896683d1fb..cebbd881b6 100644 --- a/dbm-ui/backend/db_meta/api/cluster/es/shrink.py +++ b/dbm-ui/backend/db_meta/api/cluster/es/shrink.py @@ -14,13 +14,12 @@ from django.db import transaction from django.db.models import Q -from backend import env -from backend.components import CCApi from backend.db_meta import request_validator from backend.db_meta.enums import ClusterType, InstanceRole, MachineType from backend.db_meta.models import Cluster, ClusterEntry, ClusterMonitorTopo, StorageInstance from backend.flow.consts import InstanceFuncAliasEnum -from backend.flow.utils.es.es_module_operate import init_instance_service +from backend.flow.utils.cc_manage import CcManage +from backend.flow.utils.es.es_module_operate import EsCCTopoOperator logger = logging.getLogger("root") @@ -43,9 +42,7 @@ def shrink( storage.delete(keep_parents=True) if not storage.machine.storageinstance_set.exists(): # 这个 api 不需要检查返回值, 转移主机到待回收模块,转移模块这里会把服务实例删除 - CCApi.transfer_host_to_recyclemodule( - {"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "bk_host_id": [storage.machine.bk_host_id]} - ) + CcManage(storage.bk_biz_id).recycle_host([storage.machine.bk_host_id]) storage.machine.delete(keep_parents=True) # 修正entry到instance的关系 @@ -75,19 +72,4 @@ def shrink( cluster_entry.storageinstance_set.add(instance) # 判断服务实例是否还存在,不存在则安装 - service_instance = StorageInstance.objects.filter(cluster=cluster).exclude(bk_instance_id=0) - if not service_instance.exists(): - # 写入监控信息 - bk_module_id = ClusterMonitorTopo.objects.get( - bk_biz_id=cluster.bk_biz_id, cluster_id=cluster.id, machine_type=MachineType.ES_DATANODE.value - ).bk_module_id - instance = StorageInstance.objects.filter( - cluster=cluster, machine__machine_type=MachineType.ES_DATANODE.value - ).first() - init_instance_service( - cluster=cluster, - ins=instance, - bk_module_id=bk_module_id, - instance_role=instance.instance_role, - func_name=InstanceFuncAliasEnum.ES_FUNC_ALIAS.value, - ) + EsCCTopoOperator(cluster).init_instances_service(MachineType.ES_DATANODE.value) diff --git a/dbm-ui/backend/db_meta/api/cluster/hdfs/create.py b/dbm-ui/backend/db_meta/api/cluster/hdfs/create.py index dd08f62d5a..2c50050ebf 100644 --- a/dbm-ui/backend/db_meta/api/cluster/hdfs/create.py +++ b/dbm-ui/backend/db_meta/api/cluster/hdfs/create.py @@ -47,7 +47,7 @@ def create( db_module_id: int, storages: Optional[List] = None, creator: str = "", -) -> int: +) -> Cluster: """ 注册 HDFS 集群 """ @@ -91,4 +91,4 @@ def create( storage_instance.save() machine.save() - return cluster.id + return cluster diff --git a/dbm-ui/backend/db_meta/api/cluster/hdfs/destroy.py b/dbm-ui/backend/db_meta/api/cluster/hdfs/destroy.py index 3d787d7d6d..c88c8d5184 100644 --- a/dbm-ui/backend/db_meta/api/cluster/hdfs/destroy.py +++ b/dbm-ui/backend/db_meta/api/cluster/hdfs/destroy.py @@ -13,11 +13,9 @@ from django.db import transaction from django.utils.translation import ugettext as _ -from backend import env -from backend.components import CCApi from backend.configuration.constants import DBType -from backend.db_meta.api.db_module import delete_cluster_modules from backend.db_meta.models import Cluster, ClusterEntry +from backend.flow.utils.cc_manage import CcManage logger = logging.getLogger("root") @@ -29,6 +27,7 @@ def destroy(cluster_id: int): """ cluster = Cluster.objects.get(id=cluster_id) + cc_manage = CcManage(cluster.bk_biz_id) # 删除storage instance for storage in cluster.storageinstance_set.all(): @@ -36,17 +35,16 @@ def destroy(cluster_id: int): if not storage.machine.storageinstance_set.exists(): # 将主机转移到待回收模块下 logger.info(_("将主机{}转移到待回收模块").format(storage.machine.ip)) - CCApi.transfer_host_to_recyclemodule( - {"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "bk_host_id": [storage.machine.bk_host_id]} - ) - + cc_manage.recycle_host([storage.machine.bk_host_id]) storage.machine.delete(keep_parents=True) + else: + cc_manage.delete_service_instance(bk_instance_ids=[storage.bk_instance_id]) # 删除entry for ce in ClusterEntry.objects.filter(cluster=cluster).all(): ce.delete(keep_parents=True) # 删除cluster monitor topo, CC API删除模块 - delete_cluster_modules(db_type=DBType.Hdfs.value, del_cluster_id=cluster_id) + cc_manage.delete_cluster_modules(db_type=DBType.Hdfs.value, cluster=cluster) cluster.delete(keep_parents=True) diff --git a/dbm-ui/backend/db_meta/api/cluster/hdfs/detail.py b/dbm-ui/backend/db_meta/api/cluster/hdfs/detail.py index 9fe35190ff..d4299755e1 100644 --- a/dbm-ui/backend/db_meta/api/cluster/hdfs/detail.py +++ b/dbm-ui/backend/db_meta/api/cluster/hdfs/detail.py @@ -50,9 +50,6 @@ def scan_cluster(cluster: Cluster) -> Graphic: # NameNode节点 ---> ZK节点组, 关系为:读写 graph.add_line(source=nn_group, target=zk_group, label=LineLabel.ReadWrite) - # NameNode节点 ---> ZK节点组, 关系为:读写 - graph.add_line(source=nn_group, target=zk_group, label=LineLabel.ReadWrite) - # NameNode节点 ---> JN节点组, 关系为:读写 graph.add_line(source=nn_group, target=jn_group, label=LineLabel.ReadWrite) diff --git a/dbm-ui/backend/db_meta/api/cluster/hdfs/replace.py b/dbm-ui/backend/db_meta/api/cluster/hdfs/replace.py index 620cdeaf2f..8cbf7141c1 100644 --- a/dbm-ui/backend/db_meta/api/cluster/hdfs/replace.py +++ b/dbm-ui/backend/db_meta/api/cluster/hdfs/replace.py @@ -13,12 +13,11 @@ from django.db import transaction -from backend import env -from backend.components import CCApi from backend.db_meta import request_validator from backend.db_meta.api import common from backend.db_meta.enums import InstanceRole from backend.db_meta.models import Cluster, ClusterEntry, StorageInstance +from backend.flow.utils.cc_manage import CcManage logger = logging.getLogger("root") @@ -53,9 +52,7 @@ def replace( storage.delete(keep_parents=True) if not storage.machine.storageinstance_set.exists(): # 将机器挪到 待回收模块 - CCApi.transfer_host_to_recyclemodule( - {"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "bk_host_id": [storage.machine.bk_host_id]} - ) + CcManage(storage.bk_biz_id).recycle_host([storage.machine.bk_host_id]) storage.machine.delete(keep_parents=True) cluster.storageinstance_set.remove(*storage_objs) cluster.save() diff --git a/dbm-ui/backend/db_meta/api/cluster/hdfs/shrink.py b/dbm-ui/backend/db_meta/api/cluster/hdfs/shrink.py index 8311d090af..b0bc322727 100644 --- a/dbm-ui/backend/db_meta/api/cluster/hdfs/shrink.py +++ b/dbm-ui/backend/db_meta/api/cluster/hdfs/shrink.py @@ -13,11 +13,10 @@ from django.db import transaction -from backend import env -from backend.components import CCApi from backend.db_meta import request_validator from backend.db_meta.api import common from backend.db_meta.models import Cluster, ClusterEntry, StorageInstance +from backend.flow.utils.cc_manage import CcManage logger = logging.getLogger("root") @@ -40,9 +39,7 @@ def shrink( storage.delete(keep_parents=True) if not storage.machine.storageinstance_set.exists(): # 将机器挪到 待回收 模块 - CCApi.transfer_host_to_recyclemodule( - {"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "bk_host_id": [storage.machine.bk_host_id]} - ) + CcManage(storage.bk_biz_id).recycle_host([storage.machine.bk_host_id]) storage.machine.delete(keep_parents=True) cluster.storageinstance_set.remove(*storage_objs) diff --git a/dbm-ui/backend/db_meta/api/cluster/influxdb/destroy.py b/dbm-ui/backend/db_meta/api/cluster/influxdb/destroy.py index e969562401..72ec8e7320 100644 --- a/dbm-ui/backend/db_meta/api/cluster/influxdb/destroy.py +++ b/dbm-ui/backend/db_meta/api/cluster/influxdb/destroy.py @@ -14,13 +14,10 @@ from django.db import transaction from django.utils.translation import ugettext as _ -from backend import env -from backend.components import CCApi from backend.configuration.constants import DBType -from backend.db_meta.api.common import del_service_instance -from backend.db_meta.api.db_module import delete_instance_modules from backend.db_meta.enums import ClusterType from backend.db_meta.models import GroupInstance, StorageInstance +from backend.flow.utils.cc_manage import CcManage logger = logging.getLogger("root") @@ -35,21 +32,18 @@ def destroy(addresses: Optional[List]): for storage in storages: GroupInstance.objects.get(instance_id=storage.id).delete() # 删除storage instance - del_instance_id = storage.id storage.delete(keep_parents=True) + cc_manage = CcManage(storage.bk_biz_id) if not storage.machine.storageinstance_set.exists(): # 将主机转移到待回收模块下 - logger.info(_("将主机{}转移到待回收").format(storage.machine.ip)) - CCApi.transfer_host_to_recyclemodule( - {"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "bk_host_id": [storage.machine.bk_host_id]} - ) + logger.info(_("将主机{}转移到待回收模块").format(storage.machine.ip)) + cc_manage.recycle_host([storage.machine.bk_host_id]) storage.machine.delete(keep_parents=True) else: - del_service_instance(bk_instance_id=storage.bk_instance_id) + cc_manage.delete_service_instance(bk_instance_ids=[storage.bk_instance_id]) # 删除模块 - delete_instance_modules( + cc_manage.delete_instance_modules( db_type=DBType.InfluxDB.value, - del_instance_id=del_instance_id, - bk_biz_id=storage.bk_biz_id, + ins=storage, cluster_type=ClusterType.Influxdb.value, ) diff --git a/dbm-ui/backend/db_meta/api/cluster/influxdb/replace.py b/dbm-ui/backend/db_meta/api/cluster/influxdb/replace.py index bde9ea3ecc..af62b8b2a0 100644 --- a/dbm-ui/backend/db_meta/api/cluster/influxdb/replace.py +++ b/dbm-ui/backend/db_meta/api/cluster/influxdb/replace.py @@ -14,15 +14,12 @@ from django.db import transaction from django.utils.translation import ugettext as _ -from backend import env -from backend.components import CCApi from backend.configuration.constants import DBType from backend.db_meta import request_validator from backend.db_meta.api import common -from backend.db_meta.api.common import del_service_instance -from backend.db_meta.api.db_module import delete_instance_modules from backend.db_meta.enums import ClusterType from backend.db_meta.models import GroupInstance, StorageInstance +from backend.flow.utils.cc_manage import CcManage logger = logging.getLogger("root") @@ -49,19 +46,17 @@ def replace( # 删除storage instance del_instance_id = storage.id storage.delete(keep_parents=True) + cc_manage = CcManage(storage.bk_biz_id) if not storage.machine.storageinstance_set.exists(): # 将主机转移到待回收模块下 - logger.info(_("将主机{}转移到待回收").format(storage.machine.ip)) - CCApi.transfer_host_to_recyclemodule( - {"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "bk_host_id": [storage.machine.bk_host_id]} - ) + logger.info(_("将主机{}转移到待回收模块").format(storage.machine.ip)) + cc_manage.recycle_host([storage.machine.bk_host_id]) storage.machine.delete(keep_parents=True) else: - del_service_instance(bk_instance_id=storage.bk_instance_id) + cc_manage.delete_service_instance(bk_instance_ids=[storage.bk_instance_id]) # 删除模块 - delete_instance_modules( + cc_manage.delete_instance_modules( db_type=DBType.InfluxDB.value, - del_instance_id=del_instance_id, - bk_biz_id=storage.bk_biz_id, + ins=storage, cluster_type=ClusterType.Influxdb.value, ) diff --git a/dbm-ui/backend/db_meta/api/cluster/kafka/create.py b/dbm-ui/backend/db_meta/api/cluster/kafka/create.py index 789808279f..93c5a60e00 100644 --- a/dbm-ui/backend/db_meta/api/cluster/kafka/create.py +++ b/dbm-ui/backend/db_meta/api/cluster/kafka/create.py @@ -56,7 +56,7 @@ def create( db_version: str, storages: Optional[List] = None, creator: str = "", -): +) -> Cluster: """ 注册 Kafka 集群 """ @@ -102,4 +102,4 @@ def create( ins.save() machine.save() - return cluster.id + return cluster diff --git a/dbm-ui/backend/db_meta/api/cluster/kafka/destroy.py b/dbm-ui/backend/db_meta/api/cluster/kafka/destroy.py index 37579a3826..e80b14bc66 100644 --- a/dbm-ui/backend/db_meta/api/cluster/kafka/destroy.py +++ b/dbm-ui/backend/db_meta/api/cluster/kafka/destroy.py @@ -13,12 +13,9 @@ from django.db import transaction from django.utils.translation import ugettext as _ -from backend import env -from backend.components import CCApi from backend.configuration.constants import DBType -from backend.db_meta.api.common import del_service_instance -from backend.db_meta.api.db_module import delete_cluster_modules from backend.db_meta.models import Cluster, ClusterEntry +from backend.flow.utils.cc_manage import CcManage logger = logging.getLogger("root") @@ -30,23 +27,22 @@ def destroy(cluster_id: int): """ cluster = Cluster.objects.get(id=cluster_id) + cc_manage = CcManage(cluster.bk_biz_id) # 删除storage instance for storage in cluster.storageinstance_set.all(): storage.delete(keep_parents=True) if not storage.machine.storageinstance_set.exists(): # 将主机转移到待回收模块下 - logger.info(_("将主机{}转移到待回收").format(storage.machine.ip)) - CCApi.transfer_host_to_recyclemodule( - {"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "bk_host_id": [storage.machine.bk_host_id]} - ) + logger.info(_("将主机{}转移到待回收模块").format(storage.machine.ip)) + cc_manage.recycle_host([storage.machine.bk_host_id]) storage.machine.delete(keep_parents=True) else: - del_service_instance(bk_instance_id=storage.bk_instance_id) + cc_manage.delete_service_instance(bk_instance_ids=[storage.bk_instance_id]) # 删除entry for ce in ClusterEntry.objects.filter(cluster=cluster).all(): ce.delete(keep_parents=True) - delete_cluster_modules(db_type=DBType.Kafka.value, del_cluster_id=cluster.id) + cc_manage.delete_cluster_modules(db_type=DBType.Kafka.value, cluster=cluster) cluster.delete(keep_parents=True) diff --git a/dbm-ui/backend/db_meta/api/cluster/kafka/enable.py b/dbm-ui/backend/db_meta/api/cluster/kafka/enable.py index a7bd477759..a6a1d59122 100644 --- a/dbm-ui/backend/db_meta/api/cluster/kafka/enable.py +++ b/dbm-ui/backend/db_meta/api/cluster/kafka/enable.py @@ -25,5 +25,6 @@ def enable(cluster_id: int): """ cluster = Cluster.objects.get(id=cluster_id) + cluster.phase = ClusterPhase.ONLINE.value cluster.save() diff --git a/dbm-ui/backend/db_meta/api/cluster/kafka/replace.py b/dbm-ui/backend/db_meta/api/cluster/kafka/replace.py index d8655d21ac..76d3167bea 100644 --- a/dbm-ui/backend/db_meta/api/cluster/kafka/replace.py +++ b/dbm-ui/backend/db_meta/api/cluster/kafka/replace.py @@ -14,12 +14,11 @@ from django.db import transaction from django.utils.translation import ugettext as _ -from backend import env -from backend.components import CCApi from backend.db_meta import request_validator from backend.db_meta.api import common from backend.db_meta.enums import InstanceRole from backend.db_meta.models import Cluster, ClusterEntry, StorageInstance +from backend.flow.utils.cc_manage import CcManage logger = logging.getLogger("root") @@ -49,9 +48,7 @@ def replace( if not storage.machine.storageinstance_set.exists(): # 将主机转移到待回收模块下 logger.info(_("将主机{}转移到待回收").format(storage.machine.ip)) - CCApi.transfer_host_to_recyclemodule( - {"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "bk_host_id": [storage.machine.bk_host_id]} - ) + CcManage(storage.bk_biz_id).recycle_host([storage.machine.bk_host_id]) storage.machine.delete(keep_parents=True) cluster.storageinstance_set.remove(*old_storage_objs) diff --git a/dbm-ui/backend/db_meta/api/cluster/kafka/shrink.py b/dbm-ui/backend/db_meta/api/cluster/kafka/shrink.py index e4a677c97d..a64ec6f01e 100644 --- a/dbm-ui/backend/db_meta/api/cluster/kafka/shrink.py +++ b/dbm-ui/backend/db_meta/api/cluster/kafka/shrink.py @@ -13,12 +13,10 @@ from django.db import transaction -from backend import env -from backend.components import CCApi from backend.db_meta import request_validator from backend.db_meta.api import common -from backend.db_meta.api.common import del_service_instance from backend.db_meta.models import Cluster, ClusterEntry, StorageInstance +from backend.flow.utils.cc_manage import CcManage logger = logging.getLogger("root") @@ -41,12 +39,10 @@ def shrink( storage.delete(keep_parents=True) if not storage.machine.storageinstance_set.exists(): # 将机器挪到待回收模块 - CCApi.transfer_host_to_recyclemodule( - {"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "bk_host_id": [storage.machine.bk_host_id]} - ) + CcManage(storage.bk_biz_id).recycle_host([storage.machine.bk_host_id]) storage.machine.delete(keep_parents=True) else: - del_service_instance(bk_instance_id=storage.bk_instance_id) + CcManage(storage.bk_biz_id).delete_service_instance(bk_instance_ids=[storage.bk_instance_id]) cluster.storageinstance_set.remove(*storage_objs) cluster.save() diff --git a/dbm-ui/backend/db_meta/api/cluster/mongocluster/create.py b/dbm-ui/backend/db_meta/api/cluster/mongocluster/create.py index 7f16396363..e4103c51ba 100644 --- a/dbm-ui/backend/db_meta/api/cluster/mongocluster/create.py +++ b/dbm-ui/backend/db_meta/api/cluster/mongocluster/create.py @@ -16,7 +16,6 @@ from backend.constants import DEFAULT_BK_CLOUD_ID from backend.db_meta import request_validator -from backend.db_meta.api.cluster.nosqlcomm.cc_ops import cc_add_instances from backend.db_meta.api.cluster.nosqlcomm.create_cluster import update_cluster_type from backend.db_meta.api.cluster.nosqlcomm.create_instances import create_mongo_instances, create_proxies from backend.db_meta.api.cluster.nosqlcomm.precheck import ( @@ -27,16 +26,9 @@ create_proxies_precheck, create_storage_precheck, ) -from backend.db_meta.enums import ( - ClusterEntryType, - ClusterPhase, - ClusterStatus, - ClusterType, - DBCCModule, - InstanceRole, - MachineType, -) +from backend.db_meta.enums import ClusterEntryType, ClusterPhase, ClusterStatus, ClusterType, InstanceRole, MachineType from backend.db_meta.models import Cluster, ClusterEntry, StorageInstance +from backend.flow.utils.mongodb.mongodb_module_operate import MongoDBCCTopoOperator logger = logging.getLogger("flow") @@ -131,9 +123,10 @@ def create_mongo_cluster( logger.error(traceback.format_exc()) raise Exception("mongocluster add dns entry failed {}".format(e)) - cc_add_instances(cluster, mongos_objs, DBCCModule.MONGODB.value) - cc_add_instances(cluster, config_objs, DBCCModule.MONGODB.value) - cc_add_instances(cluster, storage_objs, DBCCModule.MONGODB.value) + cc_topo_operator = MongoDBCCTopoOperator(cluster) + cc_topo_operator.transfer_instances_to_cluster_module(mongos_objs) + cc_topo_operator.transfer_instances_to_cluster_module(config_objs) + cc_topo_operator.transfer_instances_to_cluster_module(storage_objs) @transaction.atomic @@ -150,6 +143,7 @@ def pkg_create_mongo_cluster( creator: str = "", bk_cloud_id: int = DEFAULT_BK_CLOUD_ID, region: str = "", + machine_specs: Optional[Dict] = None, cluster_type=ClusterType.MongoShardedCluster.value, ): """创建副本集 MongoSet 实例 @@ -161,6 +155,7 @@ def pkg_create_mongo_cluster( proxies: [{},{}] configes: [{},{},{}] storages: [{"shard":"S1","nodes":[{"ip":,"port":,"role":},{},{}]},] + machine_specs:{"mongos":{"spec_id":0,"spec_config":""},"mongo_config":{"spec_id":0,"spec_config":""}} """ bk_biz_id = request_validator.validated_integer(bk_biz_id) @@ -179,10 +174,26 @@ def pkg_create_mongo_cluster( before_create_storage_precheck(all_instances) # 实例创建,关系创建 - create_proxies(bk_biz_id, bk_cloud_id, MachineType.MONGOS.value, proxies) - create_mongo_instances(bk_biz_id, bk_cloud_id, MachineType.MONOG_CONFIG.value, configs) + machine_specs = machine_specs or {} + spec_id, spec_config = 0, "" + if machine_specs.get(MachineType.MONGOS.value): + spec_id = machine_specs[MachineType.MONGOS.value]["spec_id"] + spec_config = machine_specs[MachineType.MONGOS.value]["spec_config"] + create_proxies(bk_biz_id, bk_cloud_id, MachineType.MONGOS.value, proxies, spec_id, spec_config) + + spec_id, spec_config = 0, "" + if machine_specs.get(MachineType.MONOG_CONFIG.value): + spec_id = machine_specs[MachineType.MONOG_CONFIG.value]["spec_id"] + spec_config = machine_specs[MachineType.MONOG_CONFIG.value]["spec_config"] + create_mongo_instances(bk_biz_id, bk_cloud_id, MachineType.MONOG_CONFIG.value, configs, spec_id, spec_config) for shard_pair in storages: - create_mongo_instances(bk_biz_id, bk_cloud_id, MachineType.MONGODB.value, shard_pair["nodes"]) + spec_id, spec_config = 0, "" + if machine_specs.get(MachineType.MONGODB.value): + spec_id = machine_specs[MachineType.MONGODB.value]["spec_id"] + spec_config = machine_specs[MachineType.MONGODB.value]["spec_config"] + create_mongo_instances( + bk_biz_id, bk_cloud_id, MachineType.MONGODB.value, shard_pair["nodes"], spec_id, spec_config + ) create_mongo_cluster( bk_biz_id=bk_biz_id, diff --git a/dbm-ui/backend/db_meta/api/cluster/mongorepset/create.py b/dbm-ui/backend/db_meta/api/cluster/mongorepset/create.py index d259bb292f..7576c68062 100644 --- a/dbm-ui/backend/db_meta/api/cluster/mongorepset/create.py +++ b/dbm-ui/backend/db_meta/api/cluster/mongorepset/create.py @@ -10,13 +10,12 @@ """ import logging import traceback -from typing import Dict, List, Optional +from typing import List, Optional from django.db import transaction from backend.constants import DEFAULT_BK_CLOUD_ID from backend.db_meta import request_validator -from backend.db_meta.api.cluster.nosqlcomm.cc_ops import cc_add_instances from backend.db_meta.api.cluster.nosqlcomm.create_cluster import update_cluster_type from backend.db_meta.api.cluster.nosqlcomm.create_instances import create_mongo_instances from backend.db_meta.api.cluster.nosqlcomm.precheck import ( @@ -25,16 +24,9 @@ create_domain_precheck, create_storage_precheck, ) -from backend.db_meta.enums import ( - ClusterEntryType, - ClusterPhase, - ClusterStatus, - ClusterType, - DBCCModule, - InstanceRole, - MachineType, -) +from backend.db_meta.enums import ClusterEntryType, ClusterPhase, ClusterStatus, ClusterType, InstanceRole, MachineType from backend.db_meta.models import Cluster, ClusterEntry, StorageInstance +from backend.flow.utils.mongodb.mongodb_module_operate import MongoDBCCTopoOperator logger = logging.getLogger("flow") @@ -51,6 +43,8 @@ def pkg_create_mongoset( creator: str = "", bk_cloud_id: int = DEFAULT_BK_CLOUD_ID, region: str = "", + spec_id: int = 0, + spec_config: str = "", cluster_type=ClusterType.MongoReplicaSet.value, ): """ @@ -74,7 +68,7 @@ def pkg_create_mongoset( before_create_domain_precheck(domains) before_create_storage_precheck(storages) - create_mongo_instances(bk_biz_id, bk_cloud_id, MachineType.MONGODB.value, storages) + create_mongo_instances(bk_biz_id, bk_cloud_id, MachineType.MONGODB.value, storages, spec_id, spec_config) create_mongoset( bk_biz_id=bk_biz_id, name=name, @@ -168,4 +162,4 @@ def create_mongoset( logger.error(traceback.format_exc()) raise Exception("mongoset add dns entry failed {}".format(e)) - cc_add_instances(cluster, storage_objs, DBCCModule.MONGODB.value) + MongoDBCCTopoOperator(cluster).transfer_instances_to_cluster_module(storage_objs) diff --git a/dbm-ui/backend/db_meta/api/cluster/nosqlcomm/__init__.py b/dbm-ui/backend/db_meta/api/cluster/nosqlcomm/__init__.py index 944bf82149..d509736903 100644 --- a/dbm-ui/backend/db_meta/api/cluster/nosqlcomm/__init__.py +++ b/dbm-ui/backend/db_meta/api/cluster/nosqlcomm/__init__.py @@ -8,7 +8,6 @@ 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 .cc_ops import cc_add_instance, cc_add_instances, cc_del_module, cc_del_service_instances, cc_transfer_idle from .create_cluster import ( create_twemproxy_cluster, pkg_create_twemproxy_cluster, @@ -18,7 +17,7 @@ from .create_instances import create_mongo_instances, create_proxies, create_tendis_instances from .decommission import decommission_cluster, decommission_instances, decommission_proxies, decommission_tendis from .detail_cluster import scan_cluster -from .other import get_clusters_details +from .other import get_cluster_detail, get_clusters_details from .precheck import ( before_create_domain_precheck, before_create_proxy_precheck, diff --git a/dbm-ui/backend/db_meta/api/cluster/nosqlcomm/cc_ops.py b/dbm-ui/backend/db_meta/api/cluster/nosqlcomm/cc_ops.py deleted file mode 100644 index c1a20e80da..0000000000 --- a/dbm-ui/backend/db_meta/api/cluster/nosqlcomm/cc_ops.py +++ /dev/null @@ -1,170 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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. -""" -import logging -import traceback -from dataclasses import asdict - -from django.db import transaction -from django.db.models import QuerySet - -from backend import env -from backend.components import CCApi -from backend.constants import CommonInstanceLabels -from backend.db_meta.api.common import add_service_instance -from backend.db_meta.api.db_module import get_or_create -from backend.db_meta.enums import AccessLayer, ClusterTypeMachineTypeDefine -from backend.db_meta.models import AppCache, Cluster, ClusterMonitorTopo, Machine -from backend.dbm_init.constants import CC_APP_ABBR_ATTR -from backend.flow.utils.cc_manage import CcManage - -logger = logging.getLogger("flow") - - -def cc_add_instance(cluster: Cluster, instance: object, module_name: str): - cc_transfer_host(cluster, instance) - cc_add_service_instance(cluster, instance, module_name) - - -def cc_add_instances(cluster: Cluster, instances: QuerySet, module_name: str): - machines = {} - for instance in instances: - if not machines.get(instance.machine.ip): - logger.info("transfer cc module for instance {}".format(instance)) - cc_transfer_host(cluster, instance) - machines[instance.machine.ip] = instance - cc_add_service_instance(cluster, instance, module_name) - - -def cc_add_service_instances(cluster: Cluster, instances: QuerySet, module_name: str): - """兼容了 Storage/Proxy - - Args: - cluster_obj (Cluster): _description_ - instances (QuerySet): _description_ - """ - for instance in instances: - cc_add_service_instance(cluster, instance, module_name) - - -def cc_transfer_hosts(cluster: Cluster, instances: QuerySet, module_name: str): - for instance in instances: - cc_add_service_instance(cluster, instance, module_name) - - -@transaction.atomic -def cc_add_service_instance(cluster: Cluster, instance: object, module_name: str): - """ - 3. 新增服务实例 - 4. 添加服务实例标签 - """ - machine_obj = instance.machine - - # 3. 新增服务实例 & 添加服务实例标签 - instance_role = instance.instance_role if instance.access_layer == AccessLayer.STORAGE else AccessLayer.PROXY.value - - bk_module_id = ClusterMonitorTopo.objects.get( - cluster_id=cluster.id, bk_biz_id=cluster.bk_biz_id, machine_type=instance.machine.machine_type - ).bk_module_id - - bk_instance_id = add_service_instance( - bk_module_id=bk_module_id, - bk_host_id=machine_obj.bk_host_id, - listen_ip=machine_obj.ip, - listen_port=instance.port, - func_name=module_name, # redis - labels_dict=asdict( - CommonInstanceLabels( - app=AppCache.get_app_attr(cluster.bk_biz_id, default=cluster.bk_biz_id), - app_id=str(cluster.bk_biz_id), - app_name=AppCache.get_app_attr(cluster.bk_biz_id, CC_APP_ABBR_ATTR, cluster.bk_biz_id), - bk_biz_id=str(cluster.bk_biz_id), - bk_cloud_id=str(cluster.bk_cloud_id), - cluster_domain=cluster.immute_domain, - cluster_name=cluster.name, - cluster_type=cluster.cluster_type, - instance_role=instance_role, - instance_host=machine_obj.ip, - instance=f"{machine_obj.ip}-{instance.port}", - ) - ), - ) - logger.info("added cc service instance for instance bk_instance_id:{}:{}".format(bk_instance_id, instance)) - instance.bk_instance_id = bk_instance_id - instance.save(update_fields=["bk_instance_id"]) - - -@transaction.atomic -def cc_transfer_host(cluster: Cluster, instance: object): - """1. 创建主机模块 - 2. 转移主机到模块 - - Args: - cluster (Cluster): _description_ - instance (object): _description_ - """ - # 更新DBMeta和创建cc目录,这里如果已经存在,则会报错 - get_or_create( - **{ - "bk_biz_id": cluster.bk_biz_id, - "cluster_id": cluster.id, - "cluster_type": cluster.cluster_type, - "cluster_domain": cluster.immute_domain, - } - ) - - bk_module_id = ClusterMonitorTopo.objects.get( - cluster_id=cluster.id, bk_biz_id=cluster.bk_biz_id, machine_type=instance.machine.machine_type - ).bk_module_id - logger.info("transfer host with info : ip:{}, bkmoduleid:{}".format(instance.machine.ip, bk_module_id)) - CcManage.transfer_machines( - bk_biz_id=cluster.bk_biz_id, - ip_modules=[{"ips": [instance.machine.ip], "bk_module_id": bk_module_id}], - bk_cloud_id=cluster.bk_cloud_id, - ) - - -@transaction.atomic -def cc_del_service_instances(instances: QuerySet): - # 这里因为id不存在会导致接口异常退出,这里暂时接收所有错误,不让它直接退出 - bk_instances = [instance.bk_instance_id for instance in instances] - ret = CCApi.delete_service_instance( - { - "bk_biz_id": env.DBA_APP_BK_BIZ_ID, - "service_instance_ids": bk_instances, - } - ) - logger.info("del bk service instances {} result {}".format(instances, ret)) - - -@transaction.atomic -def cc_transfer_idle(machine: Machine): - ret = CCApi.transfer_host_to_recyclemodule( - {"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "bk_host_id": [machine.bk_host_id]} - ) - logger.info("transfer hosts to recycle module {} result {}".format(machine.ip, ret)) - - -@transaction.atomic -def cc_del_module(cluster: Cluster): - for machine_type in ClusterTypeMachineTypeDefine[cluster.cluster_type]: - cluster_monitor_topo = ClusterMonitorTopo.objects.get( - cluster_id=cluster.id, bk_biz_id=cluster.bk_biz_id, machine_type=machine_type - ) - # .删除 cc 模块 - ret = CCApi.delete_module( - { - "bk_biz_id": env.DBA_APP_BK_BIZ_ID, - "bk_set_id": cluster_monitor_topo.bk_set_id, - "bk_module_id": cluster_monitor_topo.bk_module_id, - } - ) - logger.info("delete cc module for cluster {} {} result {}".format(cluster, cluster_monitor_topo, ret)) - cluster_monitor_topo.delete() diff --git a/dbm-ui/backend/db_meta/api/cluster/nosqlcomm/create_cluster.py b/dbm-ui/backend/db_meta/api/cluster/nosqlcomm/create_cluster.py index a4d8b6ee47..b913bd249e 100644 --- a/dbm-ui/backend/db_meta/api/cluster/nosqlcomm/create_cluster.py +++ b/dbm-ui/backend/db_meta/api/cluster/nosqlcomm/create_cluster.py @@ -10,7 +10,7 @@ """ import logging import traceback -from typing import List, Optional +from typing import Dict, List, Optional from django.db import IntegrityError, transaction from django.db.models import QuerySet @@ -19,17 +19,10 @@ from backend.constants import DEFAULT_BK_CLOUD_ID from backend.db_meta import request_validator from backend.db_meta.api import common -from backend.db_meta.enums import ( - AccessLayer, - ClusterEntryType, - ClusterPhase, - ClusterStatus, - ClusterType, - DBCCModule, - MachineType, -) +from backend.db_meta.enums import AccessLayer, ClusterEntryType, ClusterPhase, ClusterStatus, ClusterType, MachineType from backend.db_meta.models import Cluster, ClusterEntry, ProxyInstance, StorageInstance from backend.db_services.dbbase.constants import IP_PORT_DIVIDER +from backend.flow.utils.redis.redis_module_operate import RedisCCTopoOperator from ....exceptions import ( ClusterEntryExistException, @@ -37,7 +30,6 @@ DBMetaBaseException, ProxyBackendNotEmptyException, ) -from .cc_ops import cc_add_instance, cc_add_instances from .create_instances import create_proxies, create_tendis_instances from .precheck import before_create_domain_precheck, before_create_proxy_precheck, before_create_storage_precheck @@ -140,9 +132,10 @@ def create_twemproxy_cluster( slave_obj = storage_obj.as_ejector.get().receiver receivers.append(slave_obj) - cc_add_instances(cluster, proxy_objs, DBCCModule.REDIS.value) - cc_add_instances(cluster, storage_objs, DBCCModule.REDIS.value) - cc_add_instances(cluster, receivers, DBCCModule.REDIS.value) + cc_topo_operator = RedisCCTopoOperator(cluster) + cc_topo_operator.transfer_instances_to_cluster_module(proxy_objs) + cc_topo_operator.transfer_instances_to_cluster_module(storage_objs) + cc_topo_operator.transfer_instances_to_cluster_module(receivers) @transaction.atomic @@ -159,12 +152,14 @@ def pkg_create_twemproxy_cluster( bk_cloud_id: int = DEFAULT_BK_CLOUD_ID, region: str = "", cluster_type=ClusterType.TendisTwemproxyRedisInstance.value, + machine_specs: Optional[Dict] = None, ): """ 这里打包从头开始创建一个 MongoSet 包括 machine、storage、tuple、cluster、cluster_entry等 proxies [{"ip":,"port":},{}] storages [{"shard":"","nodes":{"master":{"ip":"","port":1],"slave":{}}}, {}, {}] + machine_specs {"proxy":{"spec_id":0,"spec_config":""},"redis":{"spec_id":0,"spec_config":""}} """ bk_biz_id = request_validator.validated_integer(bk_biz_id) immute_domain = request_validator.validated_domain(immute_domain) @@ -185,13 +180,26 @@ def pkg_create_twemproxy_cluster( before_create_proxy_precheck(proxies) before_create_storage_precheck(all_instances) + machine_specs = machine_specs or {} + spec_id, spec_config = 0, "" + if machine_specs.get("proxy"): + spec_id, spec_config = machine_specs["proxy"]["spec_id"], machine_specs["proxy"]["spec_config"] create_proxies( - bk_biz_id=bk_biz_id, bk_cloud_id=bk_cloud_id, machine_type=MachineType.TWEMPROXY.value, proxies=proxies + bk_biz_id=bk_biz_id, + bk_cloud_id=bk_cloud_id, + machine_type=MachineType.TWEMPROXY.value, + proxies=proxies, + spec_id=spec_id, + spec_config=spec_config, ) + + spec_id, spec_config = 0, "" + if machine_specs.get("redis"): + spec_id, spec_config = machine_specs["redis"]["spec_id"], machine_specs["redis"]["spec_config"] if cluster_type == ClusterType.TendisTwemproxyRedisInstance.value: - create_tendis_instances(bk_biz_id, bk_cloud_id, MachineType.TENDISCACHE.value, storages) + create_tendis_instances(bk_biz_id, bk_cloud_id, MachineType.TENDISCACHE.value, storages, spec_id, spec_config) elif cluster_type == ClusterType.TwemproxyTendisSSDInstance.value: - create_tendis_instances(bk_biz_id, bk_cloud_id, MachineType.TENDISSSD.value, storages) + create_tendis_instances(bk_biz_id, bk_cloud_id, MachineType.TENDISSSD.value, storages, spec_id, spec_config) else: raise Exception("unspourted cluster type : {}".format(cluster_type)) diff --git a/dbm-ui/backend/db_meta/api/cluster/nosqlcomm/create_instances.py b/dbm-ui/backend/db_meta/api/cluster/nosqlcomm/create_instances.py index e879f21d55..ef7ba49515 100644 --- a/dbm-ui/backend/db_meta/api/cluster/nosqlcomm/create_instances.py +++ b/dbm-ui/backend/db_meta/api/cluster/nosqlcomm/create_instances.py @@ -21,7 +21,7 @@ @transaction.atomic -def create_mongo_instances(bk_biz_id, bk_cloud_id, machine_type, storages): +def create_mongo_instances(bk_biz_id, bk_cloud_id, machine_type, storages, spec_id: int = 0, spec_config: str = ""): """打包创建 MongoShard/MongoConfig 类型实例, 一主N从 Args: @@ -37,6 +37,8 @@ def create_mongo_instances(bk_biz_id, bk_cloud_id, machine_type, storages): "bk_biz_id": bk_biz_id, "bk_cloud_id": bk_cloud_id, "machine_type": machine_type, + "spec_id": spec_id, + "spec_config": spec_config, } instances.append({"ip": storage["ip"], "port": storage["port"], "instance_role": storage["role"]}) if storage["role"] == InstanceRole.MONGO_M1: @@ -58,7 +60,7 @@ def create_mongo_instances(bk_biz_id, bk_cloud_id, machine_type, storages): @transaction.atomic -def create_proxies(bk_biz_id, bk_cloud_id, machine_type, proxies): +def create_proxies(bk_biz_id, bk_cloud_id, machine_type, proxies, spec_id: int = 0, spec_config: str = ""): """打包创建 Proxy 类型实例 proxy 部署类型为单机单实例部署!!! Args: @@ -73,6 +75,8 @@ def create_proxies(bk_biz_id, bk_cloud_id, machine_type, proxies): "bk_biz_id": bk_biz_id, "bk_cloud_id": bk_cloud_id, "machine_type": machine_type, + "spec_id": spec_id, + "spec_config": spec_config, } for proxy in proxies ] @@ -84,7 +88,7 @@ def create_proxies(bk_biz_id, bk_cloud_id, machine_type, proxies): @transaction.atomic -def create_tendis_instances(bk_biz_id, bk_cloud_id, machine_type, storages): +def create_tendis_instances(bk_biz_id, bk_cloud_id, machine_type, storages, spec_id: int = 0, spec_config: str = ""): """打包创建 TendisCache/TendisSSD/TendisSingle 类型实例, 一主N从 Args: @@ -101,12 +105,16 @@ def create_tendis_instances(bk_biz_id, bk_cloud_id, machine_type, storages): "bk_biz_id": bk_biz_id, "bk_cloud_id": bk_cloud_id, "machine_type": machine_type, + "spec_id": spec_id, + "spec_config": spec_config, } machines[slave["ip"]] = { "ip": slave["ip"], "bk_biz_id": bk_biz_id, "bk_cloud_id": bk_cloud_id, "machine_type": machine_type, + "spec_id": spec_id, + "spec_config": spec_config, } instances.append( {"ip": master["ip"], "port": master["port"], "instance_role": InstanceRole.REDIS_MASTER.value} diff --git a/dbm-ui/backend/db_meta/api/cluster/nosqlcomm/decommission.py b/dbm-ui/backend/db_meta/api/cluster/nosqlcomm/decommission.py index 525a191486..08a54e0205 100644 --- a/dbm-ui/backend/db_meta/api/cluster/nosqlcomm/decommission.py +++ b/dbm-ui/backend/db_meta/api/cluster/nosqlcomm/decommission.py @@ -16,10 +16,11 @@ from django.db import transaction from django.utils.translation import ugettext as _ +from backend.configuration.constants import DBType from backend.db_meta.api import common -from backend.db_meta.api.cluster.nosqlcomm.cc_ops import cc_del_module, cc_del_service_instances, cc_transfer_idle from backend.db_meta.enums import AccessLayer, InstanceStatus from backend.db_meta.models import Cluster, Machine, ProxyInstance, StorageInstance +from backend.flow.utils.cc_manage import CcManage logger = logging.getLogger("flow") @@ -46,9 +47,9 @@ def decommission_proxies(cluster: Cluster, proxies: List[Dict], is_all: bool = F if not is_all and not remain_objs: raise Exception(_("非集群下架模式,不允许直接下架所有实例 {}"), remain_objs) - cc_del_service_instances(proxy_objs) - machine_obj = defaultdict(dict) + cc_manage = CcManage(cluster.bk_biz_id) + cc_manage.delete_service_instance(bk_instance_ids=[obj.bk_instance_id for obj in proxy_objs]) for proxy_obj in proxy_objs: logger.info("cluster proxy {} for cluster {}".format(proxy_obj, cluster.immute_domain)) cluster.proxyinstance_set.remove(proxy_obj) @@ -77,7 +78,7 @@ def decommission_proxies(cluster: Cluster, proxies: List[Dict], is_all: bool = F logger.info("ignore storage machine {} , another instance existed.".format(proxy_obj.machine)) else: logger.info("proxy machine {}".format(proxy_obj.machine)) - cc_transfer_idle(proxy_obj.machine) + cc_manage.recycle_host([proxy_obj.machine.bk_host_id]) proxy_obj.machine.delete() except Exception as e: logger.error(traceback.format_exc()) @@ -96,6 +97,7 @@ def decommission_tendis(cluster: Cluster, tendiss: List[Dict], is_all: bool = Fa 2. 不允许直接下架RUNNING状态实例 """ logger.info("user request decmmission tendiss {} {}".format(cluster.immute_domain, tendiss)) + cc_manage = CcManage(cluster.bk_biz_id) try: storage_objs = common.filter_out_instance_obj(tendiss, cluster.storageinstance_set.all()) _t = common.not_exists(tendiss, storage_objs) @@ -106,23 +108,23 @@ def decommission_tendis(cluster: Cluster, tendiss: List[Dict], is_all: bool = Fa if not is_all and running_obj: raise Exception(_("非集群下架单,不允许直接下架状态为RUNNING状态实例 {}"), running_obj) - cc_del_service_instances(storage_objs) - hosts = defaultdict(dict) + cc_manage.delete_service_instance(bk_instance_ids=[obj.bk_instance_id for obj in storage_objs]) + machines = [] for storage_obj in storage_objs: logger.info("cluster storage instance {} for cluster {}".format(storage_obj, cluster.immute_domain)) cluster.storageinstance_set.remove(storage_obj) - hosts[storage_obj.machine.ip] = storage_obj.machine + machines.append(storage_obj.machine) logger.info("remove storage instance {} ".format(storage_obj)) storage_obj.delete() # 需要检查, 是否该机器上所有实例都已经清理干净, - for machine in hosts.values(): + for machine in machines: if StorageInstance.objects.filter(machine__ip=machine.ip).exists(): logger.info("ignore storage machine {} , another instance existed.".format(machine)) else: logger.info("storage machine {} ".format(machine)) - cc_transfer_idle(machine) + cc_manage.recycle_host([machine.bk_host_id]) machine.delete() except Exception as e: logger.error(traceback.format_exc()) @@ -150,7 +152,7 @@ def decommission_cluster(cluster: Cluster): cluster_entry_obj.delete() logger.info("cluster {}".format(cluster.__dict__)) - cc_del_module(cluster) + CcManage(cluster.bk_biz_id).delete_cluster_modules(db_type=DBType.Redis.value, cluster=cluster) cluster.delete() except Exception as e: @@ -168,24 +170,24 @@ def decommission_instances(ip: str, bk_cloud_id: int, ports: List) -> bool: """ machine_obj = Machine.objects.filter(ip=ip, bk_cloud_id=bk_cloud_id).first() - keepMachine = False + keep_machine = False if machine_obj.access_layer == AccessLayer.PROXY: logger.info(" proxyInstance {}:{}".format(ip, ports)) - ProxyInstance.objects.filter(machine__ip=ip, port__in=ports).delete() - if ProxyInstance.objects.filter(machine__ip=ip).exists(): - keepMachine = True + ProxyInstance.objects.filter(machine=machine_obj, port__in=ports).delete() + if ProxyInstance.objects.filter(machine=machine_obj).exists(): + keep_machine = True logger.info("ignore proxy machine {} , another instance existed.".format(ip)) elif machine_obj.access_layer in [AccessLayer.STORAGE, AccessLayer.CONFIG]: logger.info(" storageInstance {}:{}".format(ip, ports)) - StorageInstance.objects.filter(machine__ip=ip, port__in=ports).delete() - if StorageInstance.objects.filter(machine__ip=ip).exists(): - keepMachine = True + StorageInstance.objects.filter(machine=machine_obj, port__in=ports).delete() + if StorageInstance.objects.filter(machine=machine_obj).exists(): + keep_machine = True logger.info("ignore stroage machine {} , another instance existed.".format(ip)) - if not keepMachine: + if not keep_machine: logger.info(" machine {}".format(ip)) - cc_transfer_idle(machine_obj) + CcManage(machine_obj.bk_biz_id).recycle_host([machine_obj.bk_host_id]) machine_obj.delete() return True diff --git a/dbm-ui/backend/db_meta/api/cluster/nosqlcomm/other.py b/dbm-ui/backend/db_meta/api/cluster/nosqlcomm/other.py index 6324613332..2d0e4b99ea 100644 --- a/dbm-ui/backend/db_meta/api/cluster/nosqlcomm/other.py +++ b/dbm-ui/backend/db_meta/api/cluster/nosqlcomm/other.py @@ -28,6 +28,19 @@ def get_cluster_detail(cluster_id: int): raise Exception("get cluster detail failed {}".format(e)) +def get_cluster_proxies(cluster_id: int): + try: + cluster_obj = Cluster.objects.get(id=cluster_id) + return [ + {"ip": proxy_obj.machine.ip, "port": proxy_obj.port, "admin_port": proxy_obj.admin_port} + for proxy_obj in cluster_obj.proxyinstance_set.all() + ] + + except Cluster.DoesNotExist as e: + logger.error(traceback.format_exc()) + raise Exception("get cluster detail failed {}".format(e)) + + def get_clusters_details(cluster_ids: List): try: return flatten.tendis_cluster(Cluster.objects.filter(id__in=cluster_ids)) diff --git a/dbm-ui/backend/db_meta/api/cluster/nosqlcomm/scale_proxy.py b/dbm-ui/backend/db_meta/api/cluster/nosqlcomm/scale_proxy.py index c5cd6e43e0..67b3bdcf02 100644 --- a/dbm-ui/backend/db_meta/api/cluster/nosqlcomm/scale_proxy.py +++ b/dbm-ui/backend/db_meta/api/cluster/nosqlcomm/scale_proxy.py @@ -17,9 +17,10 @@ from django.db import transaction from backend.db_meta.api import common -from backend.db_meta.api.cluster.nosqlcomm.cc_ops import cc_add_instances, cc_del_service_instances -from backend.db_meta.enums import AccessLayer, ClusterMachineAccessTypeDefine, DBCCModule, InstanceInnerRole +from backend.db_meta.enums import AccessLayer, ClusterMachineAccessTypeDefine, InstanceInnerRole from backend.db_meta.models import Cluster, ProxyInstance +from backend.flow.utils.cc_manage import CcManage +from backend.flow.utils.redis.redis_module_operate import RedisCCTopoOperator logger = logging.getLogger("flow") @@ -77,7 +78,7 @@ def add_proxies(cluster: Cluster, proxies: List[Dict]): proxy_obj.storageinstance.add(*master_objs) proxy_obj.save(update_fields=["db_module_id", "cluster_type"]) logger.info("cluster {} add storageinstance {}".format(cluster.immute_domain, master_objs)) - cc_add_instances(cluster, proxy_objs, DBCCModule.REDIS.value) + RedisCCTopoOperator(cluster).transfer_instances_to_cluster_module(proxy_objs) except Exception as e: # NOCC:broad-except(检查工具误报) logger.error(traceback.format_exc()) raise e @@ -107,8 +108,7 @@ def delete_proxies(cluster: Cluster, proxies: List[Dict]): cluster.immute_domain, cluster_entry_obj.entry, proxy_objs ) ) - - cc_del_service_instances(proxy_objs) + CcManage(cluster.bk_biz_id).delete_service_instance(bk_instance_ids=[obj.bk_instance_id for obj in proxy_objs]) # 修改表 db_meta_proxyinstance_storageinstance bUG Fixed for proxy_obj in proxy_objs: proxy_obj.storageinstance.clear() diff --git a/dbm-ui/backend/db_meta/api/cluster/nosqlcomm/scale_tendis.py b/dbm-ui/backend/db_meta/api/cluster/nosqlcomm/scale_tendis.py index eb82433dee..3fe9f03b31 100644 --- a/dbm-ui/backend/db_meta/api/cluster/nosqlcomm/scale_tendis.py +++ b/dbm-ui/backend/db_meta/api/cluster/nosqlcomm/scale_tendis.py @@ -16,9 +16,9 @@ from django.db import transaction from backend.db_meta.api import common -from backend.db_meta.api.cluster.nosqlcomm.cc_ops import cc_add_instances -from backend.db_meta.enums import ClusterType, DBCCModule, InstanceInnerRole, InstanceRole, InstanceStatus, SyncType +from backend.db_meta.enums import ClusterType, InstanceInnerRole, InstanceRole, InstanceStatus, SyncType from backend.db_meta.models import Cluster, StorageInstance, StorageInstanceTuple +from backend.flow.utils.redis.redis_module_operate import RedisCCTopoOperator logger = logging.getLogger("flow") @@ -40,9 +40,10 @@ def redo_slaves(cluster: Cluster, tendisss: List[Dict], created_by: str = ""): for ej_obj in ejector_objs: if ej_obj.instance_inner_role != InstanceInnerRole.MASTER: raise Exception("ejector {} is not master ,but {}".format(ej_obj, ej_obj.instance_inner_role)) - slave = ej_obj.as_ejector.get().receiver - if slave.status == InstanceStatus.RUNNING: - raise Exception("master {} has slave {} which status is running.".format(ej_obj, slave)) + if ej_obj.as_ejector and ej_obj.as_ejector.first(): + slave = ej_obj.as_ejector.get().receiver + if slave.status == InstanceStatus.RUNNING: + raise Exception("master {} has slave {} which status is running.".format(ej_obj, slave)) try: # 修改表 db_meta_storageinstance @@ -55,7 +56,6 @@ def redo_slaves(cluster: Cluster, tendisss: List[Dict], created_by: str = ""): rec_obj.machine.ip, rec_obj.port, InstanceRole.REDIS_SLAVE ) ) - cc_add_instances(cluster, receiver_objs, DBCCModule.REDIS.value) # 修改表 db_meta_storageinstancetuple ,## 这个时候会出现一主多从 ! for ms_pair in tendisss: @@ -69,6 +69,8 @@ def redo_slaves(cluster: Cluster, tendisss: List[Dict], created_by: str = ""): logger.info("create link info {} -> {}".format(ejector_obj, receiver_obj)) # 修改表 db_meta_storageinstance_cluster cluster.storageinstance_set.add(*receiver_objs) + + RedisCCTopoOperator(cluster).transfer_instances_to_cluster_module(receiver_objs) logger.info("cluster {} add storageinstance {}".format(cluster.immute_domain, receiver_objs)) except Exception as e: # NOCC:broad-except(检查工具误报) logger.error(traceback.format_exc()) @@ -133,7 +135,7 @@ def make_sync_mms(cluster: Cluster, tendisss: List[Dict]): @transaction.atomic -def switch_tendis(cluster: Cluster, tendisss: List[Dict]): +def switch_tendis(cluster: Cluster, tendisss: List[Dict], switch_type: str = SyncType.MMS.value): """ TendisCache 建立Sync 关系为: M1->S1->M2->S2 TendisPlus 建立Sync 关系为: M1->M2->S2 @@ -189,11 +191,21 @@ def switch_tendis(cluster: Cluster, tendisss: List[Dict]): new_ejector_obj = StorageInstance.objects.get( machine__ip=ms_pair["receiver"]["ip"], port=ms_pair["receiver"]["port"] ) - new_receiver_obj = new_ejector_obj.as_ejector.get().receiver - ejector_objs.append(new_ejector_obj) - receiver_objs.append(new_receiver_obj) - cc_add_instances(cluster, ejector_objs, DBCCModule.REDIS.value) - cc_add_instances(cluster, receiver_objs, DBCCModule.REDIS.value) + if switch_type != SyncType.MS.value: + logger.info("switch for sync {} need move cc module & add cc instance".format(switch_type)) + new_receiver_obj = new_ejector_obj.as_ejector.get().receiver + ejector_objs.append(new_ejector_obj) + receiver_objs.append(new_receiver_obj) + else: + logger.info( + "switch for sync {} need update role info {} 2 master".format(switch_type, ms_pair["receiver"]) + ) + new_ejector_obj.instance_role = InstanceRole.REDIS_MASTER.value + new_ejector_obj.instance_inner_role = InstanceInnerRole.MASTER.value + new_ejector_obj.save(update_fields=["instance_role", "instance_inner_role"]) + + RedisCCTopoOperator(cluster).transfer_instances_to_cluster_module(ejector_objs) + RedisCCTopoOperator(cluster).transfer_instances_to_cluster_module(receiver_objs) except Exception as e: # NOCC:broad-except(检查工具误报) logger.error(traceback.format_exc()) raise e diff --git a/dbm-ui/backend/db_meta/api/cluster/pulsar/create.py b/dbm-ui/backend/db_meta/api/cluster/pulsar/create.py index f68f887f94..5ab0f86bdd 100644 --- a/dbm-ui/backend/db_meta/api/cluster/pulsar/create.py +++ b/dbm-ui/backend/db_meta/api/cluster/pulsar/create.py @@ -18,11 +18,7 @@ from backend.db_meta.enums import ClusterEntryType, ClusterPhase, ClusterStatus, ClusterType, InstanceRole, MachineType from backend.db_meta.models import Cluster, ClusterEntry, ClusterMonitorTopo, StorageInstance from backend.flow.consts import InstanceFuncAliasEnum -from backend.flow.utils.pulsar.pulsar_module_operate import ( - create_bk_module_for_cluster_id, - init_instance_service, - transfer_host_in_cluster_module, -) +from backend.flow.utils.pulsar.pulsar_module_operate import PulsarCCTopoOperator logger = logging.getLogger("root") @@ -86,31 +82,5 @@ def create( ins.save() m.save() - # 生成CC 域名模块 - create_bk_module_for_cluster_id(cluster_id=cluster.id) - - # pulsar主机转移模块、添加对应的服务实例 - for machine_type in [ - MachineType.PULSAR_BROKER.value, - MachineType.PULSAR_BOOKKEEPER.value, - MachineType.PULSAR_ZOOKEEPER.value, - ]: - - ip_set = set([ins.machine.ip for ins in storage_objs.filter(machine__machine_type=machine_type)]) - transfer_host_in_cluster_module( - cluster_id=cluster.id, - ip_list=list(ip_set), - machine_type=machine_type, - bk_cloud_id=cluster.bk_cloud_id, - ) - bk_module_id = ClusterMonitorTopo.objects.get( - bk_biz_id=cluster.bk_biz_id, cluster_id=cluster.id, machine_type=machine_type - ).bk_module_id - for ins in storage_objs.filter(machine__machine_type=machine_type): - init_instance_service( - cluster=cluster, - ins=ins, - bk_module_id=bk_module_id, - instance_role=ins.instance_role, - func_name=InstanceFuncAliasEnum.PULSAR_FUNC_ALIAS.value, - ) + # 生成 CC 域名模块, pulsar主机转移模块、添加对应的服务实例 + PulsarCCTopoOperator(cluster).transfer_instances_to_cluster_module(storage_objs) diff --git a/dbm-ui/backend/db_meta/api/cluster/pulsar/destroy.py b/dbm-ui/backend/db_meta/api/cluster/pulsar/destroy.py index b99421d2d0..5a1e5aa374 100644 --- a/dbm-ui/backend/db_meta/api/cluster/pulsar/destroy.py +++ b/dbm-ui/backend/db_meta/api/cluster/pulsar/destroy.py @@ -12,11 +12,9 @@ from django.db import transaction -from backend import env -from backend.components import CCApi from backend.configuration.constants import DBType -from backend.db_meta.api.db_module import delete_cluster_modules from backend.db_meta.models import Cluster, ClusterEntry +from backend.flow.utils.cc_manage import CcManage logger = logging.getLogger("root") @@ -28,23 +26,21 @@ def destroy(cluster_id: int): """ cluster = Cluster.objects.get(id=cluster_id) - + cc_manage = CcManage(cluster.bk_biz_id) # 删除storage instance for storage in cluster.storageinstance_set.all(): storage.delete(keep_parents=True) if not storage.machine.storageinstance_set.exists(): # 将主机转移到待回收模块下 - CCApi.transfer_host_to_recyclemodule( - {"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "bk_host_id": [storage.machine.bk_host_id]} - ) - + cc_manage.recycle_host([storage.machine.bk_host_id]) storage.machine.delete(keep_parents=True) + else: + cc_manage.delete_service_instance(bk_instance_ids=[storage.bk_instance_id]) # 删除entry for ce in ClusterEntry.objects.filter(cluster=cluster).all(): ce.delete(keep_parents=True) # 删除cluster monitor topo, CC API删除模块 - delete_cluster_modules(db_type=DBType.Pulsar.value, del_cluster_id=cluster_id) - + cc_manage.delete_cluster_modules(db_type=DBType.Pulsar.value, cluster=cluster) cluster.delete(keep_parents=True) diff --git a/dbm-ui/backend/db_meta/api/cluster/pulsar/replace.py b/dbm-ui/backend/db_meta/api/cluster/pulsar/replace.py index 2bf91da807..d4e0a0cab5 100644 --- a/dbm-ui/backend/db_meta/api/cluster/pulsar/replace.py +++ b/dbm-ui/backend/db_meta/api/cluster/pulsar/replace.py @@ -19,7 +19,8 @@ from backend.db_meta.api import common from backend.db_meta.enums import MachineType from backend.db_meta.models import Cluster, StorageInstance -from backend.flow.utils.pulsar.pulsar_module_operate import transfer_host_in_cluster_module +from backend.flow.utils.cc_manage import CcManage +from backend.flow.utils.pulsar.pulsar_module_operate import PulsarCCTopoOperator logger = logging.getLogger("root") @@ -42,9 +43,7 @@ def replace( storage.delete(keep_parents=True) if not storage.machine.storageinstance_set.exists(): # 将机器挪到 待回收 模块 - CCApi.transfer_host_to_recyclemodule( - {"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "bk_host_id": [storage.machine.bk_host_id]} - ) + CcManage(storage.bk_biz_id).recycle_host([storage.machine.bk_host_id]) storage.machine.delete(keep_parents=True) cluster.storageinstance_set.remove(*storage_objs) @@ -61,12 +60,4 @@ def replace( machine.save() # pulsar主机转移模块、添加对应的服务实例 - ip_set = set([ins.machine.ip for ins in storage_objs.filter(machine__machine_type=MachineType.PULSAR_ZOOKEEPER)]) - if ip_set: - transfer_host_in_cluster_module( - cluster_id=cluster.id, - ip_list=list(ip_set), - machine_type=MachineType.PULSAR_ZOOKEEPER, - bk_cloud_id=cluster.bk_cloud_id, - ) - cluster.save() + PulsarCCTopoOperator(cluster).transfer_instances_to_cluster_module(storage_objs) diff --git a/dbm-ui/backend/db_meta/api/cluster/pulsar/scale_up.py b/dbm-ui/backend/db_meta/api/cluster/pulsar/scale_up.py index 5baa900669..3793195d05 100644 --- a/dbm-ui/backend/db_meta/api/cluster/pulsar/scale_up.py +++ b/dbm-ui/backend/db_meta/api/cluster/pulsar/scale_up.py @@ -17,7 +17,7 @@ from backend.db_meta.api import common from backend.db_meta.enums import MachineType from backend.db_meta.models import Cluster, ClusterEntry, StorageInstance -from backend.flow.utils.pulsar.pulsar_module_operate import transfer_host_in_cluster_module +from backend.flow.utils.pulsar.pulsar_module_operate import PulsarCCTopoOperator logger = logging.getLogger("root") @@ -48,12 +48,4 @@ def scale_up( machine.save() # pulsar主机转移模块、添加对应的服务实例 - for machine_type in [MachineType.PULSAR_BROKER.value, MachineType.PULSAR_BOOKKEEPER.value]: - ip_set = set([ins.machine.ip for ins in storage_objs.filter(machine__machine_type=machine_type)]) - if ip_set: - transfer_host_in_cluster_module( - cluster_id=cluster.id, - ip_list=list(ip_set), - machine_type=machine_type, - bk_cloud_id=cluster.bk_cloud_id, - ) + PulsarCCTopoOperator(cluster).transfer_instances_to_cluster_module(storage_objs) diff --git a/dbm-ui/backend/db_meta/api/cluster/pulsar/shrink.py b/dbm-ui/backend/db_meta/api/cluster/pulsar/shrink.py index ce8c1d3a30..3261c79604 100644 --- a/dbm-ui/backend/db_meta/api/cluster/pulsar/shrink.py +++ b/dbm-ui/backend/db_meta/api/cluster/pulsar/shrink.py @@ -18,6 +18,7 @@ from backend.db_meta import request_validator from backend.db_meta.api import common from backend.db_meta.models import Cluster, ClusterEntry, StorageInstance +from backend.flow.utils.cc_manage import CcManage logger = logging.getLogger("root") @@ -40,9 +41,7 @@ def shrink( storage.delete(keep_parents=True) if not storage.machine.storageinstance_set.exists(): # 将机器挪到 待回收 模块 - CCApi.transfer_host_to_recyclemodule( - {"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "bk_host_id": [storage.machine.bk_host_id]} - ) + CcManage(storage.bk_biz_id).recycle_host([storage.machine.bk_host_id]) storage.machine.delete(keep_parents=True) cluster.storageinstance_set.remove(*storage_objs) diff --git a/dbm-ui/backend/db_meta/api/cluster/riak/__init__.py b/dbm-ui/backend/db_meta/api/cluster/riak/__init__.py index 4385787926..0419f3e0ec 100644 --- a/dbm-ui/backend/db_meta/api/cluster/riak/__init__.py +++ b/dbm-ui/backend/db_meta/api/cluster/riak/__init__.py @@ -9,3 +9,8 @@ specific language governing permissions and limitations under the License. """ from .create import create +from .destroy import destroy +from .disable import disable +from .enable import enable +from .scale_in import scale_in +from .scale_out import scale_out diff --git a/dbm-ui/backend/db_meta/api/cluster/riak/create.py b/dbm-ui/backend/db_meta/api/cluster/riak/create.py index 1cb34c4b08..6d89c69b8e 100644 --- a/dbm-ui/backend/db_meta/api/cluster/riak/create.py +++ b/dbm-ui/backend/db_meta/api/cluster/riak/create.py @@ -16,9 +16,11 @@ from backend.db_meta import request_validator from backend.db_meta.api import common -from backend.db_meta.enums import ClusterEntryType, ClusterPhase, ClusterStatus, ClusterType +from backend.db_meta.enums import ClusterEntryType, ClusterPhase, ClusterStatus, ClusterType, MachineType from backend.db_meta.exceptions import DBMetaException -from backend.db_meta.models import Cluster, ClusterEntry, StorageInstance +from backend.db_meta.models import Cluster, ClusterEntry, ClusterMonitorTopo, StorageInstance +from backend.flow.consts import InstanceFuncAliasEnum +from backend.flow.utils.riak.riak_module_operate import RiakCCTopoOperator logger = logging.getLogger("root") @@ -58,6 +60,7 @@ def create( name=name, alias=alias, cluster_type=ClusterType.Riak.value, + db_module_id=db_module_id, immute_domain=immute_domain, creator=creator, phase=ClusterPhase.ONLINE.value, @@ -81,4 +84,7 @@ def create( ins.save(update_fields=["db_module_id"]) m.save(update_fields=["db_module_id"]) - return cluster.id + # 生成CC 域名模块、主机转移模块、添加对应的服务实例 + RiakCCTopoOperator(cluster).transfer_instances_to_cluster_module(storage_objs) + + return cluster diff --git a/dbm-ui/backend/db_meta/api/cluster/riak/destroy.py b/dbm-ui/backend/db_meta/api/cluster/riak/destroy.py new file mode 100644 index 0000000000..31ca51fdda --- /dev/null +++ b/dbm-ui/backend/db_meta/api/cluster/riak/destroy.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging + +from django.db import transaction + +from backend.configuration.constants import DBType +from backend.db_meta.models import Cluster, ClusterEntry +from backend.flow.utils.cc_manage import CcManage + +logger = logging.getLogger("root") + + +@transaction.atomic +def destroy(cluster_id: int): + """ + 清理DBMeta + """ + + cluster = Cluster.objects.get(id=cluster_id) + cc_manage = CcManage(cluster.bk_biz_id) + # 删除storage instance + for storage in cluster.storageinstance_set.all(): + storage.delete(keep_parents=True) + if not storage.machine.storageinstance_set.exists(): + # 这个 api 不需要检查返回值, 转移主机到待回收模块,转移模块这里会把服务实例删除 + cc_manage.recycle_host([storage.machine.bk_host_id]) + storage.machine.delete(keep_parents=True) + else: + cc_manage.delete_service_instance(bk_instance_ids=[storage.bk_instance_id]) + + # 删除entry + for ce in ClusterEntry.objects.filter(cluster=cluster).all(): + ce.delete(keep_parents=True) + + # 删除cmdb中的模块 + cc_manage.delete_cluster_modules(db_type=DBType.Riak.value, cluster=cluster) + + # 删除集群 + cluster.delete(keep_parents=True) diff --git a/dbm-ui/backend/db_meta/api/cluster/riak/disable.py b/dbm-ui/backend/db_meta/api/cluster/riak/disable.py new file mode 100644 index 0000000000..2720abe9f1 --- /dev/null +++ b/dbm-ui/backend/db_meta/api/cluster/riak/disable.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging + +from django.db import transaction + +from backend.db_meta.enums import ClusterPhase, InstancePhase, InstanceStatus +from backend.db_meta.models import Cluster + +logger = logging.getLogger("root") + + +@transaction.atomic +def disable(cluster_id: int): + """ + 禁用DBMeta + """ + + cluster = Cluster.objects.get(id=cluster_id) + cluster.phase = ClusterPhase.OFFLINE.value + cluster.save() + + for storage in cluster.storageinstance_set.all(): + storage.status = InstanceStatus.UNAVAILABLE.value + storage.phase = InstancePhase.OFFLINE.value + storage.save() diff --git a/dbm-ui/backend/db_meta/api/cluster/riak/enable.py b/dbm-ui/backend/db_meta/api/cluster/riak/enable.py new file mode 100644 index 0000000000..f88958d347 --- /dev/null +++ b/dbm-ui/backend/db_meta/api/cluster/riak/enable.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging + +from django.db import transaction + +from backend.db_meta.enums import ClusterPhase, InstancePhase, InstanceStatus +from backend.db_meta.models import Cluster + +logger = logging.getLogger("root") + + +@transaction.atomic +def enable(cluster_id: int): + """ + 启用DBMeta + """ + + cluster = Cluster.objects.get(id=cluster_id) + cluster.phase = ClusterPhase.ONLINE.value + cluster.save() + + for storage in cluster.storageinstance_set.all(): + storage.status = InstanceStatus.RUNNING.value + storage.phase = InstancePhase.ONLINE.value + storage.save() diff --git a/dbm-ui/backend/db_meta/api/cluster/riak/scale_in.py b/dbm-ui/backend/db_meta/api/cluster/riak/scale_in.py new file mode 100644 index 0000000000..3566dfe826 --- /dev/null +++ b/dbm-ui/backend/db_meta/api/cluster/riak/scale_in.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging +from typing import List, Optional + +from django.db import transaction + +from backend import env +from backend.components import CCApi +from backend.db_meta import request_validator +from backend.db_meta.api import common +from backend.db_meta.models import Cluster, ClusterEntry, StorageInstance +from backend.flow.utils.cc_manage import CcManage + +logger = logging.getLogger("root") + + +@transaction.atomic +def scale_in( + cluster_id: int, + storages: Optional[List] = None, +): + """ + 缩容 riak 集群 DBMeta + """ + + cluster = Cluster.objects.get(id=cluster_id) + cluster_entry = ClusterEntry.objects.get(cluster=cluster) + cc_manage = CcManage(cluster.bk_biz_id) + + storages = request_validator.validated_storage_list(storages, allow_empty=False, allow_null=False) + storage_objs = common.filter_out_instance_obj(storages, StorageInstance.objects.all()) + for storage in storage_objs: + storage.delete(keep_parents=True) + # 将机器挪到待回收模块, riak一台机器只有一个实例 + cc_manage.recycle_host([storage.machine.bk_host_id]) + storage.machine.delete(keep_parents=True) + + cluster.storageinstance_set.remove(*storage_objs) + cluster.save() + cluster_entry.storageinstance_set.remove(*storage_objs) + cluster_entry.save() diff --git a/dbm-ui/backend/db_meta/api/cluster/riak/scale_out.py b/dbm-ui/backend/db_meta/api/cluster/riak/scale_out.py new file mode 100644 index 0000000000..231ebf67ea --- /dev/null +++ b/dbm-ui/backend/db_meta/api/cluster/riak/scale_out.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging +from typing import List, Optional + +from django.db import transaction + +from backend.db_meta import request_validator +from backend.db_meta.api import common +from backend.db_meta.enums import MachineType +from backend.db_meta.models import Cluster, ClusterEntry, StorageInstance +from backend.flow.utils.riak.riak_module_operate import RiakCCTopoOperator + +logger = logging.getLogger("root") + + +@transaction.atomic +def scale_out( + cluster_id: int, + storages: Optional[List] = None, +): + """ + 扩容 riak 集群 DBMeta + """ + + cluster = Cluster.objects.get(id=cluster_id) + storages = request_validator.validated_storage_list(storages, allow_empty=False, allow_null=False) + storage_objs = common.filter_out_instance_obj(storages, StorageInstance.objects.all()) + cluster.storageinstance_set.add(*storage_objs) + cluster_entry = ClusterEntry.objects.get(cluster=cluster) + cluster_entry.storageinstance_set.add(*storage_objs) + cluster.save() + for ins in storage_objs: + m = ins.machine + ins.db_module_id = cluster.db_module_id + m.db_module_id = cluster.db_module_id + ins.save(update_fields=["db_module_id"]) + m.save(update_fields=["db_module_id"]) + + # 主机转移模块、添加对应的服务实例 + RiakCCTopoOperator(cluster).transfer_instances_to_cluster_module(storage_objs) + return True diff --git a/dbm-ui/backend/db_meta/api/cluster/tendbcluster/__init__.py b/dbm-ui/backend/db_meta/api/cluster/tendbcluster/__init__.py index 2a86046f45..f017b06a3c 100644 --- a/dbm-ui/backend/db_meta/api/cluster/tendbcluster/__init__.py +++ b/dbm-ui/backend/db_meta/api/cluster/tendbcluster/__init__.py @@ -7,6 +7,8 @@ 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 .add_spider import add_spiders +from .add_spider_mnt import add_spider_mnt from .create_cluster import create, create_pre_check -from .create_slave_cluster import add_spider_slaves, slave_cluster_create_pre_check +from .create_slave_cluster import slave_cluster_create_pre_check from .decommission import decommission, decommission_precheck diff --git a/dbm-ui/backend/db_meta/api/cluster/tendbcluster/add_spider.py b/dbm-ui/backend/db_meta/api/cluster/tendbcluster/add_spider.py new file mode 100644 index 0000000000..140cd7cad6 --- /dev/null +++ b/dbm-ui/backend/db_meta/api/cluster/tendbcluster/add_spider.py @@ -0,0 +1,61 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 typing import List, Optional + +from django.db import transaction + +from backend.db_meta.api import common +from backend.db_meta.enums import InstanceInnerRole, TenDBClusterSpiderRole +from backend.db_meta.models import Cluster, ClusterEntry, ProxyInstance, TenDBClusterSpiderExt + + +@transaction.atomic +def add_spiders( + cluster: Optional[Cluster], + spiders: Optional[List], + spider_role: Optional[TenDBClusterSpiderRole], + domain_entry: Optional[ClusterEntry] = None, +): + """ + 添加spider节点元信息, 包括spider不同角色的添加 + """ + # 获取相关的spider节点orm对象 + spiders_objs = common.filter_out_instance_obj(spiders, ProxyInstance.objects.all()) + + # 对所有的spider实例角色属性 + for obj in spiders_objs: + TenDBClusterSpiderExt.objects.create(instance=obj, spider_role=spider_role) + + # 添加 cluster 与所有spider实例的映射关系 + cluster.proxyinstance_set.add(*spiders_objs) + + # 对所有的分片的实例,添加与spider实例的映射关系 + for shard_info in cluster.tendbclusterstorageset_set.exclude( + storage_instance_tuple__ejector__instance_inner_role=InstanceInnerRole.REPEATER + ): + if spider_role == TenDBClusterSpiderRole.SPIDER_MASTER: + # ejector是代表master实例对象 + shard_info.storage_instance_tuple.ejector.proxyinstance_set.add(*spiders_objs) + + if spider_role == TenDBClusterSpiderRole.SPIDER_SLAVE: + # receiver是代表slave实例对象 + shard_info.storage_instance_tuple.receiver.proxyinstance_set.add(*spiders_objs) + + if not spider_role == TenDBClusterSpiderRole.SPIDER_MNT: + # 非运维节点添加域名映射 + domain_entry.proxyinstance_set.add(*spiders_objs) + + # 直到这里才有明确的 db module + for ins in spiders_objs: + m = ins.machine + ins.db_module_id = cluster.db_module_id + m.db_module_id = cluster.db_module_id + ins.save(update_fields=["db_module_id"]) + m.save(update_fields=["db_module_id"]) diff --git a/dbm-ui/backend/db_meta/api/cluster/tendbcluster/add_spider_mnt.py b/dbm-ui/backend/db_meta/api/cluster/tendbcluster/add_spider_mnt.py new file mode 100644 index 0000000000..5dfc383e65 --- /dev/null +++ b/dbm-ui/backend/db_meta/api/cluster/tendbcluster/add_spider_mnt.py @@ -0,0 +1,62 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging +from typing import List, Optional + +from django.db import transaction + +from backend.db_meta.api import common +from backend.db_meta.enums import InstanceInnerRole, TenDBClusterSpiderRole +from backend.db_meta.models import Cluster, ProxyInstance, TenDBClusterSpiderExt + +logger = logging.getLogger("root") + + +@transaction.atomic +def add_spider_mnt( + cluster: Optional[Cluster], + spiders: Optional[List], +): + """ + 添加运维节点的集群相关信息 + 与集群进行关联 + """ + # 获取相关的spider节点orm对象 + spiders_objs = common.filter_out_instance_obj(spiders, ProxyInstance.objects.all()) + + # TenDBClusterSpiderExt是对ProxyInstance的延伸,使用一对一关系来补充信息,主要补充了spider节点的类别 + for obj in spiders_objs: + TenDBClusterSpiderExt.objects.create(instance=obj, spider_role=TenDBClusterSpiderRole.SPIDER_MNT) + + # 添加 cluster 与所有spider云维实例的映射关系 + cluster.proxyinstance_set.add(*spiders_objs) + + # 对所有的分片的slave实例,添加与spider slave实例的映射关系 + for shard_info in cluster.tendbclusterstorageset_set.exclude( + storage_instance_tuple__ejector__instance_inner_role=InstanceInnerRole.REPEATER + ): + # receiver是代表slave实例对象 + # shard_info是db_meta_tendbclusterstorageset对象 + # db_meta_tendbclusterstorageset.storage_instance_tuple(id)指向db_meta_storageinstancetuple + # shard_info.storage_instance_tuple.receiver指向db_meta_storageinstance + # shard_info.storage_instance_tuple.receiver.proxyinstance_set proxyinstance_set为多对多表反向操作关联管理器 + shard_info.storage_instance_tuple.ejector.proxyinstance_set.add(*spiders_objs) + + # 添加从域名映射 + cluster_entry = cluster.clusterentry_set.first() + cluster_entry.proxyinstance_set.add(*spiders_objs) + + # 直到这里才有明确的 db module + for ins in spiders_objs: + m = ins.machine + ins.db_module_id = cluster.db_module_id + m.db_module_id = cluster.db_module_id + ins.save(update_fields=["db_module_id"]) + m.save(update_fields=["db_module_id"]) diff --git a/dbm-ui/backend/db_meta/api/cluster/tendbcluster/create_cluster.py b/dbm-ui/backend/db_meta/api/cluster/tendbcluster/create_cluster.py index bda1d666eb..e3ab5996dd 100644 --- a/dbm-ui/backend/db_meta/api/cluster/tendbcluster/create_cluster.py +++ b/dbm-ui/backend/db_meta/api/cluster/tendbcluster/create_cluster.py @@ -84,10 +84,9 @@ def create( time_zone: str, spiders: Optional[List], storages: Optional[List], - deploy_plan_id: int, creator: str = "", region: str = "", -): +) -> Cluster: """ 注册 TenDBCluster 集群 """ @@ -120,7 +119,6 @@ def create( bk_cloud_id=bk_cloud_id, time_zone=time_zone, major_version=major_version, # 这里存储集群的主版本信息,主要是为展示,存储mysql版本 - deploy_plan_id=deploy_plan_id, # 这里存储当时选择的部署方案ID region=region, # 这里保存申请资源的地域信息 ) @@ -168,4 +166,4 @@ def create( ins.save(update_fields=["db_module_id"]) m.save(update_fields=["db_module_id"]) - return cluster.id + return cluster diff --git a/dbm-ui/backend/db_meta/api/cluster/tendbcluster/create_slave_cluster.py b/dbm-ui/backend/db_meta/api/cluster/tendbcluster/create_slave_cluster.py index 34b82c1aaa..ffef7a7982 100644 --- a/dbm-ui/backend/db_meta/api/cluster/tendbcluster/create_slave_cluster.py +++ b/dbm-ui/backend/db_meta/api/cluster/tendbcluster/create_slave_cluster.py @@ -12,10 +12,9 @@ from django.db import transaction from django.utils.translation import ugettext as _ -from backend.db_meta.api import common -from backend.db_meta.enums import ClusterEntryRole, ClusterEntryType, InstanceInnerRole, TenDBClusterSpiderRole +from backend.db_meta.enums import ClusterEntryType from backend.db_meta.exceptions import DBMetaException -from backend.db_meta.models import Cluster, ClusterEntry, ProxyInstance, TenDBClusterSpiderExt +from backend.db_meta.models import ClusterEntry @transaction.atomic @@ -31,41 +30,3 @@ def slave_cluster_create_pre_check(slave_domain: str): if pre_check_errors: raise DBMetaException(message=", ".join(pre_check_errors)) - - -@transaction.atomic -def add_spider_slaves( - cluster: Optional[Cluster], - spiders: Optional[List], - cluster_slave_entry: Optional[ClusterEntry], -): - """ - 添加从节点元信息 - """ - # 获取相关的spider节点orm对象 - spiders_objs = common.filter_out_instance_obj(spiders, ProxyInstance.objects.all()) - - # 对所有的spider slave实例角色属性 - for obj in spiders_objs: - TenDBClusterSpiderExt.objects.create(instance=obj, spider_role=TenDBClusterSpiderRole.SPIDER_SLAVE) - - # 添加 cluster 与所有spider slave 实例的映射关系 - cluster.proxyinstance_set.add(*spiders_objs) - - # 对所有的分片的slave实例,添加与spider slave实例的映射关系 - for shard_info in cluster.tendbclusterstorageset_set.exclude( - storage_instance_tuple__ejector__instance_inner_role=InstanceInnerRole.REPEATER - ): - # receiver是代表slave实例对象 - shard_info.storage_instance_tuple.receiver.proxyinstance_set.add(*spiders_objs) - - # 添加从域名映射 - cluster_slave_entry.proxyinstance_set.add(*spiders_objs) - - # 直到这里才有明确的 db module - for ins in spiders_objs: - m = ins.machine - ins.db_module_id = cluster.db_module_id - m.db_module_id = cluster.db_module_id - ins.save(update_fields=["db_module_id"]) - m.save(update_fields=["db_module_id"]) diff --git a/dbm-ui/backend/db_meta/api/cluster/tendbcluster/decommission.py b/dbm-ui/backend/db_meta/api/cluster/tendbcluster/decommission.py index f93e12155a..d26fc205c8 100644 --- a/dbm-ui/backend/db_meta/api/cluster/tendbcluster/decommission.py +++ b/dbm-ui/backend/db_meta/api/cluster/tendbcluster/decommission.py @@ -12,10 +12,9 @@ from django.db import transaction from django.utils.translation import ugettext as _ -from backend import env -from backend.components import CCApi from backend.db_meta.exceptions import DBMetaException from backend.db_meta.models import Cluster, ClusterEntry, StorageInstanceTuple +from backend.flow.utils.cc_manage import CcManage logger = logging.getLogger("root") @@ -23,6 +22,7 @@ @transaction.atomic def decommission(cluster: Cluster): # 处理spider节点信息 + cc_manage = CcManage(cluster.bk_biz_id) for spider in cluster.proxyinstance_set.all(): # 先删除额外的spider关联信息,否则直接删除实例,会报ProtectedError 异常 spider.tendbclusterspiderext.delete() @@ -30,9 +30,7 @@ def decommission(cluster: Cluster): if not spider.machine.proxyinstance_set.exists(): # 这个 api 不需要检查返回值, 转移主机到空闲模块,转移模块这里会把服务实例删除 - CCApi.transfer_host_to_recyclemodule( - {"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "bk_host_id": [spider.machine.bk_host_id]} - ) + cc_manage.recycle_host([spider.machine.bk_host_id]) spider.machine.delete(keep_parents=True) # 处理remote节点信息 @@ -51,9 +49,7 @@ def decommission(cluster: Cluster): if not remote.machine.storageinstance_set.exists(): # 这个 api 不需要检查返回值, 转移主机到待回收模块,转移模块这里会把服务实例删除 - CCApi.transfer_host_to_recyclemodule( - {"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "bk_host_id": [remote.machine.bk_host_id]} - ) + cc_manage.recycle_host([remote.machine.bk_host_id]) remote.machine.delete(keep_parents=True) for ce in ClusterEntry.objects.filter(cluster=cluster).all(): @@ -68,7 +64,7 @@ def decommission(cluster: Cluster): @transaction.atomic def decommission_precheck(cluster: Cluster): """ - Tendb cluster 不可能出现集群间访问关系, 只会有同步关系 + TenDBCluster cluster 不可能出现集群间访问关系, 只会有同步关系 """ precheck_err = [] diff --git a/dbm-ui/backend/db_meta/api/cluster/tendbcluster/detail.py b/dbm-ui/backend/db_meta/api/cluster/tendbcluster/detail.py index 65cd85d5f5..ff4e5a47b8 100644 --- a/dbm-ui/backend/db_meta/api/cluster/tendbcluster/detail.py +++ b/dbm-ui/backend/db_meta/api/cluster/tendbcluster/detail.py @@ -63,6 +63,11 @@ def build_spider_remote_relations(role, spider_group, remote_group_name): entry_group_id=_("spider_slave_entry_bind"), entry_group_name=_("访问入口(从)"), ) + + # 建立spider_master和spider_slave之间的关系 + if spider_master_group and spider_slave_group: + graph.add_line(source=spider_master_group, target=spider_slave_group, label=LineLabel.Access) + # 建立spider master与remote db的关系 __, remote_db_group = build_spider_remote_relations( InstanceRole.REMOTE_MASTER, spider_group=spider_master_group, remote_group_name=_("RemoteDB") @@ -72,13 +77,15 @@ def build_spider_remote_relations(role, spider_group, remote_group_name): InstanceRole.REMOTE_SLAVE, spider_group=spider_slave_group, remote_group_name=_("RemoteDR") ) - # 收纳运维节点 TODO: 这个关系该怎么展示? + # 建立remote dr与remote db的数据同步关系 + graph.add_line(source=remote_db_group, target=remote_dr_group, label=LineLabel.Rep) + + # 收纳运维节点 spider_mnt_insts, spider_mnt_group = graph.add_spider_nodes( cluster, TenDBClusterSpiderRole.SPIDER_MNT, group_name=_("Spider 运维节点") ) if spider_mnt_insts: - graph.add_line(source=spider_mnt_insts, target=remote_db_group, label=LineLabel.Access) - graph.add_line(source=spider_mnt_insts, target=remote_dr_group, label=LineLabel.Access) + graph.add_line(source=spider_mnt_group, target=remote_db_group, label=LineLabel.Access) # 收纳中控节点 TODO: 如何表示关系 controller_group = Group(node_id=_("controller_group"), group_name=_("中控节点")) diff --git a/dbm-ui/backend/db_meta/api/cluster/tendbcluster/handler.py b/dbm-ui/backend/db_meta/api/cluster/tendbcluster/handler.py index 857e5d8f27..1dbfb8b3b0 100644 --- a/dbm-ui/backend/db_meta/api/cluster/tendbcluster/handler.py +++ b/dbm-ui/backend/db_meta/api/cluster/tendbcluster/handler.py @@ -11,16 +11,28 @@ from typing import List, Optional from django.db import transaction +from django.db.models import F +from django.utils.translation import ugettext as _ from backend.configuration.constants import DBType from backend.db_meta import api from backend.db_meta.api.cluster.base.handler import ClusterHandler -from backend.db_meta.enums import ClusterEntryRole, ClusterEntryType, ClusterType, InstanceRole, MachineType -from backend.db_meta.models import Cluster, ClusterEntry +from backend.db_meta.enums import ( + ClusterEntryRole, + ClusterEntryType, + ClusterType, + InstanceInnerRole, + InstanceRole, + MachineType, + TenDBClusterSpiderRole, +) +from backend.db_meta.exceptions import InstanceNotExistException +from backend.db_meta.models import Cluster, ClusterEntry, ProxyInstance, StorageInstanceTuple from backend.db_package.models import Package -from backend.flow.consts import MediumEnum +from backend.flow.consts import MediumEnum, TenDBBackUpLocation from backend.flow.engine.bamboo.scene.common.get_real_version import get_mysql_real_version, get_spider_real_version -from backend.flow.utils.mysql.bk_module_operate import create_bk_module_for_cluster_id, transfer_host_in_cluster_module +from backend.flow.utils.cc_manage import CcManage +from backend.flow.utils.mysql.mysql_module_operate import MysqlCCTopoOperator from backend.flow.utils.spider.spider_act_dataclass import ShardInfo @@ -46,7 +58,6 @@ def create( time_zone: str, bk_cloud_id: int, shard_infos: Optional[List[ShardInfo]], - deploy_plan_id: int, resource_spec: dict, region: str, ): @@ -113,14 +124,14 @@ def create( "version": get_spider_real_version(spider_pkg.name), } ) - api.storage_instance.create(instances=storages, creator=creator, time_zone=time_zone) - api.proxy_instance.create(proxies=spiders, creator=creator, time_zone=time_zone) + storage_objs = api.storage_instance.create(instances=storages, creator=creator, time_zone=time_zone) + proxy_objs = api.proxy_instance.create(proxies=spiders, creator=creator, time_zone=time_zone) # 录入集群的相关云信息 api.cluster.tendbcluster.create_pre_check( bk_biz_id=bk_biz_id, name=cluster_name, immutable_domain=immutable_domain, db_module_id=db_module_id ) - cluster_id = api.cluster.tendbcluster.create( + cluster = api.cluster.tendbcluster.create( bk_biz_id=bk_biz_id, name=cluster_name, immutable_domain=immutable_domain, @@ -131,29 +142,15 @@ def create( time_zone=time_zone, spiders=spiders, storages=storages, - deploy_plan_id=deploy_plan_id, creator=creator, region=region, ) - # 生成域名模块 - create_bk_module_for_cluster_id(cluster_ids=[cluster_id]) - + cc_topo_operator = MysqlCCTopoOperator(cluster) # mysql主机转移模块、添加对应的服务实例 - transfer_host_in_cluster_module( - cluster_ids=[cluster_id], - ip_list=[ip_info["ip"] for ip_info in mysql_ip_list], - machine_type=MachineType.REMOTE.value, - bk_cloud_id=bk_cloud_id, - ) - + cc_topo_operator.transfer_instances_to_cluster_module(storage_objs) # spider主机转移模块、添加对应的服务实例 - transfer_host_in_cluster_module( - cluster_ids=[cluster_id], - ip_list=[ip_info["ip"] for ip_info in spider_ip_list], - machine_type=MachineType.SPIDER.value, - bk_cloud_id=bk_cloud_id, - ) + cc_topo_operator.transfer_instances_to_cluster_module(proxy_objs) @transaction.atomic def decommission(self): @@ -167,35 +164,41 @@ def topo_graph(self): @classmethod @transaction.atomic - def add_spider_slaves( + def add_spiders( cls, cluster_id: int, creator: str, spider_version: str, - slave_domain: str, - spider_slaves: list, - is_create: bool, + add_spiders: list, + spider_role: Optional[TenDBClusterSpiderRole], + resource_spec: dict, + is_slave_cluster_create: bool, + domain: str = None, ): """ - 对已有的集群添加从集群信息 + 对已有的集群添加spider的元信息 因为从集群添加的行为spider-slave扩容行为基本类似,所以这里作为一个公共方法,对域名处理根据不同单据类型做不同的处理 @param cluster_id: 待关联的集群id @param creator: 提单的用户名称 @param spider_version: 待加入的spider版本号(包括小版本信息) - @param slave_domain: 待添加从域名 - @param spider_slaves: 待加入的spider-slave机器信息 - @param is_create: 代表这次是否是添加从集群,还是spider-slave扩容 + @param domain: 待关联的域名 + @param add_spiders: 待加入的spider机器信息 + @param spider_role: 待加入spider的角色 + @param resource_spec: 待加入spider的规格 + @param is_slave_cluster_create: 代表这次是否是添加从集群 """ cluster = Cluster.objects.get(id=cluster_id) # 录入机器 machines = [] - for ip_info in spider_slaves: + for ip_info in add_spiders: machines.append( { "ip": ip_info["ip"], "bk_biz_id": cluster.bk_biz_id, "machine_type": MachineType.SPIDER.value, + "spec_id": resource_spec[MachineType.SPIDER.value]["id"], + "spec_config": resource_spec[MachineType.SPIDER.value], }, ) # 录入机器信息 @@ -207,50 +210,121 @@ def add_spider_slaves( version=spider_version, pkg_type=MediumEnum.Spider, db_type=DBType.MySQL ) - for ip_info in spider_slaves: + for ip_info in add_spiders: spiders.append( { "ip": ip_info["ip"], "port": cluster.proxyinstance_set.first().port, - "admin_port": cluster.proxyinstance_set.first().admin_port, # spider_slave是否存储管理端口? + "admin_port": cluster.proxyinstance_set.first().admin_port, "version": get_spider_real_version(spider_pkg.name), } ) # 新增的实例继承cluster集群的时区设置 - api.proxy_instance.create(proxies=spiders, creator=creator, time_zone=cluster.time_zone) + spider_objs = api.proxy_instance.create(proxies=spiders, creator=creator, time_zone=cluster.time_zone) - # 判断is_create参数,如果是True则代表做从集群添加,需要添加从域名元信息;如果False则代表spider-slave扩容 - if is_create: - api.cluster.tendbcluster.slave_cluster_create_pre_check(slave_domain=slave_domain) - cluster_slave_entry = ClusterEntry.objects.create( + # 判断is_slave_cluster_create参数,如果是True则代表做从集群添加,需要添加从域名元信息;如果False则代表spider扩容 + if is_slave_cluster_create: + api.cluster.tendbcluster.slave_cluster_create_pre_check(slave_domain=domain) + cluster_entry = ClusterEntry.objects.create( cluster=cluster, cluster_entry_type=ClusterEntryType.DNS, - entry=slave_domain, + entry=domain, creator=creator, role=ClusterEntryRole.SLAVE_ENTRY.value, ) else: - cluster_slave_entry = cluster.clusterentry_set.get(entry=slave_domain) + if domain: + cluster_entry = cluster.clusterentry_set.get(entry=domain) + else: + # 运维节点添加不需要做域名映射 + cluster_entry = None # 录入集群相关信息 - api.cluster.tendbcluster.add_spider_slaves( - cluster=cluster, spiders=spiders, cluster_slave_entry=cluster_slave_entry + api.cluster.tendbcluster.add_spiders( + cluster=cluster, spiders=spiders, domain_entry=cluster_entry, spider_role=spider_role ) # spider主机转移模块、添加对应的服务实例 - transfer_host_in_cluster_module( - cluster_ids=[cluster_id], - ip_list=[ip_info["ip"] for ip_info in spider_slaves], - machine_type=MachineType.SPIDER.value, - bk_cloud_id=cluster.bk_cloud_id, - ) + MysqlCCTopoOperator(cluster).transfer_instances_to_cluster_module(spider_objs) @classmethod @transaction.atomic - def add_spider_master( + def reduce_spider( cls, cluster_id: int, - creator: str, - spider_masters: list, + spiders: list, ): - pass + """ + 对已有的集群删除待卸载的spider节点 + """ + cluster = Cluster.objects.get(id=cluster_id) + cc_manage = CcManage(cluster.bk_biz_id) + for info in spiders: + # 同一台spider机器专属于一个集群 + spider = cluster.proxyinstance_set.get(machine__ip=info["ip"]) + # 先删除额外的spider关联信息,否则直接删除实例,会报ProtectedError 异常 + spider.tendbclusterspiderext.delete() + spider.delete(keep_parents=True) + if not spider.machine.proxyinstance_set.exists(): + # 这个 api 不需要检查返回值, 转移主机到空闲模块,转移模块这里会把服务实例删除 + cc_manage.recycle_host([spider.machine.bk_host_id]) + spider.machine.delete(keep_parents=True) + + @classmethod + @transaction.atomic + def remote_switch(cls, cluster_id: int, switch_tuples: list): + """ + 对已有集群的remote存储对进行切换记录 + """ + cluster = Cluster.objects.get(id=cluster_id) + cc_manage = CcManage(cluster.bk_biz_id) + for switch_tuple in switch_tuples: + # 理论上remote机器专属一套TenDB-Cluster集群 + + # 机器所有的实例更改角色 + slave_objs = cluster.storageinstance_set.filter(machine__ip=switch_tuple["slave"]["ip"]) + master_objs = cluster.storageinstance_set.filter(machine__ip=switch_tuple["master"]["ip"]) + slave_objs.update(instance_role=InstanceRole.REMOTE_MASTER, instance_inner_role=InstanceInnerRole.MASTER) + master_objs.update(instance_role=InstanceRole.REMOTE_SLAVE, instance_inner_role=InstanceInnerRole.SLAVE) + + # 修改主从的映射关系 + for obj in master_objs: + StorageInstanceTuple.objects.filter(ejector=obj).update(ejector=F("receiver"), receiver=obj) + + # 切换新master服务实例角色标签 + cc_manage.add_label_for_service_instance( + bk_instance_ids=[obj.bk_instance_id for obj in slave_objs], + labels_dict={"instance_role": InstanceRole.BACKEND_MASTER.value}, + ) + + # 切换新slave服务实例角色标签 + cc_manage.add_label_for_service_instance( + bk_instance_ids=[obj.bk_instance_id for obj in master_objs], + labels_dict={"instance_role": InstanceRole.BACKEND_SLAVE.value}, + ) + + def get_remote_address(self, role=TenDBBackUpLocation.REMOTE) -> str: + """ + 查询DRS访问远程数据库的地址,你默认查询remote的db + """ + role = ( + TenDBClusterSpiderRole.SPIDER_MASTER + if role == TenDBBackUpLocation.REMOTE + else TenDBClusterSpiderRole.SPIDER_MNT + ) + + inst = ProxyInstance.objects.filter(cluster=self.cluster, tendbclusterspiderext__spider_role=role) + if not inst: + raise InstanceNotExistException(_("集群{}不具有该角色「{}」的实例").format(self.cluster.name, role)) + + return inst.first().ip_port + + @classmethod + @transaction.atomic + def clear_clusterentry(cls, cluster_id: int): + cluster = Cluster.objects.get(id=cluster_id) + clusterentry = cluster.clusterentry_set.filter( + cluster_entry_type=ClusterEntryType.DNS.value, role=ClusterEntryRole.SLAVE_ENTRY.value + ).all() + for ce in clusterentry: + ce.delete(keep_parents=True) diff --git a/dbm-ui/backend/db_meta/api/cluster/tendbcluster/remotedb_node_migrate.py b/dbm-ui/backend/db_meta/api/cluster/tendbcluster/remotedb_node_migrate.py new file mode 100644 index 0000000000..65819ac217 --- /dev/null +++ b/dbm-ui/backend/db_meta/api/cluster/tendbcluster/remotedb_node_migrate.py @@ -0,0 +1,167 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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.db import transaction + +from backend.configuration.constants import DBType +from backend.db_meta import api, request_validator +from backend.db_meta.api import common +from backend.db_meta.enums import ClusterType, InstanceRole, InstanceStatus, MachineType +from backend.db_meta.models import Cluster, StorageInstance, StorageInstanceTuple, TenDBClusterStorageSet +from backend.db_package.models import Package +from backend.flow.consts import MediumEnum +from backend.flow.engine.bamboo.scene.common.get_real_version import get_mysql_real_version +from backend.flow.utils.mysql.mysql_module_operate import MysqlCCTopoOperator + + +class TenDBClusterMigrateRemoteDb: + cluster_type = ClusterType.TenDBCluster + + @classmethod + @transaction.atomic + def storage_create( + cls, + cluster_id: int, + ports: list, + creator: str, + mysql_version: str, + resource_spec: dict, + slave_ip: str = None, + master_ip: str = None, + ): + """主从成对迁移初始化机器写入元数据,兼容单实例安装""" + cluster = Cluster.objects.get(id=cluster_id) + bk_cloud_id = cluster.bk_cloud_id + bk_biz_id = cluster.bk_biz_id + time_zone = cluster.time_zone + mysql_pkg = Package.get_latest_package(version=mysql_version, pkg_type=MediumEnum.MySQL, db_type=DBType.MySQL) + machines = [] + storages = [] + if master_ip is not None: + machines.append( + { + "ip": master_ip, + "bk_biz_id": int(bk_biz_id), + "machine_type": MachineType.REMOTE.value, + "spec_id": resource_spec[MachineType.REMOTE.value]["id"], + "spec_config": resource_spec[MachineType.REMOTE.value], + } + ) + for port in ports: + storages.append( + { + "ip": master_ip, + "port": port, + "instance_role": InstanceRole.REMOTE_MASTER.value, + "is_stand_by": True, # 标记实例属于切换组实例 + "db_version": get_mysql_real_version(mysql_pkg.name), # 存储真正的版本号信息 + }, + ) + if slave_ip is not None: + machines.append( + { + "ip": slave_ip, + "bk_biz_id": int(bk_biz_id), + "machine_type": MachineType.REMOTE.value, + "spec_id": resource_spec[MachineType.REMOTE.value]["id"], + "spec_config": resource_spec[MachineType.REMOTE.value], + } + ) + for port in ports: + storages.append( + { + "ip": slave_ip, + "port": port, + "instance_role": InstanceRole.REMOTE_SLAVE.value, + "is_stand_by": True, # 标记实例属于切换组实例 + "db_version": get_mysql_real_version(mysql_pkg.name), # 存储真正的版本号信息 + }, + ) + + api.machine.create(machines=machines, creator=creator, bk_cloud_id=bk_cloud_id) + api.storage_instance.create( + instances=storages, creator=creator, time_zone=time_zone, status=InstanceStatus.RESTORING + ) + # cluster映射关系 + storages = request_validator.validated_storage_list(storages, allow_empty=False, allow_null=False) + storage_objs = common.filter_out_instance_obj(storages, StorageInstance.objects.all()) + + cluster.storageinstance_set.add(*storage_objs) + # 转移模块 + cc_topo_operator = MysqlCCTopoOperator(cluster) + cc_topo_operator.transfer_instances_to_cluster_module(storage_objs) + + @classmethod + @transaction.atomic + def switch_remote_node(cls, cluster_id: int, source: dict, target: dict): + """ + remotedb 分片切换 + source:{master:{ip:xx,port:xx},slave:"ip:xx,port:xx"} + 修正storage的状态>映射分片 + """ + cluster = Cluster.objects.get(id=cluster_id) + bk_cloud_id = cluster.bk_cloud_id + source_master_obj = StorageInstance.objects.get( + machine__ip=source["master"]["ip"], port=source["master"]["port"], machine__bk_cloud_id=bk_cloud_id + ) + source_slave_obj = StorageInstance.objects.get( + machine__ip=source["slave"]["ip"], port=source["slave"]["port"], machine__bk_cloud_id=bk_cloud_id + ) + target_master_obj = StorageInstance.objects.get( + machine__ip=target["master"]["ip"], port=target["master"]["port"], machine__bk_cloud_id=bk_cloud_id + ) + target_slave_obj = StorageInstance.objects.get( + machine__ip=target["slave"]["ip"], port=target["slave"]["port"], machine__bk_cloud_id=bk_cloud_id + ) + target_master_obj.status = InstanceStatus.RUNNING + target_slave_obj.status = InstanceStatus.RUNNING + target_master_obj.instance_role = InstanceRole.REMOTE_MASTER + target_slave_obj.instance_role = InstanceRole.REMOTE_SLAVE + target_master_obj.save() + target_slave_obj.save() + source_tuple = StorageInstanceTuple.objects.get(ejector=source_master_obj, receiver=source_slave_obj) + target_tuple = StorageInstanceTuple.objects.get(ejector=target_master_obj, receiver=target_slave_obj) + # 删除原本shard 新增shard + storage_shard = TenDBClusterStorageSet.objects.get( + cluster_id=cluster_id, storage_instance_tuple_id=source_tuple + ) + storage_shard.storage_instance_tuple = target_tuple + storage_shard.save() + + @classmethod + @transaction.atomic + def add_storage_tuple(cls, cluster_id: int, storage: dict): + """ + 添加成对迁移的主从映射关系 + storage:{master:{ip:xx,port:xx},slave:"ip:xx,port:xx"} + 新主库改为 storageinstance 角色为repeater + """ + cluster = Cluster.objects.get(id=cluster_id) + bk_cloud_id = cluster.bk_cloud_id + master_obj = StorageInstance.objects.get( + machine__ip=storage["master"]["ip"], port=storage["master"]["port"], machine__bk_cloud_id=bk_cloud_id + ) + slave_obj = StorageInstance.objects.get( + machine__ip=storage["slave"]["ip"], port=storage["slave"]["port"], machine__bk_cloud_id=bk_cloud_id + ) + StorageInstanceTuple.objects.create(ejector=master_obj, receiver=slave_obj) + + @classmethod + @transaction.atomic + def uninstall_storage(cls, cluster_id: int, ip: str): + cluster = Cluster.objects.get(id=cluster_id) + bk_cloud_id = cluster.bk_cloud_id + cluster.storageinstance_set.remove() + storage_instances = StorageInstance.objects.filter(machine__ip=ip, machine__bk_cloud_id=bk_cloud_id) + for one in storage_instances: + StorageInstanceTuple.objects.filter(ejector=one.id).delete() + StorageInstanceTuple.objects.filter(receiver=one.id).delete() + StorageInstance.objects.filter(machine__ip=ip, machine__bk_cloud_id=bk_cloud_id).delete() + api.machine.delete(machines=["ip"], bk_cloud_id=cluster.bk_cloud_id) diff --git a/dbm-ui/backend/db_meta/api/cluster/tendbha/add_proxy.py b/dbm-ui/backend/db_meta/api/cluster/tendbha/add_proxy.py index 14d1a3b103..c70d1879e3 100644 --- a/dbm-ui/backend/db_meta/api/cluster/tendbha/add_proxy.py +++ b/dbm-ui/backend/db_meta/api/cluster/tendbha/add_proxy.py @@ -12,10 +12,9 @@ from django.db import transaction -from backend.constants import DEFAULT_BK_CLOUD_ID from backend.db_meta.enums import ClusterEntryType, InstanceInnerRole, InstanceStatus, MachineType from backend.db_meta.models import Cluster, ProxyInstance, StorageInstance -from backend.flow.utils.mysql.bk_module_operate import transfer_host_in_cluster_module +from backend.flow.utils.mysql.mysql_module_operate import MysqlCCTopoOperator logger = logging.getLogger("root") @@ -26,9 +25,9 @@ def add_proxy(cluster_ids: list, proxy_ip: str, bk_cloud_id: int): 集群添加proxy场景元数据注册方式 默认情况下新的proxy的时区信息以集群的记录为准 """ - - for cluster_id in cluster_ids: - cluster = Cluster.objects.get(id=cluster_id) + clusters = list(Cluster.objects.filter(id__in=cluster_ids)) + new_proxy_objs = [] + for cluster in clusters: cluster_proxy_port = ProxyInstance.objects.filter(cluster=cluster).all()[0].port proxy_objs = ProxyInstance.objects.filter(machine__ip=proxy_ip, port=cluster_proxy_port) master_storage_obj = StorageInstance.objects.get(cluster=cluster, instance_inner_role=InstanceInnerRole.MASTER) @@ -49,11 +48,7 @@ def add_proxy(cluster_ids: list, proxy_ip: str, bk_cloud_id: int): for proxy in proxy_objs: proxy.time_zone = cluster.time_zone proxy.save() + new_proxy_objs.append(proxy) # proxy主机转移模块、添加对应的服务实例 - transfer_host_in_cluster_module( - cluster_ids=cluster_ids, - ip_list=[proxy_ip], - machine_type=MachineType.PROXY.value, - bk_cloud_id=bk_cloud_id, - ) + MysqlCCTopoOperator(clusters).transfer_instances_to_cluster_module(new_proxy_objs) diff --git a/dbm-ui/backend/db_meta/api/cluster/tendbha/create_cluster.py b/dbm-ui/backend/db_meta/api/cluster/tendbha/create_cluster.py index ea89b09e78..3ec4f96f1e 100644 --- a/dbm-ui/backend/db_meta/api/cluster/tendbha/create_cluster.py +++ b/dbm-ui/backend/db_meta/api/cluster/tendbha/create_cluster.py @@ -9,19 +9,13 @@ specific language governing permissions and limitations under the License. """ import logging -from dataclasses import asdict from typing import List, Optional from django.db import transaction from django.utils.translation import ungettext as _ -from backend import env -from backend.components import CCApi -from backend.constants import DEFAULT_BK_CLOUD_ID, DEFAULT_TIME_ZONE, IP_PORT_DIVIDER from backend.db_meta import request_validator from backend.db_meta.api import common -from backend.db_meta.api.common import add_service_instance -from backend.db_meta.api.db_module import get_or_create from backend.db_meta.enums import ( ClusterEntryRole, ClusterEntryType, @@ -31,15 +25,7 @@ InstanceInnerRole, ) from backend.db_meta.exceptions import DBMetaException -from backend.db_meta.models import ( - BKModule, - Cluster, - ClusterEntry, - ClusterMonitorTopo, - ProxyInstance, - StorageInstance, - StorageInstanceTuple, -) +from backend.db_meta.models import Cluster, ClusterEntry, ProxyInstance, StorageInstance, StorageInstanceTuple logger = logging.getLogger("root") @@ -90,7 +76,7 @@ def create( proxies: Optional[List] = None, storages: Optional[List] = None, creator: str = "", -): +) -> Cluster: """ 注册 TenDBHA 集群 """ @@ -164,7 +150,7 @@ def create( ins.save(update_fields=["db_module_id"]) m.save(update_fields=["db_module_id"]) - return cluster.id + return cluster @transaction.atomic diff --git a/dbm-ui/backend/db_meta/api/cluster/tendbha/decommission.py b/dbm-ui/backend/db_meta/api/cluster/tendbha/decommission.py index 062513f618..67594826e0 100644 --- a/dbm-ui/backend/db_meta/api/cluster/tendbha/decommission.py +++ b/dbm-ui/backend/db_meta/api/cluster/tendbha/decommission.py @@ -13,30 +13,26 @@ from django.db import transaction from django.utils.translation import ugettext as _ -from backend import env -from backend.components import CCApi -from backend.db_meta.api.common import del_service_instance +from backend.configuration.constants import DBType from backend.db_meta.exceptions import DBMetaException from backend.db_meta.models import Cluster, ClusterEntry, StorageInstanceTuple +from backend.flow.utils.cc_manage import CcManage logger = logging.getLogger("root") @transaction.atomic def decommission(cluster: Cluster): + cc_manage = CcManage(cluster.bk_biz_id) for proxy in cluster.proxyinstance_set.all(): proxy.delete(keep_parents=True) if not proxy.machine.proxyinstance_set.exists(): - # 这个 api 不需要检查返回值, 转移主机到空闲模块,转移模块这里会把服务实例删除 - CCApi.transfer_host_to_recyclemodule( - {"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "bk_host_id": [proxy.machine.bk_host_id]} - ) + cc_manage.recycle_host([proxy.machine.bk_host_id]) proxy.machine.delete(keep_parents=True) else: # 删除服务实例 - # del_service_instance(bk_instance_id=proxy.bk_instance_id) - pass + cc_manage.delete_service_instance(bk_instance_ids=[proxy.bk_instance_id]) for storage in cluster.storageinstance_set.all(): StorageInstanceTuple.objects.filter(ejector=storage).delete() @@ -45,22 +41,19 @@ def decommission(cluster: Cluster): if not storage.machine.storageinstance_set.exists(): # 这个 api 不需要检查返回值, 转移主机到待回收模块,转移模块这里会把服务实例删除 - CCApi.transfer_host_to_recyclemodule( - {"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "bk_host_id": [storage.machine.bk_host_id]} - ) + CcManage(storage.bk_biz_id).recycle_host([storage.machine.bk_host_id]) storage.machine.delete(keep_parents=True) else: # 删除服务实例 - # del_service_instance(bk_instance_id=storage.bk_instance_id) - pass + cc_manage.delete_service_instance(bk_instance_ids=[storage.bk_instance_id]) for ce in ClusterEntry.objects.filter(cluster=cluster).all(): ce.delete(keep_parents=True) # 删除集群在bkcc对应的模块 - # todo 目前cc没有封装移除主机模块接口 - # delete_cluster_modules(db_type=DBType.MySQL.value, del_cluster_id=cluster.id) + # TODO CC 目前没有把主机移出当前模块的接口,主机还在模块下,无法删除 + # cc_manage.delete_cluster_modules(db_type=DBType.MySQL.value, cluster=cluster) cluster.delete(keep_parents=True) diff --git a/dbm-ui/backend/db_meta/api/cluster/tendbha/handler.py b/dbm-ui/backend/db_meta/api/cluster/tendbha/handler.py index 85b3da9ea5..e7758dc042 100644 --- a/dbm-ui/backend/db_meta/api/cluster/tendbha/handler.py +++ b/dbm-ui/backend/db_meta/api/cluster/tendbha/handler.py @@ -16,11 +16,13 @@ from backend.db_meta import api from backend.db_meta.api.cluster.base.handler import ClusterHandler from backend.db_meta.enums import ClusterType, InstanceInnerRole, InstanceRole, MachineType +from backend.db_meta.enums.extra_process_type import ExtraProcessType from backend.db_meta.models import StorageInstance +from backend.db_meta.models.extra_process import ExtraProcessInstance from backend.db_package.models import Package from backend.flow.consts import MediumEnum from backend.flow.engine.bamboo.scene.common.get_real_version import get_mysql_real_version -from backend.flow.utils.mysql.bk_module_operate import create_bk_module_for_cluster_id, transfer_host_in_cluster_module +from backend.flow.utils.mysql.mysql_module_operate import MysqlCCTopoOperator from .others import add_slaves, delete_slaves @@ -80,9 +82,11 @@ def create( api.machine.create(machines=machines, creator=creator, bk_cloud_id=bk_cloud_id) # 录入机器对应的集群信息 - new_cluster_ids = [] + new_clusters = [] mysql_pkg = Package.get_latest_package(version=major_version, pkg_type=MediumEnum.MySQL, db_type=DBType.MySQL) + storage_objs = [] + proxy_objs = [] for cluster in clusters: name = cluster["name"] immute_domain = cluster["master"] @@ -108,9 +112,9 @@ def create( {"ip": cluster_ip_dict["new_proxy_2_ip"], "port": cluster["proxy_port"]}, ] api.cluster.tendbha.create_precheck(bk_biz_id, name, immute_domain, db_module_id, slave_domain) - api.storage_instance.create(instances=storages, creator=creator, time_zone=time_zone) - api.proxy_instance.create(proxies=proxies, creator=creator, time_zone=time_zone) - new_cluster_ids.append( + storage_objs.extend(api.storage_instance.create(instances=storages, creator=creator, time_zone=time_zone)) + proxy_objs.extend(api.proxy_instance.create(proxies=proxies, creator=creator, time_zone=time_zone)) + new_clusters.append( api.cluster.tendbha.create( bk_biz_id=bk_biz_id, name=name, @@ -126,24 +130,13 @@ def create( region=region, ) ) - # 生成域名模块 - create_bk_module_for_cluster_id(cluster_ids=new_cluster_ids) + cc_topo_operator = MysqlCCTopoOperator(new_clusters) # mysql主机转移模块、添加对应的服务实例 - transfer_host_in_cluster_module( - cluster_ids=new_cluster_ids, - ip_list=[cluster_ip_dict["new_master_ip"], cluster_ip_dict["new_slave_ip"]], - machine_type=MachineType.BACKEND.value, - bk_cloud_id=bk_cloud_id, - ) + cc_topo_operator.transfer_instances_to_cluster_module(storage_objs) # proxy主机转移模块、添加对应的服务实例 - transfer_host_in_cluster_module( - cluster_ids=new_cluster_ids, - ip_list=[cluster_ip_dict["new_proxy_1_ip"], cluster_ip_dict["new_proxy_2_ip"]], - machine_type=MachineType.PROXY.value, - bk_cloud_id=bk_cloud_id, - ) + cc_topo_operator.transfer_instances_to_cluster_module(proxy_objs) @transaction.atomic def decommission(self): @@ -161,6 +154,53 @@ def add_slaves(self, slaves: List[Dict]): def delete_slaves(self, slaves: List[Dict]): delete_slaves(self.cluster, slaves) - def get_exec_inst(self) -> StorageInstance: - """查询集群可执行的实例""" - return StorageInstance.objects.get(cluster=self.cluster, instance_inner_role=InstanceInnerRole.MASTER.value) + def get_remote_address(self) -> StorageInstance: + """查询DRS访问远程数据库的地址""" + return StorageInstance.objects.get( + cluster=self.cluster, instance_inner_role=InstanceInnerRole.MASTER.value + ).ip_port + + @transaction.atomic + def add_tbinlogdumper(self, add_confs: list): + """ + 添加TBinlogDumper实例的信息 + """ + master = self.cluster.storageinstance_set.get(instance_role=InstanceRole.BACKEND_MASTER) + for conf in add_confs: + tbinlogdumper = ExtraProcessInstance( + bk_biz_id=self.cluster.bk_biz_id, + cluster_id=self.cluster.id, + bk_cloud_id=self.cluster.bk_cloud_id, + ip=master.machine.ip, + proc_type=ExtraProcessType.TBINLOGDUMPER, + version="", + listen_port=conf["port"], + extra_config={ + "dumper_id": str(conf["area_name"]), + "area_name": str(conf["area_name"]), + "module_id": int(conf["module_id"]), + "source_data_ip": master.machine.ip, + "source_data_port": master.port, + }, + ) + tbinlogdumper.save() + + @transaction.atomic + def reduce_tbinlogdumper(self, id_list: list): + """ + 删除TBinlogDumper实例的信息 + """ + ExtraProcessInstance.objects.filter(id__in=id_list).delete() + + @transaction.atomic() + def switch_tbinlogdumper_for_cluster(self, switch_ids: list): + """ + 切换TBinlogDumper实例的信息变更 + """ + master = self.cluster.storageinstance_set.get(instance_role=InstanceRole.BACKEND_MASTER) + for inst in ExtraProcessInstance.objects.filter(id__in=switch_ids): + inst.bk_cloud_id = master.machine.bk_cloud_id + inst.ip = master.machine.ip + inst.extra_config["source_data_ip"] = master.machine.ip + inst.extra_config["source_data_port"] = master.port + inst.save() diff --git a/dbm-ui/backend/db_meta/api/cluster/tendbha/switch_proxy.py b/dbm-ui/backend/db_meta/api/cluster/tendbha/switch_proxy.py index 697795708b..db40e2bc23 100644 --- a/dbm-ui/backend/db_meta/api/cluster/tendbha/switch_proxy.py +++ b/dbm-ui/backend/db_meta/api/cluster/tendbha/switch_proxy.py @@ -12,12 +12,10 @@ from django.db import transaction -from backend import env -from backend.components import CCApi -from backend.db_meta.api.common import del_service_instance from backend.db_meta.enums import ClusterEntryType, InstanceInnerRole, MachineType from backend.db_meta.models import Cluster, ProxyInstance, StorageInstance -from backend.flow.utils.mysql.bk_module_operate import transfer_host_in_cluster_module +from backend.flow.utils.cc_manage import CcManage +from backend.flow.utils.mysql.mysql_module_operate import MysqlCCTopoOperator logger = logging.getLogger("root") @@ -27,9 +25,9 @@ def switch_proxy(cluster_ids: list, target_proxy_ip: str, origin_proxy_ip: str, """ 集群替换proxy场景元数据注册方式 """ - - for cluster_id in cluster_ids: - cluster = Cluster.objects.get(id=cluster_id) + clusters = list(Cluster.objects.filter(id__in=cluster_ids)) + new_proxy_objs = [] + for cluster in clusters: cluster_proxy_port = ProxyInstance.objects.filter(cluster=cluster).all()[0].port proxy_objs = ProxyInstance.objects.filter(machine__ip=target_proxy_ip, port=cluster_proxy_port) master_storage_obj = StorageInstance.objects.get(cluster=cluster, instance_inner_role=InstanceInnerRole.MASTER) @@ -50,29 +48,19 @@ def switch_proxy(cluster_ids: list, target_proxy_ip: str, origin_proxy_ip: str, for proxy in proxy_objs: proxy.time_zone = cluster.time_zone proxy.save() + new_proxy_objs.append(proxy) # proxy主机转移模块、添加对应的服务实例 - transfer_host_in_cluster_module( - cluster_ids=cluster_ids, - ip_list=[target_proxy_ip], - machine_type=MachineType.PROXY.value, - bk_cloud_id=bk_cloud_id, - ) + MysqlCCTopoOperator(clusters).transfer_instances_to_cluster_module(new_proxy_objs) # 回收proxy实例 - for cluster_id in cluster_ids: - cluster = Cluster.objects.get(id=cluster_id) - + for cluster in clusters: + cc_manage = CcManage(cluster.bk_biz_id) for proxy in ProxyInstance.objects.filter(cluster=cluster, machine__ip=origin_proxy_ip).all(): proxy.delete(keep_parents=True) if not proxy.machine.proxyinstance_set.exists(): - - # 这个 api 不需要检查返回值, 转移主机到待回收模块 - CCApi.transfer_host_to_recyclemodule( - {"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "bk_host_id": [proxy.machine.bk_host_id]} - ) - + cc_manage.recycle_host([proxy.machine.bk_host_id]) proxy.machine.delete(keep_parents=True) else: # 删除服务实例 - del_service_instance(bk_instance_id=proxy.bk_instance_id) + cc_manage.delete_service_instance(bk_instance_ids=[proxy.bk_instance_id]) diff --git a/dbm-ui/backend/db_meta/api/cluster/tendbsingle/create_cluster.py b/dbm-ui/backend/db_meta/api/cluster/tendbsingle/create_cluster.py index f6e5076343..6022d9a62e 100644 --- a/dbm-ui/backend/db_meta/api/cluster/tendbsingle/create_cluster.py +++ b/dbm-ui/backend/db_meta/api/cluster/tendbsingle/create_cluster.py @@ -52,7 +52,7 @@ def create( time_zone: str, region: str, creator: str = "", -): +) -> Cluster: bk_biz_id = request_validator.validated_integer(bk_biz_id) immute_domain = request_validator.validated_domain(immute_domain) db_module_id = request_validator.validated_integer(db_module_id) @@ -60,7 +60,7 @@ def create( storage_objs = common.filter_out_instance_obj([storage], StorageInstance.objects.all()) - c = Cluster.objects.create( + cluster = Cluster.objects.create( bk_biz_id=bk_biz_id, name=name, cluster_type=ClusterType.TenDBSingle.value, @@ -74,10 +74,10 @@ def create( major_version=major_version, region=region, ) - c.storageinstance_set.add(*storage_objs) + cluster.storageinstance_set.add(*storage_objs) ce = ClusterEntry.objects.create( - cluster=c, cluster_entry_type=ClusterEntryType.DNS, entry=immute_domain, creator=creator + cluster=cluster, cluster_entry_type=ClusterEntryType.DNS, entry=immute_domain, creator=creator ) ce.storageinstance_set.add(*storage_objs) @@ -89,4 +89,4 @@ def create( ins.save(update_fields=["db_module_id"]) m.save(update_fields=["db_module_id"]) - return c.id + return cluster diff --git a/dbm-ui/backend/db_meta/api/cluster/tendbsingle/decommission.py b/dbm-ui/backend/db_meta/api/cluster/tendbsingle/decommission.py index 2187d2dd7e..67ce53d8cd 100644 --- a/dbm-ui/backend/db_meta/api/cluster/tendbsingle/decommission.py +++ b/dbm-ui/backend/db_meta/api/cluster/tendbsingle/decommission.py @@ -12,38 +12,29 @@ from django.db import transaction -from backend import env -from backend.components import CCApi -from backend.configuration.constants import DBType -from backend.db_meta.api.common import del_service_instance -from backend.db_meta.api.db_module import delete_cluster_modules from backend.db_meta.models import Cluster, ClusterEntry +from backend.flow.utils.cc_manage import CcManage logger = logging.getLogger("root") @transaction.atomic def decommission(cluster: Cluster): - + cc_manage = CcManage(cluster.bk_biz_id) for storage in cluster.storageinstance_set.all(): storage.delete(keep_parents=True) + # MySQL 可能存在单机多集群/多实例的场景,因此下架时,需判断主机的所有实例是否都被下架了 if not storage.machine.storageinstance_set.exists(): - - # 这个 api 不需要检查返回值, 转移主机到待回收模块, 主机转移到空闲模块后会把相关服务实例删除 - CCApi.transfer_host_to_recyclemodule( - {"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "bk_host_id": [storage.machine.bk_host_id]} - ) - + # 转移主机到待回收 + cc_manage.recycle_host([storage.machine.bk_host_id]) storage.machine.delete(keep_parents=True) else: # 删除服务实例 - # del_service_instance(bk_instance_id=storage.bk_instance_id) - pass + cc_manage.delete_service_instance(bk_instance_ids=[storage.bk_instance_id]) for ce in ClusterEntry.objects.filter(cluster=cluster).all(): ce.delete(keep_parents=True) # 删除集群在bkcc对应的模块 - # todo 目前cc没有封装移除主机模块接口 - # delete_cluster_modules(db_type=DBType.MySQL.value, del_cluster_id=cluster.id) + # cc_manage.delete_cluster_modules(db_type=DBType.MySQL.value, cluster=cluster) cluster.delete(keep_parents=True) diff --git a/dbm-ui/backend/db_meta/api/cluster/tendbsingle/handler.py b/dbm-ui/backend/db_meta/api/cluster/tendbsingle/handler.py index b0ec6e51b4..24ba8f0a4d 100644 --- a/dbm-ui/backend/db_meta/api/cluster/tendbsingle/handler.py +++ b/dbm-ui/backend/db_meta/api/cluster/tendbsingle/handler.py @@ -8,7 +8,6 @@ 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. """ -import json from django.db import transaction @@ -16,7 +15,7 @@ from backend.db_meta.api.cluster.base.handler import ClusterHandler from backend.db_meta.enums import ClusterType, InstanceRole, MachineType from backend.db_meta.models import StorageInstance -from backend.flow.utils.mysql.bk_module_operate import create_bk_module_for_cluster_id, transfer_host_in_cluster_module +from backend.flow.utils.mysql.mysql_module_operate import MysqlCCTopoOperator class TenDBSingleClusterHandler(ClusterHandler): @@ -53,24 +52,27 @@ def create( creator=creator, bk_cloud_id=bk_cloud_id, ) - new_cluster_ids = [] + new_clusters = [] + new_storage_objs = [] for cluster in clusters: storage = {"ip": ip, "port": cluster["mysql_port"]} immute_domain = cluster["master"] - api.storage_instance.create( - instances=[ - { - "ip": ip, - "port": cluster["mysql_port"], - "instance_role": InstanceRole.ORPHAN.value, - "bk_cloud_id": bk_cloud_id, - } - ], - creator=creator, - time_zone=time_zone, + new_storage_objs.extend( + api.storage_instance.create( + instances=[ + { + "ip": ip, + "port": cluster["mysql_port"], + "instance_role": InstanceRole.ORPHAN.value, + "bk_cloud_id": bk_cloud_id, + } + ], + creator=creator, + time_zone=time_zone, + ) ) api.cluster.tendbsingle.create_precheck(bk_biz_id, cluster["name"], immute_domain, db_module_id) - new_cluster_ids.append( + new_clusters.append( api.cluster.tendbsingle.create( bk_biz_id=bk_biz_id, major_version=major_version, @@ -85,16 +87,8 @@ def create( ) ) - # 生成域名模块 - create_bk_module_for_cluster_id(cluster_ids=new_cluster_ids) - # mysql主机转移模块、添加对应的服务实例 - transfer_host_in_cluster_module( - cluster_ids=new_cluster_ids, - ip_list=[ip], - machine_type=MachineType.SINGLE.value, - bk_cloud_id=bk_cloud_id, - ) + MysqlCCTopoOperator(new_clusters).transfer_instances_to_cluster_module(new_storage_objs) @transaction.atomic def decommission(self): @@ -105,6 +99,6 @@ def topo_graph(self): """「必须」提供集群关系拓扑图""" return api.cluster.tendbsingle.scan_cluster(self.cluster).to_dict() - def get_exec_inst(self) -> StorageInstance: - """查询集群可执行的实例""" - return StorageInstance.objects.get(cluster=self.cluster) + def get_remote_address(self) -> StorageInstance: + """查询DRS访问远程数据库的地址""" + return StorageInstance.objects.get(cluster=self.cluster).ip_port diff --git a/dbm-ui/backend/db_meta/api/cluster/tendispluscluster/__init__.py b/dbm-ui/backend/db_meta/api/cluster/tendispluscluster/__init__.py index 17da5dac83..fb5d50eb46 100644 --- a/dbm-ui/backend/db_meta/api/cluster/tendispluscluster/__init__.py +++ b/dbm-ui/backend/db_meta/api/cluster/tendispluscluster/__init__.py @@ -8,14 +8,6 @@ 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 backend.db_meta.api.cluster.nosqlcomm.cc_ops import cc_add_service_instance, cc_add_service_instances -from backend.db_meta.api.cluster.nosqlcomm.decommission import ( - decommission_cluster, - decommission_proxies, - decommission_tendis, -) -from backend.db_meta.api.cluster.nosqlcomm.scale_proxy import add_proxies, delete_proxies -from backend.db_meta.api.cluster.nosqlcomm.scale_tendis import make_sync_mms, redo_slaves, switch_tendis from .create import create from .detail import scan_cluster diff --git a/dbm-ui/backend/db_meta/api/cluster/tendispluscluster/create.py b/dbm-ui/backend/db_meta/api/cluster/tendispluscluster/create.py index 940e18df21..0fd0d5b840 100644 --- a/dbm-ui/backend/db_meta/api/cluster/tendispluscluster/create.py +++ b/dbm-ui/backend/db_meta/api/cluster/tendispluscluster/create.py @@ -18,9 +18,9 @@ from backend.constants import DEFAULT_BK_CLOUD_ID from backend.db_meta import request_validator from backend.db_meta.api import common -from backend.db_meta.api.cluster.nosqlcomm.cc_ops import cc_add_instance, cc_add_instances -from backend.db_meta.enums import ClusterEntryType, ClusterType, DBCCModule +from backend.db_meta.enums import ClusterEntryType, ClusterType, MachineType from backend.db_meta.models import Cluster, ClusterEntry, ProxyInstance, StorageInstance +from backend.flow.utils.redis.redis_module_operate import RedisCCTopoOperator from ....exceptions import ClusterEntryExistException, CreateTendisPreCheckException, ProxyBackendNotEmptyException @@ -129,9 +129,10 @@ def create( slave_machine.cluster_type = ClusterType.TendisPredixyTendisplusCluster slave_machine.save(update_fields=["cluster_type"]) - cc_add_instances(cluster, slave_objs, DBCCModule.REDIS.value) - cc_add_instances(cluster, proxy_objs, DBCCModule.REDIS.value) - cc_add_instances(cluster, storage_objs, DBCCModule.REDIS.value) + cc_topo_operator = RedisCCTopoOperator(cluster) + cc_topo_operator.transfer_instances_to_cluster_module(slave_objs) + cc_topo_operator.transfer_instances_to_cluster_module(storage_objs) + cc_topo_operator.transfer_instances_to_cluster_module(proxy_objs) def create_precheck(domain, proxies, storages): diff --git a/dbm-ui/backend/db_meta/api/cluster/tendissingle/single.py b/dbm-ui/backend/db_meta/api/cluster/tendissingle/single.py index e7e2938736..ee04ac9893 100644 --- a/dbm-ui/backend/db_meta/api/cluster/tendissingle/single.py +++ b/dbm-ui/backend/db_meta/api/cluster/tendissingle/single.py @@ -14,11 +14,9 @@ from typing import List, Optional from django.db import IntegrityError, transaction -from django.utils.translation import gettext_lazy as _ from backend.constants import DEFAULT_BK_CLOUD_ID from backend.db_meta import request_validator -from backend.db_meta.api.cluster.nosqlcomm.cc_ops import cc_add_instance, cc_add_instances from backend.db_meta.api.cluster.nosqlcomm.create_cluster import update_storage_cluster_type from backend.db_meta.api.cluster.nosqlcomm.create_instances import create_tendis_instances from backend.db_meta.api.cluster.nosqlcomm.precheck import ( @@ -32,13 +30,13 @@ ClusterPhase, ClusterStatus, ClusterType, - DBCCModule, InstanceInnerRole, InstanceRole, InstanceStatus, MachineType, ) from backend.db_meta.models import Cluster, ClusterEntry, StorageInstance, StorageInstanceTuple +from backend.flow.utils.redis.redis_module_operate import RedisCCTopoOperator logger = logging.getLogger("flow") @@ -171,13 +169,12 @@ def create_single( logger.error(traceback.format_exc()) raise error - # CC 诺模块,以及注册服务实例 + # CC 挪模块,以及注册服务实例 slave_objs = [] for storage_obj in storage_objs: slave_obj = storage_obj.as_ejector.get().receiver slave_objs.append(slave_obj) - cc_add_instances(cluster, slave_objs, DBCCModule.REDIS.value) - cc_add_instances(cluster, storage_objs, DBCCModule.REDIS.value) + RedisCCTopoOperator(cluster).transfer_instances_to_cluster_module(storage_objs) @transaction.atomic diff --git a/dbm-ui/backend/db_meta/api/common/common.py b/dbm-ui/backend/db_meta/api/common/common.py index 6e3ffa094e..ef45613143 100644 --- a/dbm-ui/backend/db_meta/api/common/common.py +++ b/dbm-ui/backend/db_meta/api/common/common.py @@ -48,83 +48,3 @@ def equ_list_of_dict(a, b: List) -> bool: def remain_instance_obj(instances: List[Dict], qs: QuerySet) -> List[Dict]: ne = set(qs.values_list("machine__ip", "port")) - set(map(lambda e: (e["ip"], e["port"]), instances)) return list(map(lambda e: {"ip": e[0], "port": e[1]}, ne)) - - -def add_service_instance( - bk_module_id: int, - bk_host_id: int, - listen_ip: str, - listen_port: int, - func_name: str, - labels_dict: dict = None, - func_type: str = "custom", -) -> int: - """ - 定义添加bk-cc的服务实例的公共方法 - @param: bk_module_id: 模块idx - @param: bk_host_id: 机器id - @param: listen_ip: 进程监听ip - @param: listen_port: 进程监听端口 - @param: func_name: 进程名称 - @param: labels_dict: 待加入的标签字典 - @param: func_type: 进程类型 - """ - - # 添加服务实例信息,目前只操作一个,所以返回也是只有一个元素 - bk_instance_ids = list( - CCApi.create_service_instance( - { - "bk_biz_id": env.DBA_APP_BK_BIZ_ID, - "bk_module_id": bk_module_id, - "instances": [ - { - "bk_host_id": bk_host_id, - "processes": [ - { - "process_template_id": 0, - "process_info": { - "bk_func_name": func_name, - "bk_process_name": func_name, - "bind_info": [ - { - "enable": False, - "ip": listen_ip, - "port": str(listen_port), - "protocol": "1", - # "type": func_type, - } - ], - }, - } - ], - } - ], - } - ) - ) - - # 添加集群信息标签 - if labels_dict: - CCApi.add_label_for_service_instance( - { - "bk_biz_id": env.DBA_APP_BK_BIZ_ID, - "instance_ids": bk_instance_ids, - "labels": labels_dict, - } - ) - - return bk_instance_ids[0] - - -def del_service_instance(bk_instance_id: int): - # 这里因为id不存在会导致接口异常退出,这里暂时接收所有错误,不让它直接退出 - - try: - CCApi.delete_service_instance( - { - "bk_biz_id": env.DBA_APP_BK_BIZ_ID, - "service_instance_ids": [bk_instance_id], - } - ) - except Exception as error: - logger.warning(error) diff --git a/dbm-ui/backend/db_meta/api/db_module/apis.py b/dbm-ui/backend/db_meta/api/db_module/apis.py index 534e177ad5..fb78209574 100644 --- a/dbm-ui/backend/db_meta/api/db_module/apis.py +++ b/dbm-ui/backend/db_meta/api/db_module/apis.py @@ -13,12 +13,9 @@ from django.db import IntegrityError, transaction from django.forms import model_to_dict -from backend import env -from backend.components import CCApi from backend.db_meta import request_validator -from backend.db_meta.enums import ClusterTypeMachineTypeDefine from backend.db_meta.exceptions import DbModuleExistException -from backend.db_meta.models import AppMonitorTopo, Cluster, ClusterMonitorTopo, DBModule +from backend.db_meta.models import DBModule logger = logging.getLogger("root") @@ -42,177 +39,3 @@ def create(bk_biz_id: int, name: str, cluster_type: str, creator: str = ""): raise DbModuleExistException(db_module_name=name) return model_to_dict(db_module) - - -@transaction.atomic -def get_or_create( - bk_biz_id: int, - cluster_id: int, - cluster_type: str, - cluster_domain: str, - creator: str = "", -): - """创建监控拓扑相关模块""" - bk_biz_id = request_validator.validated_integer(bk_biz_id, min_value=0) - cluster_type = request_validator.validated_str(cluster_type) - - machine_topo = {} - for machine_type in ClusterTypeMachineTypeDefine[cluster_type]: - app_monitor_topo = AppMonitorTopo.objects.get(bk_biz_id=env.DBA_APP_BK_BIZ_ID, machine_type=machine_type) - bk_set_id = app_monitor_topo.bk_set_id - - # CCApi.get_or_create_module - res = CCApi.search_module( - { - "bk_biz_id": env.DBA_APP_BK_BIZ_ID, - "bk_set_id": bk_set_id, - "condition": {"bk_module_name": cluster_domain}, - }, - use_admin=True, - ) - - if res["count"]: - module = res["info"][0] - else: - module = CCApi.create_module( - { - "bk_biz_id": env.DBA_APP_BK_BIZ_ID, - "bk_set_id": bk_set_id, - "data": {"bk_parent_id": bk_set_id, "bk_module_name": cluster_domain}, - }, - use_admin=True, - ) - - # 保留一份集群监控拓扑数据 - module, created = ClusterMonitorTopo.objects.get_or_create( - defaults={ - "bk_biz_id": bk_biz_id, - "creator": creator, - }, - machine_type=machine_type, - bk_biz_id=bk_biz_id, - cluster_id=cluster_id, - bk_set_id=bk_set_id, - bk_module_id=module["bk_module_id"], - ) - machine_topo[machine_type] = module.bk_module_id - - return machine_topo - - -@transaction.atomic -def get_or_create_influxdb( - bk_biz_id: int, - cluster_type: str, - instance_ip: str, - instance_id: int, - creator: str = "", -): - """ - influxdb专用 - This function is used to create a monitoring topology related module, - specifically for Influxdb. It requires bk_biz_id, cluster_type, instance_ip, - instance_id, and creator as parameters. If the module already exists, - it will retrieve it, and if not, it will create the module. - Finally, the function will return a dictionary containing machine type and corresponding bk_module_id. - """ - - bk_biz_id = request_validator.validated_integer(bk_biz_id, min_value=0) - cluster_type = request_validator.validated_str(cluster_type) - - machine_topo = {} - for machine_type in ClusterTypeMachineTypeDefine[cluster_type]: - app_monitor_topo = AppMonitorTopo.objects.get(bk_biz_id=env.DBA_APP_BK_BIZ_ID, machine_type=machine_type) - bk_set_id = app_monitor_topo.bk_set_id - - res = CCApi.search_module( - { - "bk_biz_id": env.DBA_APP_BK_BIZ_ID, - "bk_set_id": bk_set_id, - "condition": {"bk_module_name": instance_ip}, - }, - use_admin=True, - ) - - if res["count"]: - module = res["info"][0] - else: - module = CCApi.create_module( - { - "bk_biz_id": env.DBA_APP_BK_BIZ_ID, - "bk_set_id": bk_set_id, - "data": {"bk_parent_id": bk_set_id, "bk_module_name": instance_ip}, - }, - use_admin=True, - ) - - # 保留一份集群监控拓扑数据 - module, created = ClusterMonitorTopo.objects.get_or_create( - defaults={ - "bk_biz_id": bk_biz_id, - "creator": creator, - }, - machine_type=machine_type, - bk_biz_id=bk_biz_id, - bk_set_id=bk_set_id, - instance_id=instance_id, - bk_module_id=module["bk_module_id"], - ) - machine_topo[machine_type] = module.bk_module_id - - return machine_topo - - -@transaction.atomic -def delete_cluster_modules(db_type, del_cluster_id: int): - """ - 封装方法:现在bkcc的模块是跟cluster_id、db_type 的结合对应 - 根据这些信息删除对应模块, 使用场景是回收集群 - @param db_type: db组件类型 - @param del_cluster_id: 待删除模块所关联的cluster_id - """ - cluster = Cluster.objects.get(id=del_cluster_id) - for machine_type in ClusterTypeMachineTypeDefine[cluster.cluster_type]: - bk_set_id = AppMonitorTopo.objects.get( - bk_biz_id=env.DBA_APP_BK_BIZ_ID, db_type=db_type, machine_type=machine_type - ).bk_set_id - - bk_module_obj = ClusterMonitorTopo.objects.get( - machine_type=machine_type, - bk_biz_id=cluster.bk_biz_id, - cluster_id=del_cluster_id, - bk_set_id=bk_set_id, - ) - - CCApi.delete_module( - {"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "bk_set_id": bk_set_id, "bk_module_id": bk_module_obj.bk_module_id}, - ) - bk_module_obj.delete(keep_parents=True) - - -@transaction.atomic -def delete_instance_modules(db_type, del_instance_id: int, bk_biz_id: int, cluster_type): - """ - 封装方法:现在bkcc的模块是跟cluster_id、db_type 的结合对应 - 根据这些信息删除对应模块, 使用场景是回收集群 - @param db_type: db组件类型 - @param del_instance_id: 待删除模块所关联的ins_id - @param bk_biz_id: 待删除模块所关联的bk_biz_id - @param cluster_type: 集群类型 - """ - for machine_type in ClusterTypeMachineTypeDefine[cluster_type]: - bk_set_id = AppMonitorTopo.objects.get( - bk_biz_id=env.DBA_APP_BK_BIZ_ID, db_type=db_type, machine_type=machine_type - ).bk_set_id - - bk_module_obj = ClusterMonitorTopo.objects.get( - machine_type=machine_type, - bk_biz_id=bk_biz_id, - instance_id=del_instance_id, - bk_set_id=bk_set_id, - ) - - CCApi.delete_module( - {"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "bk_set_id": bk_set_id, "bk_module_id": bk_module_obj.bk_module_id}, - ) - bk_module_obj.delete(keep_parents=True) diff --git a/dbm-ui/backend/db_meta/api/dbha/apis.py b/dbm-ui/backend/db_meta/api/dbha/apis.py index 7b02019bd8..bdb6f97a01 100644 --- a/dbm-ui/backend/db_meta/api/dbha/apis.py +++ b/dbm-ui/backend/db_meta/api/dbha/apis.py @@ -21,6 +21,7 @@ from backend.db_meta import flatten, request_validator, validators from backend.db_meta.enums import ClusterEntryType, ClusterStatus, ClusterType, InstanceInnerRole, InstanceStatus from backend.db_meta.exceptions import ( + ClusterNotExistException, ClusterSetDtlExistException, InstanceNotExistException, TendisClusterNotExistException, @@ -42,11 +43,30 @@ def entry_detail(domains: List[str]) -> List[Dict]: try: cluster_obj = Cluster.objects.get(immute_domain=domain) except ObjectDoesNotExist: - raise TendisClusterNotExistException(cluster=domain) + raise ClusterNotExistException(cluster=domain) for cluster_entry_obj in cluster_obj.clusterentry_set.all(): if cluster_entry_obj.cluster_entry_type == ClusterEntryType.DNS: - clusterentry_set[cluster_entry_obj.cluster_entry_type].append({"domain": cluster_entry_obj.entry}) + + if cluster_entry_obj.storageinstance_set.exists(): + bind_ips = list(set([ele.machine.ip for ele in list(cluster_entry_obj.storageinstance_set.all())])) + bind_port = cluster_entry_obj.storageinstance_set.first().port + elif cluster_entry_obj.proxyinstance_set.exists(): + bind_ips = list(set([ele.machine.ip for ele in list(cluster_entry_obj.proxyinstance_set.all())])) + bind_port = cluster_entry_obj.proxyinstance_set.first().port + else: + bind_ips = [] + bind_port = 0 + + clusterentry_set[cluster_entry_obj.cluster_entry_type].append( + { + "domain": cluster_entry_obj.entry, + "entry_role": cluster_entry_obj.role, + "forward_entry_id": cluster_entry_obj.forward_to_id, + "bind_ips": bind_ips, + "bind_port": bind_port, + } + ) elif cluster_entry_obj.cluster_entry_type == ClusterEntryType.CLB: de = cluster_entry_obj.clbentrydetail_set.get() clusterentry_set[cluster_entry_obj.cluster_entry_type].append( @@ -191,8 +211,10 @@ def __swap(ins1: StorageInstance, ins2: StorageInstance): ins2.proxyinstance_set.clear() ins2.proxyinstance_set.add(*temp_proxy_set) - StorageInstanceTuple.objects.get(ejector=ins1, receiver=ins2).delete(keep_parents=True) - StorageInstanceTuple.objects.create(ejector=ins2, receiver=ins1) + st = StorageInstanceTuple.objects.get(ejector=ins1, receiver=ins2) + st.ejector = ins2 + st.receiver = ins1 + st.save(update_fields=["ejector", "receiver"]) temp_instance_role = ins1.instance_role tmep_instance_inner_role = ins1.instance_inner_role diff --git a/dbm-ui/backend/db_meta/api/fake/__init__.py b/dbm-ui/backend/db_meta/api/fake/__init__.py index 72fb0d852e..c34517ddd1 100644 --- a/dbm-ui/backend/db_meta/api/fake/__init__.py +++ b/dbm-ui/backend/db_meta/api/fake/__init__.py @@ -8,5 +8,6 @@ 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 .fake_tendbcluster import fake_reset_tendbcluster_cluster from .fake_tendbha import fake_create_tendbha_cluster, fake_reset_tendbha_cluster from .fake_tendbsingle import fake_create_tendbsingle diff --git a/dbm-ui/backend/db_meta/api/fake/fake_tendbcluster.py b/dbm-ui/backend/db_meta/api/fake/fake_tendbcluster.py new file mode 100644 index 0000000000..4f07749aae --- /dev/null +++ b/dbm-ui/backend/db_meta/api/fake/fake_tendbcluster.py @@ -0,0 +1,102 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 typing import Dict, List, Optional + +from django.db import transaction +from django.db.models import Q + +from backend.constants import IP_PORT_DIVIDER +from backend.db_meta.enums import ClusterType, InstanceInnerRole, InstanceRole, InstanceStatus +from backend.db_meta.models import ( + Cluster, + ClusterEntry, + ProxyInstance, + StorageInstance, + StorageInstanceTuple, + TenDBClusterStorageSet, +) + + +@transaction.atomic +def fake_reset_tendbcluster_cluster( + spider_masters: List[str], + immute_domain: str, + storage_sets: List[Dict], + spider_slaves: Optional[List[str]] = None, + slave_domain: Optional[str] = None, +): + """ + storage_sets: [ + { + "master_instance": "a.b.c.d:20000", + "slave_instance": "e.f.g.h:20000", + } + ... + ] + """ + cluster = Cluster.objects.get(immute_domain=immute_domain, cluster_type=ClusterType.TenDBCluster) + master_entry = ClusterEntry.objects.get(entry=immute_domain, cluster=cluster) + master_entry.proxyinstance_set.clear() + + TenDBClusterStorageSet.objects.filter(cluster=cluster).delete() + + for idx, storage_set in enumerate(storage_sets): + [master_ip, master_port] = storage_set["master_instance"].split(IP_PORT_DIVIDER) + master_obj = StorageInstance.objects.get(machine__ip=master_ip, port=master_port, cluster=cluster) + master_obj.instance_role = InstanceRole.REMOTE_MASTER.value + master_obj.instance_inner_role = InstanceInnerRole.MASTER.value + master_obj.status = InstanceStatus.RUNNING.value + master_obj.save(update_fields=["instance_role", "instance_inner_role", "status"]) + + [slave_ip, slave_port] = storage_set["slave_instance"].split(IP_PORT_DIVIDER) + slave_obj = StorageInstance.objects.get(machine__ip=slave_ip, port=slave_port, cluster=cluster) + slave_obj.instance_role = InstanceRole.REMOTE_SLAVE.value + slave_obj.instance_inner_role = InstanceInnerRole.SLAVE.value + slave_obj.status = InstanceStatus.RUNNING.value + slave_obj.save(update_fields=["instance_role", "instance_inner_role", "status"]) + + StorageInstanceTuple.objects.filter( + (Q(ejector=master_obj) & Q(receiver=slave_obj)) | (Q(ejector=slave_obj) & Q(receiver=master_obj)) + ).delete() + + st_obj = StorageInstanceTuple.objects.create(ejector=master_obj, receiver=slave_obj) + TenDBClusterStorageSet.objects.create(shard_id=idx, cluster=cluster, storage_instance_tuple=st_obj) + + for spider_master in spider_masters: + [spider_ip, spider_port] = spider_master.split(IP_PORT_DIVIDER) + spider_obj = ProxyInstance.objects.get(machine__ip=spider_ip, port=spider_port, cluster=cluster) + spider_obj.storageinstance.clear() + spider_obj.storageinstance.add( + *list( + StorageInstance.objects.filter(cluster=cluster, instance_role=InstanceRole.REMOTE_MASTER.value).all() + ) + ) + spider_obj.status = InstanceStatus.RUNNING.value + spider_obj.save(update_fields=["status"]) + master_entry.proxyinstance_set.add(spider_obj) + + if slave_domain and spider_slaves: + slave_entry = ClusterEntry.objects.get(entry=slave_domain, cluster=cluster) + slave_entry.storageinstance_set.clear() + for spider_slave in spider_slaves: + [spider_ip, spider_port] = spider_slave.split(IP_PORT_DIVIDER) + spider_obj = ProxyInstance.objects.get(machine__ip=spider_ip, port=spider_port, cluster=cluster) + spider_obj.storageinstance.clear() + spider_obj.storageinstance.add( + *list( + StorageInstance.objects.filter( + cluster=cluster, instance_role=InstanceRole.REMOTE_SLAVE.value + ).all() + ) + ) + spider_obj.status = InstanceStatus.RUNNING.value + spider_obj.save(update_fields=["status"]) + slave_entry.proxyinstance_set.add(spider_obj) diff --git a/dbm-ui/backend/db_meta/api/machine/apis.py b/dbm-ui/backend/db_meta/api/machine/apis.py index bf18701023..07618b5138 100644 --- a/dbm-ui/backend/db_meta/api/machine/apis.py +++ b/dbm-ui/backend/db_meta/api/machine/apis.py @@ -14,12 +14,11 @@ from django.db import transaction -from backend import env from backend.components import CCApi from backend.db_meta import request_validator -from backend.db_meta.enums import MachineType, MachineTypeAccessLayerMap, machine_type_to_cluster_type +from backend.db_meta.enums import MachineTypeAccessLayerMap, machine_type_to_cluster_type from backend.db_meta.models import BKCity, Machine -from backend.flow.utils.mysql.bk_module_operate import transfer_host_in_cluster_module +from backend.flow.utils.cc_manage import CcManage logger = logging.getLogger("root") @@ -115,25 +114,13 @@ def create( @transaction.atomic def delete(machines: Optional[List], bk_cloud_id: int): - Machine.objects.filter(ip__in=machines, bk_cloud_id=bk_cloud_id).delete() - - -@transaction.atomic -def trans_module(bk_cloud_id: int, cluster_ids: Optional[List] = None, machines: Optional[List] = None, idle=False): - - machines_obj = Machine.objects.filter(ip__in=machines) - - if idle: - machine_host_id_list = [] - for machine in machines_obj: - machine_host_id_list.append(machine.bk_host_id) - - CCApi.transfer_host_to_recyclemodule({"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "bk_host_id": machine_host_id_list}) - else: - # mysql主机转移模块、添加对应的服务实例 - transfer_host_in_cluster_module( - cluster_ids=cluster_ids, - ip_list=machines, - machine_type=MachineType.BACKEND.value, - bk_cloud_id=bk_cloud_id, - ) + """ + 删除主机并挪到待回收模块 + """ + machines = Machine.objects.filter(ip__in=machines, bk_cloud_id=bk_cloud_id) + if not machines: + return + bk_biz_id = machines[0].bk_biz_id + bk_host_ids = list(machines.values_list("bk_host_id", flat=True)) + machines.delete() + CcManage(bk_biz_id).recycle_host(bk_host_ids) diff --git a/dbm-ui/backend/db_meta/api/meta/apis.py b/dbm-ui/backend/db_meta/api/meta/apis.py index f0c2a8acb6..30aee76167 100644 --- a/dbm-ui/backend/db_meta/api/meta/apis.py +++ b/dbm-ui/backend/db_meta/api/meta/apis.py @@ -71,6 +71,10 @@ def query_cluster_by_hosts(hosts: List): "bk_host_id": storage.machine.bk_host_id, "instance_role": storage.instance_role, "machine_type": storage.machine.machine_type, + "cs_ports": [ + cstorage.port + for cstorage in cluster.storageinstance_set.filter(machine__ip=storage.machine.ip) + ], "cluster": cluster.immute_domain, "cluster_name": cluster.name, "cluster_id": cluster.id, @@ -91,6 +95,10 @@ def query_cluster_by_hosts(hosts: List): "bk_host_id": storage.machine.bk_host_id, "instance_role": storage.machine_type, "machine_type": storage.machine.machine_type, + "cs_ports": [ + cstorage.port + for cstorage in cluster.storageinstance_set.filter(machine__ip=storage.machine.ip) + ], "cluster": cluster.immute_domain, "cluster_name": cluster.name, "cluster_id": cluster.id, diff --git a/dbm-ui/backend/db_meta/api/proxy_instance/apis.py b/dbm-ui/backend/db_meta/api/proxy_instance/apis.py index 20926979c0..6fdf478f86 100644 --- a/dbm-ui/backend/db_meta/api/proxy_instance/apis.py +++ b/dbm-ui/backend/db_meta/api/proxy_instance/apis.py @@ -17,9 +17,9 @@ from backend.constants import DEFAULT_TIME_ZONE from backend.db_meta import request_validator from backend.db_meta.api import common -from backend.db_meta.api.cluster.nosqlcomm.cc_ops import cc_del_service_instances, cc_transfer_idle from backend.db_meta.enums import AccessLayer, InstanceStatus from backend.db_meta.models import Machine, ProxyInstance +from backend.flow.utils.cc_manage import CcManage logger = logging.getLogger("flow") @@ -30,12 +30,12 @@ def create( creator: str = "", status: str = "", time_zone: str = DEFAULT_TIME_ZONE, -): +) -> List[ProxyInstance]: """ ToDo: 冗余属性校验 """ proxies = request_validator.validated_proxy_list(proxies, allow_empty=False, allow_null=False) - + proxy_objs = [] for proxy in proxies: proxy_ip = proxy["ip"] proxy_port = proxy["port"] @@ -47,20 +47,23 @@ def create( real_status = status if status != "" else InstanceStatus.RUNNING - ProxyInstance.objects.create( - machine=machine_obj, - port=proxy_port, - admin_port=proxy_port + 1000, - db_module_id=machine_obj.db_module_id, - bk_biz_id=machine_obj.bk_biz_id, - access_layer=machine_obj.access_layer, - machine_type=machine_obj.machine_type, - cluster_type=machine_obj.cluster_type, - status=real_status, - creator=creator, - time_zone=time_zone, - version=version, + proxy_objs.append( + ProxyInstance.objects.create( + machine=machine_obj, + port=proxy_port, + admin_port=proxy_port + 1000, + db_module_id=machine_obj.db_module_id, + bk_biz_id=machine_obj.bk_biz_id, + access_layer=machine_obj.access_layer, + machine_type=machine_obj.machine_type, + cluster_type=machine_obj.cluster_type, + status=real_status, + creator=creator, + time_zone=time_zone, + version=version, + ) ) + return proxy_objs @transaction.atomic @@ -103,13 +106,14 @@ def decommission(instances: List[Dict]): for proxy_obj in proxy_objs: logger.info("remove proxy {} ".format(proxy_obj)) - cc_del_service_instances(proxy_obj) - proxy_obj.delete() + CcManage(proxy_obj.bk_biz_id).delete_service_instance(bk_instance_ids=[proxy_obj.bk_instance_id]) # 需要检查, 是否该机器上所有实例都已经清理干净, if len(ProxyInstance.objects.filter(machine__ip=proxy_obj.machine.ip).all()) > 0: logger.info("ignore storage machine {} , another instance existed.".format(proxy_obj.machine)) else: logger.info("proxy machine {}".format(proxy_obj.machine)) - cc_transfer_idle(proxy_obj.machine) + CcManage( + proxy_obj.bk_biz_id, + ).recycle_host([proxy_obj.machine.bk_host_id]) proxy_obj.machine.delete() diff --git a/dbm-ui/backend/db_meta/api/storage_instance/apis.py b/dbm-ui/backend/db_meta/api/storage_instance/apis.py index ba7a7d3f44..79564d5664 100644 --- a/dbm-ui/backend/db_meta/api/storage_instance/apis.py +++ b/dbm-ui/backend/db_meta/api/storage_instance/apis.py @@ -27,13 +27,16 @@ @transaction.atomic -def create(instances, creator: str = "", time_zone: str = DEFAULT_TIME_ZONE, status: str = InstanceStatus.RUNNING): +def create( + instances, creator: str = "", time_zone: str = DEFAULT_TIME_ZONE, status: str = InstanceStatus.RUNNING +) -> List[StorageInstance]: """ ToDo meta role 的合法性 这里没法确定实例的 db module """ instances = request_validator.validated_storage_with_role_list(instances, allow_empty=False, allow_null=False) + storage_objs = [] for ins in instances: ip = ins["ip"] port = ins["port"] @@ -51,25 +54,28 @@ def create(instances, creator: str = "", time_zone: str = DEFAULT_TIME_ZONE, sta ) instance_role = ins["instance_role"] - StorageInstance.objects.create( - port=port, - machine=machine_obj, - db_module_id=machine_obj.db_module_id, - bk_biz_id=machine_obj.bk_biz_id, - # cluster 留空 - access_layer=machine_obj.access_layer, - machine_type=machine_obj.machine_type, - instance_role=instance_role, - instance_inner_role=InstanceRoleInstanceInnerRoleMap[instance_role], - cluster_type=machine_obj.cluster_type, - status=status, - # bind entry 留空 - creator=creator, - name=name, - time_zone=time_zone, - version=version, - is_stand_by=is_stand_by, + storage_objs.append( + StorageInstance.objects.create( + port=port, + machine=machine_obj, + db_module_id=machine_obj.db_module_id, + bk_biz_id=machine_obj.bk_biz_id, + # cluster 留空 + access_layer=machine_obj.access_layer, + machine_type=machine_obj.machine_type, + instance_role=instance_role, + instance_inner_role=InstanceRoleInstanceInnerRoleMap[instance_role], + cluster_type=machine_obj.cluster_type, + status=status, + # bind entry 留空 + creator=creator, + name=name, + time_zone=time_zone, + version=version, + is_stand_by=is_stand_by, + ) ) + return storage_objs @transaction.atomic diff --git a/dbm-ui/backend/db_meta/apps.py b/dbm-ui/backend/db_meta/apps.py index bf740f4a2d..49aca0bd03 100644 --- a/dbm-ui/backend/db_meta/apps.py +++ b/dbm-ui/backend/db_meta/apps.py @@ -17,10 +17,12 @@ logger = logging.getLogger("root") -def init_city(sender, **kwargs): +def init_db_meta(sender, **kwargs): """初始化配置""" + # 初始化城市配置 from .models.city_map import BKCity, LogicalCity + from .models.spec import Spec try: logical_city = LogicalCity.objects.create(name="default") @@ -28,10 +30,16 @@ def init_city(sender, **kwargs): except IntegrityError: logger.warning("city already init") + # 初始化规格配置 + try: + Spec.init_spec() + except (IntegrityError, Exception): + logger.warning("spec init occur error, maybe already init...") + class DBMeta(AppConfig): default_auto_field = "django.db.models.BigAutoField" name = "backend.db_meta" def ready(self): - post_migrate.connect(init_city, sender=self) + post_migrate.connect(init_db_meta, sender=self) diff --git a/dbm-ui/backend/db_meta/enums/__init__.py b/dbm-ui/backend/db_meta/enums/__init__.py index c37236eef5..3170a38752 100644 --- a/dbm-ui/backend/db_meta/enums/__init__.py +++ b/dbm-ui/backend/db_meta/enums/__init__.py @@ -15,6 +15,7 @@ from .cluster_status import ClusterDBHAStatusFlags, ClusterStatus, ClusterTenDBClusterStatusFlag from .cluster_type import ClusterType from .comm import DBCCModule, SyncType +from .destroyed_status import DestroyedStatus from .instance_inner_role import InstanceInnerRole from .instance_phase import InstancePhase from .instance_role import InstanceRole, TenDBClusterSpiderRole diff --git a/dbm-ui/backend/db_meta/enums/cluster_entry_type.py b/dbm-ui/backend/db_meta/enums/cluster_entry_type.py index 4e46674593..b8baf39e63 100644 --- a/dbm-ui/backend/db_meta/enums/cluster_entry_type.py +++ b/dbm-ui/backend/db_meta/enums/cluster_entry_type.py @@ -16,3 +16,4 @@ class ClusterEntryType(str, StructuredEnum): DNS = EnumField("dns", _("dns")) CLB = EnumField("clb", _("clb")) POLARIS = EnumField("polaris", _("polaris")) + CLBDNS = EnumField("clbDns", _("clbDns")) diff --git a/dbm-ui/backend/db_meta/enums/cluster_status.py b/dbm-ui/backend/db_meta/enums/cluster_status.py index 90834c1c48..bce35f62f1 100644 --- a/dbm-ui/backend/db_meta/enums/cluster_status.py +++ b/dbm-ui/backend/db_meta/enums/cluster_status.py @@ -18,6 +18,8 @@ class ClusterStatus(str, StructuredEnum): NORMAL = EnumField("normal", _("normal")) ABNORMAL = EnumField("ABNORMAL", _("ABNORMAL")) + # spider 定点构造的集群状态标记为临时集群 + TEMPORARY = EnumField("temporary", _("temporary")) class ClusterStatusFlags(IntFlag): @@ -26,6 +28,10 @@ def flag_text(self) -> List[str]: return flag_str.split("|") +class ClusterDBSingleStatusFlags(ClusterStatusFlags): + SingleUnavailable = auto() + + class ClusterDBHAStatusFlags(ClusterStatusFlags): ProxyUnavailable = auto() BackendMasterUnavailable = auto() diff --git a/dbm-ui/backend/db_meta/enums/cluster_type.py b/dbm-ui/backend/db_meta/enums/cluster_type.py index 3de40c0b03..0e1a6fd1a6 100644 --- a/dbm-ui/backend/db_meta/enums/cluster_type.py +++ b/dbm-ui/backend/db_meta/enums/cluster_type.py @@ -29,6 +29,11 @@ class ClusterType(str, StructuredEnum): TendisRedisCluster = EnumField("RedisCluster", _("RedisCluster集群")) TendisTendisplusCluster = EnumField("TendisplusCluster", _("TendisplusCluster集群")) + # GetTendisType 获取redis类型,返回RedisInstance or TendisplusInstance or TendisSSDInstance + TendisplusInstance = EnumField("TendisplusInstance", _("Tendisplus存储版集群 GetTendisType 获取redis类型值")) + RedisInstance = EnumField("RedisInstance", _("TendisCache集群 GetTendisType 获取redis类型值")) + TendisSSDInstance = EnumField("TendisSSDInstance", _("TendisSSD集群 GetTendisType 获取redis类型值")) + Es = EnumField("es", _("ES集群")) Kafka = EnumField("kafka", _("Kafka集群")) Hdfs = EnumField("hdfs", _("Hdfs集群")) diff --git a/dbm-ui/backend/db_meta/enums/comm.py b/dbm-ui/backend/db_meta/enums/comm.py index 8bd13c84e1..6618d250ed 100644 --- a/dbm-ui/backend/db_meta/enums/comm.py +++ b/dbm-ui/backend/db_meta/enums/comm.py @@ -14,6 +14,7 @@ class SyncType(str, StructuredEnum): + MS = EnumField("ms", _("ms")) SMS = EnumField("sms", _("sms")) MMS = EnumField("mms", _("mms")) @@ -21,3 +22,14 @@ class SyncType(str, StructuredEnum): class DBCCModule(str, StructuredEnum): REDIS = EnumField("redis", _("redis")) MONGODB = EnumField("mongodb", _("mongodb")) + + +class TagType(str, StructuredEnum): + CUSTOM = EnumField("custom", _("custom")) + SYSTEM = EnumField("system", _("system")) + + +class SystemTagEnum(str, StructuredEnum): + """系统内置的tag名称""" + + TEMPORARY = EnumField("temporary", _("temporary")) diff --git a/dbm-ui/backend/db_meta/enums/destroyed_status.py b/dbm-ui/backend/db_meta/enums/destroyed_status.py new file mode 100644 index 0000000000..639b35e770 --- /dev/null +++ b/dbm-ui/backend/db_meta/enums/destroyed_status.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 blue_krill.data_types.enum import StructuredEnum + + +class DestroyedStatus(int, StructuredEnum): + """ + 销毁状态枚举 + """ + + NOT_DESTROYED = 0 # 未销毁 + DESTROYING = 1 # 销毁中 + DESTROYED = 2 # 已销毁 diff --git a/dbm-ui/backend/db_meta/enums/extra_process_type.py b/dbm-ui/backend/db_meta/enums/extra_process_type.py new file mode 100644 index 0000000000..4f3e9acda1 --- /dev/null +++ b/dbm-ui/backend/db_meta/enums/extra_process_type.py @@ -0,0 +1,16 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 blue_krill.data_types.enum import EnumField, StructuredEnum +from django.utils.translation import ugettext_lazy as _ + + +class ExtraProcessType(str, StructuredEnum): + TBINLOGDUMPER = EnumField("tbinlogdumper", _("tbinlogdumper")) diff --git a/dbm-ui/backend/db_meta/enums/machine_type.py b/dbm-ui/backend/db_meta/enums/machine_type.py index 1bf37603a5..2f167fe218 100644 --- a/dbm-ui/backend/db_meta/enums/machine_type.py +++ b/dbm-ui/backend/db_meta/enums/machine_type.py @@ -39,6 +39,7 @@ class MachineType(str, StructuredEnum): MONGOS = EnumField("mongos", _("mongos")) MONGODB = EnumField("mongodb", _("mongodb")) MONOG_CONFIG = EnumField("mongo_config", _("mongo_config")) + INFLUXDB = EnumField("influxdb", _("influxdb")) PULSAR_ZOOKEEPER = EnumField("pulsar_zookeeper", _("pulsar_zookeeper")) diff --git a/dbm-ui/backend/db_meta/exceptions.py b/dbm-ui/backend/db_meta/exceptions.py index 52d7529cb5..f237833b12 100644 --- a/dbm-ui/backend/db_meta/exceptions.py +++ b/dbm-ui/backend/db_meta/exceptions.py @@ -144,12 +144,6 @@ class ClusterProxyExtraNotDefine(DBMetaBaseException): MESSAGE_TPL = _("集群类型:{cluster_type} proxy 无附加信息") -class ClusterDeployPlanNotMatchException(DBMetaBaseException): - ERROR_CODE = "022" - MESSAGE = _("部署方案不匹配") - MESSAGE_TPL = _("集群类型:{cluster_type} 和部署方案:{deploy_plan_class} 不匹配") - - class ClusterDeployHasRefException(DBMetaBaseException): ERROR_CODE = "023" MESSAGE = _("部署方案使用中") diff --git a/dbm-ui/backend/db_meta/flatten/proxy_instance.py b/dbm-ui/backend/db_meta/flatten/proxy_instance.py index a81cf9b274..30553d2e11 100644 --- a/dbm-ui/backend/db_meta/flatten/proxy_instance.py +++ b/dbm-ui/backend/db_meta/flatten/proxy_instance.py @@ -14,7 +14,9 @@ from django.db.models import QuerySet -from ..models import ProxyInstance +from backend.db_meta.enums import ClusterEntryType, MachineType +from backend.db_meta.models import ProxyInstance + from .machine import _machine_prefetch, _single_machine_cc_info, _single_machine_city_info logger = logging.getLogger("root") @@ -46,6 +48,9 @@ def proxy_instance(proxies: QuerySet) -> List[Dict]: "status": ins.status, } + if ins.machine_type == MachineType.SPIDER.value: + info["spider_role"] = ins.tendbclusterspiderext.spider_role + storageinstance = [] for s in ins.storageinstance.all(): sinfo = {"ip": s.machine.ip, "port": s.port, "is_stand_by": s.is_stand_by} @@ -54,7 +59,18 @@ def proxy_instance(proxies: QuerySet) -> List[Dict]: bind_entry = defaultdict(list) for be in ins.bind_entry.all(): - bind_entry[be.cluster_entry_type].append(be.entry) + if be.cluster_entry_type == ClusterEntryType.DNS: + bind_entry[be.cluster_entry_type].append( + { + "domain": be.entry, + "entry_role": be.role, + "forward_entry_id": be.forward_to_id, + "bind_ips": list(set([ele.machine.ip for ele in list(be.proxyinstance_set.all())])), + "bind_port": be.proxyinstance_set.first().port, + } + ) + else: + bind_entry[be.cluster_entry_type].append(be.entry) info["bind_entry"] = dict(bind_entry) diff --git a/dbm-ui/backend/db_meta/flatten/storage_instance.py b/dbm-ui/backend/db_meta/flatten/storage_instance.py index c7be9dbc32..45c377cbde 100644 --- a/dbm-ui/backend/db_meta/flatten/storage_instance.py +++ b/dbm-ui/backend/db_meta/flatten/storage_instance.py @@ -14,6 +14,8 @@ from django.db.models import QuerySet +from backend.db_meta.enums import ClusterEntryType + from ..models import StorageInstance from .machine import _machine_prefetch, _single_machine_cc_info, _single_machine_city_info @@ -75,7 +77,17 @@ def storage_instance(storages: QuerySet) -> List[Dict]: bind_entry = defaultdict(list) for be in ins.bind_entry.all(): - bind_entry[be.cluster_entry_type].append(be.entry) + if be.cluster_entry_type == ClusterEntryType.DNS: + bind_entry[be.cluster_entry_type].append( + { + "domain": be.entry, + "entry_role": be.role, + "bind_ips": list(set([ele.machine.ip for ele in list(be.storageinstance_set.all())])), + "bind_port": be.storageinstance_set.first().port, + } + ) + else: + bind_entry[be.cluster_entry_type].append(be.entry) info["bind_entry"] = dict(bind_entry) diff --git a/dbm-ui/backend/db_meta/init/spec_init.json b/dbm-ui/backend/db_meta/init/spec_init.json new file mode 100644 index 0000000000..399476dbdc --- /dev/null +++ b/dbm-ui/backend/db_meta/init/spec_init.json @@ -0,0 +1,1785 @@ +{ + "es": { + "es_client": [ + { + "spec_name": "16核_32G", + "cpu": { + "max": 16, + "min": 16 + }, + "mem": { + "max": 32, + "min": 32 + }, + "device_class": [], + "storage_spec": [], + "desc": "client规格中", + "instance_num": 0, + "qps": {} + }, + { + "spec_name": "32核_64G", + "cpu": { + "max": 32, + "min": 32 + }, + "mem": { + "max": 64, + "min": 64 + }, + "device_class": [], + "storage_spec": [], + "desc": "client规格大", + "instance_num": 0, + "qps": {} + }, + { + "spec_name": "8核_32G", + "cpu": { + "max": 8, + "min": 8 + }, + "mem": { + "max": 32, + "min": 32 + }, + "device_class": [], + "storage_spec": [], + "desc": "client规格小", + "instance_num": 0, + "qps": {} + } + ], + "es_master": [ + { + "spec_name": "2核_8G_50G", + "cpu": { + "max": 2, + "min": 2 + }, + "mem": { + "max": 8, + "min": 8 + }, + "device_class": [], + "storage_spec": [ + { + "size": 50, + "type": "SSD", + "mount_point": "/data" + } + ], + "desc": "master规格小", + "instance_num": 0, + "qps": {} + }, + { + "spec_name": "4核_16G_50G", + "cpu": { + "max": 4, + "min": 4 + }, + "mem": { + "max": 16, + "min": 16 + }, + "device_class": [], + "storage_spec": [ + { + "size": 50, + "type": "SSD", + "mount_point": "/data" + } + ], + "desc": "master规格中", + "instance_num": 0, + "qps": {} + }, + { + "spec_name": "8核_32G_50G", + "cpu": { + "max": 8, + "min": 8 + }, + "mem": { + "max": 32, + "min": 32 + }, + "device_class": [], + "storage_spec": [ + { + "size": 50, + "type": "SSD", + "mount_point": "/data" + } + ], + "desc": "master规格大", + "instance_num": 0, + "qps": {} + } + ], + "es_datanode": [ + { + "spec_name": "4核_16G_100G", + "cpu": { + "max": 4, + "min": 4 + }, + "mem": { + "max": 16, + "min": 16 + }, + "device_class": [], + "storage_spec": [ + { + "size": 100, + "type": "SSD", + "mount_point": "/data" + } + ], + "desc": "热节点规格小", + "instance_num": 1, + "qps": {} + }, + { + "spec_name": "16核_64G_3500G", + "cpu": { + "max": 16, + "min": 16 + }, + "mem": { + "max": 64, + "min": 64 + }, + "device_class": [], + "storage_spec": [ + { + "size": 3500, + "type": "SSD", + "mount_point": "/data" + } + ], + "desc": "热节点规格大", + "instance_num": 1, + "qps": {} + }, + { + "spec_name": "8核_32G_800G", + "cpu": { + "max": 8, + "min": 8 + }, + "mem": { + "max": 32, + "min": 32 + }, + "device_class": [], + "storage_spec": [ + { + "size": 800, + "type": "SSD", + "mount_point": "/data" + } + ], + "desc": "热节点规格中", + "instance_num": 1, + "qps": {} + }, + { + "spec_name": "2核_8G_100G", + "cpu": { + "max": 2, + "min": 2 + }, + "mem": { + "max": 8, + "min": 8 + }, + "device_class": [], + "storage_spec": [ + { + "size": 100, + "type": "HDD", + "mount_point": "/data" + } + ], + "desc": "冷节点规格小(假的)", + "instance_num": 1, + "qps": {} + }, + { + "spec_name": "86核_212G_3500G", + "cpu": { + "max": 86, + "min": 86 + }, + "mem": { + "max": 212, + "min": 212 + }, + "device_class": [], + "storage_spec": [ + { + "size": 3500, + "type": "HDD", + "mount_point": "/data" + }, + { + "size": 3500, + "type": "HDD", + "mount_point": "/data1" + }, + { + "size": 3500, + "type": "HDD", + "mount_point": "/data2" + }, + { + "size": 3500, + "type": "HDD", + "mount_point": "/data3" + }, + { + "size": 3500, + "type": "HDD", + "mount_point": "/data4" + }, + { + "size": 3500, + "type": "HDD", + "mount_point": "/data5" + }, + { + "size": 3500, + "type": "HDD", + "mount_point": "/data6" + }, + { + "size": 3500, + "type": "HDD", + "mount_point": "/data7" + }, + { + "size": 3500, + "type": "HDD", + "mount_point": "/data8" + }, + { + "size": 3500, + "type": "HDD", + "mount_point": "/data9" + }, + { + "size": 3500, + "type": "HDD", + "mount_point": "/data10" + }, + { + "size": 3500, + "type": "HDD", + "mount_point": "/data11" + } + ], + "desc": "冷节点规格大(da2)", + "instance_num": 4, + "qps": {} + } + ] + }, + "pulsar": { + "pulsar_zookeeper": [ + { + "spec_name": "2核_4G_100G", + "cpu": { + "max": 2, + "min": 2 + }, + "mem": { + "max": 4, + "min": 4 + }, + "device_class": [], + "storage_spec": [ + { + "size": 100, + "type": "SSD", + "mount_point": "/data" + } + ], + "desc": "zookeeper规格", + "instance_num": 0, + "qps": {} + } + ], + "pulsar_bookkeeper": [ + { + "spec_name": "4核_16G_200G", + "cpu": { + "max": 4, + "min": 4 + }, + "mem": { + "max": 16, + "min": 16 + }, + "device_class": [], + "storage_spec": [ + { + "size": 200, + "type": "SSD", + "mount_point": "/data" + } + ], + "desc": "bookie规格小", + "instance_num": 0, + "qps": {} + }, + { + "spec_name": "8核_8G_800G", + "cpu": { + "max": 32, + "min": 8 + }, + "mem": { + "max": 32, + "min": 8 + }, + "device_class": [], + "storage_spec": [ + { + "size": 800, + "type": "SSD", + "mount_point": "/data" + } + ], + "desc": "bookie规格中", + "instance_num": 0, + "qps": {} + }, + { + "spec_name": "16核_64G_3500G", + "cpu": { + "max": 16, + "min": 16 + }, + "mem": { + "max": 64, + "min": 64 + }, + "device_class": [], + "storage_spec": [ + { + "size": 3500, + "type": "SSD", + "mount_point": "/data" + } + ], + "desc": "bookie规格大", + "instance_num": 0, + "qps": {} + } + ], + "pulsar_broker": [ + { + "spec_name": "8核_16G_100G", + "cpu": { + "max": 8, + "min": 8 + }, + "mem": { + "max": 16, + "min": 16 + }, + "device_class": [], + "storage_spec": [ + { + "size": 100, + "type": "HDD", + "mount_point": "/data" + } + ], + "desc": "broker规格小", + "instance_num": 0, + "qps": {} + }, + { + "spec_name": "16核_32G_100G", + "cpu": { + "max": 16, + "min": 16 + }, + "mem": { + "max": 32, + "min": 32 + }, + "device_class": [], + "storage_spec": [ + { + "size": 100, + "type": "HDD", + "mount_point": "/data" + } + ], + "desc": "broker规格中", + "instance_num": 0, + "qps": {} + }, + { + "spec_name": "32核_64G_100G", + "cpu": { + "max": 32, + "min": 32 + }, + "mem": { + "max": 64, + "min": 64 + }, + "device_class": [], + "storage_spec": [ + { + "size": 100, + "type": "HDD", + "mount_point": "/data" + } + ], + "desc": "broker规格大", + "instance_num": 0, + "qps": {} + } + ] + }, + "kafka": { + "broker": [ + { + "spec_name": "4核_8G_200G", + "cpu": { + "max": 4, + "min": 4 + }, + "mem": { + "max": 8, + "min": 8 + }, + "device_class": [], + "storage_spec": [ + { + "size": 200, + "type": "SSD", + "mount_point": "/data" + } + ], + "desc": "broker规格小", + "instance_num": 0, + "qps": {} + }, + { + "spec_name": "8核_16G_800G", + "cpu": { + "max": 8, + "min": 8 + }, + "mem": { + "max": 16, + "min": 16 + }, + "device_class": [], + "storage_spec": [ + { + "size": 800, + "type": "SSD", + "mount_point": "/data" + } + ], + "desc": "broker规格中", + "instance_num": 0, + "qps": {} + }, + { + "spec_name": "16核_64G_3500G", + "cpu": { + "max": 16, + "min": 16 + }, + "mem": { + "max": 64, + "min": 64 + }, + "device_class": [], + "storage_spec": [ + { + "size": 3500, + "type": "SSD", + "mount_point": "/data" + } + ], + "desc": "broker规格大", + "instance_num": 0, + "qps": {} + } + ], + "zookeeper": [ + { + "spec_name": "2核_4G_100G", + "cpu": { + "max": 2, + "min": 2 + }, + "mem": { + "max": 4, + "min": 4 + }, + "device_class": [], + "storage_spec": [ + { + "size": 100, + "type": "SSD", + "mount_point": "/data" + } + ], + "desc": "zookeeper规格", + "instance_num": 0, + "qps": {} + } + ] + }, + "influxdb": { + "influxdb": [ + { + "spec_name": "32核_120G_1024G", + "cpu": { + "max": 32, + "min": 32 + }, + "mem": { + "max": 130, + "min": 120 + }, + "device_class": [], + "storage_spec": [ + { + "size": 1024, + "type": "SSD", + "mount_point": "/data1" + } + ], + "desc": "高规格", + "instance_num": 0, + "qps": {} + }, + { + "spec_name": "16核_60G_400G", + "cpu": { + "max": 16, + "min": 16 + }, + "mem": { + "max": 65, + "min": 60 + }, + "device_class": [], + "storage_spec": [ + { + "size": 400, + "type": "SSD", + "mount_point": "/data1" + } + ], + "desc": "中规格", + "instance_num": 0, + "qps": {} + }, + { + "spec_name": "8核_16G_200G", + "cpu": { + "max": 8, + "min": 8 + }, + "mem": { + "max": 17, + "min": 16 + }, + "device_class": [], + "storage_spec": [ + { + "size": 200, + "type": "SSD", + "mount_point": "/data1" + } + ], + "desc": "低规格", + "instance_num": 0, + "qps": {} + } + ] + }, + "hdfs": { + "hdfs_master": [ + { + "spec_name": "2核_4G_100G", + "cpu": { + "max": 4, + "min": 2 + }, + "mem": { + "max": 16, + "min": 4 + }, + "device_class": [], + "storage_spec": [ + { + "size": 100, + "type": "ALL", + "mount_point": "/data" + } + ], + "desc": "ZK/JN规格", + "instance_num": 0, + "qps": {} + }, + { + "spec_name": "2核_8G_100G", + "cpu": { + "max": 2, + "min": 2 + }, + "mem": { + "max": 8, + "min": 8 + }, + "device_class": [], + "storage_spec": [ + { + "size": 100, + "type": "ALL", + "mount_point": "/data" + } + ], + "desc": "NameNode规格-小", + "instance_num": 0, + "qps": {} + }, + { + "spec_name": "16核_128G_1000G", + "cpu": { + "max": 48, + "min": 16 + }, + "mem": { + "max": 192, + "min": 128 + }, + "device_class": [], + "storage_spec": [ + { + "size": 1000, + "type": "ALL", + "mount_point": "/data" + } + ], + "desc": "NameNode规格-大", + "instance_num": 0, + "qps": {} + }, + { + "spec_name": "8核_32G_100G", + "cpu": { + "max": 16, + "min": 8 + }, + "mem": { + "max": 64, + "min": 32 + }, + "device_class": [], + "storage_spec": [ + { + "size": 100, + "type": "ALL", + "mount_point": "/data" + } + ], + "desc": "NameNode规格-中", + "instance_num": 0, + "qps": {} + } + ], + "hdfs_datanode": [ + { + "spec_name": "2核_8G_100G", + "cpu": { + "max": 8, + "min": 2 + }, + "mem": { + "max": 32, + "min": 8 + }, + "device_class": [], + "storage_spec": [ + { + "size": 100, + "type": "ALL", + "mount_point": "/data1" + } + ], + "desc": "DataNode规格-小", + "instance_num": 0, + "qps": {} + }, + { + "spec_name": "16核_64G_3500G", + "cpu": { + "max": 16, + "min": 16 + }, + "mem": { + "max": 64, + "min": 64 + }, + "device_class": [], + "storage_spec": [ + { + "size": 3500, + "type": "ALL", + "mount_point": "/data1" + } + ], + "desc": "DataNode规格-中", + "instance_num": 0, + "qps": {} + }, + { + "spec_name": "16核_64G_12000G", + "cpu": { + "max": 48, + "min": 16 + }, + "mem": { + "max": 256, + "min": 64 + }, + "device_class": [], + "storage_spec": [ + { + "size": 12000, + "type": "ALL", + "mount_point": "/data1" + } + ], + "desc": "DataNode规格-大", + "instance_num": 0, + "qps": {} + } + ] + }, + "tendbha": { + "backend": [ + { + "spec_name": "2核_1G_50G", + "cpu": { + "max": 4, + "min": 2 + }, + "mem": { + "max": 4, + "min": 1 + }, + "device_class": [], + "storage_spec": [ + { + "size": 50, + "type": "ALL", + "mount_point": "/data" + } + ], + "desc": "", + "instance_num": 0, + "qps": {} + }, + { + "spec_name": "4核_8G_100G", + "cpu": { + "max": 4, + "min": 4 + }, + "mem": { + "max": 8, + "min": 8 + }, + "device_class": [], + "storage_spec": [ + { + "size": 100, + "type": "ALL", + "mount_point": "/data" + } + ], + "desc": "", + "instance_num": 0, + "qps": {} + }, + { + "spec_name": "8核_16G_500G", + "cpu": { + "max": 8, + "min": 8 + }, + "mem": { + "max": 16, + "min": 16 + }, + "device_class": [], + "storage_spec": [ + { + "size": 500, + "type": "ALL", + "mount_point": "/data" + } + ], + "desc": "", + "instance_num": 0, + "qps": {} + }, + { + "spec_name": "16核_32G_500G", + "cpu": { + "max": 16, + "min": 16 + }, + "mem": { + "max": 32, + "min": 32 + }, + "device_class": [], + "storage_spec": [ + { + "size": 500, + "type": "ALL", + "mount_point": "/data" + } + ], + "desc": "", + "instance_num": 0, + "qps": {} + }, + { + "spec_name": "16核_32G_1000G", + "cpu": { + "max": 16, + "min": 16 + }, + "mem": { + "max": 64, + "min": 32 + }, + "device_class": [], + "storage_spec": [ + { + "size": 1000, + "type": "ALL", + "mount_point": "/data" + } + ], + "desc": "", + "instance_num": 0, + "qps": {} + }, + { + "spec_name": "32核_64G_1000G", + "cpu": { + "max": 32, + "min": 32 + }, + "mem": { + "max": 64, + "min": 64 + }, + "device_class": [], + "storage_spec": [ + { + "size": 1000, + "type": "ALL", + "mount_point": "/data" + } + ], + "desc": "", + "instance_num": 0, + "qps": {} + }, + { + "spec_name": "64核_128G_1000G", + "cpu": { + "max": 64, + "min": 64 + }, + "mem": { + "max": 128, + "min": 128 + }, + "device_class": [], + "storage_spec": [ + { + "size": 1000, + "type": "SSD", + "mount_point": "/data" + } + ], + "desc": "", + "instance_num": 0, + "qps": {} + } + ], + "proxy": [ + { + "spec_name": "通用proxy配置", + "cpu": { + "max": 4, + "min": 2 + }, + "mem": { + "max": 8, + "min": 4 + }, + "device_class": [], + "storage_spec": [ + { + "size": 50, + "type": "ALL", + "mount_point": "/data" + } + ], + "desc": "", + "instance_num": 0, + "qps": {} + } + ] + }, + "tendbsingle": { + "single": [ + { + "spec_name": "2核_1G_50G", + "cpu": { + "max": 4, + "min": 2 + }, + "mem": { + "max": 4, + "min": 1 + }, + "device_class": [], + "storage_spec": [ + { + "size": 50, + "type": "ALL", + "mount_point": "/data" + } + ], + "desc": "", + "instance_num": 0, + "qps": {} + }, + { + "spec_name": "4核_8G_100G", + "cpu": { + "max": 4, + "min": 4 + }, + "mem": { + "max": 8, + "min": 8 + }, + "device_class": [], + "storage_spec": [ + { + "size": 100, + "type": "ALL", + "mount_point": "/data" + } + ], + "desc": "", + "instance_num": 0, + "qps": {} + }, + { + "spec_name": "8核_16G_500G", + "cpu": { + "max": 8, + "min": 8 + }, + "mem": { + "max": 16, + "min": 16 + }, + "device_class": [], + "storage_spec": [ + { + "size": 500, + "type": "ALL", + "mount_point": "/data" + } + ], + "desc": "", + "instance_num": 0, + "qps": {} + }, + { + "spec_name": "16核_32G_500G", + "cpu": { + "max": 16, + "min": 16 + }, + "mem": { + "max": 32, + "min": 32 + }, + "device_class": [], + "storage_spec": [ + { + "size": 500, + "type": "ALL", + "mount_point": "/data" + } + ], + "desc": "", + "instance_num": 0, + "qps": {} + }, + { + "spec_name": "16核_32G_1000G", + "cpu": { + "max": 16, + "min": 16 + }, + "mem": { + "max": 64, + "min": 32 + }, + "device_class": [], + "storage_spec": [ + { + "size": 1000, + "type": "ALL", + "mount_point": "/data" + } + ], + "desc": "", + "instance_num": 0, + "qps": {} + }, + { + "spec_name": "32核_64G_1000G", + "cpu": { + "max": 32, + "min": 32 + }, + "mem": { + "max": 64, + "min": 64 + }, + "device_class": [], + "storage_spec": [ + { + "size": 1000, + "type": "ALL", + "mount_point": "/data" + } + ], + "desc": "", + "instance_num": 0, + "qps": {} + }, + { + "spec_name": "64核_128G_1000G", + "cpu": { + "max": 64, + "min": 64 + }, + "mem": { + "max": 128, + "min": 128 + }, + "device_class": [], + "storage_spec": [ + { + "size": 1000, + "type": "SSD", + "mount_point": "/data" + } + ], + "desc": "", + "instance_num": 0, + "qps": {} + } + ] + }, + "TwemproxyRedisInstance": { + "twemproxy":[ + { + "spec_name":"2核_4G_50G", + "cpu":{ + "max":2, + "min":2 + }, + "mem":{ + "max":4, + "min":4 + }, + "device_class":[ + + ], + "storage_spec":[ + { + "size":50, + "type":"ALL", + "mount_point":"/data" + } + ], + "desc":"基础机型", + "instance_num":0, + "qps":{ + + } + }, + { + "spec_name":"4核_16G_50G", + "cpu":{ + "max":4, + "min":4 + }, + "mem":{ + "max":16, + "min":16 + }, + "device_class":[ + + ], + "storage_spec":[ + { + "size":50, + "type":"ALL", + "mount_point":"/data" + } + ], + "desc":"中等机型", + "instance_num":0, + "qps":{ + + } + }, + { + "spec_name":"8核_32G_100G", + "cpu":{ + "max":8, + "min":8 + }, + "mem":{ + "max":32, + "min":32 + }, + "device_class":[ + + ], + "storage_spec":[ + { + "size":100, + "type":"ALL", + "mount_point":"/data" + } + ], + "desc":"高等机型", + "instance_num":0, + "qps":{ + + } + } + ], + "tendiscache":[ + { + "spec_name":"2核_8G_100G_1/s", + "cpu":{ + "max":4, + "min":2 + }, + "mem":{ + "max":8, + "min":8 + }, + "device_class":[ + + ], + "storage_spec":[ + { + "size":100, + "type":"ALL", + "mount_point":"/data" + } + ], + "desc":"S5.MEDIUM8,S5.LARGE8,SA2.LARGE8", + "instance_num":0, + "qps":{ + "max":15000, + "min":1 + } + }, + { + "spec_name":"16核_128G_600G_1/s", + "cpu":{ + "max":32, + "min":16 + }, + "mem":{ + "max":128, + "min":128 + }, + "device_class":[ + + ], + "storage_spec":[ + { + "size":600, + "type":"ALL", + "mount_point":"/data" + } + ], + "desc":"S5.4XLARGE128,M5.4XLARGE128,SA3.8XLARGE128", + "instance_num":0, + "qps":{ + "max":300000, + "min":1 + } + }, + { + "spec_name":"2核_4G_50G_1/s", + "cpu":{ + "max":2, + "min":2 + }, + "mem":{ + "max":4, + "min":4 + }, + "device_class":[ + + ], + "storage_spec":[ + { + "size":50, + "type":"ALL", + "mount_point":"/data" + } + ], + "desc":"SA2.MEDIUM4,S5.MEDIUM4", + "instance_num":0, + "qps":{ + "max":10000, + "min":1 + } + }, + { + "spec_name":"4核_16G_200G_1/s", + "cpu":{ + "max":6, + "min":4 + }, + "mem":{ + "max":16, + "min":16 + }, + "device_class":[ + + ], + "storage_spec":[ + { + "size":200, + "type":"ALL", + "mount_point":"/data" + } + ], + "desc":"SA3.LARGE16,S5.LARGE16", + "instance_num":0, + "qps":{ + "max":100000, + "min":1 + } + }, + { + "spec_name":"4核_32G_300G_1/s", + "cpu":{ + "max":8, + "min":4 + }, + "mem":{ + "max":32, + "min":32 + }, + "device_class":[ + + ], + "storage_spec":[ + { + "size":300, + "type":"ALL", + "mount_point":"/data" + } + ], + "desc":"S5.2XLARGE32,S5.4XLARGE32", + "instance_num":0, + "qps":{ + "max":160000, + "min":1 + } + }, + { + "spec_name":"8核_64G_400G_1/s", + "cpu":{ + "max":16, + "min":8 + }, + "mem":{ + "max":64, + "min":64 + }, + "device_class":[ + + ], + "storage_spec":[ + { + "size":400, + "type":"ALL", + "mount_point":"/data" + } + ], + "desc":"S5.4XLARGE64,SA3.4XLARGE64,M5.2XLARGE64", + "instance_num":0, + "qps":{ + "max":300000, + "min":1 + } + } + ] + }, + "TwemproxyTendisSSDInstance": { + "tendisssd":[ + { + "spec_name":"16核_55G_1500G_50000/s", + "cpu":{ + "max":16, + "min":16 + }, + "mem":{ + "max":66, + "min":55 + }, + "device_class":[ + + ], + "storage_spec":[ + { + "size":1500, + "type":"ALL", + "mount_point":"/data" + }, + { + "size":1500, + "type":"SSD", + "mount_point":"/data1" + } + ], + "desc":"IT5.4XLARGE64,I6t.4XLARGE56", + "instance_num":0, + "qps":{ + "max":70000, + "min":50000 + } + }, + { + "spec_name":"32核_110G_3500G_80000/s", + "cpu":{ + "max":32, + "min":32 + }, + "mem":{ + "max":130, + "min":110 + }, + "device_class":[ + + ], + "storage_spec":[ + { + "size":3500, + "type":"ALL", + "mount_point":"/data" + }, + { + "size":3500, + "type":"SSD", + "mount_point":"/data1" + } + ], + "desc":"IT5.8XLARGE128,I6t.8XLARGE112", + "instance_num":0, + "qps":{ + "max":100000, + "min":80000 + } + } + ], + "twemproxy":[ + { + "spec_name":"2核_4G_50G", + "cpu":{ + "max":2, + "min":2 + }, + "mem":{ + "max":4, + "min":4 + }, + "device_class":[ + + ], + "storage_spec":[ + { + "size":50, + "type":"ALL", + "mount_point":"/data" + } + ], + "desc":"基础机型", + "instance_num":0, + "qps":{ + + } + }, + { + "spec_name":"4核_16G_50G", + "cpu":{ + "max":4, + "min":4 + }, + "mem":{ + "max":16, + "min":16 + }, + "device_class":[ + + ], + "storage_spec":[ + { + "size":50, + "type":"ALL", + "mount_point":"/data" + } + ], + "desc":"中等机型", + "instance_num":0, + "qps":{ + + } + }, + { + "spec_name":"8核_32G_100G", + "cpu":{ + "max":8, + "min":8 + }, + "mem":{ + "max":32, + "min":32 + }, + "device_class":[ + + ], + "storage_spec":[ + { + "size":100, + "type":"ALL", + "mount_point":"/data" + } + ], + "desc":"高等机型", + "instance_num":0, + "qps":{ + + } + } + ] + }, + "PredixyTendisplusCluster": { + "tendisplus":[ + { + "spec_name":"2核_4G_50G_5000/s", + "cpu":{ + "max":2, + "min":2 + }, + "mem":{ + "max":4, + "min":4 + }, + "device_class":[ + + ], + "storage_spec":[ + { + "size":50, + "type":"ALL", + "mount_point":"/data" + }, + { + "size":50, + "type":"SSD", + "mount_point":"/data1" + } + ], + "desc":"SA2.MEDIUM4,S5.MEDIUM4", + "instance_num":0, + "qps":{ + "max":10000, + "min":5000 + } + }, + { + "spec_name":"2核_8G_100G_10000/s", + "cpu":{ + "max":4, + "min":2 + }, + "mem":{ + "max":8, + "min":8 + }, + "device_class":[ + + ], + "storage_spec":[ + { + "size":100, + "type":"ALL", + "mount_point":"/data" + }, + { + "size":100, + "type":"SSD", + "mount_point":"/data1" + } + ], + "desc":"S5.MEDIUM8,S5.LARGE8,SA2.LARGE8", + "instance_num":0, + "qps":{ + "max":15000, + "min":10000 + } + }, + { + "spec_name":"4核_16G_200G_20000/s", + "cpu":{ + "max":6, + "min":4 + }, + "mem":{ + "max":16, + "min":16 + }, + "device_class":[ + + ], + "storage_spec":[ + { + "size":200, + "type":"ALL", + "mount_point":"/data" + }, + { + "size":200, + "type":"ALL", + "mount_point":"/data1" + } + ], + "desc":"SA3.LARGE16,S5.LARGE16,M5.MEDIUM16", + "instance_num":0, + "qps":{ + "max":30000, + "min":20000 + } + }, + { + "spec_name":"4核_32G_500G_30000/s", + "cpu":{ + "max":8, + "min":4 + }, + "mem":{ + "max":32, + "min":32 + }, + "device_class":[ + + ], + "storage_spec":[ + { + "size":500, + "type":"ALL", + "mount_point":"/data" + }, + { + "size":500, + "type":"SSD", + "mount_point":"/data1" + } + ], + "desc":"S5.2XLARGE32,S5.4XLARGE32,M5.LARGE32", + "instance_num":0, + "qps":{ + "max":50000, + "min":30000 + } + }, + { + "spec_name":"16核_55G_1500G_50000/s", + "cpu":{ + "max":16, + "min":16 + }, + "mem":{ + "max":66, + "min":55 + }, + "device_class":[ + + ], + "storage_spec":[ + { + "size":1500, + "type":"ALL", + "mount_point":"/data" + }, + { + "size":1500, + "type":"SSD", + "mount_point":"/data1" + } + ], + "desc":"IT5.4XLARGE64,I6t.4XLARGE56", + "instance_num":0, + "qps":{ + "max":80000, + "min":50000 + } + }, + { + "spec_name":"32核_110G_3500G_80000/s", + "cpu":{ + "max":32, + "min":32 + }, + "mem":{ + "max":130, + "min":110 + }, + "device_class":[ + + ], + "storage_spec":[ + { + "size":3500, + "type":"ALL", + "mount_point":"/data" + }, + { + "size":3500, + "type":"SSD", + "mount_point":"/data1" + } + ], + "desc":"IT5.8XLARGE128,I6t.8XLARGE112", + "instance_num":0, + "qps":{ + "max":120000, + "min":80000 + } + } + ], + "predixy":[ + { + "spec_name":"2核_4G_50G", + "cpu":{ + "max":2, + "min":2 + }, + "mem":{ + "max":4, + "min":4 + }, + "device_class":[ + + ], + "storage_spec":[ + { + "size":50, + "type":"ALL", + "mount_point":"/data" + } + ], + "desc":"基础机型", + "instance_num":0, + "qps":{ + + } + }, + { + "spec_name":"4核_16G_50G", + "cpu":{ + "max":4, + "min":4 + }, + "mem":{ + "max":16, + "min":16 + }, + "device_class":[ + + ], + "storage_spec":[ + { + "size":50, + "type":"ALL", + "mount_point":"/data" + } + ], + "desc":"中等机型", + "instance_num":0, + "qps":{ + + } + }, + { + "spec_name":"8核_32G_100G", + "cpu":{ + "max":8, + "min":8 + }, + "mem":{ + "max":32, + "min":32 + }, + "device_class":[ + + ], + "storage_spec":[ + { + "size":100, + "type":"ALL", + "mount_point":"/data" + } + ], + "desc":"高等机型", + "instance_num":0, + "qps":{ + + } + } + ] + } +} \ No newline at end of file diff --git a/dbm-ui/backend/db_meta/migrations/0001_initial.py b/dbm-ui/backend/db_meta/migrations/0001_initial.py index cfcde3f0a4..978831b2bd 100644 --- a/dbm-ui/backend/db_meta/migrations/0001_initial.py +++ b/dbm-ui/backend/db_meta/migrations/0001_initial.py @@ -381,7 +381,7 @@ class Migration(migrations.Migration): "ordering": ("-create_at",), "unique_together": {("machine", "port")}, }, - bases=(backend.db_meta.models.instance.InstanceStatusMixin, models.Model), + bases=(backend.db_meta.models.instance.InstanceMixin, models.Model), ), migrations.CreateModel( name="SyncFailedMachine", @@ -603,6 +603,6 @@ class Migration(migrations.Migration): "ordering": ("-create_at",), "unique_together": {("machine", "port")}, }, - bases=(backend.db_meta.models.instance.InstanceStatusMixin, models.Model), + bases=(backend.db_meta.models.instance.InstanceMixin, models.Model), ), ] diff --git a/dbm-ui/backend/db_meta/migrations/0007_auto_20230510_1955.py b/dbm-ui/backend/db_meta/migrations/0007_auto_20230510_1955.py index 4a565674ab..76569423d2 100644 --- a/dbm-ui/backend/db_meta/migrations/0007_auto_20230510_1955.py +++ b/dbm-ui/backend/db_meta/migrations/0007_auto_20230510_1955.py @@ -13,33 +13,6 @@ class Migration(migrations.Migration): ] operations = [ - migrations.CreateModel( - name="ClusterDeployPlan", - fields=[ - ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), - ("creator", models.CharField(max_length=64, verbose_name="创建人")), - ("create_at", models.DateTimeField(auto_now_add=True, verbose_name="创建时间")), - ("updater", models.CharField(max_length=64, verbose_name="修改人")), - ("update_at", models.DateTimeField(auto_now=True, verbose_name="更新时间")), - ("name", models.CharField(default="", max_length=128)), - ("shard_cnt", models.PositiveIntegerField(default=0, help_text="集群分片总数")), - ("capacity", models.PositiveIntegerField(default=0, help_text="集群存储预估总容量/G")), - ("machine_pair_cnt", models.PositiveIntegerField(default=0, help_text="机器组数: (每组两台)")), - ( - "cluster_type", - models.CharField( - choices=ClusterType.get_choices(), - help_text="集群类型", - max_length=128, - ), - ), - ("desc", models.TextField(blank=True, default="", help_text="方案描述", null=True)), - ("spec", models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to="db_meta.spec")), - ], - options={ - "abstract": False, - }, - ), migrations.AlterField( model_name="clusterentry", name="role", diff --git a/dbm-ui/backend/db_meta/migrations/0012_alter_machine_spec_config.py b/dbm-ui/backend/db_meta/migrations/0012_alter_machine_spec_config.py deleted file mode 100644 index 1902fe5c0a..0000000000 --- a/dbm-ui/backend/db_meta/migrations/0012_alter_machine_spec_config.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.19 on 2023-06-14 06:44 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("db_meta", "0011_alter_cluster_name"), - ] - - operations = [ - migrations.AlterField( - model_name="machine", - name="spec_config", - field=models.JSONField(default=dict, help_text="当前的虚拟规格配置"), - ), - ] diff --git a/dbm-ui/backend/db_meta/migrations/0013_alter_machine_spec_config.py b/dbm-ui/backend/db_meta/migrations/0013_alter_machine_spec_config.py new file mode 100644 index 0000000000..68d84b2703 --- /dev/null +++ b/dbm-ui/backend/db_meta/migrations/0013_alter_machine_spec_config.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.19 on 2023-06-15 07:09 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("db_meta", "0011_alter_cluster_name"), + ] + + operations = [ + migrations.AlterField( + model_name="machine", + name="spec_config", + field=models.JSONField(default=dict, help_text="当前的虚拟规格配置"), + ), + ] diff --git a/dbm-ui/backend/db_meta/migrations/0014_auto_20230627_1716.py b/dbm-ui/backend/db_meta/migrations/0014_auto_20230627_1716.py new file mode 100644 index 0000000000..df92034b52 --- /dev/null +++ b/dbm-ui/backend/db_meta/migrations/0014_auto_20230627_1716.py @@ -0,0 +1,26 @@ +# Generated by Django 3.2.19 on 2023-06-27 09:16 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("db_meta", "0013_alter_machine_spec_config"), + ] + + operations = [ + migrations.AddField( + model_name="spec", + name="instance_num", + field=models.IntegerField(default=0, help_text="实例数(es专属)"), + ), + migrations.AlterUniqueTogether( + name="spec", + unique_together=set(), + ), + migrations.AlterIndexTogether( + name="spec", + index_together={("spec_cluster_type", "spec_machine_type", "spec_name")}, + ), + ] diff --git a/dbm-ui/backend/db_meta/migrations/0015_auto_20230703_2002.py b/dbm-ui/backend/db_meta/migrations/0015_auto_20230703_2002.py new file mode 100644 index 0000000000..837c4dfd83 --- /dev/null +++ b/dbm-ui/backend/db_meta/migrations/0015_auto_20230703_2002.py @@ -0,0 +1,22 @@ +# Generated by Django 3.2.19 on 2023-07-03 12:02 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("db_meta", "0014_auto_20230627_1716"), + ] + + operations = [ + migrations.RemoveField( + model_name="cluster", + name="deploy_plan_id", + ), + migrations.AddField( + model_name="spec", + name="qps", + field=models.JSONField(default=dict, help_text="qps规格描述:{'min': 1, 'max': 100}"), + ), + ] diff --git a/dbm-ui/backend/db_meta/migrations/0016_alter_spec_desc.py b/dbm-ui/backend/db_meta/migrations/0016_alter_spec_desc.py new file mode 100644 index 0000000000..9a0d1f4cbe --- /dev/null +++ b/dbm-ui/backend/db_meta/migrations/0016_alter_spec_desc.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.19 on 2023-07-21 02:11 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("db_meta", "0015_auto_20230703_2002"), + ] + + operations = [ + migrations.AlterField( + model_name="spec", + name="desc", + field=models.TextField(blank=True, help_text="资源规格描述", null=True), + ), + ] diff --git a/dbm-ui/backend/db_meta/migrations/0017_alter_cluster_immute_domain.py b/dbm-ui/backend/db_meta/migrations/0017_alter_cluster_immute_domain.py new file mode 100644 index 0000000000..7f103136d2 --- /dev/null +++ b/dbm-ui/backend/db_meta/migrations/0017_alter_cluster_immute_domain.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.19 on 2023-08-16 08:35 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("db_meta", "0016_alter_spec_desc"), + ] + + operations = [ + migrations.AlterField( + model_name="cluster", + name="immute_domain", + field=models.CharField(db_index=True, default="", max_length=255), + ), + ] diff --git a/dbm-ui/backend/db_meta/migrations/0018_extraprocessinstance.py b/dbm-ui/backend/db_meta/migrations/0018_extraprocessinstance.py new file mode 100644 index 0000000000..b6a8f48159 --- /dev/null +++ b/dbm-ui/backend/db_meta/migrations/0018_extraprocessinstance.py @@ -0,0 +1,46 @@ +# Generated by Django 3.2.19 on 2023-08-24 09:15 + +import django.db.models.deletion +from django.db import migrations, models + +from backend.db_meta.enums.extra_process_type import ExtraProcessType + + +class Migration(migrations.Migration): + + dependencies = [ + ("db_meta", "0017_alter_cluster_immute_domain"), + ] + + operations = [ + migrations.CreateModel( + name="ExtraProcessInstance", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("creator", models.CharField(max_length=64, verbose_name="创建人")), + ("create_at", models.DateTimeField(auto_now_add=True, verbose_name="创建时间")), + ("updater", models.CharField(max_length=64, verbose_name="修改人")), + ("update_at", models.DateTimeField(auto_now=True, verbose_name="更新时间")), + ("bk_biz_id", models.IntegerField(default=0)), + ("cluster_id", models.IntegerField(default=0, help_text="关联的cluster_id")), + ( + "proc_type", + models.CharField( + choices=ExtraProcessType.get_choices(), default="", help_text="进程类型", max_length=64 + ), + ), + ("version", models.CharField(default="", help_text="版本号", max_length=64)), + ("listen_port", models.PositiveIntegerField(default=0, help_text="进程监听端口")), + ("extra_config", models.JSONField(default=dict, help_text="进程的定制化属性")), + ( + "machine", + models.ForeignKey( + help_text="关联的machine信息", on_delete=django.db.models.deletion.PROTECT, to="db_meta.machine" + ), + ), + ], + options={ + "abstract": False, + }, + ), + ] diff --git a/dbm-ui/backend/db_meta/migrations/0019_auto_20230829_1133.py b/dbm-ui/backend/db_meta/migrations/0019_auto_20230829_1133.py new file mode 100644 index 0000000000..87b24218ad --- /dev/null +++ b/dbm-ui/backend/db_meta/migrations/0019_auto_20230829_1133.py @@ -0,0 +1,36 @@ +# Generated by Django 3.2.19 on 2023-08-29 03:33 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("db_meta", "0018_extraprocessinstance"), + ] + + operations = [ + migrations.AddField( + model_name="extraprocessinstance", + name="bk_cloud_id", + field=models.IntegerField(default=0, help_text="云区域 ID,对应cmdb"), + ), + migrations.AddField( + model_name="extraprocessinstance", + name="ip", + field=models.GenericIPAddressField(default="", help_text="IP 地址"), + ), + migrations.AlterField( + model_name="extraprocessinstance", + name="bk_biz_id", + field=models.IntegerField(default=0, help_text="关联的业务id,对应cmdb"), + ), + migrations.AlterUniqueTogether( + name="extraprocessinstance", + unique_together={("ip", "bk_cloud_id", "listen_port")}, + ), + migrations.RemoveField( + model_name="extraprocessinstance", + name="machine", + ), + ] diff --git a/dbm-ui/backend/db_meta/migrations/0020_auto_20230913_1041.py b/dbm-ui/backend/db_meta/migrations/0020_auto_20230913_1041.py new file mode 100644 index 0000000000..b662828016 --- /dev/null +++ b/dbm-ui/backend/db_meta/migrations/0020_auto_20230913_1041.py @@ -0,0 +1,34 @@ +# Generated by Django 3.2.19 on 2023-09-13 02:41 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("db_meta", "0019_auto_20230829_1133"), + ] + + operations = [ + migrations.AddField( + model_name="cluster", + name="tag", + field=models.ManyToManyField(blank=True, help_text="集群标签", to="db_meta.Tag"), + ), + migrations.AddField( + model_name="tag", + name="type", + field=models.CharField( + choices=[("custom", "custom"), ("system", "system")], + default="system", + help_text="tag类型", + max_length=64, + ), + preserve_default=False, + ), + migrations.AlterField( + model_name="tag", + name="name", + field=models.CharField(default="", help_text="tag名称", max_length=64), + ), + ] diff --git a/dbm-ui/backend/db_meta/models/__init__.py b/dbm-ui/backend/db_meta/models/__init__.py index bffd0bfc2f..b7ef5d8ada 100644 --- a/dbm-ui/backend/db_meta/models/__init__.py +++ b/dbm-ui/backend/db_meta/models/__init__.py @@ -11,14 +11,14 @@ from .app import App, AppCache from .city_map import BKCity, LogicalCity from .cluster import Cluster -from .cluster_entry import ClusterEntry +from .cluster_entry import CLBEntryDetail, ClusterEntry, PolarisEntryDetail from .cluster_monitor import AppMonitorTopo, ClusterMonitorTopo from .db_module import BKModule, DBModule from .group import Group, GroupInstance from .instance import ProxyInstance, StorageInstance from .machine import Machine from .proxy_instance_ext import TenDBClusterSpiderExt -from .spec import ClusterDeployPlan, SnapshotSpec, Spec +from .spec import SnapshotSpec, Spec from .storage_instance_tuple import StorageInstanceTuple from .storage_set_dtl import NosqlStorageSetDtl, TenDBClusterStorageSet from .tag import Tag diff --git a/dbm-ui/backend/db_meta/models/cluster.py b/dbm-ui/backend/db_meta/models/cluster.py index 0c14d64085..fd0ebeec22 100644 --- a/dbm-ui/backend/db_meta/models/cluster.py +++ b/dbm-ui/backend/db_meta/models/cluster.py @@ -12,7 +12,7 @@ from typing import Dict, List from django.db import models -from django.db.models import QuerySet +from django.db.models import Count, QuerySet from django.forms import model_to_dict from django.utils.translation import ugettext_lazy as _ @@ -29,7 +29,9 @@ InstanceStatus, TenDBClusterSpiderRole, ) +from backend.db_meta.enums.cluster_status import ClusterDBSingleStatusFlags from backend.db_meta.exceptions import ClusterExclusiveOperateException, DBMetaException +from backend.db_meta.models.tag import Tag from backend.db_services.version.constants import LATEST, PredixyVersion, TwemproxyVersion from backend.ticket.constants import TicketType from backend.ticket.models import ClusterOperateRecord @@ -43,16 +45,14 @@ class Cluster(AuditedModel): bk_biz_id = models.IntegerField(default=0) cluster_type = models.CharField(max_length=64, choices=ClusterType.get_choices(), default="") db_module_id = models.BigIntegerField(default=0) - immute_domain = models.CharField(max_length=200, default="", db_index=True) + immute_domain = models.CharField(max_length=255, default="", db_index=True) major_version = models.CharField(max_length=64, default="", help_text=_("主版本号")) phase = models.CharField(max_length=64, choices=ClusterPhase.get_choices(), default=ClusterPhase.ONLINE.value) status = models.CharField(max_length=64, choices=ClusterStatus.get_choices(), default=ClusterStatus.NORMAL.value) bk_cloud_id = models.IntegerField(default=DEFAULT_BK_CLOUD_ID, help_text=_("云区域 ID")) region = models.CharField(max_length=128, default="", help_text=_("地域")) time_zone = models.CharField(max_length=16, default=DEFAULT_TIME_ZONE, help_text=_("集群所在的时区")) - deploy_plan_id = models.BigIntegerField(default=0, help_text=_("部署方法ID")) - - # tag = models.ManyToManyField(Tag, blank=True) + tag = models.ManyToManyField(Tag, blank=True, help_text=_("集群标签")) class Meta: unique_together = ("bk_biz_id", "name", "cluster_type", "db_module_id") @@ -63,6 +63,38 @@ def __str__(self): def to_dict(self): return {**model_to_dict(self), "cluster_type_name": str(ClusterType.get_choice_label(self.cluster_type))} + @property + def simple_desc(self): + return model_to_dict( + self, + [ + "id", + "name", + "bk_cloud_id", + "region", + "cluster_type", + "immute_domain", + "major_version", + ], + ) + + @property + def extra_desc(self): + """追加额外信息,不适合大批量序列化场景""" + + simple_desc = self.simple_desc + + # 填充额外统计信息 + simple_desc["proxy_count"] = self.proxyinstance_set.all().count() + for storage in ( + self.storageinstance_set.values("instance_role") + .annotate(cnt=Count("machine__ip", distinct=True)) + .order_by() + ): + simple_desc["{}_count".format(storage["instance_role"])] = storage["cnt"] + + return simple_desc + @classmethod def get_cluster_id_immute_domain_map(cls, cluster_ids: List[int]) -> Dict[int, str]: """查询集群ID和域名的映射关系""" @@ -70,20 +102,19 @@ def get_cluster_id_immute_domain_map(cls, cluster_ids: List[int]) -> Dict[int, s return {cluster.id: cluster.immute_domain for cluster in clusters} @classmethod - def is_exclusive(cls, cluster_id, ticket_type=None): + def is_exclusive(cls, cluster_id, ticket_type=None, **kwargs): if not ticket_type: return None - return ClusterOperateRecord.objects.has_exclusive_operations(ticket_type, cluster_id) + return ClusterOperateRecord.objects.has_exclusive_operations(ticket_type, cluster_id, **kwargs) @classmethod - def handle_exclusive_operations(cls, cluster_ids: List[int], ticket_type: str): + def handle_exclusive_operations(cls, cluster_ids: List[int], ticket_type: str, **kwargs): """ 处理当前的动作是否和集群正在运行的动作存在执行互斥 """ - for cluster_id in cluster_ids: - exclusive_infos = cls.is_exclusive(cluster_id, ticket_type) + exclusive_infos = cls.is_exclusive(cluster_id, ticket_type, **kwargs) if not exclusive_infos: continue @@ -148,6 +179,10 @@ def __status_flag(self): status=InstanceStatus.UNAVAILABLE.value, instance_inner_role=InstanceInnerRole.SLAVE.value ).exists(): flag_obj |= ClusterTenDBClusterStatusFlag.RemoteSlaveUnavailable + elif self.cluster_type == ClusterType.TenDBSingle.value: + flag_obj = ClusterDBSingleStatusFlags(0) + if self.storageinstance_set.filter(status=InstanceStatus.UNAVAILABLE.value).exists(): + flag_obj |= ClusterDBSingleStatusFlags.SingleUnavailable else: raise DBMetaException(message=_("{} 未实现 status flag".format(self.cluster_type))) @@ -181,10 +216,13 @@ def get_partition_port(self): return self.storageinstance_set.first().port elif self.cluster_type == ClusterType.TenDBHA: return self.proxyinstance_set.first().port - - @classmethod - def is_refer_deploy_plan(cls, deploy_plan_ids): - return cls.objects.filter(deploy_plan_id__in=deploy_plan_ids).exists() + # TODO: tendbcluster的端口是spider master? + elif self.cluster_type == ClusterType.TenDBCluster: + return ( + self.proxyinstance_set.filter(tendbclusterspiderext__spider_role=TenDBClusterSpiderRole.SPIDER_MASTER) + .first() + .port + ) def tendbcluster_ctl_primary_address(self) -> str: """ @@ -205,20 +243,20 @@ def tendbcluster_ctl_primary_address(self) -> str: res = DRSApi.rpc( { "addresses": [ctl_address], - "cmds": ["set tc_admin=0", "show slave status"], + "cmds": ["tdbctl get primary"], "force": False, "bk_cloud_id": self.bk_cloud_id, } ) - logger.info("find primary show slave status res: {}".format(res)) + logger.info("tdbctl get primary res: {}".format(res)) if res[0]["error_msg"]: - raise DBMetaException(message=_("find primary show slave status failed: {}".format(res[0]["error_msg"]))) + raise DBMetaException(message=_("get primary failed: {}".format(res[0]["error_msg"]))) - slave_info_table_data = res[0]["cmd_results"][1]["table_data"] - if slave_info_table_data: + primary_info_table_data = res[0]["cmd_results"][0]["table_data"] + if primary_info_table_data: return "{}{}{}".format( - slave_info_table_data[0]["Master_Host"], IP_PORT_DIVIDER, slave_info_table_data[0]["Master_Port"] + primary_info_table_data[0]["HOST"], IP_PORT_DIVIDER, primary_info_table_data[0]["PORT"] ) else: return ctl_address diff --git a/dbm-ui/backend/db_meta/models/cluster_monitor.py b/dbm-ui/backend/db_meta/models/cluster_monitor.py index be53ead63f..0ff1c2555d 100644 --- a/dbm-ui/backend/db_meta/models/cluster_monitor.py +++ b/dbm-ui/backend/db_meta/models/cluster_monitor.py @@ -24,42 +24,59 @@ INSTANCE_MONITOR_PLUGINS = { DBType.MySQL: { - MachineType.PROXY: {"name": "proxy", "plugin_id": "dbm_mysqlproxy_exporter"}, - MachineType.BACKEND: {"name": "mysql", "plugin_id": "dbm_mysqld_exporter"}, - MachineType.SPIDER: {"name": "spider", "plugin_id": "dbm_spider_exporter"}, - MachineType.REMOTE: {"name": "mysql", "plugin_id": "dbm_mysqld_exporter"}, - MachineType.SINGLE: {"name": "mysql", "plugin_id": "dbm_mysqld_exporter"}, + MachineType.PROXY: {"name": "proxy", "plugin_id": "dbm_mysqlproxy_exporter", "func_name": "mysql-proxy"}, + MachineType.BACKEND: {"name": "mysql", "plugin_id": "dbm_mysqld_exporter", "func_name": "mysqld"}, + MachineType.SPIDER: {"name": "spider", "plugin_id": "dbm_spider_exporter", "func_name": "mysqld"}, + MachineType.REMOTE: {"name": "mysql", "plugin_id": "dbm_mysqld_exporter", "func_name": "mysqld"}, + MachineType.SINGLE: {"name": "mysql", "plugin_id": "dbm_mysqld_exporter", "func_name": "mysqld"}, }, DBType.Redis: { - MachineType.TWEMPROXY: {"name": "twemproxy", "plugin_id": "dbm_twemproxy_exporter"}, - MachineType.PREDIXY: {"name": "predixy", "plugin_id": "dbm_predixy_exporter"}, - MachineType.TENDISCACHE: {"name": "tendiscache", "plugin_id": "dbm_redis_exporter"}, - MachineType.TENDISPLUS: {"name": "tendisplus", "plugin_id": "dbm_redis_exporter"}, - MachineType.TENDISSSD: {"name": "tendisssd", "plugin_id": "dbm_redis_exporter"}, + MachineType.TWEMPROXY: {"name": "twemproxy", "plugin_id": "dbm_twemproxy_exporter", "func_name": "nutcracker"}, + MachineType.PREDIXY: {"name": "predixy", "plugin_id": "dbm_predixy_exporter", "func_name": "predixy"}, + MachineType.TENDISCACHE: { + "name": "tendiscache", + "plugin_id": "dbm_redis_exporter", + "func_name": "redis-server", + }, + MachineType.TENDISPLUS: {"name": "tendisplus", "plugin_id": "dbm_redis_exporter", "func_name": "tendisplus"}, + MachineType.TENDISSSD: {"name": "tendisssd", "plugin_id": "dbm_redis_exporter", "func_name": "redis-server"}, }, DBType.Es: { - MachineType.ES_DATANODE: {"name": "es", "plugin_id": "dbm_elasticsearch_exporter"}, - MachineType.ES_MASTER: {"name": "es", "plugin_id": "dbm_elasticsearch_exporter"}, - MachineType.ES_CLIENT: {"name": "es", "plugin_id": "dbm_elasticsearch_exporter"}, + MachineType.ES_DATANODE: {"name": "es", "plugin_id": "dbm_elasticsearch_exporter", "func_name": "java"}, + MachineType.ES_MASTER: {"name": "es", "plugin_id": "dbm_elasticsearch_exporter", "func_name": "java"}, + MachineType.ES_CLIENT: {"name": "es", "plugin_id": "dbm_elasticsearch_exporter", "func_name": "java"}, }, DBType.Kafka: { - MachineType.BROKER: {"name": "kafka", "plugin_id": "dbm_kafka_bkpull"}, - MachineType.ZOOKEEPER: {"name": "zookeeper", "plugin_id": "dbm_kafka_exporter"}, + MachineType.BROKER: {"name": "kafka", "plugin_id": "dbm_kafka_bkpull", "func_name": "java"}, + MachineType.ZOOKEEPER: {"name": "zookeeper", "plugin_id": "dbm_kafka_exporter", "func_name": "java"}, }, DBType.Hdfs: { - MachineType.HDFS_MASTER: {"name": "hdfs", "plugin_id": "dbm_hdfs_exporter"}, - MachineType.HDFS_DATANODE: {"name": "hdfs", "plugin_id": "dbm_hdfs_exporter"}, + MachineType.HDFS_MASTER: {"name": "hdfs", "plugin_id": "dbm_hdfs_exporter", "func_name": "java"}, + MachineType.HDFS_DATANODE: {"name": "hdfs", "plugin_id": "dbm_hdfs_exporter", "func_name": "java"}, }, DBType.Pulsar: { - MachineType.PULSAR_BROKER: {"name": "broker", "plugin_id": "dbm_pulsarbroker_bkpull"}, - MachineType.PULSAR_ZOOKEEPER: {"name": "zookeeper", "plugin_id": "dbm_pulsarzookeeper_bkpull"}, - MachineType.PULSAR_BOOKKEEPER: {"name": "bookkeeper", "plugin_id": "dbm_pulsarbookkeeper_bkpull"}, + MachineType.PULSAR_BROKER: {"name": "broker", "plugin_id": "dbm_pulsarbroker_bkpull", "func_name": "java"}, + MachineType.PULSAR_ZOOKEEPER: { + "name": "zookeeper", + "plugin_id": "dbm_pulsarzookeeper_bkpull", + "func_name": "java", + }, + MachineType.PULSAR_BOOKKEEPER: { + "name": "bookkeeper", + "plugin_id": "dbm_pulsarbookkeeper_bkpull", + "func_name": "java", + }, }, DBType.InfluxDB: { - MachineType.INFLUXDB: {"name": "influxdb", "plugin_id": "dbm_influxdb_bkpull"}, + MachineType.INFLUXDB: {"name": "influxdb", "plugin_id": "dbm_influxdb_bkpull", "func_name": "influxd"}, + }, + DBType.Riak: { + MachineType.RIAK: {"name": "riak", "plugin_id": "dbm_riak_exporter", "func_name": "beam.smp"}, }, } +SET_NAME_TEMPLATE = "db.{db_type}.{monitor_plugin_name}" + def get_monitor_plugin(db_type, machine_type): """主机实例 -> 监控插件类型""" @@ -91,14 +108,19 @@ class AppMonitorTopo(AuditedModel): @classmethod def get_set_by_dbtype(cls, db_type): return [ - {"machine_type": obj.machine_type, "bk_set_id": obj.bk_set_id, "bk_set_name": obj.bk_set_name} + { + "machine_type": obj.machine_type, + "bk_set_id": obj.bk_set_id, + "bk_set_name": obj.bk_set_name, + "bk_biz_id": obj.bk_biz_id, + } for obj in cls.objects.filter(db_type=db_type) ] @classmethod def get_set_by_plugin_id(cls, plugin_id): return list( - cls.objects.filter(monitor_plugin_id__contains=plugin_id).values_list("bk_set_id", flat=True).distinct() + cls.objects.filter(monitor_plugin_id__contains=plugin_id).values_list("bk_set_id", "bk_biz_id").distinct() ) @classmethod @@ -124,7 +146,6 @@ def init_topo(cls): machine_type=machine_type, db_type=db_type, monitor_plugin=monitor_plugin_name, - # monitor_plugin_id=monitor_plugin_id, ) # 不同machine类型复用相同plugin及topo @@ -137,7 +158,7 @@ def init_topo(cls): obj.save() continue - bk_set_name = f"db.{db_type}.{monitor_plugin_name}" + bk_set_name = SET_NAME_TEMPLATE.format(db_type=db_type, monitor_plugin_name=monitor_plugin_name) # 本地没有 -> 远程没有 -> 创建远程 | # -> 远程有 |---> 更新本地 @@ -178,14 +199,6 @@ def init_topo(cls): } ) bk_set_id = res["bk_set_id"] - # logger.info( - # "init_topo -> [%s, %s, %s], create_set(%s) -> %s.", - # db_type, - # machine_type, - # monitor_plugin_name, - # bk_set_name, - # bk_set_id, - # ) if not obj.bk_set_id: logger.info( diff --git a/dbm-ui/backend/db_meta/models/extra_process.py b/dbm-ui/backend/db_meta/models/extra_process.py new file mode 100644 index 0000000000..ad974b826c --- /dev/null +++ b/dbm-ui/backend/db_meta/models/extra_process.py @@ -0,0 +1,43 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +import logging + +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +from backend.bk_web.models import AuditedModel +from backend.db_meta.enums.extra_process_type import ExtraProcessType + +logger = logging.getLogger("root") + + +class ExtraProcessInstance(AuditedModel): + """ + 引入“附属”进程的概念,“附属”进程是指一类在DB机器安装在非DB进程, + 在DBM系统中,TBinlogDumper可以认为是这类的进程。 + 1:这张表是以进程的维度进行存储,所以,进程信息作为唯一约束, + 如果DBM系统托管的是非监听端口的进程,listen_port设置默认0或者其他有意义的值即可。 + 2:表中信息应该和db_meta其他的核心表做“割裂”,比如设置外键约束等,这样对之前写的代码不影响,但可以跟字段做隐形关联。 + """ + + bk_biz_id = models.IntegerField(default=0, help_text=_("关联的业务id,对应cmdb")) + cluster_id = models.IntegerField(default=0, help_text=_("关联的cluster_id")) + bk_cloud_id = models.IntegerField(default=0, help_text=_("云区域 ID,对应cmdb")) + ip = models.GenericIPAddressField(default="", help_text=_("IP 地址")) + proc_type = models.CharField( + max_length=64, choices=ExtraProcessType.get_choices(), default="", help_text=_("进程类型") + ) + version = models.CharField(max_length=64, default="", help_text=_("版本号")) + listen_port = models.PositiveIntegerField(default=0, help_text=_("进程监听端口")) + extra_config = models.JSONField(default=dict, help_text=_("进程的定制化属性")) + + class Meta: + unique_together = ("ip", "bk_cloud_id", "listen_port") diff --git a/dbm-ui/backend/db_meta/models/instance.py b/dbm-ui/backend/db_meta/models/instance.py index 5ec3144108..c8ddda8e90 100644 --- a/dbm-ui/backend/db_meta/models/instance.py +++ b/dbm-ui/backend/db_meta/models/instance.py @@ -13,7 +13,7 @@ from typing import Dict, List, Union from django.db import models -from django.db.models import Q +from django.db.models import Q, QuerySet from django.utils.translation import ugettext_lazy as _ from backend import constants @@ -35,11 +35,34 @@ from .machine import Machine -class InstanceStatusMixin(object): +class InstanceMixin(object): """ 封装实例的状态查询的相关方法 """ + def __str__(self): + return self.ip_port + + @property + def simple_desc(self): + return { + "name": self.name, + "ip": self.machine.ip, + "port": self.port, + "instance": "{}{}{}".format(self.machine.ip, IP_PORT_DIVIDER, self.port), + "status": self.status, + "phase": getattr(self, "phase", None), + "bk_instance_id": self.bk_instance_id, + "bk_host_id": self.machine.bk_host_id, + "bk_cloud_id": self.machine.bk_cloud_id, + "spec_config": self.machine.spec_config, + "bk_biz_id": self.bk_biz_id, + } + + @property + def ip_port(self): + return f"{self.machine.ip}{constants.IP_PORT_DIVIDER}{self.port}" + @property def instance_type(self): raise NotImplementedError() @@ -57,7 +80,7 @@ def can_access(self) -> (bool, str): return True, "" @classmethod - def find_insts_by_addresses(cls, addresses: List[Union[str, Dict]], divider: str = IP_PORT_DIVIDER): + def find_insts_by_addresses(cls, addresses: List[Union[str, Dict]], divider: str = IP_PORT_DIVIDER) -> QuerySet: """通过实例的ip:port查询实例""" if not addresses: @@ -78,8 +101,35 @@ def find_insts_by_addresses(cls, addresses: List[Union[str, Dict]], divider: str ) return cls.objects.select_related("machine").filter(address_filters) - -class StorageInstance(InstanceStatusMixin, AuditedModel): + @classmethod + def filter_by_ips(cls, bk_biz_id: int, ips: List[str]): + """通过ip列表反查实例列表""" + instances = [] + unique_ip_roles = set() + for inst in cls.objects.filter(bk_biz_id=bk_biz_id, machine__ip__in=ips): + ip_role = IP_PORT_DIVIDER.join([inst.machine.ip, inst.instance_role]) + if ip_role in unique_ip_roles: + continue + + # 目前基本上一个实例仅属于一个集群,此处循环不会超过1次 + unique_ip_roles.add(ip_role) + for cluster in inst.cluster.all(): + instances.append( + { + "ip": inst.machine.ip, + "bk_host_id": inst.machine.bk_host_id, + "bk_cloud_id": inst.machine.bk_cloud_id, + "spec_id": inst.machine.spec_id, + "spec_config": inst.machine.spec_config, + "role": inst.instance_role, + "cluster": cluster.extra_desc, + } + ) + + return instances + + +class StorageInstance(InstanceMixin, AuditedModel): version = models.CharField(max_length=64, default="", help_text=_("版本号")) port = models.PositiveIntegerField(default=0) machine = models.ForeignKey(Machine, on_delete=models.PROTECT) @@ -108,35 +158,12 @@ class Meta: ) ordering = ("-create_at",) - def __str__(self): - return self.ip_port - @classmethod def get_instance_id_ip_port_map(cls, instance_id: List[int]) -> Dict[int, str]: """查询实例 ID 和 IP:PORT 的映射关系""" instances = cls.objects.select_related("machine").filter(id__in=instance_id) return {instance.id: instance.ip_port for instance in instances} - @property - def ip_port(self): - return f"{self.machine.ip}{constants.IP_PORT_DIVIDER}{self.port}" - - @property - def simple_desc(self): - return { - "name": self.name, - "ip": self.machine.ip, - "port": self.port, - "instance": "{}{}{}".format(self.machine.ip, IP_PORT_DIVIDER, self.port), - "status": self.status, - "phase": self.phase, - "bk_instance_id": self.bk_instance_id, - "bk_host_id": self.machine.bk_host_id, - "bk_cloud_id": self.machine.bk_cloud_id, - "bk_biz_id": self.bk_biz_id, - # - } - @property def instance_type(self): return InstanceType.STORAGE.value @@ -152,7 +179,7 @@ def find_storage_instance_by_addresses(cls, addresses: List[Union[str, Dict]]): return cls.objects.select_related("machine").filter(address_filters) -class ProxyInstance(InstanceStatusMixin, AuditedModel): +class ProxyInstance(InstanceMixin, AuditedModel): version = models.CharField(max_length=64, default="", help_text=_("版本号")) port = models.PositiveIntegerField(default=0) admin_port = models.PositiveIntegerField(default=0) @@ -182,26 +209,9 @@ class Meta: ordering = ("-create_at",) @property - def ip_port(self): - return f"{self.machine.ip}{constants.IP_PORT_DIVIDER}{self.port}" - - @property - def simple_desc(self): - return { - "name": self.name, - "ip": self.machine.ip, - "port": self.port, - "instance": "{}{}{}".format(self.machine.ip, IP_PORT_DIVIDER, self.port), - "status": self.status, - "bk_instance_id": self.bk_instance_id, - "bk_host_id": self.machine.bk_host_id, - "bk_cloud_id": self.machine.bk_cloud_id, - "bk_biz_id": self.bk_biz_id, - } + def instance_role(self): + return InstanceType.PROXY.value @property def instance_type(self): return InstanceType.PROXY.value - - def __str__(self): - return f"{self.machine.ip}{constants.IP_PORT_DIVIDER}{self.port}" diff --git a/dbm-ui/backend/db_meta/models/machine.py b/dbm-ui/backend/db_meta/models/machine.py index 4f2df8a309..88be79453b 100644 --- a/dbm-ui/backend/db_meta/models/machine.py +++ b/dbm-ui/backend/db_meta/models/machine.py @@ -11,6 +11,7 @@ from dataclasses import asdict from django.db import models +from django.forms import model_to_dict from django.utils.translation import ugettext_lazy as _ from backend.bk_web.models import AuditedModel @@ -144,3 +145,7 @@ def get_host_info_from_cmdb(cls, bk_host_id: int) -> dict: def is_refer_spec(cls, spec_ids): """是否引用了相关规格""" return cls.objects.filter(spec_id__in=spec_ids).exists() + + @property + def simple_desc(self): + return model_to_dict(self) diff --git a/dbm-ui/backend/db_meta/models/mysql_open_area.py b/dbm-ui/backend/db_meta/models/mysql_open_area.py new file mode 100644 index 0000000000..46c4104326 --- /dev/null +++ b/dbm-ui/backend/db_meta/models/mysql_open_area.py @@ -0,0 +1,57 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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.db import models +from django.utils.translation import ugettext_lazy as _ + +from backend.bk_web.models import AuditedModel +from backend.constants import DEFAULT_BK_CLOUD_ID + + +class TendbOpenAreaConfig(AuditedModel): + bk_biz_id = models.IntegerField(default=0) + bk_cloud_id = models.IntegerField(default=DEFAULT_BK_CLOUD_ID, help_text=_("云区域 ID")) + config_name = models.CharField(max_length=256, default="") + domain_name = models.CharField(max_length=256, default="") + source_cluster_id = models.BigIntegerField(default=0) + cluster_type = models.CharField(max_length=64, default="") + + class Meta: + unique_together = ("bk_biz_id", "domain_name", "source_cluster_id", "cluster_type") + + +class TendbOpenAreaSubConfig(AuditedModel): + config = models.ManyToManyField(TendbOpenAreaConfig, blank=True) + db_schema_source_db = models.CharField(max_length=256, help_text=_("获取库表结构的源db"), unique=True) + # json字段,直接存储待克隆表结构列表,克隆所有表时为空列表 + db_schema_source_tblist = models.JSONField(help_text=_("获取表结构的源tb列表"), default=list) + # json字段,直接存储待克隆表数据列表,隆所有表时为空列表 + db_data_source_tblist = models.JSONField(help_text=_("获取数据的源tb列表"), default=list) + db_schema_source_db_model = models.CharField(max_length=256, help_text=_("目标db范式")) + # json字段,存储权限模板列表 + priv_init_id = models.JSONField(help_text=_("权限关联模板id"), default=list) + creator = models.CharField(max_length=100) + create_time = models.DateTimeField(auto_now_add=True, help_text=_("配置创建时间")) + updater = models.CharField(max_length=100) + update_time = models.DateTimeField(auto_now=True, help_text=_("配置创建时间")) + + class Meta: + unique_together = ( + "db_schema_source_db", + "db_schema_source_db_model", + "db_schema_source_tblist", + "db_data_source_tblist", + ) + + +class TendbOpenAreaConfigLog(AuditedModel): + config = models.ManyToManyField(TendbOpenAreaConfig, blank=True) + operator = models.CharField(max_length=100) + operate_time = models.DateTimeField(auto_now=True, help_text=_("配置修改时间")) + config_change_log = models.JSONField(help_text=_("开区配置修改记录")) diff --git a/dbm-ui/backend/db_meta/models/spec.py b/dbm-ui/backend/db_meta/models/spec.py index cc3fd387ad..2f09904f50 100644 --- a/dbm-ui/backend/db_meta/models/spec.py +++ b/dbm-ui/backend/db_meta/models/spec.py @@ -8,13 +8,20 @@ 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. """ +import json +from collections import defaultdict +from typing import Dict, List from django.db import models from django.utils.translation import ugettext_lazy as _ +from backend import env from backend.bk_web.models import AuditedModel - -from ..enums import ClusterType, MachineType +from backend.configuration.constants import SystemSettingsEnum +from backend.configuration.models import SystemSettings +from backend.constants import INT_MAX +from backend.db_meta.enums import ClusterType, MachineType +from backend.ticket.constants import AffinityEnum class Spec(AuditedModel): @@ -32,22 +39,87 @@ class Spec(AuditedModel): mem = models.JSONField(null=True, help_text=_("mem规格描述:{'min':100,'max':1000}")) device_class = models.JSONField(null=True, help_text=_("实际机器机型: ['class1','class2'] ")) storage_spec = models.JSONField(null=True, help_text=_("存储磁盘需求配置:{'mount_point':'/data','size':500,'type':'ssd'}")) - desc = models.TextField(help_text=_("资源规格描述"), default="") + desc = models.TextField(help_text=_("资源规格描述"), null=True, blank=True) + # es专属 + instance_num = models.IntegerField(default=0, help_text=_("实例数(es专属)")) + # spider,redis集群专属 + qps = models.JSONField(default=dict, help_text=_("qps规格描述:{'min': 1, 'max': 100}")) class Meta: - unique_together = [("spec_cluster_type", "spec_machine_type", "spec_name")] - - def get_apply_params_detail(self, group_mark, count, affinity=None, location_spec=None): + index_together = [("spec_cluster_type", "spec_machine_type", "spec_name")] + + @property + def capacity(self): + """ + 根据不同集群类型,计算该规格的容量 + TendbCluster: 如果只有/data数据盘,则容量/2; 如果有/data和/data1数据盘,则按照/data1为准 + TendisPlus, TendisSSD: 一定有两块盘,以/data1为准 + TendisCache: 以内存为准,内存不是范围,是一个准确的值 + 默认:磁盘总容量 + """ + mount_point__size: Dict[str, int] = {disk["mount_point"]: disk["size"] for disk in self.storage_spec} + if self.spec_cluster_type == ClusterType.TenDBCluster: + return mount_point__size.get("data1") or mount_point__size["/data"] / 2 + + if self.spec_cluster_type in [ + ClusterType.TwemproxyTendisSSDInstance, + ClusterType.TendisPredixyTendisplusCluster, + ]: + return mount_point__size["/data1"] + + if self.spec_cluster_type == ClusterType.TendisTwemproxyRedisInstance: + # 取min, max都一样 + return self.mem["min"] + + return sum(map(lambda x: int(x), mount_point__size.values())) + + def get_apply_params_detail(self, group_mark, count, bk_cloud_id, affinity=AffinityEnum.NONE, location_spec=None): # 获取资源申请的detail过程,暂时忽略亲和性和位置参数过滤 + spec_offset = SystemSettings.get_setting_value(SystemSettingsEnum.SPEC_OFFSET) + # 如果暂不支持城市和亲和性,则忽略 + affinity = affinity if env.RESOURCE_SUPPORT_AFFINITY else AffinityEnum.NONE + location_spec = location_spec if env.RESOURCE_SUPPORT_AFFINITY else None return { "group_mark": group_mark, - # "device_class": self.device_class, - "spec": {"cpu": self.cpu, "ram": self.mem}, - # "storage_spec": self.storage_spec, + "bk_cloud_id": bk_cloud_id, + "device_class": self.device_class, + "spec": { + "cpu": self.cpu, + # 内存GB-->MB,只偏移左边 + "ram": { + "min": max(int(self.mem["min"] * 1024 - spec_offset["mem"]), 0), + "max": int(self.mem["max"] * 1024), + }, + }, + "storage_spec": [ + { + "mount_point": storage_spec["mount_point"], + # 如果是all,则需要传空 + "disk_type": "" if storage_spec["type"] == "ALL" else storage_spec["type"], + "min": storage_spec["size"] - spec_offset["disk"], + "max": INT_MAX, + } + for storage_spec in self.storage_spec + ], "count": count, - # TODO: 暂时忽略affinity(亲和性)和location_spec(位置信息) + "affinity": affinity, + "location_spec": location_spec, } + def get_backend_group_apply_params_detail(self, bk_cloud_id, backend_group): + # 专属于后端:如果一组master/slave有特殊要求,则采用backend_group申请 + backend_group_params = [ + self.get_apply_params_detail( + group_mark=f"backend_group_{group}", + count=2, + bk_cloud_id=bk_cloud_id, + affinity=backend_group.get("affinity", AffinityEnum.NONE), + location_spec=backend_group.get("location_spec", None), + ) + for group in range(backend_group["count"]) + ] + return backend_group_params + def get_spec_info(self): # 获取规格的基本信息 return { @@ -55,29 +127,45 @@ def get_spec_info(self): "name": self.spec_name, "cpu": self.cpu, "mem": self.mem, + "qps": self.qps, "device_class": self.device_class, "storage_spec": self.storage_spec, } - -class ClusterDeployPlan(AuditedModel): - """ - Spider、TendisCache、TendisPlus、TendisSSD 部署方案 - """ - - name = models.CharField(max_length=128, default="") - shard_cnt = models.PositiveIntegerField(default=0, help_text=_("集群分片总数")) - capacity = models.PositiveIntegerField(default=0, help_text=_("集群存储预估总容量/G")) - machine_pair_cnt = models.PositiveIntegerField(default=0, help_text=_("机器组数: (每组两台)")) - spec = models.ForeignKey(Spec, on_delete=models.PROTECT) - cluster_type = models.CharField(help_text=_("集群类型"), choices=ClusterType.get_choices(), max_length=128) - desc = models.TextField(default="", help_text=_("方案描述"), blank=True, null=True) - - def get_apply_params_details(self, affinity=None, location_spec=None): - # 获取资源申请的参数,暂时忽略亲和性和位置参数过滤 - backend_master_detail = self.spec.get_apply_params_detail(group_mark="master", count=self.machine_pair_cnt) - backend_slave_detail = self.spec.get_apply_params_detail(group_mark="slave", count=self.machine_pair_cnt) - return [backend_master_detail, backend_slave_detail] + def compare_to(self, spec: "Spec", compare_flag: bool): + """比较规格""" + # 比较存储配置里磁盘的最小值 TODO: TendisCache可能是内存比较 + self_min_config = min([storage.get("size", 0) for storage in self.storage_spec]) + spec_min_config = min([storage.get("size", 0) for storage in spec.storage_spec]) + + if compare_flag: + return self_min_config >= spec_min_config + else: + return self_min_config <= spec_min_config + + @classmethod + def init_spec(cls): + """初始化系统规格配置""" + spec_namespace__name: Dict[str, Dict[str, List]] = defaultdict(lambda: defaultdict(list)) + for spec in Spec.objects.all(): + spec_namespace__name[spec.spec_cluster_type][spec.spec_machine_type].append(spec.spec_name) + + with open("backend/db_meta/init/spec_init.json", "r") as f: + system_spec_init_map = json.loads(f.read()) + + to_init_specs: List[Spec] = [] + for spec_cluster_type in system_spec_init_map.keys(): + for spec_machine_type in system_spec_init_map[spec_cluster_type].keys(): + for spec_details in system_spec_init_map[spec_cluster_type][spec_machine_type]: + # 排除已经存在的同名规格 + if spec_details["spec_name"] in spec_namespace__name[spec_cluster_type][spec_machine_type]: + continue + + to_init_specs.append( + Spec(**spec_details, spec_machine_type=spec_machine_type, spec_cluster_type=spec_cluster_type) + ) + + Spec.objects.bulk_create(to_init_specs) class SnapshotSpec(AuditedModel): diff --git a/dbm-ui/backend/db_meta/models/tag.py b/dbm-ui/backend/db_meta/models/tag.py index 54ac71aa45..34acae2475 100644 --- a/dbm-ui/backend/db_meta/models/tag.py +++ b/dbm-ui/backend/db_meta/models/tag.py @@ -9,13 +9,16 @@ specific language governing permissions and limitations under the License. """ from django.db import models +from django.utils.translation import ugettext_lazy as _ from backend.bk_web.models import AuditedModel +from backend.db_meta.enums.comm import TagType class Tag(AuditedModel): bk_biz_id = models.IntegerField(default=0) - name = models.CharField(max_length=64, default="") + name = models.CharField(max_length=64, default="", help_text=_("tag名称")) + type = models.CharField(max_length=64, help_text=_("tag类型"), choices=TagType.get_choices()) class Meta: unique_together = ["bk_biz_id", "name"] diff --git a/dbm-ui/backend/db_meta/tasks.py b/dbm-ui/backend/db_meta/tasks.py deleted file mode 100644 index 3c17f2b112..0000000000 --- a/dbm-ui/backend/db_meta/tasks.py +++ /dev/null @@ -1,144 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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. -""" -import datetime -import json -import logging - -from celery import shared_task - -from .. import env -from ..components import CCApi -from ..dbm_init.constants import CC_APP_ABBR_ATTR, CC_HOST_DBM_ATTR -from .models import AppCache, Cluster, Machine -from .models.cluster_monitor import SyncFailedMachine - -logger = logging.getLogger("celery") - - -@shared_task -def update_app_cache(): - """缓存空闲机拓扑""" - - now = datetime.datetime.now() - logger.warning("[db_meta] start update app cache start: %s", now) - - bizs = CCApi.search_business().get("info", []) - - updated_hosts, created_hosts = [], [] - for biz in bizs: - try: - logger.warning("[db_meta] sync app : %s", biz["bk_biz_id"]) - bk_app_abbr = biz.get(env.BK_APP_ABBR, "") - db_app_abbr = biz.get(CC_APP_ABBR_ATTR, "") - - # 目标环境中存在bk_app_abbr,则同步过来 - if env.BK_APP_ABBR and env.BK_APP_ABBR != CC_APP_ABBR_ATTR: - # db_app_abbr为空才同步 - if not db_app_abbr and db_app_abbr != bk_app_abbr: - # 大写转小写,空格替换为- - bk_app_abbr = "-".join(map(lambda x: x.lower(), bk_app_abbr.split())) - CCApi.update_business( - {"bk_biz_id": biz["bk_biz_id"], "data": {CC_APP_ABBR_ATTR: bk_app_abbr}}, use_admin=True - ) - db_app_abbr = bk_app_abbr - - # 规范化处理 - obj, created = AppCache.objects.update_or_create( - defaults={ - "db_app_abbr": db_app_abbr, - "bk_biz_name": biz["bk_biz_name"], - "language": biz["language"], - "time_zone": biz["time_zone"], - "bk_biz_maintainer": biz["bk_biz_maintainer"], - }, - bk_biz_id=biz["bk_biz_id"], - ) - - if created: - created_hosts.append(obj.bk_biz_id) - else: - updated_hosts.append(obj.bk_biz_id) - except Exception as e: # pylint: disable=wildcard-import - logger.error("[db_meta] cache app error: %s (%s)", biz, e) - - logger.warning( - "[db_meta] finish update app cache end: %s, create_cnt: %s, update_cnt: %s", - datetime.datetime.now() - now, - len(created_hosts), - len(updated_hosts), - ) - - -@shared_task -def update_host_dbmeta(bk_biz_id=None, cluster_id=None, cluster_ips=None, dbm_meta=None): - """更新集群主机的dbm_meta属性""" - - now = datetime.datetime.now() - logger.info("[update_host_dbmeta] start update begin: %s", now) - - # 0为默认的无效machine - failed_host_ids = list(SyncFailedMachine.objects.all().values_list("bk_host_id", flat=True)) + [0] - - # 支持业务级别的更新 - machines = Machine.objects.all() - if bk_biz_id: - machines = machines.filter(bk_biz_id=bk_biz_id) - - machines = machines.exclude( - bk_host_id__in=failed_host_ids, - ).order_by("-create_at") - - if cluster_id: - cluster = Cluster.objects.get(pk=cluster_id) - cluster_bk_host_ids = set( - list(cluster.storageinstance_set.values_list("machine__bk_host_id", flat=True)) - + list(cluster.proxyinstance_set.values_list("machine__bk_host_id", flat=True)) - ) - machines = Machine.objects.filter(bk_host_id__in=cluster_bk_host_ids) - - if cluster_ips: - machines = machines.filter(ip__in=cluster_ips) - - # 批量更新接口限制最多500条,这里取456条 - STEP = 456 - updated_hosts, failed_updates = [], [] - machine_count = machines.count() - for step in range(machine_count // STEP + 1): - updates = [] - for machine in machines[step * STEP : (step + 1) * STEP]: - cc_dbm_meta = machine.dbm_meta if dbm_meta is None else dbm_meta - updates.append( - {"properties": {CC_HOST_DBM_ATTR: json.dumps(cc_dbm_meta)}, "bk_host_id": machine.bk_host_id} - ) - updated_hosts.extend(updates) - - try: - CCApi.batch_update_host({"update": updates}, use_admin=True) - except Exception as e: # pylint: disable=wildcard-import - failed_updates.extend(updates) - logger.error("[update_host_dbmeta] batch update exception: %s (%s)", updates, e) - - # 容错处理:逐个更新,避免批量更新误伤有效ip - for fail_update in failed_updates: - try: - CCApi.update_host( - {"bk_host_id": fail_update["bk_host_id"], "data": fail_update["properties"]}, use_admin=True - ) - except Exception as e: # pylint: disable=wildcard-import - # 记录异常ip,下次任务直接排除掉,尽量走批量更新 - SyncFailedMachine.objects.get_or_create(bk_host_id=fail_update["bk_host_id"], error=str(e)) - logger.error("[update_host_dbmeta] single update error: %s (%s)", fail_update, e) - - logger.info( - "[update_host_dbmeta] finish update end: %s, update_cnt: %s", - datetime.datetime.now() - now, - len(updated_hosts), - ) diff --git a/dbm-ui/backend/db_meta/urls.py b/dbm-ui/backend/db_meta/urls.py index 79024f2cfc..989b67c9d2 100644 --- a/dbm-ui/backend/db_meta/urls.py +++ b/dbm-ui/backend/db_meta/urls.py @@ -115,4 +115,9 @@ views.fake.fake_reset_tendbha_cluster, name="fake-tendbha-reset_cluster", ), + path( + "fake/tendbcluster/reset_cluster", + views.fake.fake_reset_tendbcluster_cluster, + name="fake-tendbcluster-reset_cluster", + ), ] diff --git a/dbm-ui/backend/db_meta/utils.py b/dbm-ui/backend/db_meta/utils.py index b4dd3fabcb..b44c98e57b 100644 --- a/dbm-ui/backend/db_meta/utils.py +++ b/dbm-ui/backend/db_meta/utils.py @@ -19,7 +19,6 @@ from backend import env from backend.components import CCApi, JobApi from backend.configuration.constants import DBType -from backend.db_meta.api.db_module import delete_cluster_modules from backend.db_meta.enums import ClusterPhase, ClusterType from backend.db_meta.models import ( Cluster, @@ -30,8 +29,9 @@ StorageInstance, StorageInstanceTuple, ) -from backend.db_meta.tasks import update_host_dbmeta +from backend.db_periodic_task.local_tasks import update_host_dbmeta from backend.db_services.ipchooser.query import resource +from backend.flow.utils.cc_manage import CcManage logger = logging.getLogger("root") @@ -99,15 +99,14 @@ def remove_cluster(cluster_id, job_clean=True, cc_clean=True): logger.error("drop_cluster job_clean exception: cluster_id=%s, %s", cluster_id, e) if cc_clean and cluster_bk_host_ids: + cc_manage = CcManage(cluster.bk_biz_id) try: - CCApi.transfer_host_to_recyclemodule( - {"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "bk_host_id": list(cluster_bk_host_ids)}, use_admin=True, raw=True - ) + cc_manage.recycle_host(list(cluster_bk_host_ids)) except Exception as e: # pylint: disable=broad-except logger.error("drop_cluster cc_clean exception: cluster_id=%s, %s", cluster_id, e) update_host_dbmeta(cluster_id=cluster.id, dbm_meta=[]) - delete_cluster_modules(db_type, cluster.id) + cc_manage.delete_cluster_modules(db_type, cluster) cluster.nosqlstoragesetdtl_set.all().delete() cluster.storageinstance_set.all().delete() @@ -122,7 +121,10 @@ def remove_all_cluster(job_clean=True, cc_clean=True): # 正常清理 for cluster in Cluster.objects.all(): - remove_cluster(cluster.id, job_clean, cc_clean) + try: + remove_cluster(cluster.id, job_clean, cc_clean) + except Exception as err: + logger.error(f"failed to clean cluster {cluster.id}, {err}") # 异常收尾 NosqlStorageSetDtl.objects.all().delete() @@ -131,6 +133,7 @@ def remove_all_cluster(job_clean=True, cc_clean=True): Machine.objects.all().delete() ClusterEntry.objects.all().delete() StorageInstanceTuple.objects.all().delete() + Cluster.objects.all().delete() def remove_cluster_ips(cluster_ips, job_clean=True, cc_clean=True): diff --git a/dbm-ui/backend/db_meta/views/fake/views.py b/dbm-ui/backend/db_meta/views/fake/views.py index 84e6cc4e0c..c8a779d3b2 100644 --- a/dbm-ui/backend/db_meta/views/fake/views.py +++ b/dbm-ui/backend/db_meta/views/fake/views.py @@ -80,3 +80,13 @@ def fake_reset_tendbha_cluster(request: Request): return JsonResponse({"msg": "", "code": 0, "data": api.fake.fake_reset_tendbha_cluster(**request.data)}) except Exception as e: # pylint: disable=broad-except return JsonResponse({"msg": "{}".format(e), "code": 1, "data": ""}) + + +@api_view(["POST"]) +@permission_classes([AllowAny]) +@csrf_exempt +def fake_reset_tendbcluster_cluster(request: Request): + try: + return JsonResponse({"msg": "", "code": 0, "data": api.fake.fake_reset_tendbcluster_cluster(**request.data)}) + except Exception as e: # pylint: disable=broad-except + return JsonResponse({"msg": "{}".format(e), "code": 1, "data": ""}) diff --git a/dbm-ui/backend/db_monitor/admin.py b/dbm-ui/backend/db_monitor/admin.py new file mode 100644 index 0000000000..edf5fe62ba --- /dev/null +++ b/dbm-ui/backend/db_monitor/admin.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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.contrib import admin + +from . import models + + +@admin.register(models.MonitorPolicy) +class MonitorPolicyAdmin(admin.ModelAdmin): + list_display = ( + "parent_id", + "id", + "monitor_policy_id", + "name", + "db_type", + "bk_biz_id", + "target_level", + "target_keyword", + "is_enabled", + "is_synced", + "sync_at", + "event_count", + "dispatch_group_id", + ) + list_filter = ("bk_biz_id", "db_type") + search_fields = ("name",) diff --git a/dbm-ui/backend/db_monitor/constants.py b/dbm-ui/backend/db_monitor/constants.py index babc99e93e..29dde464e9 100644 --- a/dbm-ui/backend/db_monitor/constants.py +++ b/dbm-ui/backend/db_monitor/constants.py @@ -18,6 +18,8 @@ TPLS_COLLECT_DIR = os.path.join(DB_MONITOR_TPLS_DIR, "collect") TPLS_ALARM_DIR = os.path.join(DB_MONITOR_TPLS_DIR, "alarm") +SWAGGER_TAG = "db_monitor" + class GroupType(str, StructuredEnum): """告警组类别: 平台级->业务级->集群级->一次性""" @@ -26,3 +28,73 @@ class GroupType(str, StructuredEnum): APP = EnumField("APP", _("app")) CLUSTER = EnumField("CLUSTER", _("cluster")) SINGLE = EnumField("SINGLE", _("single")) + + +class TargetLevel(str, StructuredEnum): + """告警策略类别: 平台级->业务级->模块级->集群级->实例级 + ROLE: 角色不确定所处的位置 + CUSTOM: 用于表达额外过滤条件 + """ + + PLATFORM = EnumField("platform", _("platform")) + APP = EnumField("app_id", _("app id")) + MODULE = EnumField("db_module", _("db module")) + CLUSTER = EnumField("cluster_domain", _("cluster domain")) + CUSTOM = EnumField("custom", _("custom")) + + +class TargetPriority(int, StructuredEnum): + """监控策略优先级: 0-10000""" + + PLATFORM = EnumField(0, _("platform")) + APP = EnumField(1, _("app id")) + MODULE = EnumField(10, _("db module")) + CLUSTER = EnumField(100, _("cluster domain")) + CUSTOM = EnumField(5000, _("custom")) + + +TARGET_LEVEL_TO_PRIORITY = { + TargetLevel.PLATFORM.value: TargetPriority.PLATFORM, + TargetLevel.APP.value: TargetPriority.APP, + TargetLevel.MODULE.value: TargetPriority.MODULE, + TargetLevel.CLUSTER.value: TargetPriority.CLUSTER, + TargetLevel.CUSTOM.value: TargetPriority.CUSTOM, +} + + +class PolicyStatus(str, StructuredEnum): + """监控策略状态""" + + VALID = EnumField("valid", _("有效")) + TARGET_INVALID = EnumField("target_invalid", _("监控目标已失效")) + + +# 蓝鲸监控保存用户组模板 +BK_MONITOR_SAVE_USER_GROUP_TEMPLATE = { + "name": "", + "desc": "", + "need_duty": False, + "duty_arranges": [{"duty_type": "always", "work_time": "always", "users": []}], + "alert_notice": [ + { + "time_range": "00:00:00--23:59:00", + "notify_config": [ + {"level": 3, "notice_ways": [{"name": "mail"}]}, + {"level": 2, "notice_ways": [{"name": "mail"}]}, + {"level": 1, "notice_ways": [{"name": "mail"}]}, + ], + } + ], + "action_notice": [ + { + "time_range": "00:00:00--23:59:00", + "notify_config": [ + {"phase": 3, "notice_ways": [{"name": "mail"}]}, + {"phase": 2, "notice_ways": [{"name": "mail"}]}, + {"phase": 1, "notice_ways": [{"name": "mail"}]}, + ], + } + ], + "channels": ["user"], + "bk_biz_id": 0, +} diff --git a/dbm-ui/backend/db_monitor/exceptions.py b/dbm-ui/backend/db_monitor/exceptions.py new file mode 100644 index 0000000000..e9d8f8db27 --- /dev/null +++ b/dbm-ui/backend/db_monitor/exceptions.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 as _ + +from backend.exceptions import AppBaseException, ErrorCode + + +class DBMonitorBaseException(AppBaseException): + MODULE_CODE = ErrorCode.DB_MONITOR_CODE + MESSAGE = _("监控异常") + + +class BuiltInNotAllowDeleteException(DBMonitorBaseException): + ERROR_CODE = "001" + MESSAGE = _("内置告警组不允许删除") + + +class BkMonitorSaveAlarmException(DBMonitorBaseException): + ERROR_CODE = "201" + MESSAGE = _("监控策略保存失败") + MESSAGE_TPL = _("监控策略保存失败: {message}") + + +class BkMonitorDeleteAlarmException(DBMonitorBaseException): + ERROR_CODE = "202" + MESSAGE = _("监控策略删除失败") + MESSAGE_TPL = _("监控策略删除失败: {message}") diff --git a/dbm-ui/backend/db_monitor/handlers/__init__.py b/dbm-ui/backend/db_monitor/handlers/__init__.py new file mode 100644 index 0000000000..aa5085c628 --- /dev/null +++ b/dbm-ui/backend/db_monitor/handlers/__init__.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" diff --git a/dbm-ui/backend/db_monitor/handlers/notice_group.py b/dbm-ui/backend/db_monitor/handlers/notice_group.py new file mode 100644 index 0000000000..2baf833529 --- /dev/null +++ b/dbm-ui/backend/db_monitor/handlers/notice_group.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + + +class NoticeGroupHandler: + pass diff --git a/dbm-ui/backend/db_monitor/management/commands/export_template.py b/dbm-ui/backend/db_monitor/management/commands/export_template.py index 6267c618ab..94716661f0 100644 --- a/dbm-ui/backend/db_monitor/management/commands/export_template.py +++ b/dbm-ui/backend/db_monitor/management/commands/export_template.py @@ -53,16 +53,14 @@ def handle(self, *args, **options): # 转base64并写文件 template = model_to_dict(template, fields=self.COLLECT_FIELDS) template_json = json.dumps(template) - template_json64 = base64.b64encode(template_json.encode()).decode() - template_file_name = "{bk_biz_id}.{db_type}.{plugin_id}.tpl64".format(**template) + template_file_name = "{bk_biz_id}.{db_type}.{plugin_id}.json".format(**template) with open(os.path.join(TPLS_COLLECT_DIR, template_file_name), "w") as template_file: - template_file.write(template_json64) + template_file.write(template_json) if template_type in ["all", "alarm"]: for template in alarm_templates: template = model_to_dict(template, fields=self.ALARM_FIELDS) template_json = json.dumps(template) - template_json64 = base64.b64encode(template_json.encode()).decode() - template_file_name = "{bk_biz_id}.{db_type}.{monitor_strategy_id}.tpl64".format(**template) + template_file_name = "{db_type}#{name}.json".format(**template) with open(os.path.join(TPLS_ALARM_DIR, template_file_name), "w") as template_file: - template_file.write(template_json64) + template_file.write(template_json) diff --git a/dbm-ui/backend/db_monitor/migrations/0001_initial.py b/dbm-ui/backend/db_monitor/migrations/0001_initial.py index 2425f14c11..a5011b1316 100644 --- a/dbm-ui/backend/db_monitor/migrations/0001_initial.py +++ b/dbm-ui/backend/db_monitor/migrations/0001_initial.py @@ -145,7 +145,7 @@ class Migration(migrations.Migration): ( "db_type", models.CharField( - choices=GroupType.get_choices(), + choices=DBType.get_choices(), max_length=32, verbose_name="数据库类型", ), diff --git a/dbm-ui/backend/db_monitor/migrations/0002_alter_noticegroup_db_type.py b/dbm-ui/backend/db_monitor/migrations/0002_alter_noticegroup_db_type.py new file mode 100644 index 0000000000..c8be8503e1 --- /dev/null +++ b/dbm-ui/backend/db_monitor/migrations/0002_alter_noticegroup_db_type.py @@ -0,0 +1,24 @@ +# Generated by Django 3.2.19 on 2023-06-29 03:25 + +from django.db import migrations, models + +from backend.configuration.constants import DBType + + +class Migration(migrations.Migration): + + dependencies = [ + ("db_monitor", "0001_initial"), + ] + + operations = [ + migrations.AlterField( + model_name="noticegroup", + name="db_type", + field=models.CharField( + choices=DBType.get_choices(), + max_length=32, + verbose_name="数据库类型", + ), + ), + ] diff --git a/dbm-ui/backend/db_monitor/migrations/0003_alter_noticegroup_db_type.py b/dbm-ui/backend/db_monitor/migrations/0003_alter_noticegroup_db_type.py new file mode 100644 index 0000000000..826de47dea --- /dev/null +++ b/dbm-ui/backend/db_monitor/migrations/0003_alter_noticegroup_db_type.py @@ -0,0 +1,24 @@ +# Generated by Django 3.2.19 on 2023-08-21 02:28 + +from django.db import migrations, models + +from backend.configuration.constants import DBType + + +class Migration(migrations.Migration): + + dependencies = [ + ("db_monitor", "0002_alter_noticegroup_db_type"), + ] + + operations = [ + migrations.AlterField( + model_name="noticegroup", + name="db_type", + field=models.CharField( + choices=DBType.get_choices(), + max_length=32, + verbose_name="数据库类型", + ), + ), + ] diff --git a/dbm-ui/backend/db_monitor/migrations/0004_auto_20230829_1033.py b/dbm-ui/backend/db_monitor/migrations/0004_auto_20230829_1033.py new file mode 100644 index 0000000000..c7c42c762e --- /dev/null +++ b/dbm-ui/backend/db_monitor/migrations/0004_auto_20230829_1033.py @@ -0,0 +1,119 @@ +# Generated by Django 3.2.19 on 2023-08-29 02:33 + +from django.db import migrations, models + +from backend.configuration.constants import DBType +from backend.db_monitor.constants import PolicyStatus, TargetLevel + + +class Migration(migrations.Migration): + + dependencies = [ + ("db_monitor", "0003_alter_noticegroup_db_type"), + ] + + operations = [ + migrations.CreateModel( + name="DispatchGroup", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("creator", models.CharField(max_length=64, verbose_name="创建人")), + ("create_at", models.DateTimeField(auto_now_add=True, verbose_name="创建时间")), + ("updater", models.CharField(max_length=64, verbose_name="修改人")), + ("update_at", models.DateTimeField(auto_now=True, verbose_name="更新时间")), + ("monitor_dispatch_id", models.IntegerField(default=0, verbose_name="蓝鲸监控分派策略组ID")), + ("name", models.CharField(max_length=128, verbose_name="策略名称,全局唯一")), + ("priority", models.PositiveIntegerField(verbose_name="监控策略优先级,跟随targets调整")), + ("details", models.JSONField(default=dict, verbose_name="策略模板详情,可用于还原")), + ("is_synced", models.BooleanField(default=False, verbose_name="是否已同步到监控")), + ("sync_at", models.DateTimeField(null=True, verbose_name="最近一次的同步时间")), + ], + options={ + "verbose_name": "分派策略组", + }, + ), + migrations.CreateModel( + name="MonitorPolicy", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("creator", models.CharField(max_length=64, verbose_name="创建人")), + ("create_at", models.DateTimeField(auto_now_add=True, verbose_name="创建时间")), + ("updater", models.CharField(max_length=64, verbose_name="修改人")), + ("update_at", models.DateTimeField(auto_now=True, verbose_name="更新时间")), + ("parent_id", models.IntegerField(default=0, verbose_name="父级策略ID,0代表父级")), + ("name", models.CharField(max_length=128, unique=True, verbose_name="策略名称,全局唯一")), + ("bk_biz_id", models.IntegerField(default=0, verbose_name="业务ID, 0代表全业务")), + ( + "db_type", + models.CharField( + choices=DBType.get_choices(), + default="mysql", + max_length=64, + verbose_name="DB类型", + ), + ), + ("details", models.JSONField(default=dict, verbose_name="策略模板详情,可用于还原")), + ("targets", models.JSONField(default=dict, verbose_name="监控目标")), + ( + "target_level", + models.CharField( + choices=TargetLevel.get_choices(), + default="APP", + max_length=64, + verbose_name="监控目标级别,跟随targets调整", + ), + ), + ("target_priority", models.PositiveIntegerField(verbose_name="监控策略优先级,跟随targets调整")), + ("test_rules", models.JSONField(default=dict, verbose_name="检测规则")), + ("notify_rules", models.JSONField(default=dict, verbose_name="通知规则")), + ("notify_groups", models.JSONField(default=dict, verbose_name="通知组")), + ("is_enabled", models.BooleanField(default=True, verbose_name="是否已启用")), + ("is_synced", models.BooleanField(default=False, verbose_name="是否已同步到监控")), + ("sync_at", models.DateTimeField(null=True, verbose_name="最近一次的同步时间")), + ("event_count", models.IntegerField(default=-1, verbose_name="告警事件数量,初始值设置为-1")), + ( + "policy_status", + models.CharField( + choices=PolicyStatus.get_choices(), + default="valid", + max_length=64, + verbose_name="策略状态", + ), + ), + ("dispatch_group_id", models.BigIntegerField(default=0, verbose_name="分派策略组ID,0代表没有对应的策略")), + ("monitor_policy_id", models.BigIntegerField(default=0, verbose_name="蓝鲸监控策略ID")), + ], + options={ + "verbose_name": "告警策略", + }, + ), + migrations.RemoveField( + model_name="noticegroup", + name="users", + ), + migrations.AddField( + model_name="noticegroup", + name="details", + field=models.JSONField(default=dict, verbose_name="通知方式详情"), + ), + migrations.AddField( + model_name="noticegroup", + name="is_synced", + field=models.BooleanField(default=False, verbose_name="是否已同步到监控"), + ), + migrations.AddField( + model_name="noticegroup", + name="receivers", + field=models.JSONField(default=dict, verbose_name="告警接收人员/组列表"), + ), + migrations.AddField( + model_name="noticegroup", + name="sync_at", + field=models.DateTimeField(null=True, verbose_name="最近一次的同步时间"), + ), + migrations.AlterField( + model_name="ruletemplate", + name="name", + field=models.CharField(max_length=128, unique=True, verbose_name="策略名称监控侧要求唯一"), + ), + ] diff --git a/dbm-ui/backend/db_monitor/migrations/0005_auto_20230905_1023.py b/dbm-ui/backend/db_monitor/migrations/0005_auto_20230905_1023.py new file mode 100644 index 0000000000..7e00221723 --- /dev/null +++ b/dbm-ui/backend/db_monitor/migrations/0005_auto_20230905_1023.py @@ -0,0 +1,32 @@ +# Generated by Django 3.2.19 on 2023-09-05 02:23 + +from django.db import migrations, models + +from backend.db_monitor.constants import TargetLevel + + +class Migration(migrations.Migration): + + dependencies = [ + ("db_monitor", "0004_auto_20230829_1033"), + ] + + operations = [ + migrations.AddField( + model_name="monitorpolicy", + name="parent_details", + field=models.JSONField(default=dict, verbose_name="父级策略模板详情,可用于还原"), + ), + migrations.AlterField( + model_name="monitorpolicy", + name="details", + field=models.JSONField(default=dict, verbose_name="当前策略详情,可用于patch"), + ), + migrations.AlterField( + model_name="monitorpolicy", + name="target_level", + field=models.CharField( + choices=TargetLevel.get_choices(), default="app_id", max_length=64, verbose_name="监控目标级别,跟随targets调整" + ), + ), + ] diff --git a/dbm-ui/backend/db_monitor/migrations/0006_monitorpolicy_target_keyword.py b/dbm-ui/backend/db_monitor/migrations/0006_monitorpolicy_target_keyword.py new file mode 100644 index 0000000000..b6c4ac2f1c --- /dev/null +++ b/dbm-ui/backend/db_monitor/migrations/0006_monitorpolicy_target_keyword.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.19 on 2023-09-06 03:21 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("db_monitor", "0005_auto_20230905_1023"), + ] + + operations = [ + migrations.AddField( + model_name="monitorpolicy", + name="target_keyword", + field=models.TextField(default="", verbose_name="监控目标检索冗余字段"), + ), + ] diff --git a/dbm-ui/backend/db_monitor/migrations/0007_auto_20230912_1128.py b/dbm-ui/backend/db_monitor/migrations/0007_auto_20230912_1128.py new file mode 100644 index 0000000000..acb123aad0 --- /dev/null +++ b/dbm-ui/backend/db_monitor/migrations/0007_auto_20230912_1128.py @@ -0,0 +1,34 @@ +# Generated by Django 3.2.19 on 2023-09-12 03:28 + +from django.db import migrations, models + +from backend.configuration.constants import DBType + + +class Migration(migrations.Migration): + + dependencies = [ + ("db_monitor", "0006_monitorpolicy_target_keyword"), + ] + + operations = [ + migrations.RemoveField( + model_name="noticegroup", + name="group_type", + ), + migrations.AddField( + model_name="noticegroup", + name="is_built_in", + field=models.BooleanField(default=False, verbose_name="是否内置"), + ), + migrations.AddField( + model_name="noticegroup", + name="name", + field=models.CharField(default="", max_length=255, verbose_name="告警通知组名称"), + ), + migrations.AlterField( + model_name="noticegroup", + name="db_type", + field=models.CharField(choices=DBType.get_choices(), default="", max_length=32, verbose_name="数据库类型"), + ), + ] diff --git a/dbm-ui/backend/db_monitor/mock_data.py b/dbm-ui/backend/db_monitor/mock_data.py new file mode 100644 index 0000000000..57e008b7e3 --- /dev/null +++ b/dbm-ui/backend/db_monitor/mock_data.py @@ -0,0 +1,381 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import copy + +from django.utils.translation import ugettext_lazy as _ + +GET_MESSAGE_TYPE = [ + {"type": "rtx", "label": _("企业微信"), "is_active": True, "icon": "base64xxxxxxx"}, + {"type": "weixin", "label": _("微信"), "is_active": False, "icon": "base64xxxxxxx"}, + {"type": "mail", "label": _("邮件"), "is_active": True, "icon": "base64xxxxxxx"}, + {"type": "sms", "label": _("短信"), "is_active": True, "icon": "base64xxxxxxx"}, + {"type": "voice", "label": _("语音"), "is_active": True, "icon": "base64xxxxxxx"}, + {"type": "wxwork-bot", "label": _("群机器人"), "is_active": True, "icon": "base64xxxxxxx"}, +] + +GET_RECEIVER_GROUP = [ + {"id": "bk_biz_maintainer", "display_name": _("运维人员"), "logo": "", "type": "group", "members": ["admin"]}, + {"id": "bk_biz_productor", "display_name": _("产品人员"), "logo": "", "type": "group", "members": []}, + {"id": "bk_biz_tester", "display_name": _("测试人员"), "logo": "", "type": "group", "members": []}, + {"id": "bk_biz_developer", "display_name": _("开发人员"), "logo": "", "type": "group", "members": []}, + {"id": "operator", "display_name": _("主负责人"), "logo": "", "type": "group", "members": []}, + {"id": "bk_bak_operator", "display_name": _("备份负责人"), "logo": "", "type": "group", "members": []}, +] + +NOTICE_GROUP_DETAIL = { + "alert_notice": [ + { + "time_range": "00:00:00--11:59:00", + "notify_config": [ + { + "notice_ways": [{"name": "weixin"}], + "level": 3, + }, + { + "notice_ways": [{"name": "mail"}], + "level": 2, + }, + { + "notice_ways": [{"name": "rtx"}, {"name": "voice"}], + "level": 1, + }, + ], + }, + { + "time_range": "12:00:00--23:59:00", + "notify_config": [ + { + "notice_ways": [{"name": "weixin"}], + "level": 3, + }, + { + "notice_ways": [{"name": "wxwork-bot", "receivers": ["123"]}], + "level": 2, + }, + { + "notice_ways": [{"name": "rtx"}, {"name": "voice"}], + "level": 1, + }, + ], + }, + ] +} + +LIST_NOTICE_GROUP = { + "count": 2, + "results": [ + { + "id": 1, + "name": "MySQL DBA", + "updater": "admin", + "update_at": "2023-08-29 15:36:53", + "bk_biz_id": 0, + "monitor_group_id": 0, + "related_policy_count": 1, + "group_type": "PLATFORM", + "db_type": "mysql", + "receivers": [{"type": "group", "id": "bk_biz_maintainer"}, {"type": "user", "id": "admin"}], + "details": { + "alert_notice": [ + { + "time_range": "00:00:00--23:59:00", + "notify_config": [ + { + "notice_ways": [{"name": "weixin"}], + "level": 3, + }, + { + "notice_ways": [{"name": "mail"}], + "level": 2, + }, + { + "notice_ways": [{"name": "rtx"}, {"name": "voice"}], + "level": 1, + }, + ], + } + ] + }, + "is_built_in": True, + }, + { + "id": 2, + "name": _("自定义告警组名称"), + "updater": "admin", + "update_at": "2023-08-29 15:36:53", + "bk_biz_id": 0, + "monitor_group_id": 0, + "related_policy_count": 2, + "group_type": "PLATFORM", + "db_type": "mysql", + "receivers": [{"type": "user", "id": "admin"}], + "details": copy.deepcopy(NOTICE_GROUP_DETAIL), + "is_built_in": False, + }, + ], +} + +CREATE_NOTICE_GROUP = { + "name": _("新建告警组"), + "receivers": [{"type": "group", "id": "bk_biz_maintainer"}, {"type": "user", "id": "admin"}], + "details": copy.deepcopy(NOTICE_GROUP_DETAIL), +} + +UPDATE_NOTICE_GROUP = {"id": 1, **CREATE_NOTICE_GROUP} + +GET_RELATED_POLICY = [{"id": 1, "name": _("策略 A")}, {"id": 2, "name": _("策略 B")}] + +CREATE_DUTY_RULE = { + "name": _("周末轮值"), + "duty_type": "periodic", + "duty_biz_list": [], + "ignore_biz_list": [], + "members": ["admin"], + "shift_number": 2, + "shift_day": 1, + "start_time": "2023-09-05", + "end_time": "2023-10-31", + "work_type": "daily", + "work_days": [6, 7], + "work_times": ["00:00--11:59", "12:00--23:59"], +} + +# bk monitor api +BK_MONITOR_CREATE_NOTICE_GROUP_REQUEST = { + "name": "durant0905", + "desc": "", + "need_duty": False, + "duty_arranges": [ + { + "duty_type": "always", + "work_time": "always", + "users": [{"display_name": "admin", "logo": "", "id": "admin", "type": "user"}], + } + ], + "alert_notice": [ + { + "time_range": "00:00:00--17:59:00", + "notify_config": [ + {"level": 3, "notice_ways": [{"name": "rtx"}, {"name": "wxwork-bot", "receivers": ["123"]}]}, + {"level": 2, "notice_ways": [{"name": "rtx"}]}, + {"level": 1, "notice_ways": [{"name": "rtx"}]}, + ], + }, + { + "time_range": "18:00:00--23:59:00", + "notify_config": [ + {"level": 3, "notice_ways": [{"name": "rtx"}]}, + {"level": 2, "notice_ways": [{"name": "rtx"}]}, + {"level": 1, "notice_ways": [{"name": "rtx"}]}, + ], + }, + ], + "action_notice": [ + { + "time_range": "00:00:00--23:59:00", + "notify_config": [ + {"level": 3, "notice_ways": [{"name": "rtx"}], "phase": 3}, + {"level": 2, "notice_ways": [{"name": "rtx"}], "phase": 2}, + {"level": 1, "notice_ways": [{"name": "rtx"}], "phase": 1}, + ], + } + ], + "channels": ["user", "wxwork-bot"], + "bk_biz_id": 5005578, +} + +BK_MONITOR_NOTICE_GROUP_DETAIL = { + "id": 29, + "name": _("mysql 周末轮值 告警组"), + "bk_biz_id": 3, + "desc": "", + "update_user": "admin", + "update_time": "2023-08-28 12:02:27+0800", + "create_user": "admin", + "create_time": "2023-08-23 17:53:46+0800", + "duty_arranges": [ + { + "id": 35, + "user_group_id": 29, + "need_rotation": True, + "duty_time": [{"work_type": "weekly", "work_days": [6, 7], "work_time": "00:00--23:59"}], + "effective_time": "2023-08-28t12:02:00+08:00", + "handoff_time": {"date": 1, "time": "00:00", "rotation_type": "daily"}, + "users": [], + "duty_users": [ + [{"id": "durant", "display_name": "durant", "type": "user"}], + [{"id": "edwin", "display_name": "edwin", "type": "user"}], + [{"id": "hongsong", "display_name": "hongsong", "type": "user"}], + [{"id": "kio", "display_name": "kio", "type": "user"}], + ], + "backups": [], + "order": 1, + }, + { + "id": 36, + "user_group_id": 29, + "need_rotation": True, + "duty_time": [{"work_type": "weekly", "work_days": [6, 7], "work_time": "00:00--23:59"}], + "effective_time": "2023-08-28t12:02:00+08:00", + "handoff_time": {"date": 1, "time": "00:00", "rotation_type": "daily"}, + "users": [], + "duty_users": [ + [{"id": "hongsong", "display_name": "hongsong", "type": "user"}], + [{"id": "kio", "display_name": "kio", "type": "user"}], + [{"id": "durant", "display_name": "durant", "type": "user"}], + [{"id": "edwin", "display_name": "edwin", "type": "user"}], + ], + "backups": [], + "order": 2, + }, + ], + "alert_notice": [ + { + "time_range": "00:00:00--23:59:00", + "notify_config": [ + {"type": [], "notice_ways": [{"name": "weixin", "receivers": []}], "level": 3}, + {"type": [], "notice_ways": [{"name": "weixin", "receivers": []}], "level": 2}, + {"type": [], "notice_ways": [{"name": "weixin", "receivers": []}], "level": 1}, + ], + } + ], + "action_notice": [ + { + "time_range": "00:00:00--23:59:00", + "notify_config": [ + {"type": [], "notice_ways": [{"name": "weixin", "receivers": []}], "phase": 3}, + {"type": [], "notice_ways": [{"name": "weixin", "receivers": []}], "phase": 2}, + {"type": [], "notice_ways": [{"name": "weixin", "receivers": []}], "phase": 1}, + ], + } + ], + "need_duty": True, + "path": "", + "channels": ["user"], + "users": [], + "strategy_count": 1, + "delete_allowed": False, + "edit_allowed": True, + "config_source": "ui", + "duty_plans": [ + { + "id": 365, + "user_group_id": 29, + "duty_arrange_id": 35, + "duty_time": [{"work_days": [6, 7], "work_time": "00:00--23:59", "work_type": "weekly"}], + "begin_time": "2023-09-04 00:00:00+0800", + "end_time": "2023-09-05 00:00:00+0800", + "users": [{"id": "kio", "display_name": "kio", "type": "user"}], + "order": 1, + "is_active": False, + }, + { + "id": 390, + "user_group_id": 29, + "duty_arrange_id": 35, + "duty_time": [{"work_days": [6, 7], "work_time": "00:00--23:59", "work_type": "weekly"}], + "begin_time": "2023-09-05 00:00:00+0800", + "end_time": "2023-09-06 00:00:00+0800", + "users": [{"id": "durant", "display_name": "durant", "type": "user"}], + "order": 1, + "is_active": False, + }, + { + "id": 391, + "user_group_id": 29, + "duty_arrange_id": 35, + "duty_time": [{"work_days": [6, 7], "work_time": "00:00--23:59", "work_type": "weekly"}], + "begin_time": "2023-09-06 00:00:00+0800", + "end_time": "2023-09-07 00:00:00+0800", + "users": [{"id": "edwin", "display_name": "edwin", "type": "user"}], + "order": 1, + "is_active": False, + }, + { + "id": 392, + "user_group_id": 29, + "duty_arrange_id": 35, + "duty_time": [{"work_days": [6, 7], "work_time": "00:00--23:59", "work_type": "weekly"}], + "begin_time": "2023-09-07 00:00:00+0800", + "end_time": "2023-09-08 00:00:00+0800", + "users": [{"id": "hongsong", "display_name": "hongsong", "type": "user"}], + "order": 1, + "is_active": False, + }, + { + "id": 393, + "user_group_id": 29, + "duty_arrange_id": 35, + "duty_time": [{"work_days": [6, 7], "work_time": "00:00--23:59", "work_type": "weekly"}], + "begin_time": "2023-09-08 00:00:00+0800", + "end_time": "2023-09-09 00:00:00+0800", + "users": [{"id": "kio", "display_name": "kio", "type": "user"}], + "order": 1, + "is_active": False, + }, + { + "id": 369, + "user_group_id": 29, + "duty_arrange_id": 36, + "duty_time": [{"work_days": [6, 7], "work_time": "00:00--23:59", "work_type": "weekly"}], + "begin_time": "2023-09-04 00:00:00+0800", + "end_time": "2023-09-05 00:00:00+0800", + "users": [{"id": "edwin", "display_name": "edwin", "type": "user"}], + "order": 2, + "is_active": False, + }, + { + "id": 394, + "user_group_id": 29, + "duty_arrange_id": 36, + "duty_time": [{"work_days": [6, 7], "work_time": "00:00--23:59", "work_type": "weekly"}], + "begin_time": "2023-09-05 00:00:00+0800", + "end_time": "2023-09-06 00:00:00+0800", + "users": [{"id": "hongsong", "display_name": "hongsong", "type": "user"}], + "order": 2, + "is_active": False, + }, + { + "id": 395, + "user_group_id": 29, + "duty_arrange_id": 36, + "duty_time": [{"work_days": [6, 7], "work_time": "00:00--23:59", "work_type": "weekly"}], + "begin_time": "2023-09-06 00:00:00+0800", + "end_time": "2023-09-07 00:00:00+0800", + "users": [{"id": "kio", "display_name": "kio", "type": "user"}], + "order": 2, + "is_active": False, + }, + { + "id": 396, + "user_group_id": 29, + "duty_arrange_id": 36, + "duty_time": [{"work_days": [6, 7], "work_time": "00:00--23:59", "work_type": "weekly"}], + "begin_time": "2023-09-07 00:00:00+0800", + "end_time": "2023-09-08 00:00:00+0800", + "users": [{"id": "durant", "display_name": "durant", "type": "user"}], + "order": 2, + "is_active": False, + }, + { + "id": 397, + "user_group_id": 29, + "duty_arrange_id": 36, + "duty_time": [{"work_days": [6, 7], "work_time": "00:00--23:59", "work_type": "weekly"}], + "begin_time": "2023-09-08 00:00:00+0800", + "end_time": "2023-09-09 00:00:00+0800", + "users": [{"id": "edwin", "display_name": "edwin", "type": "user"}], + "order": 2, + "is_active": False, + }, + ], + "rule_count": 1, +} diff --git a/dbm-ui/backend/db_monitor/models.py b/dbm-ui/backend/db_monitor/models.py deleted file mode 100644 index 51f95f2a8a..0000000000 --- a/dbm-ui/backend/db_monitor/models.py +++ /dev/null @@ -1,130 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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.db import models -from django.utils.translation import gettext_lazy as _ - -from backend.bk_web.constants import LEN_LONG, LEN_MIDDLE, LEN_NORMAL, LEN_SHORT -from backend.bk_web.models import AuditedModel -from backend.configuration.constants import PLAT_BIZ_ID, DBType -from backend.db_meta.enums import ClusterType -from backend.db_monitor.constants import GroupType - - -class NoticeGroup(AuditedModel): - """告警通知组:一期粒度仅支持到业务级,可开关是否同步DBA人员数据""" - - bk_biz_id = models.IntegerField(help_text=_("业务ID, 0代表全业务"), default=PLAT_BIZ_ID) - monitor_group_id = models.BigIntegerField(help_text=_("监控通知组ID"), default=0) - group_type = models.CharField( - _("告警通知组类别"), choices=GroupType.get_choices(), max_length=LEN_NORMAL, default=GroupType.PLATFORM - ) - - db_type = models.CharField(_("数据库类型"), choices=DBType.get_choices(), max_length=LEN_SHORT) - users = models.JSONField(_("人员列表")) - - dba_sync = models.BooleanField(help_text=_("自动同步DBA人员配置"), default=True) - - class Meta: - verbose_name = _("告警通知组") - - @classmethod - def get_monitor_groups(cls, db_type, group_type=GroupType.PLATFORM): - return list( - cls.objects.filter(group_type=group_type, db_type=db_type).values_list("monitor_group_id", flat=True) - ) - - -class RuleTemplate(AuditedModel): - """告警策略模板""" - - bk_biz_id = models.IntegerField(help_text=_("业务ID, 0代表全业务"), default=PLAT_BIZ_ID) - monitor_strategy_id = models.IntegerField(help_text=_("监控策略ID"), default=0) - - name = models.CharField(verbose_name=_("策略名称"), max_length=LEN_MIDDLE, default="") - db_type = models.CharField( - _("DB类型"), choices=DBType.get_choices(), max_length=LEN_NORMAL, default=DBType.MySQL.value - ) - - details = models.JSONField(verbose_name=_("模板详情"), default=dict) - is_enabled = models.BooleanField(_("是否启用"), default=True) - - class Meta: - verbose_name = _("告警策略模板") - - -class AlertRule(AuditedModel): - """告警策略实例""" - - bk_biz_id = models.IntegerField(help_text=_("业务ID, 0代表全业务"), default=PLAT_BIZ_ID) - - template_id = models.IntegerField(help_text=_("监控模板ID"), default=0) - monitor_strategy_id = models.IntegerField(help_text=_("监控策略ID"), default=0) - - name = models.CharField(verbose_name=_("策略名称"), max_length=LEN_MIDDLE, default="") - db_type = models.CharField( - _("DB类型"), choices=DBType.get_choices(), max_length=LEN_NORMAL, default=DBType.MySQL.value - ) - - details = models.JSONField(verbose_name=_("实例详情"), default=dict) - is_enabled = models.BooleanField(_("是否启用"), default=True) - - class Meta: - verbose_name = _("告警策略实例") - - -class CollectTemplateBase(AuditedModel): - """采集策略模板""" - - bk_biz_id = models.IntegerField(help_text=_("业务ID, 0代表全业务"), default=PLAT_BIZ_ID) - plugin_id = models.CharField(verbose_name=_("插件ID"), max_length=LEN_MIDDLE, unique=True) - db_type = models.CharField( - _("DB类型"), choices=DBType.get_choices(), max_length=LEN_NORMAL, default=DBType.MySQL.value - ) - details = models.JSONField(verbose_name=_("详情"), default=dict) - - class Meta: - abstract = True - verbose_name = _("采集策略模板") - - -class CollectTemplate(CollectTemplateBase): - class Meta: - verbose_name = _("采集策略模板") - - -class CollectInstance(CollectTemplateBase): - """采集策略实例""" - - template_id = models.IntegerField(help_text=_("监控插件模板ID"), default=0) - collect_id = models.IntegerField(help_text=_("监控采集策略ID")) - - class Meta: - verbose_name = _("采集策略实例") - - -class Dashboard(AuditedModel): - """仪表盘""" - - name = models.CharField(verbose_name=_("名称"), max_length=LEN_MIDDLE, default="") - cluster_type = models.CharField(max_length=64, choices=ClusterType.get_choices(), default="") - - details = models.JSONField(verbose_name=_("详情"), default=dict) - variables = models.JSONField(verbose_name=_("变量"), default=dict) - - # grafana相关 - org_id = models.BigIntegerField(verbose_name=_("grafana-org_id")) - org_name = models.CharField(verbose_name=_("grafana-org_name"), max_length=LEN_NORMAL) - uid = models.CharField(verbose_name=_("grafana-uid"), max_length=LEN_NORMAL) - url = models.CharField(verbose_name=_("grafana-url"), max_length=LEN_LONG) - - class Meta: - verbose_name = _("仪表盘") diff --git a/dbm-ui/backend/db_monitor/models/__init__.py b/dbm-ui/backend/db_monitor/models/__init__.py new file mode 100644 index 0000000000..74b64891c2 --- /dev/null +++ b/dbm-ui/backend/db_monitor/models/__init__.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 .alarm import * +from .collect import * +from .dashboard import * diff --git a/dbm-ui/backend/db_monitor/models/alarm.py b/dbm-ui/backend/db_monitor/models/alarm.py new file mode 100644 index 0000000000..2cdf0a50dd --- /dev/null +++ b/dbm-ui/backend/db_monitor/models/alarm.py @@ -0,0 +1,548 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import copy +import datetime +import logging +from collections import defaultdict + +from django.db import models +from django.utils.translation import gettext_lazy as _ + +from backend import env +from backend.bk_web.constants import LEN_LONG, LEN_MIDDLE, LEN_NORMAL, LEN_SHORT +from backend.bk_web.models import AuditedModel +from backend.components import BKMonitorV3Api +from backend.configuration.constants import PLAT_BIZ_ID, DBType, SystemSettingsEnum +from backend.configuration.models import SystemSettings +from backend.db_monitor.constants import ( + BK_MONITOR_SAVE_USER_GROUP_TEMPLATE, + TARGET_LEVEL_TO_PRIORITY, + GroupType, + PolicyStatus, + TargetLevel, +) +from backend.db_monitor.exceptions import ( + BkMonitorDeleteAlarmException, + BkMonitorSaveAlarmException, + BuiltInNotAllowDeleteException, +) + +__all__ = ["NoticeGroup", "AlertRule", "RuleTemplate", "DispatchGroup", "MonitorPolicy"] + + +logger = logging.getLogger("root") + + +class NoticeGroup(AuditedModel): + """告警通知组:一期粒度仅支持到业务级,可开关是否同步DBA人员数据""" + + bk_biz_id = models.IntegerField(help_text=_("业务ID, 0代表全业务"), default=PLAT_BIZ_ID) + name = models.CharField(_("告警通知组名称"), max_length=LEN_LONG, default="") + monitor_group_id = models.BigIntegerField(help_text=_("监控通知组ID"), default=0) + db_type = models.CharField(_("数据库类型"), choices=DBType.get_choices(), max_length=LEN_SHORT, default="") + receivers = models.JSONField(_("告警接收人员/组列表"), default=dict) + details = models.JSONField(verbose_name=_("通知方式详情"), default=dict) + is_synced = models.BooleanField(verbose_name=_("是否已同步到监控"), default=False) + is_built_in = models.BooleanField(verbose_name=_("是否内置"), default=False) + sync_at = models.DateTimeField(_("最近一次的同步时间"), null=True) + dba_sync = models.BooleanField(help_text=_("自动同步DBA人员配置"), default=True) + + class Meta: + verbose_name = _("告警通知组") + + @classmethod + def get_monitor_groups(cls, db_type): + return list(cls.objects.filter(db_type=db_type).values_list("monitor_group_id", flat=True)) + + def save(self, *args, **kwargs): + """ + 保存告警组 + """ + data = copy.deepcopy(BK_MONITOR_SAVE_USER_GROUP_TEMPLATE) + data.update( + { + "name": f"{self.name}_{self.bk_biz_id}", + "alert_notice": self.details["alert_notice"], + "bk_biz_id": self.bk_biz_id, + } + ) + data["duty_arranges"][0]["users"] = self.receivers + if self.monitor_group_id: + data["id"] = self.monitor_group_id + resp = BKMonitorV3Api.save_user_group(data) + self.monitor_group_id = resp["id"] + super().save(*args, **kwargs) + + def delete(self, using=None, keep_parents=False): + if self.is_built_in: + raise BuiltInNotAllowDeleteException + BKMonitorV3Api.delete_user_groups({"ids": [self.monitor_group_id], "bk_biz_ids": [self.bk_biz_id]}) + super().delete() + + +class RuleTemplate(AuditedModel): + """告警策略模板:just for export""" + + bk_biz_id = models.IntegerField(help_text=_("业务ID, 0代表全业务"), default=PLAT_BIZ_ID) + monitor_strategy_id = models.IntegerField(help_text=_("监控策略ID"), default=0) + + name = models.CharField(verbose_name=_("策略名称监控侧要求唯一"), max_length=LEN_MIDDLE, unique=True) + db_type = models.CharField( + _("DB类型"), choices=DBType.get_choices(), max_length=LEN_NORMAL, default=DBType.MySQL.value + ) + + details = models.JSONField(verbose_name=_("模板详情"), default=dict) + is_enabled = models.BooleanField(_("是否启用"), default=True) + + class Meta: + verbose_name = _("告警策略模板") + + +class AlertRule(AuditedModel): + """TODO: 告警策略实例:deprecated""" + + bk_biz_id = models.IntegerField(help_text=_("业务ID, 0代表全业务"), default=PLAT_BIZ_ID) + + template_id = models.IntegerField(help_text=_("监控模板ID"), default=0) + monitor_strategy_id = models.IntegerField(help_text=_("监控策略ID"), default=0) + + name = models.CharField(verbose_name=_("策略名称"), max_length=LEN_MIDDLE, default="") + db_type = models.CharField( + _("DB类型"), choices=DBType.get_choices(), max_length=LEN_NORMAL, default=DBType.MySQL.value + ) + + details = models.JSONField(verbose_name=_("实例详情"), default=dict) + is_enabled = models.BooleanField(_("是否启用"), default=True) + + class Meta: + verbose_name = _("告警策略实例") + + +class DispatchGroup(AuditedModel): + """分派策略组""" + + monitor_dispatch_id = models.IntegerField(verbose_name=_("蓝鲸监控分派策略组ID"), default=0) + name = models.CharField(verbose_name=_("策略名称,全局唯一"), max_length=LEN_MIDDLE) + priority = models.PositiveIntegerField(verbose_name=_("监控策略优先级,跟随targets调整")) + details = models.JSONField(verbose_name=_("策略模板详情,可用于还原"), default=dict) + is_synced = models.BooleanField(verbose_name=_("是否已同步到监控"), default=False) + sync_at = models.DateTimeField(_("最近一次的同步时间"), null=True) + + class Meta: + verbose_name = _("分派策略组") + + +class MonitorPolicy(AuditedModel): + """监控策略""" + + parent_id = models.IntegerField(verbose_name=_("父级策略ID,0代表父级"), default=0) + parent_details = models.JSONField(verbose_name=_("父级策略模板详情,可用于还原"), default=dict) + + name = models.CharField(verbose_name=_("策略名称,全局唯一"), max_length=LEN_MIDDLE, unique=True) + bk_biz_id = models.IntegerField(verbose_name=_("业务ID, 0代表全业务"), default=PLAT_BIZ_ID) + + db_type = models.CharField( + _("DB类型"), choices=DBType.get_choices(), max_length=LEN_NORMAL, default=DBType.MySQL.value + ) + + details = models.JSONField(verbose_name=_("当前策略详情,可用于patch"), default=dict) + + # 拆解部分details中的字段到外层 + # TODO: MethodEnum: eq|neq|include|exclude|regex|nregex + # item[*].query_configs[*].agg_condition[*] + # { + # "agg_condition": [ + # { + # "key": "app_id", + # "dimension_name": "dbm_meta app id", + # "value": [ + # "2" + # ], + # "method": "eq", + # "condition": "and" + # }, + # { + # "key": "db_module", + # "dimension_name": "dbm_meta db module", + # "value": [ + # "zookeeper" + # ], + # "method": "eq", + # "condition": "and" + # } + # { + # "key": "cluster_domain", + # "dimension_name": "dbm_meta cluster domain", + # "value": [ + # "spider.spidertest.db" + # ], + # "method": "eq", + # "condition": "and" + # }, + # ] + # } + # [{"level": platform, "rule":{"key": "app_id/db_module/cluster_domain", "value": ["aa", "bb"]}}] + targets = models.JSONField(verbose_name=_("监控目标"), default=dict) + target_level = models.CharField( + verbose_name=_("监控目标级别,跟随targets调整"), + choices=TargetLevel.get_choices(), + max_length=LEN_NORMAL, + default=TargetLevel.APP.value, + ) + target_priority = models.PositiveIntegerField(verbose_name=_("监控策略优先级,跟随targets调整")) + target_keyword = models.TextField(verbose_name=_("监控目标检索冗余字段"), default="") + + # Type 目前仅支持 Threshold + # level: 1(致命)、2(预警)、3(提醒) + # item[*].algorithms[*]: + # { + # "id": 6212, + # "type": "Threshold", + # "level": 1, + # "config": [ + # [ + # { + # "method": "gte", + # "threshold": 90 + # } + # ] + # ], + # "unit_prefix": "%" + # } + # [{"level": 1, "config": [[{"method": "gte", "threshold": 90}]], "unit_prefix": "%"}] + test_rules = models.JSONField(verbose_name=_("检测规则"), default=dict) + # NoticeSignalEnum: notice.signal -> ["recovered", "abnormal", "closed", "ack", "no_data"] + # item[*].no_data_config.is_enabled + notify_rules = models.JSONField(verbose_name=_("通知规则"), default=dict) + # [1,2,3] + notify_groups = models.JSONField(verbose_name=_("通知组"), default=dict) + + is_enabled = models.BooleanField(verbose_name=_("是否已启用"), default=True) + is_synced = models.BooleanField(verbose_name=_("是否已同步到监控"), default=False) + + # 当 is_synced=True时,才有效 + sync_at = models.DateTimeField(_("最近一次的同步时间"), null=True) + event_count = models.IntegerField(verbose_name=_("告警事件数量,初始值设置为-1"), default=-1) + + # 意义不大 + policy_status = models.CharField( + verbose_name=_("策略状态"), + choices=PolicyStatus.get_choices(), + max_length=LEN_NORMAL, + default=PolicyStatus.VALID.value, + ) + + # 分派策略组ID,目前专供内置策略 + dispatch_group_id = models.BigIntegerField(verbose_name=_("分派策略组ID,0代表没有对应的策略"), default=0) + monitor_policy_id = models.BigIntegerField(verbose_name=_("蓝鲸监控策略ID"), default=0) + + def calc_from_targets(self): + """根据目标计算优先级""" + + target_levels = list(map(lambda x: x["level"], self.targets)) + + if TargetLevel.CUSTOM.value in target_levels: + target_level = TargetLevel.CUSTOM.value + elif TargetLevel.CLUSTER.value in target_levels: + target_level = TargetLevel.CLUSTER.value + elif TargetLevel.MODULE.value in target_levels: + target_level = TargetLevel.MODULE.value + elif TargetLevel.APP.value in target_levels: + target_level = TargetLevel.APP.value + else: + target_level = TargetLevel.PLATFORM.value + + self.target_level = target_level + self.target_priority = TARGET_LEVEL_TO_PRIORITY.get(target_level).value + + self.target_keyword = ",".join( + [str(value) for target_level in self.targets for value in target_level["rule"]["value"]] + ) + + self.local_save() + + def patch_bk_biz_id(self, details, bk_biz_id=env.DBA_APP_BK_BIZ_ID): + details["bk_biz_id"] = self.bk_biz_id or bk_biz_id + return details + + def patch_notice_group(self, details): + """"TODO: 告警组""" + + # 用最新告警组覆盖模板中的 + details["notice"]["user_groups"] = NoticeGroup.get_monitor_groups(db_type=self.db_type) + return details + + def patch_metric_id(self, details): + """自定义事件和指标需要渲染 + metric_id: {bk_biz_id}_bkmoinitor_event_{event_data_id} + """ + + bkm_dbm_report = SystemSettings.get_setting_value(key=SystemSettingsEnum.BKM_DBM_REPORT.value) + + items = details["items"] + for item in items: + for query_config in item["query_configs"]: + if "custom.event" not in query_config["metric_id"]: + continue + query_config["metric_id"] = query_config["metric_id"].format( + bk_biz_id=env.DBA_APP_BK_BIZ_ID, event_data_id=bkm_dbm_report["event"]["data_id"] + ) + query_config["result_table_id"] = query_config["result_table_id"].format( + bk_biz_id=env.DBA_APP_BK_BIZ_ID, event_data_id=bkm_dbm_report["event"]["data_id"] + ) + return details + + def patch_priority_and_agg_conditions(self, details): + """将监控目标映射为所有查询的where条件""" + + self.calc_from_targets() + + # patch priority + details["priority"] = self.target_priority + + # patch agg conditions + agg_conditions = [] + for target in self.targets: + if target["level"] == TargetLevel.PLATFORM.value: + continue + + target_rule = target["rule"] + agg_conditions.append( + { + "key": target_rule["key"], + "dimension_name": TargetLevel.get_choice_label(target_rule["key"]), + "value": target_rule["value"], + "method": "eq", + "condition": "and", + } + ) + + for item in details["items"]: + for query_config in item["query_configs"]: + if "agg_condition" in query_config: + # remove same type conditions + query_config_agg_condition = list( + filter(lambda cond: cond["key"] not in TargetLevel.get_values(), query_config["agg_condition"]) + ) + query_config_agg_condition.extend(agg_conditions) + # overwrite agg_condition + query_config["agg_condition"] = query_config_agg_condition + else: + logger.error(_("{name}: 无法配置目标,暂不支持promql策略填充目标").format(**details)) + + return details + + def patch_algorithms(self, details): + """检测条件""" + + for rule in self.test_rules: + rule["type"] = "Threshold" + rule.pop("id", None) + + for item in details["items"]: + item["algorithms"] = self.test_rules + + return details + + def patch_notice(self, details): + """TODO: 依赖告警组的落地规则""" + # notify_rules -> notice.signal + details["notice"]["signal"] = self.notify_rules + + # notice_groups -> notice.user_groups + details["notice"]["user_groups"] = self.notify_groups + + return details + + @classmethod + def bkm_save_alarm_strategy(cls, params): + response = BKMonitorV3Api.save_alarm_strategy_v3(params, use_admin=True, raw=True) + if not response.get("result"): + logger.error("bkm_save_alarm_strategy failed: params: %s\n response: %s", params, response) + raise BkMonitorSaveAlarmException(message=response.get("message")) + logger.info("bkm_save_alarm_strategy success: %s", params["name"]) + return response["data"] + + def bkm_delete_alarm_strategy(self): + params = {"bk_biz_id": self.bk_biz_id or env.DBA_APP_BK_BIZ_ID, "ids": [self.monitor_policy_id]} + response = BKMonitorV3Api.delete_alarm_strategy_v3(params, use_admin=True, raw=True) + if not response.get("result"): + logger.error("bkm_delete_alarm_strategy failed: params: %s\n response: %s", params, response) + raise BkMonitorDeleteAlarmException(message=response.get("message")) + logger.info("bkm_delete_alarm_strategy success: %s", self.name) + return response["data"] + + def local_save(self, *args, **kwargs): + """仅保存到本地,不同步到监控""" + super().save(*args, **kwargs) + + def patch_all(self): + # model.targets -> agg_condition + details = self.patch_priority_and_agg_conditions(self.details) + + # model.test_rules -> algorithms + details = self.patch_algorithms(details) + + # model.notify_xxx -> notice + details = self.patch_notice(details) + + # other + details = self.patch_bk_biz_id(details) + details = self.patch_notice_group(details) + details = self.patch_metric_id(details) + + return details + + def save(self, *args, **kwargs): + """保存策略对象的同时,同步记录到监控""" + + # step1. sync to model + details = self.patch_all() + + # step2. sync to bkm + res = self.bkm_save_alarm_strategy(details) + + # overwrite by bkm strategy details + # if not self.pk: + # pass + + self.details = res + self.monitor_policy_id = self.details["id"] + + self.is_synced = True + self.sync_at = datetime.datetime.now() + + # step3. save to db + super().save(*args, **kwargs) + + def delete(self, using=None, keep_parents=False): + if self.is_synced and self.monitor_policy_id: + self.bkm_delete_alarm_strategy() + + super().delete(using, keep_parents) + + def enable(self) -> bool: + """启用:is_enabled:true -> save""" + self.is_enabled = True + self.details.update(is_enabled=self.is_enabled) + self.save(update_fields=["is_enabled"]) + + return self.is_enabled + + def disable(self) -> bool: + """禁用:is_enabled:false -> save""" + self.is_enabled = False + self.details.update(is_enabled=self.is_enabled) + self.save(update_fields=["is_enabled"]) + + return self.is_enabled + + @classmethod + def clone(cls, params, username="system") -> dict: + """克隆:patch -> create""" + + # params -> model + policy = cls(**params) + + # transfer details from parent to self + parent = cls.objects.get(id=policy.parent_id) + policy.parent_details = copy.deepcopy(parent.details) + policy.details = copy.deepcopy(parent.details) + policy.db_type = parent.db_type + policy.details.pop("id", None) + policy.details.update(name=policy.name) + policy.creator = policy.updater = username + policy.id = None + + # obj.calc_from_targets() + + # create -> overwrite details + policy.save() + + # 重新获取policy + policy.refresh_from_db() + + return {"local_id": policy.id, "bkm_id": policy.monitor_policy_id} + + def update(self, params, username="system") -> dict: + """更新:patch -> update""" + + update_fields = ["targets", "test_rules", "notify_rules", "notify_groups"] + + # param -> model + for key in update_fields: + setattr(self, key, params[key]) + # self.calc_from_targets() + + # update -> overwrite details + self.creator = self.updater = username + self.save() + + return {"local_id": self.id, "bkm_id": self.monitor_policy_id} + + def parse_details(self, details=None): + """从模板反向提取部分参数""" + + details = details or self.details + result = defaultdict(list) + + result["test_rules"] = [ + { + "level": alg["level"], + "config": alg["config"], + "unit_prefix": alg["unit_prefix"], + } + # 这里假设item长度恒为1 + for alg in details["items"][0]["algorithms"] + ] + + first_query_config = details["items"][0]["query_configs"][0] + target_conditions = ( + list(filter(lambda cond: cond["key"] in TargetLevel.get_values(), first_query_config["agg_condition"])) + if "agg_condition" in first_query_config + else [] + ) + + result["targets"] = [ + {"level": condition["key"], "rule": {"key": condition["key"], "value": condition["value"]}} + for condition in target_conditions + ] + + # 默认填充平台级目标 + if not result["targets"]: + result["targets"].append( + {"level": TargetLevel.PLATFORM.value, "rule": {"key": TargetLevel.PLATFORM.value, "value": []}} + ) + + result["notify_rules"] = details["notice"]["signal"] + # todo: 这里的user_groups可能需要转换为本地的notify_group + result["notify_groups"] = details["notice"]["user_groups"] or [0] + + return result + + def reset(self) -> dict: + """恢复默认:parent_details -> save""" + + # patch by parent details + self.details = self.parent_details + self.details["id"] = self.monitor_policy_id + self.details["name"] = self.name + + # fetch targets/test_rules/notify_rules/notify_groups from parent details + for attr, value in self.parse_details().items(): + setattr(self, attr, value) + + self.save() + + return {"local_id": self.id, "bkm_id": self.monitor_policy_id} + + class Meta: + verbose_name = _("告警策略") diff --git a/dbm-ui/backend/db_monitor/models/collect.py b/dbm-ui/backend/db_monitor/models/collect.py new file mode 100644 index 0000000000..c866701776 --- /dev/null +++ b/dbm-ui/backend/db_monitor/models/collect.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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.db import models +from django.utils.translation import gettext_lazy as _ + +from backend.bk_web.constants import LEN_LONG, LEN_MIDDLE, LEN_NORMAL, LEN_SHORT +from backend.bk_web.models import AuditedModel +from backend.configuration.constants import PLAT_BIZ_ID, DBType + +__all__ = [ + "CollectTemplate", + "CollectInstance", +] + + +class CollectTemplateBase(AuditedModel): + """采集策略模板""" + + bk_biz_id = models.IntegerField(help_text=_("业务ID, 0代表全业务"), default=PLAT_BIZ_ID) + plugin_id = models.CharField(verbose_name=_("插件ID"), max_length=LEN_MIDDLE, unique=True) + db_type = models.CharField( + _("DB类型"), choices=DBType.get_choices(), max_length=LEN_NORMAL, default=DBType.MySQL.value + ) + details = models.JSONField(verbose_name=_("详情"), default=dict) + + class Meta: + abstract = True + verbose_name = _("采集策略模板") + + +class CollectTemplate(CollectTemplateBase): + class Meta: + verbose_name = _("采集策略模板") + + +class CollectInstance(CollectTemplateBase): + """采集策略实例""" + + template_id = models.IntegerField(help_text=_("监控插件模板ID"), default=0) + collect_id = models.IntegerField(help_text=_("监控采集策略ID")) + + class Meta: + verbose_name = _("采集策略实例") diff --git a/dbm-ui/backend/db_monitor/models/dashboard.py b/dbm-ui/backend/db_monitor/models/dashboard.py new file mode 100644 index 0000000000..ae8eb16b99 --- /dev/null +++ b/dbm-ui/backend/db_monitor/models/dashboard.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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.db import models +from django.utils.translation import gettext_lazy as _ + +from backend.bk_web.constants import LEN_LONG, LEN_MIDDLE, LEN_NORMAL, LEN_SHORT +from backend.bk_web.models import AuditedModel +from backend.db_meta.enums import ClusterType + +__all__ = ["Dashboard"] + + +class Dashboard(AuditedModel): + """仪表盘""" + + name = models.CharField(verbose_name=_("名称"), max_length=LEN_MIDDLE, default="") + cluster_type = models.CharField(max_length=64, choices=ClusterType.get_choices(), default="") + + details = models.JSONField(verbose_name=_("详情"), default=dict) + variables = models.JSONField(verbose_name=_("变量"), default=dict) + + # grafana相关 + org_id = models.BigIntegerField(verbose_name=_("grafana-org_id")) + org_name = models.CharField(verbose_name=_("grafana-org_name"), max_length=LEN_NORMAL) + uid = models.CharField(verbose_name=_("grafana-uid"), max_length=LEN_NORMAL) + url = models.CharField(verbose_name=_("grafana-url"), max_length=LEN_LONG) + + class Meta: + verbose_name = _("仪表盘") diff --git a/dbm-ui/backend/db_monitor/models/dataclass/__init__.py b/dbm-ui/backend/db_monitor/models/dataclass/__init__.py new file mode 100644 index 0000000000..204908cf77 --- /dev/null +++ b/dbm-ui/backend/db_monitor/models/dataclass/__init__.py @@ -0,0 +1,18 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 dataclasses import dataclass, field +from typing import List + + +@dataclass() +class Target(object): + apps: List[int] + modules: List[int] + clusters: List[int] diff --git a/dbm-ui/backend/db_monitor/serializers.py b/dbm-ui/backend/db_monitor/serializers.py index a5e03b5ca8..75d206ede7 100644 --- a/dbm-ui/backend/db_monitor/serializers.py +++ b/dbm-ui/backend/db_monitor/serializers.py @@ -14,7 +14,8 @@ from backend.bk_web.serializers import AuditedSerializer from backend.db_meta.enums import ClusterType -from backend.db_monitor.models import CollectTemplate, RuleTemplate +from backend.db_monitor.models import CollectTemplate, MonitorPolicy, NoticeGroup, RuleTemplate +from backend.db_periodic_task.constants import NoticeSignalEnum class GetDashboardSerializer(serializers.Serializer): @@ -28,6 +29,12 @@ class DashboardUrlSerializer(serializers.Serializer): url = serializers.URLField(help_text=_("监控仪表盘地址")) +class NoticeGroupSerializer(AuditedSerializer, serializers.ModelSerializer): + class Meta: + model = NoticeGroup + fields = "__all__" + + class CollectTemplateSerializer(AuditedSerializer, serializers.ModelSerializer): class Meta: model = CollectTemplate @@ -38,3 +45,34 @@ class RuleTemplateSerializer(AuditedSerializer, serializers.ModelSerializer): class Meta: model = RuleTemplate fields = "__all__" + + +class MonitorPolicySerializer(AuditedSerializer, serializers.ModelSerializer): + class Meta: + model = MonitorPolicy + fields = "__all__" + + +class MonitorPolicyListSerializer(MonitorPolicySerializer): + class Meta: + model = MonitorPolicy + exclude = ["details", "parent_details"] + + +class MonitorPolicyUpdateSerializer(AuditedSerializer, serializers.ModelSerializer): + class Meta: + model = MonitorPolicy + fields = ["targets", "test_rules", "notify_rules", "notify_groups"] + + +class MonitorPolicyCloneSerializer(MonitorPolicyUpdateSerializer): + bk_biz_id = serializers.IntegerField(help_text=_("业务ID"), min_value=1) + notify_rules = serializers.ListField(child=serializers.ChoiceField(choices=NoticeSignalEnum.get_choices())) + + class Meta: + model = MonitorPolicy + fields = ["name", "bk_biz_id", "parent_id", "targets", "test_rules", "notify_rules", "notify_groups"] + + +class MonitorPolicyEmptySerializer(serializers.Serializer): + pass diff --git a/dbm-ui/backend/db_monitor/tasks.py b/dbm-ui/backend/db_monitor/tasks.py deleted file mode 100644 index 9856ed7e1e..0000000000 --- a/dbm-ui/backend/db_monitor/tasks.py +++ /dev/null @@ -1,99 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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. -""" -import datetime -import logging - -from celery import shared_task -from django.utils.translation import ugettext as _ - -from backend import env -from backend.configuration.models import DBAdministrator - -from ..components import BKMonitorV3Api -from ..configuration.constants import DEFAULT_DB_ADMINISTRATORS -from .models import NoticeGroup - -logger = logging.getLogger("celery") - - -@shared_task -def update_local_notice_group(): - """同步告警组""" - - now = datetime.datetime.now() - logger.info("[local_notice_group] start update local group at: %s", now) - - platform_dbas = DBAdministrator.objects.filter(bk_biz_id=0) - updated_groups, created_groups = 0, 0 - - for dba in platform_dbas: - # 跳过不需要同步的告警组 - if NoticeGroup.objects.filter(db_type=dba.db_type, dba_sync=False).exists(): - continue - - obj, updated = NoticeGroup.objects.update_or_create( - defaults={"users": dba.users}, bk_biz_id=0, db_type=dba.db_type - ) - - if updated: - updated_groups += 1 - else: - created_groups += 1 - - logger.info( - "[local_notice_group] finish update local group end: %s, create_cnt: %s, update_cnt: %s", - datetime.datetime.now() - now, - created_groups, - updated_groups, - ) - - -@shared_task -def update_remote_notice_group(): - """同步告警组""" - - now = datetime.datetime.now() - logger.info("[remote_notice_group] start update remote group start: %s", now) - - groups = NoticeGroup.objects.filter(dba_sync=True) - - updated_groups = 0 - for group in groups: - try: - logger.info("[remote_notice_group] update remote group: %s " % group.db_type) - group_users = set(group.users + DEFAULT_DB_ADMINISTRATORS) - group_params = { - "bk_biz_id": env.DBA_APP_BK_BIZ_ID, - "notice_receiver": [{"type": "user", "id": user} for user in group_users], - "name": f"{group.get_db_type_display()}_DBA_{group.bk_biz_id}", - "notice_way": {"1": ["rtx", "voice"], "2": ["rtx"], "3": ["rtx"]}, - "webhook_url": "", - "message": _("DBA系统专用"), - "wxwork_group": {}, - } - - # update or create - if group.monitor_group_id: - group_params["id"] = group.monitor_group_id - - res = BKMonitorV3Api.save_notice_group(group_params) - group.monitor_group_id = res["id"] - group.save() - - updated_groups += 1 - except Exception as e: # pylint: disable=wildcard-import - logger.error("[remote_notice_group] update remote group error: %s (%s)", group.db_type, e) - - logger.info( - "[remote_notice_group] finish update remote group end: %s, update_cnt: %s", - datetime.datetime.now() - now, - updated_groups, - ) diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.es.5668.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.es.5668.tpl64 deleted file mode 100644 index e4426bccec..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.es.5668.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU2NjgsICJuYW1lIjogIltFU10tXHU0ZTNiXHU2NzNhXHU3OGMxXHU3NmQ4SU9cdTUyMjlcdTc1MjhcdTczODciLCAiZGJfdHlwZSI6ICJlcyIsICJkZXRhaWxzIjogeyJhcHAiOiAiIiwgIm5hbWUiOiAiW0VTXS1cdTRlM2JcdTY3M2FcdTc4YzFcdTc2ZDhJT1x1NTIyOVx1NzUyOFx1NzM4NyIsICJwYXRoIjogIiIsICJ0eXBlIjogIm1vbml0b3IiLCAiaXRlbXMiOiBbeyJuYW1lIjogIkFWRyhJL09cdTRmN2ZcdTc1MjhcdTczODcpIiwgInRhcmdldCI6IFtbeyJmaWVsZCI6ICJob3N0X3RvcG9fbm9kZSIsICJ2YWx1ZSI6IFt7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgxNTR9LCB7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgxNTR9LCB7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgxNTR9XSwgIm1ldGhvZCI6ICJlcSJ9XV0sICJmdW5jdGlvbnMiOiBbXSwgImFsZ29yaXRobXMiOiBbeyJpZCI6IDYyMTEsICJ0eXBlIjogIlRocmVzaG9sZCIsICJsZXZlbCI6IDEsICJjb25maWciOiBbW3sibWV0aG9kIjogImd0ZSIsICJ0aHJlc2hvbGQiOiA5MH1dXSwgInVuaXRfcHJlZml4IjogIiUifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICIiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNjIwNywgIm5hbWUiOiAiSS9PXHU0ZjdmXHU3NTI4XHU3Mzg3IiwgInVuaXQiOiAicGVyY2VudHVuaXQiLCAiYWxpYXMiOiAiYSIsICJmdW5jdGlvbnMiOiBbXSwgIm1ldHJpY19pZCI6ICJia19tb25pdG9yLnN5c3RlbS5pby51dGlsIiwgImFnZ19tZXRob2QiOiAiQVZHIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAibWV0cmljX2ZpZWxkIjogInV0aWwiLCAiYWdnX2NvbmRpdGlvbiI6IFtdLCAiYWdnX2RpbWVuc2lvbiI6IFsiYmtfdGFyZ2V0X2lwIiwgImJrX3RhcmdldF9jbG91ZF9pZCIsICJkZXZpY2VfbmFtZSJdLCAiZGF0YV90eXBlX2xhYmVsIjogInRpbWVfc2VyaWVzIiwgInJlc3VsdF90YWJsZV9pZCI6ICJzeXN0ZW0uaW8iLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAiYmtfbW9uaXRvciJ9XSwgIm5vX2RhdGFfY29uZmlnIjogeyJsZXZlbCI6IDIsICJjb250aW51b3VzIjogMTAsICJpc19lbmFibGVkIjogZmFsc2UsICJhZ2dfZGltZW5zaW9uIjogWyJia190YXJnZXRfaXAiLCAiYmtfdGFyZ2V0X2Nsb3VkX2lkIl19fV0sICJsYWJlbHMiOiBbIkVTIiwgIkRCTV9FUyIsICJEQk1fRVMiLCAiREJNIl0sICJub3RpY2UiOiB7ImNvbmZpZyI6IHsidGVtcGxhdGUiOiBbeyJzaWduYWwiOiAiYWJub3JtYWwiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJyZWNvdmVyZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJjbG9zZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9XSwgIm5lZWRfcG9sbCI6IHRydWUsICJub3RpZnlfaW50ZXJ2YWwiOiA3MjAwLCAiaW50ZXJ2YWxfbm90aWZ5X21vZGUiOiAic3RhbmRhcmQifSwgInNpZ25hbCI6IFsiYWJub3JtYWwiLCAibm9fZGF0YSJdLCAib3B0aW9ucyI6IHsiZW5kX3RpbWUiOiAiMjM6NTk6NTkiLCAic3RhcnRfdGltZSI6ICIwMDowMDowMCIsICJhc3NpZ25fbW9kZSI6IFtdLCAidXBncmFkZV9jb25maWciOiB7fSwgImNvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAxLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzdHJhdGVneV9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiZGltZW5zaW9ucyJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifV0sICJ0aW1lZGVsdGEiOiA2MCwgImlzX2VuYWJsZWQiOiB0cnVlLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0IiwgIm5lZWRfYml6X2NvbnZlcmdlIjogdHJ1ZSwgInN1Yl9jb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMiwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifV0sICJ0aW1lZGVsdGEiOiA2MCwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdF9hbGFybSJ9fSwgImV4Y2x1ZGVfbm90aWNlX3dheXMiOiB7ImNsb3NlZCI6IFtdLCAicmVjb3ZlcmVkIjogW119LCAibm9pc2VfcmVkdWNlX2NvbmZpZyI6IHsidW5pdCI6ICJwZXJjZW50IiwgImNvdW50IjogMTAsICJ0aW1lZGVsdGEiOiA1LCAiZGltZW5zaW9ucyI6IFtdLCAiaXNfZW5hYmxlZCI6IGZhbHNlfX0sICJjb25maWdfaWQiOiA4NzIyLCAicmVsYXRlX3R5cGUiOiAiTk9USUNFIiwgInVzZXJfZ3JvdXBzIjogWzBdfSwgInNvdXJjZSI6ICJia21vbml0b3J2MyIsICJhY3Rpb25zIjogW10sICJkZXRlY3RzIjogW3sibGV2ZWwiOiAxLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJvcyIsICJia19iaXpfaWQiOiAyMDA1MDAwMTk0LCAiZGF0YV9zb3VyY2VfdHlwZSI6ICJcdTc2ZDFcdTYzYTdcdTkxYzdcdTk2YzZcdTYzMDdcdTY4MDcifSwgImlzX2VuYWJsZWQiOiB0cnVlfQ== \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.es.5669.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.es.5669.tpl64 deleted file mode 100644 index 8f1cda6e68..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.es.5669.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU2NjksICJuYW1lIjogIltFU10tXHU0ZTNiXHU2NzNhIENQVSBcdTRmN2ZcdTc1MjhcdTczODciLCAiZGJfdHlwZSI6ICJlcyIsICJkZXRhaWxzIjogeyJhcHAiOiAiIiwgIm5hbWUiOiAiW0VTXS1cdTRlM2JcdTY3M2EgQ1BVIFx1NGY3Zlx1NzUyOFx1NzM4NyIsICJwYXRoIjogIiIsICJ0eXBlIjogIm1vbml0b3IiLCAiaXRlbXMiOiBbeyJuYW1lIjogIkFWRyhDUFVcdTRmN2ZcdTc1MjhcdTczODcpIiwgInRhcmdldCI6IFtbeyJmaWVsZCI6ICJob3N0X3RvcG9fbm9kZSIsICJ2YWx1ZSI6IFt7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgxNTR9LCB7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgxNTR9LCB7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgxNTR9XSwgIm1ldGhvZCI6ICJlcSJ9XV0sICJmdW5jdGlvbnMiOiBbXSwgImFsZ29yaXRobXMiOiBbeyJpZCI6IDYyMTIsICJ0eXBlIjogIlRocmVzaG9sZCIsICJsZXZlbCI6IDEsICJjb25maWciOiBbW3sibWV0aG9kIjogImd0ZSIsICJ0aHJlc2hvbGQiOiA5MH1dXSwgInVuaXRfcHJlZml4IjogIiUifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICIiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNjIwOCwgIm5hbWUiOiAiQ1BVXHU0ZjdmXHU3NTI4XHU3Mzg3IiwgInVuaXQiOiAicGVyY2VudCIsICJhbGlhcyI6ICJhIiwgImZ1bmN0aW9ucyI6IFtdLCAibWV0cmljX2lkIjogImJrX21vbml0b3Iuc3lzdGVtLmNwdV9zdW1tYXJ5LnVzYWdlIiwgImFnZ19tZXRob2QiOiAiQVZHIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAibWV0cmljX2ZpZWxkIjogInVzYWdlIiwgImFnZ19jb25kaXRpb24iOiBbXSwgImFnZ19kaW1lbnNpb24iOiBbImJrX3RhcmdldF9jbG91ZF9pZCIsICJia190YXJnZXRfaXAiXSwgImRhdGFfdHlwZV9sYWJlbCI6ICJ0aW1lX3NlcmllcyIsICJyZXN1bHRfdGFibGVfaWQiOiAic3lzdGVtLmNwdV9zdW1tYXJ5IiwgImRhdGFfc291cmNlX2xhYmVsIjogImJrX21vbml0b3IifV0sICJub19kYXRhX2NvbmZpZyI6IHsibGV2ZWwiOiAyLCAiY29udGludW91cyI6IDEwLCAiaXNfZW5hYmxlZCI6IGZhbHNlLCAiYWdnX2RpbWVuc2lvbiI6IFsiYmtfdGFyZ2V0X2lwIiwgImJrX3RhcmdldF9jbG91ZF9pZCJdfX1dLCAibGFiZWxzIjogWyJFUyIsICJEQk1fRVMiLCAiREJNX0VTIiwgIkRCTSJdLCAibm90aWNlIjogeyJjb25maWciOiB7InRlbXBsYXRlIjogW3sic2lnbmFsIjogImFibm9ybWFsIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAicmVjb3ZlcmVkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAiY2xvc2VkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifV0sICJuZWVkX3BvbGwiOiB0cnVlLCAibm90aWZ5X2ludGVydmFsIjogNzIwMCwgImludGVydmFsX25vdGlmeV9tb2RlIjogInN0YW5kYXJkIn0sICJzaWduYWwiOiBbImFibm9ybWFsIiwgIm5vX2RhdGEiXSwgIm9wdGlvbnMiOiB7ImVuZF90aW1lIjogIjIzOjU5OjU5IiwgInN0YXJ0X3RpbWUiOiAiMDA6MDA6MDAiLCAiYXNzaWduX21vZGUiOiBbXSwgInVwZ3JhZGVfY29uZmlnIjoge30sICJjb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMSwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic3RyYXRlZ3lfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImRpbWVuc2lvbnMifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In1dLCAidGltZWRlbHRhIjogNjAsICJpc19lbmFibGVkIjogdHJ1ZSwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdCIsICJuZWVkX2Jpel9jb252ZXJnZSI6IHRydWUsICJzdWJfY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDIsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn1dLCAidGltZWRlbHRhIjogNjAsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3RfYWxhcm0ifX0sICJleGNsdWRlX25vdGljZV93YXlzIjogeyJjbG9zZWQiOiBbXSwgInJlY292ZXJlZCI6IFtdfSwgIm5vaXNlX3JlZHVjZV9jb25maWciOiB7InVuaXQiOiAicGVyY2VudCIsICJjb3VudCI6IDEwLCAidGltZWRlbHRhIjogNSwgImRpbWVuc2lvbnMiOiBbXSwgImlzX2VuYWJsZWQiOiBmYWxzZX19LCAiY29uZmlnX2lkIjogODcyMywgInJlbGF0ZV90eXBlIjogIk5PVElDRSIsICJ1c2VyX2dyb3VwcyI6IFswXX0sICJzb3VyY2UiOiAiYmttb25pdG9ydjMiLCAiYWN0aW9ucyI6IFtdLCAiZGV0ZWN0cyI6IFt7ImxldmVsIjogMSwgImNvbm5lY3RvciI6ICJhbmQiLCAiZXhwcmVzc2lvbiI6ICIiLCAidHJpZ2dlcl9jb25maWciOiB7ImNvdW50IjogMSwgInVwdGltZSI6IHsiY2FsZW5kYXJzIjogW10sICJ0aW1lX3JhbmdlcyI6IFt7ImVuZCI6ICIyMzo1OSIsICJzdGFydCI6ICIwMDowMCJ9XX0sICJjaGVja193aW5kb3ciOiA1fSwgInJlY292ZXJ5X2NvbmZpZyI6IHsiY2hlY2tfd2luZG93IjogNSwgInN0YXR1c19zZXR0ZXIiOiAicmVjb3ZlcnkifX1dLCAic2NlbmFyaW8iOiAib3MiLCAiYmtfYml6X2lkIjogMjAwNTAwMDE5NCwgImRhdGFfc291cmNlX3R5cGUiOiAiXHU3NmQxXHU2M2E3XHU5MWM3XHU5NmM2XHU2MzA3XHU2ODA3In0sICJpc19lbmFibGVkIjogdHJ1ZX0= \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.es.5670.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.es.5670.tpl64 deleted file mode 100644 index 4f3d50f718..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.es.5670.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU2NzAsICJuYW1lIjogIltFU10tXHU0ZTNiXHU2NzNhXHU3OGMxXHU3NmQ4XHU3YTdhXHU5NWY0XHU0ZjdmXHU3NTI4XHU3Mzg3IiwgImRiX3R5cGUiOiAiZXMiLCAiZGV0YWlscyI6IHsiYXBwIjogIiIsICJuYW1lIjogIltFU10tXHU0ZTNiXHU2NzNhXHU3OGMxXHU3NmQ4XHU3YTdhXHU5NWY0XHU0ZjdmXHU3NTI4XHU3Mzg3IiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAiQVZHKFx1NzhjMVx1NzZkOFx1N2E3YVx1OTVmNFx1NGY3Zlx1NzUyOFx1NzM4NykiLCAidGFyZ2V0IjogW1t7ImZpZWxkIjogImhvc3RfdG9wb19ub2RlIiwgInZhbHVlIjogW3siYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1NH0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1NH0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1NH1dLCAibWV0aG9kIjogImVxIn1dXSwgImZ1bmN0aW9ucyI6IFtdLCAiYWxnb3JpdGhtcyI6IFt7ImlkIjogNjIxMywgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMSwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDg1fV1dLCAidW5pdF9wcmVmaXgiOiAiJSJ9XSwgImV4cHJlc3Npb24iOiAiYSIsICJvcmlnaW5fc3FsIjogIiIsICJxdWVyeV9jb25maWdzIjogW3siaWQiOiA2MjA5LCAibmFtZSI6ICJcdTc4YzFcdTc2ZDhcdTdhN2FcdTk1ZjRcdTRmN2ZcdTc1MjhcdTczODciLCAidW5pdCI6ICJwZXJjZW50IiwgImFsaWFzIjogImEiLCAiZnVuY3Rpb25zIjogW10sICJtZXRyaWNfaWQiOiAiYmtfbW9uaXRvci5zeXN0ZW0uZGlzay5pbl91c2UiLCAiYWdnX21ldGhvZCI6ICJBVkciLCAiYWdnX2ludGVydmFsIjogNjAsICJtZXRyaWNfZmllbGQiOiAiaW5fdXNlIiwgImFnZ19jb25kaXRpb24iOiBbXSwgImFnZ19kaW1lbnNpb24iOiBbImJrX3RhcmdldF9pcCIsICJia190YXJnZXRfY2xvdWRfaWQiLCAibW91bnRfcG9pbnQiXSwgImRhdGFfdHlwZV9sYWJlbCI6ICJ0aW1lX3NlcmllcyIsICJyZXN1bHRfdGFibGVfaWQiOiAic3lzdGVtLmRpc2siLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAiYmtfbW9uaXRvciJ9XSwgIm5vX2RhdGFfY29uZmlnIjogeyJsZXZlbCI6IDIsICJjb250aW51b3VzIjogMTAsICJpc19lbmFibGVkIjogZmFsc2UsICJhZ2dfZGltZW5zaW9uIjogWyJia190YXJnZXRfaXAiLCAiYmtfdGFyZ2V0X2Nsb3VkX2lkIl19fV0sICJsYWJlbHMiOiBbIkVTIiwgIkRCTV9FUyIsICJEQk1fRVMiLCAiREJNIl0sICJub3RpY2UiOiB7ImNvbmZpZyI6IHsidGVtcGxhdGUiOiBbeyJzaWduYWwiOiAiYWJub3JtYWwiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJyZWNvdmVyZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJjbG9zZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9XSwgIm5lZWRfcG9sbCI6IHRydWUsICJub3RpZnlfaW50ZXJ2YWwiOiA3MjAwLCAiaW50ZXJ2YWxfbm90aWZ5X21vZGUiOiAic3RhbmRhcmQifSwgInNpZ25hbCI6IFsiYWJub3JtYWwiLCAibm9fZGF0YSJdLCAib3B0aW9ucyI6IHsiZW5kX3RpbWUiOiAiMjM6NTk6NTkiLCAic3RhcnRfdGltZSI6ICIwMDowMDowMCIsICJhc3NpZ25fbW9kZSI6IFtdLCAidXBncmFkZV9jb25maWciOiB7fSwgImNvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAxLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzdHJhdGVneV9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiZGltZW5zaW9ucyJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifV0sICJ0aW1lZGVsdGEiOiA2MCwgImlzX2VuYWJsZWQiOiB0cnVlLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0IiwgIm5lZWRfYml6X2NvbnZlcmdlIjogdHJ1ZSwgInN1Yl9jb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMiwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifV0sICJ0aW1lZGVsdGEiOiA2MCwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdF9hbGFybSJ9fSwgImV4Y2x1ZGVfbm90aWNlX3dheXMiOiB7ImNsb3NlZCI6IFtdLCAicmVjb3ZlcmVkIjogW119LCAibm9pc2VfcmVkdWNlX2NvbmZpZyI6IHsidW5pdCI6ICJwZXJjZW50IiwgImNvdW50IjogMTAsICJ0aW1lZGVsdGEiOiA1LCAiZGltZW5zaW9ucyI6IFtdLCAiaXNfZW5hYmxlZCI6IGZhbHNlfX0sICJjb25maWdfaWQiOiA4NzI0LCAicmVsYXRlX3R5cGUiOiAiTk9USUNFIiwgInVzZXJfZ3JvdXBzIjogWzBdfSwgInNvdXJjZSI6ICJia21vbml0b3J2MyIsICJhY3Rpb25zIjogW10sICJkZXRlY3RzIjogW3sibGV2ZWwiOiAxLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJvcyIsICJia19iaXpfaWQiOiAyMDA1MDAwMTk0LCAiZGF0YV9zb3VyY2VfdHlwZSI6ICJcdTc2ZDFcdTYzYTdcdTkxYzdcdTk2YzZcdTYzMDdcdTY4MDcifSwgImlzX2VuYWJsZWQiOiB0cnVlfQ== \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.es.5671.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.es.5671.tpl64 deleted file mode 100644 index 01dafacc96..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.es.5671.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU2NzEsICJuYW1lIjogIltFU10tXHU0ZTNiXHU2NzNhXHU3ZjUxXHU3ZWRjXHU1MTY1XHU2ZDQxXHU5MWNmIiwgImRiX3R5cGUiOiAiZXMiLCAiZGV0YWlscyI6IHsiYXBwIjogIiIsICJuYW1lIjogIltFU10tXHU0ZTNiXHU2NzNhXHU3ZjUxXHU3ZWRjXHU1MTY1XHU2ZDQxXHU5MWNmIiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAiQVZHKFx1N2Y1MVx1NTM2MVx1NTE2NVx1NmQ0MVx1OTFjZikiLCAidGFyZ2V0IjogW1t7ImZpZWxkIjogImhvc3RfdG9wb19ub2RlIiwgInZhbHVlIjogW3siYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1NH0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1NH0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1NH1dLCAibWV0aG9kIjogImVxIn1dXSwgImZ1bmN0aW9ucyI6IFtdLCAiYWxnb3JpdGhtcyI6IFt7ImlkIjogNjIxNCwgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMSwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDI1MH1dXSwgInVuaXRfcHJlZml4IjogIk0ifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICIiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNjIxMCwgIm5hbWUiOiAiXHU3ZjUxXHU1MzYxXHU1MTY1XHU2ZDQxXHU5MWNmIiwgInVuaXQiOiAiQnBzIiwgImFsaWFzIjogImEiLCAiZnVuY3Rpb25zIjogW10sICJtZXRyaWNfaWQiOiAiYmtfbW9uaXRvci5zeXN0ZW0ubmV0LnNwZWVkX3JlY3YiLCAiYWdnX21ldGhvZCI6ICJBVkciLCAiYWdnX2ludGVydmFsIjogNjAsICJtZXRyaWNfZmllbGQiOiAic3BlZWRfcmVjdiIsICJhZ2dfY29uZGl0aW9uIjogW10sICJhZ2dfZGltZW5zaW9uIjogWyJia190YXJnZXRfaXAiLCAiYmtfdGFyZ2V0X2Nsb3VkX2lkIiwgImRldmljZV9uYW1lIl0sICJkYXRhX3R5cGVfbGFiZWwiOiAidGltZV9zZXJpZXMiLCAicmVzdWx0X3RhYmxlX2lkIjogInN5c3RlbS5uZXQiLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAiYmtfbW9uaXRvciJ9XSwgIm5vX2RhdGFfY29uZmlnIjogeyJsZXZlbCI6IDIsICJjb250aW51b3VzIjogMTAsICJpc19lbmFibGVkIjogZmFsc2UsICJhZ2dfZGltZW5zaW9uIjogWyJia190YXJnZXRfaXAiLCAiYmtfdGFyZ2V0X2Nsb3VkX2lkIl19fV0sICJsYWJlbHMiOiBbIkVTIiwgIkRCTV9FUyIsICJEQk1fRVMiLCAiREJNIl0sICJub3RpY2UiOiB7ImNvbmZpZyI6IHsidGVtcGxhdGUiOiBbeyJzaWduYWwiOiAiYWJub3JtYWwiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJyZWNvdmVyZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJjbG9zZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9XSwgIm5lZWRfcG9sbCI6IHRydWUsICJub3RpZnlfaW50ZXJ2YWwiOiA3MjAwLCAiaW50ZXJ2YWxfbm90aWZ5X21vZGUiOiAic3RhbmRhcmQifSwgInNpZ25hbCI6IFsiYWJub3JtYWwiLCAibm9fZGF0YSJdLCAib3B0aW9ucyI6IHsiZW5kX3RpbWUiOiAiMjM6NTk6NTkiLCAic3RhcnRfdGltZSI6ICIwMDowMDowMCIsICJhc3NpZ25fbW9kZSI6IFtdLCAidXBncmFkZV9jb25maWciOiB7fSwgImNvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAxLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzdHJhdGVneV9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiZGltZW5zaW9ucyJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifV0sICJ0aW1lZGVsdGEiOiA2MCwgImlzX2VuYWJsZWQiOiB0cnVlLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0IiwgIm5lZWRfYml6X2NvbnZlcmdlIjogdHJ1ZSwgInN1Yl9jb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMiwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifV0sICJ0aW1lZGVsdGEiOiA2MCwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdF9hbGFybSJ9fSwgImV4Y2x1ZGVfbm90aWNlX3dheXMiOiB7ImNsb3NlZCI6IFtdLCAicmVjb3ZlcmVkIjogW119LCAibm9pc2VfcmVkdWNlX2NvbmZpZyI6IHsidW5pdCI6ICJwZXJjZW50IiwgImNvdW50IjogMTAsICJ0aW1lZGVsdGEiOiA1LCAiZGltZW5zaW9ucyI6IFtdLCAiaXNfZW5hYmxlZCI6IGZhbHNlfX0sICJjb25maWdfaWQiOiA4NzI1LCAicmVsYXRlX3R5cGUiOiAiTk9USUNFIiwgInVzZXJfZ3JvdXBzIjogWzBdfSwgInNvdXJjZSI6ICJia21vbml0b3J2MyIsICJhY3Rpb25zIjogW10sICJkZXRlY3RzIjogW3sibGV2ZWwiOiAxLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJvcyIsICJia19iaXpfaWQiOiAyMDA1MDAwMTk0LCAiZGF0YV9zb3VyY2VfdHlwZSI6ICJcdTc2ZDFcdTYzYTdcdTkxYzdcdTk2YzZcdTYzMDdcdTY4MDcifSwgImlzX2VuYWJsZWQiOiB0cnVlfQ== \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.es.5673.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.es.5673.tpl64 deleted file mode 100644 index c4d3bc951f..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.es.5673.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU2NzMsICJuYW1lIjogIltFU10tXHU1MTk5XHU2MzkyXHU5NjFmIiwgImRiX3R5cGUiOiAiZXMiLCAiZGV0YWlscyI6IHsiYXBwIjogIiIsICJuYW1lIjogIltFU10tXHU1MTk5XHU2MzkyXHU5NjFmIiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAiW0VTXS1cdTUxOTlcdTYzOTJcdTk2MWYiLCAidGFyZ2V0IjogW1t7ImZpZWxkIjogImhvc3RfdG9wb19ub2RlIiwgInZhbHVlIjogW3siYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1NH0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1NH0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1NH1dLCAibWV0aG9kIjogImVxIn1dXSwgImZ1bmN0aW9ucyI6IFtdLCAiYWxnb3JpdGhtcyI6IFt7ImlkIjogNjIxNiwgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMiwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3QiLCAidGhyZXNob2xkIjogMTAwfV1dLCAidW5pdF9wcmVmaXgiOiAiIn1dLCAiZXhwcmVzc2lvbiI6ICJhIiwgIm9yaWdpbl9zcWwiOiAibWF4IGJ5KGJrX3RhcmdldF9zZXJ2aWNlX2luc3RhbmNlX2lkLCBhcHAsIGNsdXN0ZXJfbmFtZSwgbmFtZSkgKG1heF9vdmVyX3RpbWUoYmttb25pdG9yOmV4cG9ydGVyX2RibV9lbGFzdGljc2VhcmNoX2V4cG9ydGVyOmdyb3VwNjplbGFzdGljc2VhcmNoX3RocmVhZF9wb29sX3F1ZXVlX2NvdW50e3R5cGU9XCJ3cml0ZVwifVsxbV0pKSIsICJxdWVyeV9jb25maWdzIjogW3siaWQiOiA2MjEyLCAibmFtZSI6ICIiLCAiYWxpYXMiOiAiYSIsICJwcm9tcWwiOiAibWF4IGJ5KGJrX3RhcmdldF9zZXJ2aWNlX2luc3RhbmNlX2lkLCBhcHAsIGNsdXN0ZXJfbmFtZSwgbmFtZSkgKG1heF9vdmVyX3RpbWUoYmttb25pdG9yOmV4cG9ydGVyX2RibV9lbGFzdGljc2VhcmNoX2V4cG9ydGVyOmdyb3VwNjplbGFzdGljc2VhcmNoX3RocmVhZF9wb29sX3F1ZXVlX2NvdW50e3R5cGU9XCJ3cml0ZVwifVsxbV0pKSIsICJmdW5jdGlvbnMiOiBbXSwgIm1ldHJpY19pZCI6ICJtYXggYnkoYmtfdGFyZ2V0X3NlcnZpY2VfaW5zdGFuY2VfaWQsIGFwcCwgY2x1c3Rlcl9uYW1lLCBuYW1lKSAoIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAiZGF0YV90eXBlX2xhYmVsIjogInRpbWVfc2VyaWVzIiwgImRhdGFfc291cmNlX2xhYmVsIjogInByb21ldGhldXMifV0sICJub19kYXRhX2NvbmZpZyI6IHsibGV2ZWwiOiAyLCAiY29udGludW91cyI6IDEwLCAiaXNfZW5hYmxlZCI6IGZhbHNlLCAiYWdnX2RpbWVuc2lvbiI6IFtdfX1dLCAibGFiZWxzIjogWyJFUyIsICJEQk1fRVMiLCAiREJNX0VTIiwgIkRCTSJdLCAibm90aWNlIjogeyJjb25maWciOiB7InRlbXBsYXRlIjogW3sic2lnbmFsIjogImFibm9ybWFsIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAicmVjb3ZlcmVkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAiY2xvc2VkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifV0sICJuZWVkX3BvbGwiOiB0cnVlLCAibm90aWZ5X2ludGVydmFsIjogNzIwMCwgImludGVydmFsX25vdGlmeV9tb2RlIjogInN0YW5kYXJkIn0sICJzaWduYWwiOiBbImFibm9ybWFsIiwgIm5vX2RhdGEiXSwgIm9wdGlvbnMiOiB7ImVuZF90aW1lIjogIjIzOjU5OjU5IiwgInN0YXJ0X3RpbWUiOiAiMDA6MDA6MDAiLCAiYXNzaWduX21vZGUiOiBbXSwgInVwZ3JhZGVfY29uZmlnIjoge30sICJjb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMSwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic3RyYXRlZ3lfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImRpbWVuc2lvbnMifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In1dLCAidGltZWRlbHRhIjogNjAsICJpc19lbmFibGVkIjogdHJ1ZSwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdCIsICJuZWVkX2Jpel9jb252ZXJnZSI6IHRydWUsICJzdWJfY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDIsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn1dLCAidGltZWRlbHRhIjogNjAsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3RfYWxhcm0ifX0sICJleGNsdWRlX25vdGljZV93YXlzIjogeyJjbG9zZWQiOiBbXSwgInJlY292ZXJlZCI6IFtdfSwgIm5vaXNlX3JlZHVjZV9jb25maWciOiB7InVuaXQiOiAicGVyY2VudCIsICJjb3VudCI6IDEwLCAidGltZWRlbHRhIjogNSwgImRpbWVuc2lvbnMiOiBbXSwgImlzX2VuYWJsZWQiOiBmYWxzZX19LCAiY29uZmlnX2lkIjogODcyNywgInJlbGF0ZV90eXBlIjogIk5PVElDRSIsICJ1c2VyX2dyb3VwcyI6IFswXX0sICJzb3VyY2UiOiAiYmttb25pdG9ydjMiLCAiYWN0aW9ucyI6IFtdLCAiZGV0ZWN0cyI6IFt7ImxldmVsIjogMiwgImNvbm5lY3RvciI6ICJhbmQiLCAiZXhwcmVzc2lvbiI6ICIiLCAidHJpZ2dlcl9jb25maWciOiB7ImNvdW50IjogMSwgInVwdGltZSI6IHsiY2FsZW5kYXJzIjogW10sICJ0aW1lX3JhbmdlcyI6IFt7ImVuZCI6ICIyMzo1OSIsICJzdGFydCI6ICIwMDowMCJ9XX0sICJjaGVja193aW5kb3ciOiA1fSwgInJlY292ZXJ5X2NvbmZpZyI6IHsiY2hlY2tfd2luZG93IjogNSwgInN0YXR1c19zZXR0ZXIiOiAicmVjb3ZlcnkifX1dLCAic2NlbmFyaW8iOiAiY29tcG9uZW50IiwgImJrX2Jpel9pZCI6IDIwMDUwMDAxOTQsICJkYXRhX3NvdXJjZV90eXBlIjogIlByb21ldGhldXMifSwgImlzX2VuYWJsZWQiOiB0cnVlfQ== \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.es.5674.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.es.5674.tpl64 deleted file mode 100644 index 8138923381..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.es.5674.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU2NzQsICJuYW1lIjogIltFU10tXHU1MTk5XHU2MmQyXHU3ZWRkIiwgImRiX3R5cGUiOiAiZXMiLCAiZGV0YWlscyI6IHsiYXBwIjogIiIsICJuYW1lIjogIltFU10tXHU1MTk5XHU2MmQyXHU3ZWRkIiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAiW0VTXS1cdTUxOTlcdTYyZDJcdTdlZGQiLCAidGFyZ2V0IjogW1t7ImZpZWxkIjogImhvc3RfdG9wb19ub2RlIiwgInZhbHVlIjogW3siYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1NH0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1NH0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1NH1dLCAibWV0aG9kIjogImVxIn1dXSwgImZ1bmN0aW9ucyI6IFtdLCAiYWxnb3JpdGhtcyI6IFt7ImlkIjogNjIxNywgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMSwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3QiLCAidGhyZXNob2xkIjogMTAwfV1dLCAidW5pdF9wcmVmaXgiOiAiIn1dLCAiZXhwcmVzc2lvbiI6ICJhIiwgIm9yaWdpbl9zcWwiOiAibWF4IGJ5KGJrX3RhcmdldF9zZXJ2aWNlX2luc3RhbmNlX2lkLCBhcHAsIGNsdXN0ZXJfbmFtZSwgbmFtZSkgKGJrbW9uaXRvcjpleHBvcnRlcl9kYm1fZWxhc3RpY3NlYXJjaF9leHBvcnRlcjpncm91cDY6ZWxhc3RpY3NlYXJjaF90aHJlYWRfcG9vbF9yZWplY3RlZF9jb3Vue3R5cGU9XCJ3cml0ZVwifSkiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNjIxMywgIm5hbWUiOiAiIiwgImFsaWFzIjogImEiLCAicHJvbXFsIjogIm1heCBieShia190YXJnZXRfc2VydmljZV9pbnN0YW5jZV9pZCwgYXBwLCBjbHVzdGVyX25hbWUsIG5hbWUpIChia21vbml0b3I6ZXhwb3J0ZXJfZGJtX2VsYXN0aWNzZWFyY2hfZXhwb3J0ZXI6Z3JvdXA2OmVsYXN0aWNzZWFyY2hfdGhyZWFkX3Bvb2xfcmVqZWN0ZWRfY291bnt0eXBlPVwid3JpdGVcIn0pIiwgImZ1bmN0aW9ucyI6IFtdLCAibWV0cmljX2lkIjogIm1heCBieShia190YXJnZXRfc2VydmljZV9pbnN0YW5jZV9pZCwgYXBwLCBjbHVzdGVyX25hbWUsIG5hbWUpICgiLCAiYWdnX2ludGVydmFsIjogNjAsICJkYXRhX3R5cGVfbGFiZWwiOiAidGltZV9zZXJpZXMiLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAicHJvbWV0aGV1cyJ9XSwgIm5vX2RhdGFfY29uZmlnIjogeyJsZXZlbCI6IDIsICJjb250aW51b3VzIjogMTAsICJpc19lbmFibGVkIjogZmFsc2UsICJhZ2dfZGltZW5zaW9uIjogW119fV0sICJsYWJlbHMiOiBbIkVTIiwgIkRCTV9FUyIsICJEQk1fRVMiLCAiREJNIl0sICJub3RpY2UiOiB7ImNvbmZpZyI6IHsidGVtcGxhdGUiOiBbeyJzaWduYWwiOiAiYWJub3JtYWwiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJyZWNvdmVyZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJjbG9zZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9XSwgIm5lZWRfcG9sbCI6IHRydWUsICJub3RpZnlfaW50ZXJ2YWwiOiA3MjAwLCAiaW50ZXJ2YWxfbm90aWZ5X21vZGUiOiAic3RhbmRhcmQifSwgInNpZ25hbCI6IFsiYWJub3JtYWwiLCAibm9fZGF0YSJdLCAib3B0aW9ucyI6IHsiZW5kX3RpbWUiOiAiMjM6NTk6NTkiLCAic3RhcnRfdGltZSI6ICIwMDowMDowMCIsICJhc3NpZ25fbW9kZSI6IFtdLCAidXBncmFkZV9jb25maWciOiB7fSwgImNvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAxLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzdHJhdGVneV9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiZGltZW5zaW9ucyJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifV0sICJ0aW1lZGVsdGEiOiA2MCwgImlzX2VuYWJsZWQiOiB0cnVlLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0IiwgIm5lZWRfYml6X2NvbnZlcmdlIjogdHJ1ZSwgInN1Yl9jb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMiwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifV0sICJ0aW1lZGVsdGEiOiA2MCwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdF9hbGFybSJ9fSwgImV4Y2x1ZGVfbm90aWNlX3dheXMiOiB7ImNsb3NlZCI6IFtdLCAicmVjb3ZlcmVkIjogW119LCAibm9pc2VfcmVkdWNlX2NvbmZpZyI6IHsidW5pdCI6ICJwZXJjZW50IiwgImNvdW50IjogMTAsICJ0aW1lZGVsdGEiOiA1LCAiZGltZW5zaW9ucyI6IFtdLCAiaXNfZW5hYmxlZCI6IGZhbHNlfX0sICJjb25maWdfaWQiOiA4NzI4LCAicmVsYXRlX3R5cGUiOiAiTk9USUNFIiwgInVzZXJfZ3JvdXBzIjogWzBdfSwgInNvdXJjZSI6ICJia21vbml0b3J2MyIsICJhY3Rpb25zIjogW10sICJkZXRlY3RzIjogW3sibGV2ZWwiOiAxLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJjb21wb25lbnQiLCAiYmtfYml6X2lkIjogMjAwNTAwMDE5NCwgImRhdGFfc291cmNlX3R5cGUiOiAiUHJvbWV0aGV1cyJ9LCAiaXNfZW5hYmxlZCI6IHRydWV9 \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.es.5675.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.es.5675.tpl64 deleted file mode 100644 index 397890b62f..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.es.5675.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU2NzUsICJuYW1lIjogIltFU10tXHU2NDFjXHU3ZDIyXHU2MmQyXHU3ZWRkIiwgImRiX3R5cGUiOiAiZXMiLCAiZGV0YWlscyI6IHsiYXBwIjogIiIsICJuYW1lIjogIltFU10tXHU2NDFjXHU3ZDIyXHU2MmQyXHU3ZWRkIiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAiW0VTXS1cdTY0MWNcdTdkMjJcdTYyZDJcdTdlZGQiLCAidGFyZ2V0IjogW1t7ImZpZWxkIjogImhvc3RfdG9wb19ub2RlIiwgInZhbHVlIjogW3siYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1NH0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1NH0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1NH1dLCAibWV0aG9kIjogImVxIn1dXSwgImZ1bmN0aW9ucyI6IFtdLCAiYWxnb3JpdGhtcyI6IFt7ImlkIjogNjIxOCwgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMSwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3QiLCAidGhyZXNob2xkIjogMTAwfV1dLCAidW5pdF9wcmVmaXgiOiAiIn1dLCAiZXhwcmVzc2lvbiI6ICJhIiwgIm9yaWdpbl9zcWwiOiAibWF4IGJ5KGJrX3RhcmdldF9zZXJ2aWNlX2luc3RhbmNlX2lkLCBhcHAsIGNsdXN0ZXJfbmFtZSwgbmFtZSkgKGJrbW9uaXRvcjpleHBvcnRlcl9kYm1fZWxhc3RpY3NlYXJjaF9leHBvcnRlcjpncm91cDY6ZWxhc3RpY3NlYXJjaF90aHJlYWRfcG9vbF9yZWplY3RlZF9jb3VudHt0eXBlPVwic2VhcmNoXCJ9KSIsICJxdWVyeV9jb25maWdzIjogW3siaWQiOiA2MjE0LCAibmFtZSI6ICIiLCAiYWxpYXMiOiAiYSIsICJwcm9tcWwiOiAibWF4IGJ5KGJrX3RhcmdldF9zZXJ2aWNlX2luc3RhbmNlX2lkLCBhcHAsIGNsdXN0ZXJfbmFtZSwgbmFtZSkgKGJrbW9uaXRvcjpleHBvcnRlcl9kYm1fZWxhc3RpY3NlYXJjaF9leHBvcnRlcjpncm91cDY6ZWxhc3RpY3NlYXJjaF90aHJlYWRfcG9vbF9yZWplY3RlZF9jb3VudHt0eXBlPVwic2VhcmNoXCJ9KSIsICJmdW5jdGlvbnMiOiBbXSwgIm1ldHJpY19pZCI6ICJtYXggYnkoYmtfdGFyZ2V0X3NlcnZpY2VfaW5zdGFuY2VfaWQsIGFwcCwgY2x1c3Rlcl9uYW1lLCBuYW1lKSAoIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAiZGF0YV90eXBlX2xhYmVsIjogInRpbWVfc2VyaWVzIiwgImRhdGFfc291cmNlX2xhYmVsIjogInByb21ldGhldXMifV0sICJub19kYXRhX2NvbmZpZyI6IHsibGV2ZWwiOiAyLCAiY29udGludW91cyI6IDEwLCAiaXNfZW5hYmxlZCI6IGZhbHNlLCAiYWdnX2RpbWVuc2lvbiI6IFtdfX1dLCAibGFiZWxzIjogWyJFUyIsICJEQk1fRVMiLCAiREJNX0VTIiwgIkRCTSJdLCAibm90aWNlIjogeyJjb25maWciOiB7InRlbXBsYXRlIjogW3sic2lnbmFsIjogImFibm9ybWFsIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAicmVjb3ZlcmVkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAiY2xvc2VkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifV0sICJuZWVkX3BvbGwiOiB0cnVlLCAibm90aWZ5X2ludGVydmFsIjogNzIwMCwgImludGVydmFsX25vdGlmeV9tb2RlIjogInN0YW5kYXJkIn0sICJzaWduYWwiOiBbImFibm9ybWFsIiwgIm5vX2RhdGEiXSwgIm9wdGlvbnMiOiB7ImVuZF90aW1lIjogIjIzOjU5OjU5IiwgInN0YXJ0X3RpbWUiOiAiMDA6MDA6MDAiLCAiYXNzaWduX21vZGUiOiBbXSwgInVwZ3JhZGVfY29uZmlnIjoge30sICJjb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMSwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic3RyYXRlZ3lfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImRpbWVuc2lvbnMifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In1dLCAidGltZWRlbHRhIjogNjAsICJpc19lbmFibGVkIjogdHJ1ZSwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdCIsICJuZWVkX2Jpel9jb252ZXJnZSI6IHRydWUsICJzdWJfY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDIsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn1dLCAidGltZWRlbHRhIjogNjAsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3RfYWxhcm0ifX0sICJleGNsdWRlX25vdGljZV93YXlzIjogeyJjbG9zZWQiOiBbXSwgInJlY292ZXJlZCI6IFtdfSwgIm5vaXNlX3JlZHVjZV9jb25maWciOiB7InVuaXQiOiAicGVyY2VudCIsICJjb3VudCI6IDEwLCAidGltZWRlbHRhIjogNSwgImRpbWVuc2lvbnMiOiBbXSwgImlzX2VuYWJsZWQiOiBmYWxzZX19LCAiY29uZmlnX2lkIjogODcyOSwgInJlbGF0ZV90eXBlIjogIk5PVElDRSIsICJ1c2VyX2dyb3VwcyI6IFswXX0sICJzb3VyY2UiOiAiYmttb25pdG9ydjMiLCAiYWN0aW9ucyI6IFtdLCAiZGV0ZWN0cyI6IFt7ImxldmVsIjogMSwgImNvbm5lY3RvciI6ICJhbmQiLCAiZXhwcmVzc2lvbiI6ICIiLCAidHJpZ2dlcl9jb25maWciOiB7ImNvdW50IjogMSwgInVwdGltZSI6IHsiY2FsZW5kYXJzIjogW10sICJ0aW1lX3JhbmdlcyI6IFt7ImVuZCI6ICIyMzo1OSIsICJzdGFydCI6ICIwMDowMCJ9XX0sICJjaGVja193aW5kb3ciOiA1fSwgInJlY292ZXJ5X2NvbmZpZyI6IHsiY2hlY2tfd2luZG93IjogNSwgInN0YXR1c19zZXR0ZXIiOiAicmVjb3ZlcnkifX1dLCAic2NlbmFyaW8iOiAiY29tcG9uZW50IiwgImJrX2Jpel9pZCI6IDIwMDUwMDAxOTQsICJkYXRhX3NvdXJjZV90eXBlIjogIlByb21ldGhldXMifSwgImlzX2VuYWJsZWQiOiB0cnVlfQ== \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.es.5757.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.es.5757.tpl64 deleted file mode 100644 index 24687691f3..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.es.5757.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU3NTcsICJuYW1lIjogIltFU10tXHU5NmM2XHU3ZmE0XHU3MmI2XHU2MDAxIiwgImRiX3R5cGUiOiAiZXMiLCAiZGV0YWlscyI6IHsiYXBwIjogIiIsICJuYW1lIjogIltFU10tXHU5NmM2XHU3ZmE0XHU3MmI2XHU2MDAxIiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAiTUFYKGVsYXN0aWNzZWFyY2hfY2x1c3Rlcl9oZWFsdGhfc3RhdHVzKSIsICJ0YXJnZXQiOiBbW3siZmllbGQiOiAiaG9zdF90b3BvX25vZGUiLCAidmFsdWUiOiBbeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTU0fSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTU0fSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTU0fV0sICJtZXRob2QiOiAiZXEifV1dLCAiZnVuY3Rpb25zIjogW10sICJhbGdvcml0aG1zIjogW3siaWQiOiA2MzAwLCAidHlwZSI6ICJUaHJlc2hvbGQiLCAibGV2ZWwiOiAxLCAiY29uZmlnIjogW1t7Im1ldGhvZCI6ICJlcSIsICJ0aHJlc2hvbGQiOiAxfV1dLCAidW5pdF9wcmVmaXgiOiAiIn1dLCAiZXhwcmVzc2lvbiI6ICJhIiwgIm9yaWdpbl9zcWwiOiAiIiwgInF1ZXJ5X2NvbmZpZ3MiOiBbeyJpZCI6IDYyOTYsICJuYW1lIjogImVsYXN0aWNzZWFyY2hfY2x1c3Rlcl9oZWFsdGhfc3RhdHVzIiwgInVuaXQiOiAiIiwgImFsaWFzIjogImEiLCAiZnVuY3Rpb25zIjogW10sICJtZXRyaWNfaWQiOiAiYmtfbW9uaXRvci5leHBvcnRlcl9kYm1fZWxhc3RpY3NlYXJjaF9leHBvcnRlci5ncm91cDE1LmVsYXN0aWNzZSIsICJhZ2dfbWV0aG9kIjogIk1BWCIsICJhZ2dfaW50ZXJ2YWwiOiA2MCwgIm1ldHJpY19maWVsZCI6ICJlbGFzdGljc2VhcmNoX2NsdXN0ZXJfaGVhbHRoX3N0YXR1cyIsICJhZ2dfY29uZGl0aW9uIjogW3sia2V5IjogImNvbG9yIiwgInZhbHVlIjogWyJyZWQiXSwgIm1ldGhvZCI6ICJlcSIsICJjb25kaXRpb24iOiAiYW5kIiwgImRpbWVuc2lvbl9uYW1lIjogImNvbG9yIn1dLCAiYWdnX2RpbWVuc2lvbiI6IFsiYmtfdGFyZ2V0X3NlcnZpY2VfaW5zdGFuY2VfaWQiLCAiYXBwIiwgImNsdXN0ZXJfZG9tYWluIl0sICJkYXRhX3R5cGVfbGFiZWwiOiAidGltZV9zZXJpZXMiLCAicmVzdWx0X3RhYmxlX2lkIjogImV4cG9ydGVyX2RibV9lbGFzdGljc2VhcmNoX2V4cG9ydGVyLmdyb3VwMTUiLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAiYmtfbW9uaXRvciJ9XSwgIm5vX2RhdGFfY29uZmlnIjogeyJsZXZlbCI6IDIsICJjb250aW51b3VzIjogMTAsICJpc19lbmFibGVkIjogZmFsc2UsICJhZ2dfZGltZW5zaW9uIjogWyJia190YXJnZXRfc2VydmljZV9pbnN0YW5jZV9pZCJdfX1dLCAibGFiZWxzIjogWyJFUyIsICJEQk1fRVMiLCAiREJNX0VTIiwgIkRCTSJdLCAibm90aWNlIjogeyJjb25maWciOiB7InRlbXBsYXRlIjogW3sic2lnbmFsIjogImFibm9ybWFsIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAicmVjb3ZlcmVkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAiY2xvc2VkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifV0sICJuZWVkX3BvbGwiOiB0cnVlLCAibm90aWZ5X2ludGVydmFsIjogNzIwMCwgImludGVydmFsX25vdGlmeV9tb2RlIjogInN0YW5kYXJkIn0sICJzaWduYWwiOiBbImFibm9ybWFsIiwgIm5vX2RhdGEiXSwgIm9wdGlvbnMiOiB7ImVuZF90aW1lIjogIjIzOjU5OjU5IiwgInN0YXJ0X3RpbWUiOiAiMDA6MDA6MDAiLCAiYXNzaWduX21vZGUiOiBbXSwgInVwZ3JhZGVfY29uZmlnIjoge30sICJjb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMSwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic3RyYXRlZ3lfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImRpbWVuc2lvbnMifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In1dLCAidGltZWRlbHRhIjogNjAsICJpc19lbmFibGVkIjogdHJ1ZSwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdCIsICJuZWVkX2Jpel9jb252ZXJnZSI6IHRydWUsICJzdWJfY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDIsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn1dLCAidGltZWRlbHRhIjogNjAsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3RfYWxhcm0ifX0sICJleGNsdWRlX25vdGljZV93YXlzIjogeyJjbG9zZWQiOiBbXSwgInJlY292ZXJlZCI6IFtdfSwgIm5vaXNlX3JlZHVjZV9jb25maWciOiB7InVuaXQiOiAicGVyY2VudCIsICJjb3VudCI6IDEwLCAidGltZWRlbHRhIjogNSwgImRpbWVuc2lvbnMiOiBbImFwcCIsICJjbHVzdGVyX2RvbWFpbiIsICJia190YXJnZXRfc2VydmljZV9pbnN0YW5jZV9pZCJdLCAiaXNfZW5hYmxlZCI6IGZhbHNlfX0sICJjb25maWdfaWQiOiA4ODExLCAicmVsYXRlX3R5cGUiOiAiTk9USUNFIiwgInVzZXJfZ3JvdXBzIjogWzBdfSwgInNvdXJjZSI6ICJia21vbml0b3J2MyIsICJhY3Rpb25zIjogW10sICJkZXRlY3RzIjogW3sibGV2ZWwiOiAxLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJjb21wb25lbnQiLCAiYmtfYml6X2lkIjogMjAwNTAwMDE5NCwgImRhdGFfc291cmNlX3R5cGUiOiAiXHU3NmQxXHU2M2E3XHU5MWM3XHU5NmM2XHU2MzA3XHU2ODA3In0sICJpc19lbmFibGVkIjogdHJ1ZX0= \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.hdfs.5891.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.hdfs.5891.tpl64 deleted file mode 100644 index 14fd240baf..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.hdfs.5891.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU4OTEsICJuYW1lIjogIltIREZTXS1cdTViYjlcdTkxY2ZcdTRmN2ZcdTc1MjhcdTczODciLCAiZGJfdHlwZSI6ICJoZGZzIiwgImRldGFpbHMiOiB7ImFwcCI6ICIiLCAibmFtZSI6ICJbSERGU10tXHU1YmI5XHU5MWNmXHU0ZjdmXHU3NTI4XHU3Mzg3IiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAiQVZHKGhhZG9vcF9uYW1lbm9kZV9jYXBhY2l0eV91c2VkKS9BVkcoaGFkb29wX25hbWVub2RlX2NhcGFjaXR5X3RvdGFsKSoxMDAiLCAidGFyZ2V0IjogW1t7ImZpZWxkIjogImhvc3RfdG9wb19ub2RlIiwgInZhbHVlIjogW3siYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1N30sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1N31dLCAibWV0aG9kIjogImVxIn1dXSwgImZ1bmN0aW9ucyI6IFtdLCAiYWxnb3JpdGhtcyI6IFt7ImlkIjogNjQ2MywgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMSwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDg2fV1dLCAidW5pdF9wcmVmaXgiOiAiIn0sIHsiaWQiOiA2NDY0LCAidHlwZSI6ICJUaHJlc2hvbGQiLCAibGV2ZWwiOiAyLCAiY29uZmlnIjogW1t7Im1ldGhvZCI6ICJndGUiLCAidGhyZXNob2xkIjogODB9XV0sICJ1bml0X3ByZWZpeCI6ICIifV0sICJleHByZXNzaW9uIjogImEvYioxMDAiLCAib3JpZ2luX3NxbCI6ICIiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNjQzNywgIm5hbWUiOiAiaGFkb29wX25hbWVub2RlX2NhcGFjaXR5X3VzZWQiLCAidW5pdCI6ICIiLCAiYWxpYXMiOiAiYSIsICJmdW5jdGlvbnMiOiBbXSwgIm1ldHJpY19pZCI6ICJia19tb25pdG9yLmV4cG9ydGVyX2RibV9oZGZzX2V4cG9ydGVyLmdyb3VwMS5oYWRvb3BfbmFtZW5vZGVfY2FwIiwgImFnZ19tZXRob2QiOiAiQVZHIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAibWV0cmljX2ZpZWxkIjogImhhZG9vcF9uYW1lbm9kZV9jYXBhY2l0eV91c2VkIiwgImFnZ19jb25kaXRpb24iOiBbXSwgImFnZ19kaW1lbnNpb24iOiBbImFwcCIsICJjbHVzdGVyX2RvbWFpbiIsICJia190YXJnZXRfc2VydmljZV9pbnN0YW5jZV9pZCJdLCAiZGF0YV90eXBlX2xhYmVsIjogInRpbWVfc2VyaWVzIiwgInJlc3VsdF90YWJsZV9pZCI6ICJleHBvcnRlcl9kYm1faGRmc19leHBvcnRlci5ncm91cDEiLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAiYmtfbW9uaXRvciJ9LCB7ImlkIjogNjQzOCwgIm5hbWUiOiAiaGFkb29wX25hbWVub2RlX2NhcGFjaXR5X3RvdGFsIiwgInVuaXQiOiAiIiwgImFsaWFzIjogImIiLCAiZnVuY3Rpb25zIjogW10sICJtZXRyaWNfaWQiOiAiYmtfbW9uaXRvci5leHBvcnRlcl9kYm1faGRmc19leHBvcnRlci5ncm91cDEuaGFkb29wX25hbWVub2RlX2NhcCIsICJhZ2dfbWV0aG9kIjogIkFWRyIsICJhZ2dfaW50ZXJ2YWwiOiA2MCwgIm1ldHJpY19maWVsZCI6ICJoYWRvb3BfbmFtZW5vZGVfY2FwYWNpdHlfdG90YWwiLCAiYWdnX2NvbmRpdGlvbiI6IFtdLCAiYWdnX2RpbWVuc2lvbiI6IFsiYmtfdGFyZ2V0X3NlcnZpY2VfaW5zdGFuY2VfaWQiLCAiY2x1c3Rlcl9kb21haW4iLCAiYXBwIl0sICJkYXRhX3R5cGVfbGFiZWwiOiAidGltZV9zZXJpZXMiLCAicmVzdWx0X3RhYmxlX2lkIjogImV4cG9ydGVyX2RibV9oZGZzX2V4cG9ydGVyLmdyb3VwMSIsICJkYXRhX3NvdXJjZV9sYWJlbCI6ICJia19tb25pdG9yIn1dLCAibm9fZGF0YV9jb25maWciOiB7ImxldmVsIjogMiwgImNvbnRpbnVvdXMiOiAxMCwgImlzX2VuYWJsZWQiOiBmYWxzZSwgImFnZ19kaW1lbnNpb24iOiBbImJrX3RhcmdldF9zZXJ2aWNlX2luc3RhbmNlX2lkIl19fV0sICJsYWJlbHMiOiBbIkhERlMiLCAiREJNX0hERlMiLCAiREJNX0hERlMiLCAiREJNIl0sICJub3RpY2UiOiB7ImNvbmZpZyI6IHsidGVtcGxhdGUiOiBbeyJzaWduYWwiOiAiYWJub3JtYWwiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJyZWNvdmVyZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJjbG9zZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9XSwgIm5lZWRfcG9sbCI6IHRydWUsICJub3RpZnlfaW50ZXJ2YWwiOiA3MjAwLCAiaW50ZXJ2YWxfbm90aWZ5X21vZGUiOiAic3RhbmRhcmQifSwgInNpZ25hbCI6IFsibm9fZGF0YSIsICJhYm5vcm1hbCJdLCAib3B0aW9ucyI6IHsiZW5kX3RpbWUiOiAiMjM6NTk6NTkiLCAic3RhcnRfdGltZSI6ICIwMDowMDowMCIsICJhc3NpZ25fbW9kZSI6IFtdLCAidXBncmFkZV9jb25maWciOiB7fSwgImNvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAxLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzdHJhdGVneV9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiZGltZW5zaW9ucyJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifV0sICJ0aW1lZGVsdGEiOiA2MCwgImlzX2VuYWJsZWQiOiB0cnVlLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0IiwgIm5lZWRfYml6X2NvbnZlcmdlIjogdHJ1ZSwgInN1Yl9jb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMiwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifV0sICJ0aW1lZGVsdGEiOiA2MCwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdF9hbGFybSJ9fSwgImV4Y2x1ZGVfbm90aWNlX3dheXMiOiB7ImNsb3NlZCI6IFtdLCAicmVjb3ZlcmVkIjogW119LCAibm9pc2VfcmVkdWNlX2NvbmZpZyI6IHsidW5pdCI6ICJwZXJjZW50IiwgImNvdW50IjogMTAsICJ0aW1lZGVsdGEiOiA1LCAiZGltZW5zaW9ucyI6IFsiYXBwIiwgImNsdXN0ZXJfZG9tYWluIiwgImJrX3RhcmdldF9zZXJ2aWNlX2luc3RhbmNlX2lkIl0sICJpc19lbmFibGVkIjogZmFsc2V9fSwgImNvbmZpZ19pZCI6IDg5NDUsICJyZWxhdGVfdHlwZSI6ICJOT1RJQ0UiLCAidXNlcl9ncm91cHMiOiBbMF19LCAic291cmNlIjogImJrbW9uaXRvcnYzIiwgImFjdGlvbnMiOiBbXSwgImRldGVjdHMiOiBbeyJsZXZlbCI6IDEsICJjb25uZWN0b3IiOiAiYW5kIiwgImV4cHJlc3Npb24iOiAiIiwgInRyaWdnZXJfY29uZmlnIjogeyJjb3VudCI6IDEsICJ1cHRpbWUiOiB7ImNhbGVuZGFycyI6IFtdLCAidGltZV9yYW5nZXMiOiBbeyJlbmQiOiAiMjM6NTkiLCAic3RhcnQiOiAiMDA6MDAifV19LCAiY2hlY2tfd2luZG93IjogNX0sICJyZWNvdmVyeV9jb25maWciOiB7ImNoZWNrX3dpbmRvdyI6IDUsICJzdGF0dXNfc2V0dGVyIjogInJlY292ZXJ5In19LCB7ImxldmVsIjogMiwgImNvbm5lY3RvciI6ICJhbmQiLCAiZXhwcmVzc2lvbiI6ICIiLCAidHJpZ2dlcl9jb25maWciOiB7ImNvdW50IjogMSwgInVwdGltZSI6IHsiY2FsZW5kYXJzIjogW10sICJ0aW1lX3JhbmdlcyI6IFt7ImVuZCI6ICIyMzo1OSIsICJzdGFydCI6ICIwMDowMCJ9XX0sICJjaGVja193aW5kb3ciOiA1fSwgInJlY292ZXJ5X2NvbmZpZyI6IHsiY2hlY2tfd2luZG93IjogNSwgInN0YXR1c19zZXR0ZXIiOiAicmVjb3ZlcnkifX1dLCAic2NlbmFyaW8iOiAiY29tcG9uZW50IiwgImJrX2Jpel9pZCI6IDIwMDUwMDAxOTQsICJkYXRhX3NvdXJjZV90eXBlIjogIlx1NzZkMVx1NjNhN1x1OTFjN1x1OTZjNlx1NjMwN1x1NjgwNyJ9LCAiaXNfZW5hYmxlZCI6IHRydWV9 \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.hdfs.5894.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.hdfs.5894.tpl64 deleted file mode 100644 index 8047bf4398..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.hdfs.5894.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU4OTQsICJuYW1lIjogIltIREZTXS1FZGl0TG9nUm9sbCIsICJkYl90eXBlIjogImhkZnMiLCAiZGV0YWlscyI6IHsiYXBwIjogIiIsICJuYW1lIjogIltIREZTXS1FZGl0TG9nUm9sbCIsICJwYXRoIjogIiIsICJ0eXBlIjogIm1vbml0b3IiLCAiaXRlbXMiOiBbeyJuYW1lIjogIk1BWChoYWRvb3BfbmFtZW5vZGVfdHJhbnNhY3Rpb25zX3NpbmNlX2xhc3RfY2hlY2twb2ludCkiLCAidGFyZ2V0IjogW1t7ImZpZWxkIjogImhvc3RfdG9wb19ub2RlIiwgInZhbHVlIjogW3siYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1N30sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1N31dLCAibWV0aG9kIjogImVxIn1dXSwgImZ1bmN0aW9ucyI6IFtdLCAiYWxnb3JpdGhtcyI6IFt7ImlkIjogNjQ2NywgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMiwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDUwMDAwMDB9XV0sICJ1bml0X3ByZWZpeCI6ICIifSwgeyJpZCI6IDY0NjgsICJ0eXBlIjogIlRocmVzaG9sZCIsICJsZXZlbCI6IDEsICJjb25maWciOiBbW3sibWV0aG9kIjogImd0ZSIsICJ0aHJlc2hvbGQiOiA2MDAwMDAwfV1dLCAidW5pdF9wcmVmaXgiOiAiIn1dLCAiZXhwcmVzc2lvbiI6ICJhIiwgIm9yaWdpbl9zcWwiOiAiIiwgInF1ZXJ5X2NvbmZpZ3MiOiBbeyJpZCI6IDY0NDEsICJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV90cmFuc2FjdGlvbnNfc2luY2VfbGFzdF9jaGVja3BvaW50IiwgInVuaXQiOiAiIiwgImFsaWFzIjogImEiLCAiZnVuY3Rpb25zIjogW10sICJtZXRyaWNfaWQiOiAiYmtfbW9uaXRvci5leHBvcnRlcl9oZGZzX2V4cG9ydGVyLmdyb3VwMS5oYWRvb3BfbmFtZW5vZGVfdHJhbnNhYyIsICJhZ2dfbWV0aG9kIjogIk1BWCIsICJhZ2dfaW50ZXJ2YWwiOiA2MCwgIm1ldHJpY19maWVsZCI6ICJoYWRvb3BfbmFtZW5vZGVfdHJhbnNhY3Rpb25zX3NpbmNlX2xhc3RfY2hlY2twb2ludCIsICJhZ2dfY29uZGl0aW9uIjogW10sICJhZ2dfZGltZW5zaW9uIjogWyJia190YXJnZXRfc2VydmljZV9pbnN0YW5jZV9pZCJdLCAiZGF0YV90eXBlX2xhYmVsIjogInRpbWVfc2VyaWVzIiwgInJlc3VsdF90YWJsZV9pZCI6ICJleHBvcnRlcl9oZGZzX2V4cG9ydGVyLmdyb3VwMSIsICJkYXRhX3NvdXJjZV9sYWJlbCI6ICJia19tb25pdG9yIn1dLCAibm9fZGF0YV9jb25maWciOiB7ImxldmVsIjogMiwgImNvbnRpbnVvdXMiOiAxMCwgImlzX2VuYWJsZWQiOiBmYWxzZSwgImFnZ19kaW1lbnNpb24iOiBbImJrX3RhcmdldF9zZXJ2aWNlX2luc3RhbmNlX2lkIl19fV0sICJsYWJlbHMiOiBbIkhERlMiLCAiREJNX0hERlMiLCAiREJNX0hERlMiLCAiREJNIl0sICJub3RpY2UiOiB7ImNvbmZpZyI6IHsidGVtcGxhdGUiOiBbeyJzaWduYWwiOiAiYWJub3JtYWwiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJyZWNvdmVyZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJjbG9zZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9XSwgIm5lZWRfcG9sbCI6IHRydWUsICJub3RpZnlfaW50ZXJ2YWwiOiA3MjAwLCAiaW50ZXJ2YWxfbm90aWZ5X21vZGUiOiAic3RhbmRhcmQifSwgInNpZ25hbCI6IFsiYWJub3JtYWwiLCAibm9fZGF0YSJdLCAib3B0aW9ucyI6IHsiZW5kX3RpbWUiOiAiMjM6NTk6NTkiLCAic3RhcnRfdGltZSI6ICIwMDowMDowMCIsICJhc3NpZ25fbW9kZSI6IFtdLCAidXBncmFkZV9jb25maWciOiB7fSwgImNvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAxLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzdHJhdGVneV9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiZGltZW5zaW9ucyJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifV0sICJ0aW1lZGVsdGEiOiA2MCwgImlzX2VuYWJsZWQiOiB0cnVlLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0IiwgIm5lZWRfYml6X2NvbnZlcmdlIjogdHJ1ZSwgInN1Yl9jb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMiwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifV0sICJ0aW1lZGVsdGEiOiA2MCwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdF9hbGFybSJ9fSwgImV4Y2x1ZGVfbm90aWNlX3dheXMiOiB7ImNsb3NlZCI6IFtdLCAicmVjb3ZlcmVkIjogW119LCAibm9pc2VfcmVkdWNlX2NvbmZpZyI6IHsidW5pdCI6ICJwZXJjZW50IiwgImNvdW50IjogMTAsICJ0aW1lZGVsdGEiOiA1LCAiZGltZW5zaW9ucyI6IFtdLCAiaXNfZW5hYmxlZCI6IGZhbHNlfX0sICJjb25maWdfaWQiOiA4OTQ4LCAicmVsYXRlX3R5cGUiOiAiTk9USUNFIiwgInVzZXJfZ3JvdXBzIjogWzBdfSwgInNvdXJjZSI6ICJia21vbml0b3J2MyIsICJhY3Rpb25zIjogW10sICJkZXRlY3RzIjogW3sibGV2ZWwiOiAyLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fSwgeyJsZXZlbCI6IDEsICJjb25uZWN0b3IiOiAiYW5kIiwgImV4cHJlc3Npb24iOiAiIiwgInRyaWdnZXJfY29uZmlnIjogeyJjb3VudCI6IDEsICJ1cHRpbWUiOiB7ImNhbGVuZGFycyI6IFtdLCAidGltZV9yYW5nZXMiOiBbeyJlbmQiOiAiMjM6NTkiLCAic3RhcnQiOiAiMDA6MDAifV19LCAiY2hlY2tfd2luZG93IjogNX0sICJyZWNvdmVyeV9jb25maWciOiB7ImNoZWNrX3dpbmRvdyI6IDUsICJzdGF0dXNfc2V0dGVyIjogInJlY292ZXJ5In19XSwgInNjZW5hcmlvIjogImNvbXBvbmVudCIsICJia19iaXpfaWQiOiAyMDA1MDAwMTk0LCAiZGF0YV9zb3VyY2VfdHlwZSI6ICJcdTc2ZDFcdTYzYTdcdTkxYzdcdTk2YzZcdTYzMDdcdTY4MDcifSwgImlzX2VuYWJsZWQiOiB0cnVlfQ== \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.hdfs.5895.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.hdfs.5895.tpl64 deleted file mode 100644 index 9b05c59cdd..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.hdfs.5895.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU4OTUsICJuYW1lIjogIltIREZTXS1Db3JydXB0QmxvY2tzIiwgImRiX3R5cGUiOiAiaGRmcyIsICJkZXRhaWxzIjogeyJhcHAiOiAiIiwgIm5hbWUiOiAiW0hERlNdLUNvcnJ1cHRCbG9ja3MiLCAicGF0aCI6ICIiLCAidHlwZSI6ICJtb25pdG9yIiwgIml0ZW1zIjogW3sibmFtZSI6ICJBVkcoaGFkb29wX25hbWVub2RlX2NvcnJ1cHRfYmxvY2tzKSIsICJ0YXJnZXQiOiBbW3siZmllbGQiOiAiaG9zdF90b3BvX25vZGUiLCAidmFsdWUiOiBbeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTU3fSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTU3fV0sICJtZXRob2QiOiAiZXEifV1dLCAiZnVuY3Rpb25zIjogW10sICJhbGdvcml0aG1zIjogW3siaWQiOiA2NDY5LCAidHlwZSI6ICJUaHJlc2hvbGQiLCAibGV2ZWwiOiAyLCAiY29uZmlnIjogW1t7Im1ldGhvZCI6ICJndGUiLCAidGhyZXNob2xkIjogMTB9XV0sICJ1bml0X3ByZWZpeCI6ICIifSwgeyJpZCI6IDY0NzAsICJ0eXBlIjogIlRocmVzaG9sZCIsICJsZXZlbCI6IDEsICJjb25maWciOiBbW3sibWV0aG9kIjogImd0ZSIsICJ0aHJlc2hvbGQiOiA1MDB9XV0sICJ1bml0X3ByZWZpeCI6ICIifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICIiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNjQ0MiwgIm5hbWUiOiAiaGFkb29wX25hbWVub2RlX2NvcnJ1cHRfYmxvY2tzIiwgInVuaXQiOiAiIiwgImFsaWFzIjogImEiLCAiZnVuY3Rpb25zIjogW10sICJtZXRyaWNfaWQiOiAiYmtfbW9uaXRvci5leHBvcnRlcl9oZGZzX2V4cG9ydGVyLmdyb3VwMS5oYWRvb3BfbmFtZW5vZGVfY29ycnVwdCIsICJhZ2dfbWV0aG9kIjogIkFWRyIsICJhZ2dfaW50ZXJ2YWwiOiA2MCwgIm1ldHJpY19maWVsZCI6ICJoYWRvb3BfbmFtZW5vZGVfY29ycnVwdF9ibG9ja3MiLCAiYWdnX2NvbmRpdGlvbiI6IFtdLCAiYWdnX2RpbWVuc2lvbiI6IFsiYmtfdGFyZ2V0X3NlcnZpY2VfaW5zdGFuY2VfaWQiXSwgImRhdGFfdHlwZV9sYWJlbCI6ICJ0aW1lX3NlcmllcyIsICJyZXN1bHRfdGFibGVfaWQiOiAiZXhwb3J0ZXJfaGRmc19leHBvcnRlci5ncm91cDEiLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAiYmtfbW9uaXRvciJ9XSwgIm5vX2RhdGFfY29uZmlnIjogeyJsZXZlbCI6IDIsICJjb250aW51b3VzIjogMTAsICJpc19lbmFibGVkIjogZmFsc2UsICJhZ2dfZGltZW5zaW9uIjogWyJia190YXJnZXRfc2VydmljZV9pbnN0YW5jZV9pZCJdfX1dLCAibGFiZWxzIjogWyJIREZTIiwgIkRCTV9IREZTIiwgIkRCTV9IREZTIiwgIkRCTSJdLCAibm90aWNlIjogeyJjb25maWciOiB7InRlbXBsYXRlIjogW3sic2lnbmFsIjogImFibm9ybWFsIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAicmVjb3ZlcmVkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAiY2xvc2VkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifV0sICJuZWVkX3BvbGwiOiB0cnVlLCAibm90aWZ5X2ludGVydmFsIjogNzIwMCwgImludGVydmFsX25vdGlmeV9tb2RlIjogInN0YW5kYXJkIn0sICJzaWduYWwiOiBbImFibm9ybWFsIiwgIm5vX2RhdGEiXSwgIm9wdGlvbnMiOiB7ImVuZF90aW1lIjogIjIzOjU5OjU5IiwgInN0YXJ0X3RpbWUiOiAiMDA6MDA6MDAiLCAiYXNzaWduX21vZGUiOiBbXSwgInVwZ3JhZGVfY29uZmlnIjoge30sICJjb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMSwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic3RyYXRlZ3lfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImRpbWVuc2lvbnMifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In1dLCAidGltZWRlbHRhIjogNjAsICJpc19lbmFibGVkIjogdHJ1ZSwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdCIsICJuZWVkX2Jpel9jb252ZXJnZSI6IHRydWUsICJzdWJfY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDIsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn1dLCAidGltZWRlbHRhIjogNjAsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3RfYWxhcm0ifX0sICJleGNsdWRlX25vdGljZV93YXlzIjogeyJjbG9zZWQiOiBbXSwgInJlY292ZXJlZCI6IFtdfSwgIm5vaXNlX3JlZHVjZV9jb25maWciOiB7InVuaXQiOiAicGVyY2VudCIsICJjb3VudCI6IDEwLCAidGltZWRlbHRhIjogNSwgImRpbWVuc2lvbnMiOiBbXSwgImlzX2VuYWJsZWQiOiBmYWxzZX19LCAiY29uZmlnX2lkIjogODk0OSwgInJlbGF0ZV90eXBlIjogIk5PVElDRSIsICJ1c2VyX2dyb3VwcyI6IFswXX0sICJzb3VyY2UiOiAiYmttb25pdG9ydjMiLCAiYWN0aW9ucyI6IFtdLCAiZGV0ZWN0cyI6IFt7ImxldmVsIjogMiwgImNvbm5lY3RvciI6ICJhbmQiLCAiZXhwcmVzc2lvbiI6ICIiLCAidHJpZ2dlcl9jb25maWciOiB7ImNvdW50IjogMSwgInVwdGltZSI6IHsiY2FsZW5kYXJzIjogW10sICJ0aW1lX3JhbmdlcyI6IFt7ImVuZCI6ICIyMzo1OSIsICJzdGFydCI6ICIwMDowMCJ9XX0sICJjaGVja193aW5kb3ciOiA1fSwgInJlY292ZXJ5X2NvbmZpZyI6IHsiY2hlY2tfd2luZG93IjogNSwgInN0YXR1c19zZXR0ZXIiOiAicmVjb3ZlcnkifX0sIHsibGV2ZWwiOiAxLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJjb21wb25lbnQiLCAiYmtfYml6X2lkIjogMjAwNTAwMDE5NCwgImRhdGFfc291cmNlX3R5cGUiOiAiXHU3NmQxXHU2M2E3XHU5MWM3XHU5NmM2XHU2MzA3XHU2ODA3In0sICJpc19lbmFibGVkIjogdHJ1ZX0= \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.hdfs.5898.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.hdfs.5898.tpl64 deleted file mode 100644 index eb20ba7862..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.hdfs.5898.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU4OTgsICJuYW1lIjogIltIREZTXS1NaXNzaW5nQmxvY2tzIiwgImRiX3R5cGUiOiAiaGRmcyIsICJkZXRhaWxzIjogeyJhcHAiOiAiIiwgIm5hbWUiOiAiW0hERlNdLU1pc3NpbmdCbG9ja3MiLCAicGF0aCI6ICIiLCAidHlwZSI6ICJtb25pdG9yIiwgIml0ZW1zIjogW3sibmFtZSI6ICJNQVgoaGFkb29wX25hbWVub2RlX21pc3NpbmdfYmxvY2tzKSIsICJ0YXJnZXQiOiBbW3siZmllbGQiOiAiaG9zdF90b3BvX25vZGUiLCAidmFsdWUiOiBbeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTU3fSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTU3fV0sICJtZXRob2QiOiAiZXEifV1dLCAiZnVuY3Rpb25zIjogW10sICJhbGdvcml0aG1zIjogW3siaWQiOiA2NDczLCAidHlwZSI6ICJUaHJlc2hvbGQiLCAibGV2ZWwiOiAxLCAiY29uZmlnIjogW1t7Im1ldGhvZCI6ICJndGUiLCAidGhyZXNob2xkIjogNTAwfV1dLCAidW5pdF9wcmVmaXgiOiAiIn0sIHsiaWQiOiA2NDc0LCAidHlwZSI6ICJUaHJlc2hvbGQiLCAibGV2ZWwiOiAyLCAiY29uZmlnIjogW1t7Im1ldGhvZCI6ICJndGUiLCAidGhyZXNob2xkIjogMTB9XV0sICJ1bml0X3ByZWZpeCI6ICIifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICIiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNjQ0NSwgIm5hbWUiOiAiaGFkb29wX25hbWVub2RlX21pc3NpbmdfYmxvY2tzIiwgInVuaXQiOiAiIiwgImFsaWFzIjogImEiLCAiZnVuY3Rpb25zIjogW10sICJtZXRyaWNfaWQiOiAiYmtfbW9uaXRvci5leHBvcnRlcl9oZGZzX2V4cG9ydGVyLmdyb3VwMS5oYWRvb3BfbmFtZW5vZGVfbWlzc2luZyIsICJhZ2dfbWV0aG9kIjogIk1BWCIsICJhZ2dfaW50ZXJ2YWwiOiA2MCwgIm1ldHJpY19maWVsZCI6ICJoYWRvb3BfbmFtZW5vZGVfbWlzc2luZ19ibG9ja3MiLCAiYWdnX2NvbmRpdGlvbiI6IFtdLCAiYWdnX2RpbWVuc2lvbiI6IFsiYmtfdGFyZ2V0X3NlcnZpY2VfaW5zdGFuY2VfaWQiXSwgImRhdGFfdHlwZV9sYWJlbCI6ICJ0aW1lX3NlcmllcyIsICJyZXN1bHRfdGFibGVfaWQiOiAiZXhwb3J0ZXJfaGRmc19leHBvcnRlci5ncm91cDEiLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAiYmtfbW9uaXRvciJ9XSwgIm5vX2RhdGFfY29uZmlnIjogeyJsZXZlbCI6IDIsICJjb250aW51b3VzIjogMTAsICJpc19lbmFibGVkIjogZmFsc2UsICJhZ2dfZGltZW5zaW9uIjogWyJia190YXJnZXRfc2VydmljZV9pbnN0YW5jZV9pZCJdfX1dLCAibGFiZWxzIjogWyJIREZTIiwgIkRCTV9IREZTIiwgIkRCTV9IREZTIiwgIkRCTSJdLCAibm90aWNlIjogeyJjb25maWciOiB7InRlbXBsYXRlIjogW3sic2lnbmFsIjogImFibm9ybWFsIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAicmVjb3ZlcmVkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAiY2xvc2VkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifV0sICJuZWVkX3BvbGwiOiB0cnVlLCAibm90aWZ5X2ludGVydmFsIjogNzIwMCwgImludGVydmFsX25vdGlmeV9tb2RlIjogInN0YW5kYXJkIn0sICJzaWduYWwiOiBbImFibm9ybWFsIiwgIm5vX2RhdGEiXSwgIm9wdGlvbnMiOiB7ImVuZF90aW1lIjogIjIzOjU5OjU5IiwgInN0YXJ0X3RpbWUiOiAiMDA6MDA6MDAiLCAiYXNzaWduX21vZGUiOiBbXSwgInVwZ3JhZGVfY29uZmlnIjoge30sICJjb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMSwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic3RyYXRlZ3lfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImRpbWVuc2lvbnMifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In1dLCAidGltZWRlbHRhIjogNjAsICJpc19lbmFibGVkIjogdHJ1ZSwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdCIsICJuZWVkX2Jpel9jb252ZXJnZSI6IHRydWUsICJzdWJfY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDIsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn1dLCAidGltZWRlbHRhIjogNjAsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3RfYWxhcm0ifX0sICJleGNsdWRlX25vdGljZV93YXlzIjogeyJjbG9zZWQiOiBbXSwgInJlY292ZXJlZCI6IFtdfSwgIm5vaXNlX3JlZHVjZV9jb25maWciOiB7InVuaXQiOiAicGVyY2VudCIsICJjb3VudCI6IDEwLCAidGltZWRlbHRhIjogNSwgImRpbWVuc2lvbnMiOiBbXSwgImlzX2VuYWJsZWQiOiBmYWxzZX19LCAiY29uZmlnX2lkIjogODk1MiwgInJlbGF0ZV90eXBlIjogIk5PVElDRSIsICJ1c2VyX2dyb3VwcyI6IFswXX0sICJzb3VyY2UiOiAiYmttb25pdG9ydjMiLCAiYWN0aW9ucyI6IFtdLCAiZGV0ZWN0cyI6IFt7ImxldmVsIjogMSwgImNvbm5lY3RvciI6ICJhbmQiLCAiZXhwcmVzc2lvbiI6ICIiLCAidHJpZ2dlcl9jb25maWciOiB7ImNvdW50IjogMSwgInVwdGltZSI6IHsiY2FsZW5kYXJzIjogW10sICJ0aW1lX3JhbmdlcyI6IFt7ImVuZCI6ICIyMzo1OSIsICJzdGFydCI6ICIwMDowMCJ9XX0sICJjaGVja193aW5kb3ciOiA1fSwgInJlY292ZXJ5X2NvbmZpZyI6IHsiY2hlY2tfd2luZG93IjogNSwgInN0YXR1c19zZXR0ZXIiOiAicmVjb3ZlcnkifX0sIHsibGV2ZWwiOiAyLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJjb21wb25lbnQiLCAiYmtfYml6X2lkIjogMjAwNTAwMDE5NCwgImRhdGFfc291cmNlX3R5cGUiOiAiXHU3NmQxXHU2M2E3XHU5MWM3XHU5NmM2XHU2MzA3XHU2ODA3In0sICJpc19lbmFibGVkIjogdHJ1ZX0= \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.hdfs.5899.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.hdfs.5899.tpl64 deleted file mode 100644 index b0ed2a2775..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.hdfs.5899.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU4OTksICJuYW1lIjogIltIREZTXS1TYWZlTm9kZSIsICJkYl90eXBlIjogImhkZnMiLCAiZGV0YWlscyI6IHsiYXBwIjogIiIsICJuYW1lIjogIltIREZTXS1TYWZlTm9kZSIsICJwYXRoIjogIiIsICJ0eXBlIjogIm1vbml0b3IiLCAiaXRlbXMiOiBbeyJuYW1lIjogIlNVTShoYWRvb3BfbmFtZW5vZGVfZl9zX3N0YXRlKSIsICJ0YXJnZXQiOiBbW3siZmllbGQiOiAiaG9zdF90b3BvX25vZGUiLCAidmFsdWUiOiBbeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTU3fSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTU3fV0sICJtZXRob2QiOiAiZXEifV1dLCAiZnVuY3Rpb25zIjogW10sICJhbGdvcml0aG1zIjogW3siaWQiOiA2NDc1LCAidHlwZSI6ICJUaHJlc2hvbGQiLCAibGV2ZWwiOiAxLCAiY29uZmlnIjogW1t7Im1ldGhvZCI6ICJsdCIsICJ0aHJlc2hvbGQiOiAxfV1dLCAidW5pdF9wcmVmaXgiOiAiIn1dLCAiZXhwcmVzc2lvbiI6ICJhIiwgIm9yaWdpbl9zcWwiOiAiIiwgInF1ZXJ5X2NvbmZpZ3MiOiBbeyJpZCI6IDY0NDYsICJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV9mX3Nfc3RhdGUiLCAidW5pdCI6ICIiLCAiYWxpYXMiOiAiYSIsICJmdW5jdGlvbnMiOiBbXSwgIm1ldHJpY19pZCI6ICJia19tb25pdG9yLmV4cG9ydGVyX2RibV9oZGZzX2V4cG9ydGVyLmdyb3VwMS5oYWRvb3BfbmFtZW5vZGVfZl9zIiwgImFnZ19tZXRob2QiOiAiU1VNIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAibWV0cmljX2ZpZWxkIjogImhhZG9vcF9uYW1lbm9kZV9mX3Nfc3RhdGUiLCAiYWdnX2NvbmRpdGlvbiI6IFt7ImtleSI6ICJpbnN0YW5jZV9yb2xlIiwgInZhbHVlIjogWyJoZGZzX25hbWVub2RlIl0sICJtZXRob2QiOiAiZXEiLCAiY29uZGl0aW9uIjogImFuZCIsICJkaW1lbnNpb25fbmFtZSI6ICJpbnN0YW5jZV9yb2xlIn1dLCAiYWdnX2RpbWVuc2lvbiI6IFsiYXBwIiwgImNsdXN0ZXJfZG9tYWluIl0sICJkYXRhX3R5cGVfbGFiZWwiOiAidGltZV9zZXJpZXMiLCAicmVzdWx0X3RhYmxlX2lkIjogImV4cG9ydGVyX2RibV9oZGZzX2V4cG9ydGVyLmdyb3VwMSIsICJkYXRhX3NvdXJjZV9sYWJlbCI6ICJia19tb25pdG9yIn1dLCAibm9fZGF0YV9jb25maWciOiB7ImxldmVsIjogMiwgImNvbnRpbnVvdXMiOiAxMCwgImlzX2VuYWJsZWQiOiBmYWxzZSwgImFnZ19kaW1lbnNpb24iOiBbXX19XSwgImxhYmVscyI6IFsiSERGUyIsICJEQk1fSERGUyIsICJEQk0iXSwgIm5vdGljZSI6IHsiY29uZmlnIjogeyJ0ZW1wbGF0ZSI6IFt7InNpZ25hbCI6ICJhYm5vcm1hbCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In0sIHsic2lnbmFsIjogInJlY292ZXJlZCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In0sIHsic2lnbmFsIjogImNsb3NlZCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In1dLCAibmVlZF9wb2xsIjogdHJ1ZSwgIm5vdGlmeV9pbnRlcnZhbCI6IDcyMDAsICJpbnRlcnZhbF9ub3RpZnlfbW9kZSI6ICJzdGFuZGFyZCJ9LCAic2lnbmFsIjogWyJub19kYXRhIiwgImFibm9ybWFsIl0sICJvcHRpb25zIjogeyJlbmRfdGltZSI6ICIyMzo1OTo1OSIsICJzdGFydF90aW1lIjogIjAwOjAwOjAwIiwgImFzc2lnbl9tb2RlIjogWyJvbmx5X25vdGljZSIsICJieV9ydWxlIl0sICJjb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMSwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic3RyYXRlZ3lfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImRpbWVuc2lvbnMifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In1dLCAidGltZWRlbHRhIjogNjAsICJpc19lbmFibGVkIjogdHJ1ZSwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdCIsICJuZWVkX2Jpel9jb252ZXJnZSI6IHRydWUsICJzdWJfY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDIsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn1dLCAidGltZWRlbHRhIjogNjAsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3RfYWxhcm0ifX0sICJleGNsdWRlX25vdGljZV93YXlzIjogeyJjbG9zZWQiOiBbXSwgInJlY292ZXJlZCI6IFtdfSwgIm5vaXNlX3JlZHVjZV9jb25maWciOiB7InVuaXQiOiAicGVyY2VudCIsICJjb3VudCI6IDEwLCAidGltZWRlbHRhIjogNSwgImRpbWVuc2lvbnMiOiBbImFwcCIsICJjbHVzdGVyX2RvbWFpbiJdLCAiaXNfZW5hYmxlZCI6IGZhbHNlfX0sICJjb25maWdfaWQiOiA4OTUzLCAicmVsYXRlX3R5cGUiOiAiTk9USUNFIiwgInVzZXJfZ3JvdXBzIjogWzBdfSwgInNvdXJjZSI6ICJia21vbml0b3J2MyIsICJhY3Rpb25zIjogW10sICJkZXRlY3RzIjogW3sibGV2ZWwiOiAxLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJjb21wb25lbnQiLCAiYmtfYml6X2lkIjogMjAwNTAwMDE5NCwgImRhdGFfc291cmNlX3R5cGUiOiAiXHU3NmQxXHU2M2E3XHU5MWM3XHU5NmM2XHU2MzA3XHU2ODA3In0sICJpc19lbmFibGVkIjogdHJ1ZX0= \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.influxdb.5946.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.influxdb.5946.tpl64 deleted file mode 100644 index d2b145c531..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.influxdb.5946.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU5NDYsICJuYW1lIjogIkluZmx1eERCIFx1NTE4NVx1NWI1OFx1NGY3Zlx1NzUyOFx1NTQ0YVx1OGI2NiIsICJkYl90eXBlIjogImluZmx1eGRiIiwgImRldGFpbHMiOiB7ImFwcCI6ICIiLCAibmFtZSI6ICJJbmZsdXhEQiBcdTUxODVcdTViNThcdTRmN2ZcdTc1MjhcdTU0NGFcdThiNjYiLCAicGF0aCI6ICIiLCAidHlwZSI6ICJtb25pdG9yIiwgIml0ZW1zIjogW3sibmFtZSI6ICJBVkcoXHU1ZTk0XHU3NTI4XHU3YTBiXHU1ZThmXHU1MTg1XHU1YjU4XHU0ZjdmXHU3NTI4XHU1MzYwXHU2YmQ0KSIsICJ0YXJnZXQiOiBbW3siZmllbGQiOiAiaG9zdF90b3BvX25vZGUiLCAidmFsdWUiOiBbeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MjQ1fV0sICJtZXRob2QiOiAiZXEifV1dLCAiZnVuY3Rpb25zIjogW10sICJhbGdvcml0aG1zIjogW3siaWQiOiA2NTI0LCAidHlwZSI6ICJUaHJlc2hvbGQiLCAibGV2ZWwiOiAyLCAiY29uZmlnIjogW1t7Im1ldGhvZCI6ICJndGUiLCAidGhyZXNob2xkIjogODB9XV0sICJ1bml0X3ByZWZpeCI6ICIlIn0sIHsiaWQiOiA2NTI1LCAidHlwZSI6ICJUaHJlc2hvbGQiLCAibGV2ZWwiOiAxLCAiY29uZmlnIjogW1t7Im1ldGhvZCI6ICJndGUiLCAidGhyZXNob2xkIjogOTV9XV0sICJ1bml0X3ByZWZpeCI6ICIlIn1dLCAiZXhwcmVzc2lvbiI6ICJhIiwgIm9yaWdpbl9zcWwiOiAiIiwgInF1ZXJ5X2NvbmZpZ3MiOiBbeyJpZCI6IDY0OTMsICJuYW1lIjogIlx1NWU5NFx1NzUyOFx1N2EwYlx1NWU4Zlx1NTE4NVx1NWI1OFx1NGY3Zlx1NzUyOFx1NTM2MFx1NmJkNCIsICJ1bml0IjogInBlcmNlbnQiLCAiYWxpYXMiOiAiYSIsICJmdW5jdGlvbnMiOiBbXSwgIm1ldHJpY19pZCI6ICJia19tb25pdG9yLnN5c3RlbS5tZW0ucGN0X3VzZWQiLCAiYWdnX21ldGhvZCI6ICJBVkciLCAiYWdnX2ludGVydmFsIjogNjAsICJtZXRyaWNfZmllbGQiOiAicGN0X3VzZWQiLCAiYWdnX2NvbmRpdGlvbiI6IFtdLCAiYWdnX2RpbWVuc2lvbiI6IFsiYmtfdGFyZ2V0X2lwIiwgImJrX3RhcmdldF9jbG91ZF9pZCJdLCAiZGF0YV90eXBlX2xhYmVsIjogInRpbWVfc2VyaWVzIiwgInJlc3VsdF90YWJsZV9pZCI6ICJzeXN0ZW0ubWVtIiwgImRhdGFfc291cmNlX2xhYmVsIjogImJrX21vbml0b3IifV0sICJub19kYXRhX2NvbmZpZyI6IHsibGV2ZWwiOiAyLCAiY29udGludW91cyI6IDEwLCAiaXNfZW5hYmxlZCI6IGZhbHNlLCAiYWdnX2RpbWVuc2lvbiI6IFsiYmtfdGFyZ2V0X2lwIiwgImJrX3RhcmdldF9jbG91ZF9pZCJdfX1dLCAibGFiZWxzIjogWyJJbmZsdXhEQiIsICJEQk1fSU5GTFVYREIiLCAiREJNIl0sICJub3RpY2UiOiB7ImNvbmZpZyI6IHsidGVtcGxhdGUiOiBbeyJzaWduYWwiOiAiYWJub3JtYWwiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5hc3NpZ25fZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJyZWNvdmVyZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5hc3NpZ25fZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJjbG9zZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5hc3NpZ25fZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9XSwgIm5lZWRfcG9sbCI6IHRydWUsICJub3RpZnlfaW50ZXJ2YWwiOiA3MjAwLCAiaW50ZXJ2YWxfbm90aWZ5X21vZGUiOiAic3RhbmRhcmQifSwgInNpZ25hbCI6IFsiYWJub3JtYWwiLCAibm9fZGF0YSJdLCAib3B0aW9ucyI6IHsiZW5kX3RpbWUiOiAiMjM6NTk6NTkiLCAic3RhcnRfdGltZSI6ICIwMDowMDowMCIsICJhc3NpZ25fbW9kZSI6IFsiYnlfcnVsZSIsICJvbmx5X25vdGljZSJdLCAidXBncmFkZV9jb25maWciOiB7ImlzX2VuYWJsZWQiOiBmYWxzZSwgInVzZXJfZ3JvdXBzIjogW10sICJ1cGdyYWRlX2ludGVydmFsIjogODY0MDB9LCAiY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDEsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInN0cmF0ZWd5X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJkaW1lbnNpb25zIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJia19iaXpfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV9yZWNlaXZlciJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3dheSJ9XSwgInRpbWVkZWx0YSI6IDYwLCAiaXNfZW5hYmxlZCI6IHRydWUsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3QiLCAibmVlZF9iaXpfY29udmVyZ2UiOiB0cnVlLCAic3ViX2NvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAyLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJia19iaXpfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV9yZWNlaXZlciJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3dheSJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9XSwgInRpbWVkZWx0YSI6IDYwLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0X2FsYXJtIn19LCAiZXhjbHVkZV9ub3RpY2Vfd2F5cyI6IHsiYWNrIjogW10sICJjbG9zZWQiOiBbXSwgInJlY292ZXJlZCI6IFtdfSwgIm5vaXNlX3JlZHVjZV9jb25maWciOiB7InVuaXQiOiAicGVyY2VudCIsICJjb3VudCI6IDEwLCAidGltZWRlbHRhIjogNSwgImRpbWVuc2lvbnMiOiBbXSwgImlzX2VuYWJsZWQiOiBmYWxzZX19LCAiY29uZmlnX2lkIjogOTAwMCwgInJlbGF0ZV90eXBlIjogIk5PVElDRSIsICJ1c2VyX2dyb3VwcyI6IFswXX0sICJzb3VyY2UiOiAiYmttb25pdG9ydjMiLCAiYWN0aW9ucyI6IFtdLCAiZGV0ZWN0cyI6IFt7ImxldmVsIjogMiwgImNvbm5lY3RvciI6ICJhbmQiLCAiZXhwcmVzc2lvbiI6ICIiLCAidHJpZ2dlcl9jb25maWciOiB7ImNvdW50IjogMSwgInVwdGltZSI6IHsiY2FsZW5kYXJzIjogW10sICJ0aW1lX3JhbmdlcyI6IFt7ImVuZCI6ICIyMzo1OSIsICJzdGFydCI6ICIwMDowMCJ9XX0sICJjaGVja193aW5kb3ciOiA1fSwgInJlY292ZXJ5X2NvbmZpZyI6IHsiY2hlY2tfd2luZG93IjogNSwgInN0YXR1c19zZXR0ZXIiOiAicmVjb3ZlcnkifX0sIHsibGV2ZWwiOiAxLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJvcyIsICJia19iaXpfaWQiOiAyMDA1MDAwMTkxLCAiZGF0YV9zb3VyY2VfdHlwZSI6ICJcdTc2ZDFcdTYzYTdcdTkxYzdcdTk2YzZcdTYzMDdcdTY4MDcifSwgImlzX2VuYWJsZWQiOiB0cnVlfQ== \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.influxdb.5947.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.influxdb.5947.tpl64 deleted file mode 100644 index 928a70650f..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.influxdb.5947.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU5NDcsICJuYW1lIjogIkluZmx1eERCIFx1NzhjMVx1NzZkOFx1N2E3YVx1OTVmNFx1NTQ0YVx1OGI2NiIsICJkYl90eXBlIjogImluZmx1eGRiIiwgImRldGFpbHMiOiB7ImFwcCI6ICIiLCAibmFtZSI6ICJJbmZsdXhEQiBcdTc4YzFcdTc2ZDhcdTdhN2FcdTk1ZjRcdTU0NGFcdThiNjYiLCAicGF0aCI6ICIiLCAidHlwZSI6ICJtb25pdG9yIiwgIml0ZW1zIjogW3sibmFtZSI6ICJNQVgoXHU3OGMxXHU3NmQ4XHU3YTdhXHU5NWY0XHU0ZjdmXHU3NTI4XHU3Mzg3KSIsICJ0YXJnZXQiOiBbW3siZmllbGQiOiAiaG9zdF90b3BvX25vZGUiLCAidmFsdWUiOiBbeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MjQ1fV0sICJtZXRob2QiOiAiZXEifV1dLCAiZnVuY3Rpb25zIjogW10sICJhbGdvcml0aG1zIjogW3siaWQiOiA2NTI2LCAidHlwZSI6ICJUaHJlc2hvbGQiLCAibGV2ZWwiOiAyLCAiY29uZmlnIjogW1t7Im1ldGhvZCI6ICJndGUiLCAidGhyZXNob2xkIjogODB9XV0sICJ1bml0X3ByZWZpeCI6ICIlIn0sIHsiaWQiOiA2NTI3LCAidHlwZSI6ICJUaHJlc2hvbGQiLCAibGV2ZWwiOiAxLCAiY29uZmlnIjogW1t7Im1ldGhvZCI6ICJndGUiLCAidGhyZXNob2xkIjogOTB9XV0sICJ1bml0X3ByZWZpeCI6ICIlIn1dLCAiZXhwcmVzc2lvbiI6ICJhIiwgIm9yaWdpbl9zcWwiOiAiIiwgInF1ZXJ5X2NvbmZpZ3MiOiBbeyJpZCI6IDY0OTQsICJuYW1lIjogIlx1NzhjMVx1NzZkOFx1N2E3YVx1OTVmNFx1NGY3Zlx1NzUyOFx1NzM4NyIsICJ1bml0IjogInBlcmNlbnQiLCAiYWxpYXMiOiAiYSIsICJmdW5jdGlvbnMiOiBbXSwgIm1ldHJpY19pZCI6ICJia19tb25pdG9yLnN5c3RlbS5kaXNrLmluX3VzZSIsICJhZ2dfbWV0aG9kIjogIk1BWCIsICJhZ2dfaW50ZXJ2YWwiOiA2MCwgIm1ldHJpY19maWVsZCI6ICJpbl91c2UiLCAiYWdnX2NvbmRpdGlvbiI6IFtdLCAiYWdnX2RpbWVuc2lvbiI6IFsiYmtfdGFyZ2V0X2lwIiwgImJrX3RhcmdldF9jbG91ZF9pZCIsICJtb3VudF9wb2ludCJdLCAiZGF0YV90eXBlX2xhYmVsIjogInRpbWVfc2VyaWVzIiwgInJlc3VsdF90YWJsZV9pZCI6ICJzeXN0ZW0uZGlzayIsICJkYXRhX3NvdXJjZV9sYWJlbCI6ICJia19tb25pdG9yIn1dLCAibm9fZGF0YV9jb25maWciOiB7ImxldmVsIjogMiwgImNvbnRpbnVvdXMiOiAxMCwgImlzX2VuYWJsZWQiOiBmYWxzZSwgImFnZ19kaW1lbnNpb24iOiBbImJrX3RhcmdldF9pcCIsICJia190YXJnZXRfY2xvdWRfaWQiXX19XSwgImxhYmVscyI6IFsiSW5mbHV4REIiLCAiREJNX0lORkxVWERCIiwgIkRCTSJdLCAibm90aWNlIjogeyJjb25maWciOiB7InRlbXBsYXRlIjogW3sic2lnbmFsIjogImFibm9ybWFsIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQuYXNzaWduX2RldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAicmVjb3ZlcmVkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQuYXNzaWduX2RldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAiY2xvc2VkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQuYXNzaWduX2RldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifV0sICJuZWVkX3BvbGwiOiB0cnVlLCAibm90aWZ5X2ludGVydmFsIjogNzIwMCwgImludGVydmFsX25vdGlmeV9tb2RlIjogInN0YW5kYXJkIn0sICJzaWduYWwiOiBbImFibm9ybWFsIiwgIm5vX2RhdGEiXSwgIm9wdGlvbnMiOiB7ImVuZF90aW1lIjogIjIzOjU5OjU5IiwgInN0YXJ0X3RpbWUiOiAiMDA6MDA6MDAiLCAiYXNzaWduX21vZGUiOiBbImJ5X3J1bGUiLCAib25seV9ub3RpY2UiXSwgInVwZ3JhZGVfY29uZmlnIjogeyJpc19lbmFibGVkIjogZmFsc2UsICJ1c2VyX2dyb3VwcyI6IFtdLCAidXBncmFkZV9pbnRlcnZhbCI6IDg2NDAwfSwgImNvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAxLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzdHJhdGVneV9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiZGltZW5zaW9ucyJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifV0sICJ0aW1lZGVsdGEiOiA2MCwgImlzX2VuYWJsZWQiOiB0cnVlLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0IiwgIm5lZWRfYml6X2NvbnZlcmdlIjogdHJ1ZSwgInN1Yl9jb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMiwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifV0sICJ0aW1lZGVsdGEiOiA2MCwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdF9hbGFybSJ9fSwgImV4Y2x1ZGVfbm90aWNlX3dheXMiOiB7ImFjayI6IFtdLCAiY2xvc2VkIjogW10sICJyZWNvdmVyZWQiOiBbXX0sICJub2lzZV9yZWR1Y2VfY29uZmlnIjogeyJ1bml0IjogInBlcmNlbnQiLCAiY291bnQiOiAxMCwgInRpbWVkZWx0YSI6IDUsICJkaW1lbnNpb25zIjogW10sICJpc19lbmFibGVkIjogZmFsc2V9fSwgImNvbmZpZ19pZCI6IDkwMDEsICJyZWxhdGVfdHlwZSI6ICJOT1RJQ0UiLCAidXNlcl9ncm91cHMiOiBbMF19LCAic291cmNlIjogImJrbW9uaXRvcnYzIiwgImFjdGlvbnMiOiBbXSwgImRldGVjdHMiOiBbeyJsZXZlbCI6IDIsICJjb25uZWN0b3IiOiAiYW5kIiwgImV4cHJlc3Npb24iOiAiIiwgInRyaWdnZXJfY29uZmlnIjogeyJjb3VudCI6IDEsICJ1cHRpbWUiOiB7ImNhbGVuZGFycyI6IFtdLCAidGltZV9yYW5nZXMiOiBbeyJlbmQiOiAiMjM6NTkiLCAic3RhcnQiOiAiMDA6MDAifV19LCAiY2hlY2tfd2luZG93IjogNX0sICJyZWNvdmVyeV9jb25maWciOiB7ImNoZWNrX3dpbmRvdyI6IDUsICJzdGF0dXNfc2V0dGVyIjogInJlY292ZXJ5In19LCB7ImxldmVsIjogMSwgImNvbm5lY3RvciI6ICJhbmQiLCAiZXhwcmVzc2lvbiI6ICIiLCAidHJpZ2dlcl9jb25maWciOiB7ImNvdW50IjogMSwgInVwdGltZSI6IHsiY2FsZW5kYXJzIjogW10sICJ0aW1lX3JhbmdlcyI6IFt7ImVuZCI6ICIyMzo1OSIsICJzdGFydCI6ICIwMDowMCJ9XX0sICJjaGVja193aW5kb3ciOiA1fSwgInJlY292ZXJ5X2NvbmZpZyI6IHsiY2hlY2tfd2luZG93IjogNSwgInN0YXR1c19zZXR0ZXIiOiAicmVjb3ZlcnkifX1dLCAic2NlbmFyaW8iOiAib3MiLCAiYmtfYml6X2lkIjogMjAwNTAwMDE5MSwgImRhdGFfc291cmNlX3R5cGUiOiAiXHU3NmQxXHU2M2E3XHU5MWM3XHU5NmM2XHU2MzA3XHU2ODA3In0sICJpc19lbmFibGVkIjogdHJ1ZX0= \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.influxdb.5948.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.influxdb.5948.tpl64 deleted file mode 100644 index 4dea7eab5f..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.influxdb.5948.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU5NDgsICJuYW1lIjogIkluZmx1eERCIFx1NTE5OVx1NTE2NVx1NWYwMlx1NWUzOFx1NTQ0YVx1OGI2NiIsICJkYl90eXBlIjogImluZmx1eGRiIiwgImRldGFpbHMiOiB7ImFwcCI6ICIiLCAibmFtZSI6ICJJbmZsdXhEQiBcdTUxOTlcdTUxNjVcdTVmMDJcdTVlMzhcdTU0NGFcdThiNjYiLCAicGF0aCI6ICIiLCAidHlwZSI6ICJtb25pdG9yIiwgIml0ZW1zIjogW3sibmFtZSI6ICJBVkcoaW5mbHV4ZGJfaHR0cGRfcG9pbnRzV3JpdHRlbkZhaWwpIiwgInRhcmdldCI6IFtbeyJmaWVsZCI6ICJob3N0X3RvcG9fbm9kZSIsICJ2YWx1ZSI6IFt7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgyNDV9XSwgIm1ldGhvZCI6ICJlcSJ9XV0sICJmdW5jdGlvbnMiOiBbXSwgImFsZ29yaXRobXMiOiBbeyJpZCI6IDY1MjgsICJ0eXBlIjogIlRocmVzaG9sZCIsICJsZXZlbCI6IDIsICJjb25maWciOiBbW3sibWV0aG9kIjogImd0ZSIsICJ0aHJlc2hvbGQiOiAxMDAwMH1dXSwgInVuaXRfcHJlZml4IjogIiJ9XSwgImV4cHJlc3Npb24iOiAiYSIsICJvcmlnaW5fc3FsIjogIiIsICJxdWVyeV9jb25maWdzIjogW3siaWQiOiA2NDk1LCAibmFtZSI6ICJpbmZsdXhkYl9odHRwZF9wb2ludHNXcml0dGVuRmFpbCIsICJ1bml0IjogIiIsICJhbGlhcyI6ICJhIiwgImZ1bmN0aW9ucyI6IFt7ImlkIjogInJhdGUiLCAicGFyYW1zIjogW3siaWQiOiAid2luZG93IiwgInZhbHVlIjogIjEwbSJ9XX1dLCAibWV0cmljX2lkIjogImJrX21vbml0b3IucHVzaGdhdGV3YXlfZGJtX2luZmx1eGRiX2JrcHVsbC5ncm91cDYuaW5mbHV4ZGJfaHR0cGRfcG9pbnRzV3JpdHRlbkZhaWwiLCAiYWdnX21ldGhvZCI6ICJBVkciLCAiYWdnX2ludGVydmFsIjogMTIwLCAibWV0cmljX2ZpZWxkIjogImluZmx1eGRiX2h0dHBkX3BvaW50c1dyaXR0ZW5GYWlsIiwgImFnZ19jb25kaXRpb24iOiBbXSwgImFnZ19kaW1lbnNpb24iOiBbImJrX3RhcmdldF9zZXJ2aWNlX2luc3RhbmNlX2lkIiwgImlwIl0sICJkYXRhX3R5cGVfbGFiZWwiOiAidGltZV9zZXJpZXMiLCAicmVzdWx0X3RhYmxlX2lkIjogInB1c2hnYXRld2F5X2RibV9pbmZsdXhkYl9ia3B1bGwuZ3JvdXA2IiwgImRhdGFfc291cmNlX2xhYmVsIjogImJrX21vbml0b3IifV0sICJub19kYXRhX2NvbmZpZyI6IHsibGV2ZWwiOiAyLCAiY29udGludW91cyI6IDEwLCAiaXNfZW5hYmxlZCI6IGZhbHNlLCAiYWdnX2RpbWVuc2lvbiI6IFsiYmtfdGFyZ2V0X3NlcnZpY2VfaW5zdGFuY2VfaWQiXX19XSwgImxhYmVscyI6IFsiSW5mbHV4REIiLCAiREJNX0lORkxVWERCIiwgIkRCTSJdLCAibm90aWNlIjogeyJjb25maWciOiB7InRlbXBsYXRlIjogW3sic2lnbmFsIjogImFibm9ybWFsIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQuYXNzaWduX2RldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAicmVjb3ZlcmVkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQuYXNzaWduX2RldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAiY2xvc2VkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQuYXNzaWduX2RldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifV0sICJuZWVkX3BvbGwiOiB0cnVlLCAibm90aWZ5X2ludGVydmFsIjogNzIwMCwgImludGVydmFsX25vdGlmeV9tb2RlIjogInN0YW5kYXJkIn0sICJzaWduYWwiOiBbImFibm9ybWFsIiwgIm5vX2RhdGEiXSwgIm9wdGlvbnMiOiB7ImVuZF90aW1lIjogIjIzOjU5OjU5IiwgInN0YXJ0X3RpbWUiOiAiMDA6MDA6MDAiLCAiYXNzaWduX21vZGUiOiBbImJ5X3J1bGUiLCAib25seV9ub3RpY2UiXSwgInVwZ3JhZGVfY29uZmlnIjogeyJpc19lbmFibGVkIjogZmFsc2UsICJ1c2VyX2dyb3VwcyI6IFtdLCAidXBncmFkZV9pbnRlcnZhbCI6IDg2NDAwfSwgImNvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAxLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzdHJhdGVneV9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiZGltZW5zaW9ucyJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifV0sICJ0aW1lZGVsdGEiOiA2MCwgImlzX2VuYWJsZWQiOiB0cnVlLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0IiwgIm5lZWRfYml6X2NvbnZlcmdlIjogdHJ1ZSwgInN1Yl9jb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMiwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifV0sICJ0aW1lZGVsdGEiOiA2MCwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdF9hbGFybSJ9fSwgImV4Y2x1ZGVfbm90aWNlX3dheXMiOiB7ImFjayI6IFtdLCAiY2xvc2VkIjogW10sICJyZWNvdmVyZWQiOiBbXX0sICJub2lzZV9yZWR1Y2VfY29uZmlnIjogeyJ1bml0IjogInBlcmNlbnQiLCAiY291bnQiOiAxMCwgInRpbWVkZWx0YSI6IDUsICJkaW1lbnNpb25zIjogW10sICJpc19lbmFibGVkIjogZmFsc2V9fSwgImNvbmZpZ19pZCI6IDkwMDIsICJyZWxhdGVfdHlwZSI6ICJOT1RJQ0UiLCAidXNlcl9ncm91cHMiOiBbMF19LCAic291cmNlIjogImJrbW9uaXRvcnYzIiwgImFjdGlvbnMiOiBbXSwgImRldGVjdHMiOiBbeyJsZXZlbCI6IDIsICJjb25uZWN0b3IiOiAiYW5kIiwgImV4cHJlc3Npb24iOiAiIiwgInRyaWdnZXJfY29uZmlnIjogeyJjb3VudCI6IDEsICJ1cHRpbWUiOiB7ImNhbGVuZGFycyI6IFtdLCAidGltZV9yYW5nZXMiOiBbeyJlbmQiOiAiMjM6NTkiLCAic3RhcnQiOiAiMDA6MDAifV19LCAiY2hlY2tfd2luZG93IjogNX0sICJyZWNvdmVyeV9jb25maWciOiB7ImNoZWNrX3dpbmRvdyI6IDUsICJzdGF0dXNfc2V0dGVyIjogInJlY292ZXJ5In19XSwgInNjZW5hcmlvIjogImNvbXBvbmVudCIsICJia19iaXpfaWQiOiAyMDA1MDAwMTkxLCAiZGF0YV9zb3VyY2VfdHlwZSI6ICJcdTc2ZDFcdTYzYTdcdTkxYzdcdTk2YzZcdTYzMDdcdTY4MDcifSwgImlzX2VuYWJsZWQiOiB0cnVlfQ== \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.influxdb.5949.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.influxdb.5949.tpl64 deleted file mode 100644 index 393cf75be6..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.influxdb.5949.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU5NDksICJuYW1lIjogIkluZmx1eERCIFx1NWI5ZVx1NGY4Ylx1N2FlZlx1NTNlM1x1NWYwMlx1NWUzOCIsICJkYl90eXBlIjogImluZmx1eGRiIiwgImRldGFpbHMiOiB7ImFwcCI6ICIiLCAibmFtZSI6ICJJbmZsdXhEQiBcdTViOWVcdTRmOGJcdTdhZWZcdTUzZTNcdTVmMDJcdTVlMzgiLCAicGF0aCI6ICIiLCAidHlwZSI6ICJtb25pdG9yIiwgIml0ZW1zIjogW3sibmFtZSI6ICJNQVgoaHR0cF9yZXNwb25zZV9yZXN1bHRfY29kZSkiLCAidGFyZ2V0IjogW1t7ImZpZWxkIjogImhvc3RfdG9wb19ub2RlIiwgInZhbHVlIjogW3siYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODI0NX1dLCAibWV0aG9kIjogImVxIn1dXSwgImZ1bmN0aW9ucyI6IFtdLCAiYWxnb3JpdGhtcyI6IFt7ImlkIjogNjUyOSwgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMiwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZXEiLCAidGhyZXNob2xkIjogM31dXSwgInVuaXRfcHJlZml4IjogIiJ9XSwgImV4cHJlc3Npb24iOiAiYSIsICJvcmlnaW5fc3FsIjogIiIsICJxdWVyeV9jb25maWdzIjogW3siaWQiOiA2NDk2LCAibmFtZSI6ICJodHRwX3Jlc3BvbnNlX3Jlc3VsdF9jb2RlIiwgInVuaXQiOiAiIiwgImFsaWFzIjogImEiLCAiZnVuY3Rpb25zIjogW10sICJtZXRyaWNfaWQiOiAiYmtfbW9uaXRvci5wdXNoZ2F0ZXdheV9kYm1faW5mbHV4ZGJfYmtwdWxsLmdyb3VwMi5odHRwX3Jlc3BvbnNlX3Jlc3VsdF9jb2RlIiwgImFnZ19tZXRob2QiOiAiTUFYIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAibWV0cmljX2ZpZWxkIjogImh0dHBfcmVzcG9uc2VfcmVzdWx0X2NvZGUiLCAiYWdnX2NvbmRpdGlvbiI6IFtdLCAiYWdnX2RpbWVuc2lvbiI6IFsiYmtfdGFyZ2V0X3NlcnZpY2VfaW5zdGFuY2VfaWQiLCAiaXAiXSwgImRhdGFfdHlwZV9sYWJlbCI6ICJ0aW1lX3NlcmllcyIsICJyZXN1bHRfdGFibGVfaWQiOiAicHVzaGdhdGV3YXlfZGJtX2luZmx1eGRiX2JrcHVsbC5ncm91cDIiLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAiYmtfbW9uaXRvciJ9XSwgIm5vX2RhdGFfY29uZmlnIjogeyJsZXZlbCI6IDIsICJjb250aW51b3VzIjogMTAsICJpc19lbmFibGVkIjogZmFsc2UsICJhZ2dfZGltZW5zaW9uIjogWyJia190YXJnZXRfc2VydmljZV9pbnN0YW5jZV9pZCJdfX1dLCAibGFiZWxzIjogWyJJbmZsdXhEQiIsICJEQk1fSU5GTFVYREIiLCAiREJNIl0sICJub3RpY2UiOiB7ImNvbmZpZyI6IHsidGVtcGxhdGUiOiBbeyJzaWduYWwiOiAiYWJub3JtYWwiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5hc3NpZ25fZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSA6XHUwMGEwY29ubmVjdGlvbl9mYWlsZWQifSwgeyJzaWduYWwiOiAicmVjb3ZlcmVkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQuYXNzaWduX2RldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAiY2xvc2VkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQuYXNzaWduX2RldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifV0sICJuZWVkX3BvbGwiOiB0cnVlLCAibm90aWZ5X2ludGVydmFsIjogNzIwMCwgImludGVydmFsX25vdGlmeV9tb2RlIjogInN0YW5kYXJkIn0sICJzaWduYWwiOiBbImFibm9ybWFsIiwgIm5vX2RhdGEiXSwgIm9wdGlvbnMiOiB7ImVuZF90aW1lIjogIjIzOjU5OjU5IiwgInN0YXJ0X3RpbWUiOiAiMDA6MDA6MDAiLCAiYXNzaWduX21vZGUiOiBbImJ5X3J1bGUiLCAib25seV9ub3RpY2UiXSwgInVwZ3JhZGVfY29uZmlnIjogeyJpc19lbmFibGVkIjogZmFsc2UsICJ1c2VyX2dyb3VwcyI6IFtdLCAidXBncmFkZV9pbnRlcnZhbCI6IDg2NDAwfSwgImNvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAxLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzdHJhdGVneV9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiZGltZW5zaW9ucyJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifV0sICJ0aW1lZGVsdGEiOiA2MCwgImlzX2VuYWJsZWQiOiB0cnVlLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0IiwgIm5lZWRfYml6X2NvbnZlcmdlIjogdHJ1ZSwgInN1Yl9jb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMiwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifV0sICJ0aW1lZGVsdGEiOiA2MCwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdF9hbGFybSJ9fSwgImV4Y2x1ZGVfbm90aWNlX3dheXMiOiB7ImFjayI6IFtdLCAiY2xvc2VkIjogW10sICJyZWNvdmVyZWQiOiBbXX0sICJub2lzZV9yZWR1Y2VfY29uZmlnIjogeyJ1bml0IjogInBlcmNlbnQiLCAiY291bnQiOiAxMCwgInRpbWVkZWx0YSI6IDUsICJkaW1lbnNpb25zIjogW10sICJpc19lbmFibGVkIjogZmFsc2V9fSwgImNvbmZpZ19pZCI6IDkwMDMsICJyZWxhdGVfdHlwZSI6ICJOT1RJQ0UiLCAidXNlcl9ncm91cHMiOiBbMF19LCAic291cmNlIjogImJrbW9uaXRvcnYzIiwgImFjdGlvbnMiOiBbXSwgImRldGVjdHMiOiBbeyJsZXZlbCI6IDIsICJjb25uZWN0b3IiOiAiYW5kIiwgImV4cHJlc3Npb24iOiAiIiwgInRyaWdnZXJfY29uZmlnIjogeyJjb3VudCI6IDEsICJ1cHRpbWUiOiB7ImNhbGVuZGFycyI6IFtdLCAidGltZV9yYW5nZXMiOiBbeyJlbmQiOiAiMjM6NTkiLCAic3RhcnQiOiAiMDA6MDAifV19LCAiY2hlY2tfd2luZG93IjogNX0sICJyZWNvdmVyeV9jb25maWciOiB7ImNoZWNrX3dpbmRvdyI6IDUsICJzdGF0dXNfc2V0dGVyIjogInJlY292ZXJ5In19XSwgInNjZW5hcmlvIjogImNvbXBvbmVudCIsICJia19iaXpfaWQiOiAyMDA1MDAwMTkxLCAiZGF0YV9zb3VyY2VfdHlwZSI6ICJcdTc2ZDFcdTYzYTdcdTkxYzdcdTk2YzZcdTYzMDdcdTY4MDcifSwgImlzX2VuYWJsZWQiOiB0cnVlfQ== \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.influxdb.5950.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.influxdb.5950.tpl64 deleted file mode 100644 index 8d3204c7e7..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.influxdb.5950.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU5NTAsICJuYW1lIjogIkluZmx1eERCIFNlcmllcyBcdTU4OWVcdTk1N2ZcdTkxY2YiLCAiZGJfdHlwZSI6ICJpbmZsdXhkYiIsICJkZXRhaWxzIjogeyJhcHAiOiAiIiwgIm5hbWUiOiAiSW5mbHV4REIgU2VyaWVzIFx1NTg5ZVx1OTU3Zlx1OTFjZiIsICJwYXRoIjogIiIsICJ0eXBlIjogIm1vbml0b3IiLCAiaXRlbXMiOiBbeyJuYW1lIjogIkFWRyhpbmZsdXhkYl9kYXRhYmFzZV9udW1TZXJpZXMpIiwgInRhcmdldCI6IFtbeyJmaWVsZCI6ICJob3N0X3RvcG9fbm9kZSIsICJ2YWx1ZSI6IFt7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgyNDV9XSwgIm1ldGhvZCI6ICJlcSJ9XV0sICJmdW5jdGlvbnMiOiBbXSwgImFsZ29yaXRobXMiOiBbeyJpZCI6IDY1MzAsICJ0eXBlIjogIlRocmVzaG9sZCIsICJsZXZlbCI6IDIsICJjb25maWciOiBbW3sibWV0aG9kIjogImd0ZSIsICJ0aHJlc2hvbGQiOiAyMDAwMDB9XV0sICJ1bml0X3ByZWZpeCI6ICIifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICIiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNjQ5NywgIm5hbWUiOiAiaW5mbHV4ZGJfZGF0YWJhc2VfbnVtU2VyaWVzIiwgInVuaXQiOiAiIiwgImFsaWFzIjogImEiLCAiZnVuY3Rpb25zIjogW3siaWQiOiAiaW5jcmVhc2UiLCAicGFyYW1zIjogW3siaWQiOiAid2luZG93IiwgInZhbHVlIjogIjEwbSJ9XX1dLCAibWV0cmljX2lkIjogImJrX21vbml0b3IucHVzaGdhdGV3YXlfZGJtX2luZmx1eGRiX2JrcHVsbC5ncm91cDEuaW5mbHV4ZGJfZGF0YWJhc2VfbnVtU2VyaWVzIiwgImFnZ19tZXRob2QiOiAiQVZHIiwgImFnZ19pbnRlcnZhbCI6IDMwMCwgIm1ldHJpY19maWVsZCI6ICJpbmZsdXhkYl9kYXRhYmFzZV9udW1TZXJpZXMiLCAiYWdnX2NvbmRpdGlvbiI6IFtdLCAiYWdnX2RpbWVuc2lvbiI6IFsiYmtfdGFyZ2V0X3NlcnZpY2VfaW5zdGFuY2VfaWQiLCAiX2RhdGFiYXNlIl0sICJkYXRhX3R5cGVfbGFiZWwiOiAidGltZV9zZXJpZXMiLCAicmVzdWx0X3RhYmxlX2lkIjogInB1c2hnYXRld2F5X2RibV9pbmZsdXhkYl9ia3B1bGwuZ3JvdXAxIiwgImRhdGFfc291cmNlX2xhYmVsIjogImJrX21vbml0b3IifV0sICJub19kYXRhX2NvbmZpZyI6IHsibGV2ZWwiOiAyLCAiY29udGludW91cyI6IDEwLCAiaXNfZW5hYmxlZCI6IGZhbHNlLCAiYWdnX2RpbWVuc2lvbiI6IFsiYmtfdGFyZ2V0X3NlcnZpY2VfaW5zdGFuY2VfaWQiXX19XSwgImxhYmVscyI6IFsiSW5mbHV4REIiLCAiREJNX0lORkxVWERCIiwgIkRCTSJdLCAibm90aWNlIjogeyJjb25maWciOiB7InRlbXBsYXRlIjogW3sic2lnbmFsIjogImFibm9ybWFsIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQuYXNzaWduX2RldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAicmVjb3ZlcmVkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQuYXNzaWduX2RldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAiY2xvc2VkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQuYXNzaWduX2RldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifV0sICJuZWVkX3BvbGwiOiB0cnVlLCAibm90aWZ5X2ludGVydmFsIjogNzIwMCwgImludGVydmFsX25vdGlmeV9tb2RlIjogInN0YW5kYXJkIn0sICJzaWduYWwiOiBbImFibm9ybWFsIiwgIm5vX2RhdGEiXSwgIm9wdGlvbnMiOiB7ImVuZF90aW1lIjogIjIzOjU5OjU5IiwgInN0YXJ0X3RpbWUiOiAiMDA6MDA6MDAiLCAiYXNzaWduX21vZGUiOiBbImJ5X3J1bGUiLCAib25seV9ub3RpY2UiXSwgInVwZ3JhZGVfY29uZmlnIjogeyJpc19lbmFibGVkIjogZmFsc2UsICJ1c2VyX2dyb3VwcyI6IFtdLCAidXBncmFkZV9pbnRlcnZhbCI6IDg2NDAwfSwgImNvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAxLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzdHJhdGVneV9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiZGltZW5zaW9ucyJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifV0sICJ0aW1lZGVsdGEiOiA2MCwgImlzX2VuYWJsZWQiOiB0cnVlLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0IiwgIm5lZWRfYml6X2NvbnZlcmdlIjogdHJ1ZSwgInN1Yl9jb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMiwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifV0sICJ0aW1lZGVsdGEiOiA2MCwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdF9hbGFybSJ9fSwgImV4Y2x1ZGVfbm90aWNlX3dheXMiOiB7ImFjayI6IFtdLCAiY2xvc2VkIjogW10sICJyZWNvdmVyZWQiOiBbXX0sICJub2lzZV9yZWR1Y2VfY29uZmlnIjogeyJ1bml0IjogInBlcmNlbnQiLCAiY291bnQiOiAxMCwgInRpbWVkZWx0YSI6IDUsICJkaW1lbnNpb25zIjogW10sICJpc19lbmFibGVkIjogZmFsc2V9fSwgImNvbmZpZ19pZCI6IDkwMDQsICJyZWxhdGVfdHlwZSI6ICJOT1RJQ0UiLCAidXNlcl9ncm91cHMiOiBbMF19LCAic291cmNlIjogImJrbW9uaXRvcnYzIiwgImFjdGlvbnMiOiBbXSwgImRldGVjdHMiOiBbeyJsZXZlbCI6IDIsICJjb25uZWN0b3IiOiAiYW5kIiwgImV4cHJlc3Npb24iOiAiIiwgInRyaWdnZXJfY29uZmlnIjogeyJjb3VudCI6IDEsICJ1cHRpbWUiOiB7ImNhbGVuZGFycyI6IFtdLCAidGltZV9yYW5nZXMiOiBbeyJlbmQiOiAiMjM6NTkiLCAic3RhcnQiOiAiMDA6MDAifV19LCAiY2hlY2tfd2luZG93IjogNX0sICJyZWNvdmVyeV9jb25maWciOiB7ImNoZWNrX3dpbmRvdyI6IDUsICJzdGF0dXNfc2V0dGVyIjogInJlY292ZXJ5In19XSwgInNjZW5hcmlvIjogImNvbXBvbmVudCIsICJia19iaXpfaWQiOiAyMDA1MDAwMTkxLCAiZGF0YV9zb3VyY2VfdHlwZSI6ICJcdTc2ZDFcdTYzYTdcdTkxYzdcdTk2YzZcdTYzMDdcdTY4MDcifSwgImlzX2VuYWJsZWQiOiB0cnVlfQ== \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.influxdb.5951.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.influxdb.5951.tpl64 deleted file mode 100644 index 856edba308..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.influxdb.5951.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU5NTEsICJuYW1lIjogIkluZmx1eERCIFx1NzhjMVx1NzZkOCBJTyBcdTUyMjlcdTc1MjhcdTczODciLCAiZGJfdHlwZSI6ICJpbmZsdXhkYiIsICJkZXRhaWxzIjogeyJhcHAiOiAiIiwgIm5hbWUiOiAiSW5mbHV4REIgXHU3OGMxXHU3NmQ4IElPIFx1NTIyOVx1NzUyOFx1NzM4NyIsICJwYXRoIjogIiIsICJ0eXBlIjogIm1vbml0b3IiLCAiaXRlbXMiOiBbeyJuYW1lIjogIkFWRyhJL09cdTRmN2ZcdTc1MjhcdTczODcpIiwgInRhcmdldCI6IFtbeyJmaWVsZCI6ICJob3N0X3RvcG9fbm9kZSIsICJ2YWx1ZSI6IFt7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgyNDV9XSwgIm1ldGhvZCI6ICJlcSJ9XV0sICJmdW5jdGlvbnMiOiBbXSwgImFsZ29yaXRobXMiOiBbeyJpZCI6IDY1MzEsICJ0eXBlIjogIlRocmVzaG9sZCIsICJsZXZlbCI6IDIsICJjb25maWciOiBbW3sibWV0aG9kIjogImd0ZSIsICJ0aHJlc2hvbGQiOiA4MH1dXSwgInVuaXRfcHJlZml4IjogIiUifSwgeyJpZCI6IDY1MzIsICJ0eXBlIjogIlRocmVzaG9sZCIsICJsZXZlbCI6IDEsICJjb25maWciOiBbW3sibWV0aG9kIjogImd0ZSIsICJ0aHJlc2hvbGQiOiA5OX1dXSwgInVuaXRfcHJlZml4IjogIiUifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICIiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNjQ5OCwgIm5hbWUiOiAiSS9PXHU0ZjdmXHU3NTI4XHU3Mzg3IiwgInVuaXQiOiAicGVyY2VudHVuaXQiLCAiYWxpYXMiOiAiYSIsICJmdW5jdGlvbnMiOiBbXSwgIm1ldHJpY19pZCI6ICJia19tb25pdG9yLnN5c3RlbS5pby51dGlsIiwgImFnZ19tZXRob2QiOiAiQVZHIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAibWV0cmljX2ZpZWxkIjogInV0aWwiLCAiYWdnX2NvbmRpdGlvbiI6IFtdLCAiYWdnX2RpbWVuc2lvbiI6IFsiYmtfdGFyZ2V0X2lwIiwgImJrX3RhcmdldF9jbG91ZF9pZCIsICJkZXZpY2VfbmFtZSJdLCAiZGF0YV90eXBlX2xhYmVsIjogInRpbWVfc2VyaWVzIiwgInJlc3VsdF90YWJsZV9pZCI6ICJzeXN0ZW0uaW8iLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAiYmtfbW9uaXRvciJ9XSwgIm5vX2RhdGFfY29uZmlnIjogeyJsZXZlbCI6IDIsICJjb250aW51b3VzIjogMTAsICJpc19lbmFibGVkIjogZmFsc2UsICJhZ2dfZGltZW5zaW9uIjogWyJia190YXJnZXRfaXAiLCAiYmtfdGFyZ2V0X2Nsb3VkX2lkIl19fV0sICJsYWJlbHMiOiBbIkluZmx1eERCIiwgIkRCTV9JTkZMVVhEQiIsICJEQk0iXSwgIm5vdGljZSI6IHsiY29uZmlnIjogeyJ0ZW1wbGF0ZSI6IFt7InNpZ25hbCI6ICJhYm5vcm1hbCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LmFzc2lnbl9kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In0sIHsic2lnbmFsIjogInJlY292ZXJlZCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LmFzc2lnbl9kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In0sIHsic2lnbmFsIjogImNsb3NlZCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LmFzc2lnbl9kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In1dLCAibmVlZF9wb2xsIjogdHJ1ZSwgIm5vdGlmeV9pbnRlcnZhbCI6IDcyMDAsICJpbnRlcnZhbF9ub3RpZnlfbW9kZSI6ICJzdGFuZGFyZCJ9LCAic2lnbmFsIjogWyJhYm5vcm1hbCIsICJub19kYXRhIl0sICJvcHRpb25zIjogeyJlbmRfdGltZSI6ICIyMzo1OTo1OSIsICJzdGFydF90aW1lIjogIjAwOjAwOjAwIiwgImFzc2lnbl9tb2RlIjogWyJieV9ydWxlIiwgIm9ubHlfbm90aWNlIl0sICJ1cGdyYWRlX2NvbmZpZyI6IHsiaXNfZW5hYmxlZCI6IGZhbHNlLCAidXNlcl9ncm91cHMiOiBbXSwgInVwZ3JhZGVfaW50ZXJ2YWwiOiA4NjQwMH0sICJjb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMSwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic3RyYXRlZ3lfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImRpbWVuc2lvbnMifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In1dLCAidGltZWRlbHRhIjogNjAsICJpc19lbmFibGVkIjogdHJ1ZSwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdCIsICJuZWVkX2Jpel9jb252ZXJnZSI6IHRydWUsICJzdWJfY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDIsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn1dLCAidGltZWRlbHRhIjogNjAsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3RfYWxhcm0ifX0sICJleGNsdWRlX25vdGljZV93YXlzIjogeyJhY2siOiBbXSwgImNsb3NlZCI6IFtdLCAicmVjb3ZlcmVkIjogW119LCAibm9pc2VfcmVkdWNlX2NvbmZpZyI6IHsidW5pdCI6ICJwZXJjZW50IiwgImNvdW50IjogMTAsICJ0aW1lZGVsdGEiOiA1LCAiZGltZW5zaW9ucyI6IFtdLCAiaXNfZW5hYmxlZCI6IGZhbHNlfX0sICJjb25maWdfaWQiOiA5MDA1LCAicmVsYXRlX3R5cGUiOiAiTk9USUNFIiwgInVzZXJfZ3JvdXBzIjogWzBdfSwgInNvdXJjZSI6ICJia21vbml0b3J2MyIsICJhY3Rpb25zIjogW10sICJkZXRlY3RzIjogW3sibGV2ZWwiOiAyLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fSwgeyJsZXZlbCI6IDEsICJjb25uZWN0b3IiOiAiYW5kIiwgImV4cHJlc3Npb24iOiAiIiwgInRyaWdnZXJfY29uZmlnIjogeyJjb3VudCI6IDEsICJ1cHRpbWUiOiB7ImNhbGVuZGFycyI6IFtdLCAidGltZV9yYW5nZXMiOiBbeyJlbmQiOiAiMjM6NTkiLCAic3RhcnQiOiAiMDA6MDAifV19LCAiY2hlY2tfd2luZG93IjogNX0sICJyZWNvdmVyeV9jb25maWciOiB7ImNoZWNrX3dpbmRvdyI6IDUsICJzdGF0dXNfc2V0dGVyIjogInJlY292ZXJ5In19XSwgInNjZW5hcmlvIjogIm9zIiwgImJrX2Jpel9pZCI6IDIwMDUwMDAxOTEsICJkYXRhX3NvdXJjZV90eXBlIjogIlx1NzZkMVx1NjNhN1x1OTFjN1x1OTZjNlx1NjMwN1x1NjgwNyJ9LCAiaXNfZW5hYmxlZCI6IHRydWV9 \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.influxdb.5952.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.influxdb.5952.tpl64 deleted file mode 100644 index 5077f94142..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.influxdb.5952.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU5NTIsICJuYW1lIjogIkluZmx1eERCIENQVSBcdTRmN2ZcdTc1MjhcdTczODdcdTU0NGFcdThiNjYiLCAiZGJfdHlwZSI6ICJpbmZsdXhkYiIsICJkZXRhaWxzIjogeyJhcHAiOiAiIiwgIm5hbWUiOiAiSW5mbHV4REIgQ1BVIFx1NGY3Zlx1NzUyOFx1NzM4N1x1NTQ0YVx1OGI2NiIsICJwYXRoIjogIiIsICJ0eXBlIjogIm1vbml0b3IiLCAiaXRlbXMiOiBbeyJuYW1lIjogIkFWRyhDUFVcdTRmN2ZcdTc1MjhcdTczODcpIiwgInRhcmdldCI6IFtbeyJmaWVsZCI6ICJob3N0X3RvcG9fbm9kZSIsICJ2YWx1ZSI6IFt7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgyNDV9XSwgIm1ldGhvZCI6ICJlcSJ9XV0sICJmdW5jdGlvbnMiOiBbXSwgImFsZ29yaXRobXMiOiBbeyJpZCI6IDY1MzMsICJ0eXBlIjogIlRocmVzaG9sZCIsICJsZXZlbCI6IDMsICJjb25maWciOiBbW3sibWV0aG9kIjogImd0ZSIsICJ0aHJlc2hvbGQiOiA2MH1dXSwgInVuaXRfcHJlZml4IjogIiUifSwgeyJpZCI6IDY1MzQsICJ0eXBlIjogIlRocmVzaG9sZCIsICJsZXZlbCI6IDIsICJjb25maWciOiBbW3sibWV0aG9kIjogImd0ZSIsICJ0aHJlc2hvbGQiOiA4MH1dXSwgInVuaXRfcHJlZml4IjogIiUifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICIiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNjQ5OSwgIm5hbWUiOiAiQ1BVXHU0ZjdmXHU3NTI4XHU3Mzg3IiwgInVuaXQiOiAicGVyY2VudCIsICJhbGlhcyI6ICJhIiwgImZ1bmN0aW9ucyI6IFtdLCAibWV0cmljX2lkIjogImJrX21vbml0b3Iuc3lzdGVtLmNwdV9zdW1tYXJ5LnVzYWdlIiwgImFnZ19tZXRob2QiOiAiQVZHIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAibWV0cmljX2ZpZWxkIjogInVzYWdlIiwgImFnZ19jb25kaXRpb24iOiBbXSwgImFnZ19kaW1lbnNpb24iOiBbImJrX3RhcmdldF9pcCIsICJia190YXJnZXRfY2xvdWRfaWQiXSwgImRhdGFfdHlwZV9sYWJlbCI6ICJ0aW1lX3NlcmllcyIsICJyZXN1bHRfdGFibGVfaWQiOiAic3lzdGVtLmNwdV9zdW1tYXJ5IiwgImRhdGFfc291cmNlX2xhYmVsIjogImJrX21vbml0b3IifV0sICJub19kYXRhX2NvbmZpZyI6IHsibGV2ZWwiOiAyLCAiY29udGludW91cyI6IDEwLCAiaXNfZW5hYmxlZCI6IGZhbHNlLCAiYWdnX2RpbWVuc2lvbiI6IFsiYmtfdGFyZ2V0X2lwIiwgImJrX3RhcmdldF9jbG91ZF9pZCJdfX1dLCAibGFiZWxzIjogWyJJbmZsdXhEQiIsICJEQk1fSU5GTFVYREIiLCAiREJNIl0sICJub3RpY2UiOiB7ImNvbmZpZyI6IHsidGVtcGxhdGUiOiBbeyJzaWduYWwiOiAiYWJub3JtYWwiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5hc3NpZ25fZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJyZWNvdmVyZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5hc3NpZ25fZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJjbG9zZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5hc3NpZ25fZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9XSwgIm5lZWRfcG9sbCI6IHRydWUsICJub3RpZnlfaW50ZXJ2YWwiOiA3MjAwLCAiaW50ZXJ2YWxfbm90aWZ5X21vZGUiOiAic3RhbmRhcmQifSwgInNpZ25hbCI6IFsiYWJub3JtYWwiLCAibm9fZGF0YSJdLCAib3B0aW9ucyI6IHsiZW5kX3RpbWUiOiAiMjM6NTk6NTkiLCAic3RhcnRfdGltZSI6ICIwMDowMDowMCIsICJhc3NpZ25fbW9kZSI6IFsiYnlfcnVsZSIsICJvbmx5X25vdGljZSJdLCAidXBncmFkZV9jb25maWciOiB7ImlzX2VuYWJsZWQiOiBmYWxzZSwgInVzZXJfZ3JvdXBzIjogW10sICJ1cGdyYWRlX2ludGVydmFsIjogODY0MDB9LCAiY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDEsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInN0cmF0ZWd5X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJkaW1lbnNpb25zIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJia19iaXpfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV9yZWNlaXZlciJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3dheSJ9XSwgInRpbWVkZWx0YSI6IDYwLCAiaXNfZW5hYmxlZCI6IHRydWUsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3QiLCAibmVlZF9iaXpfY29udmVyZ2UiOiB0cnVlLCAic3ViX2NvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAyLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJia19iaXpfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV9yZWNlaXZlciJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3dheSJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9XSwgInRpbWVkZWx0YSI6IDYwLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0X2FsYXJtIn19LCAiZXhjbHVkZV9ub3RpY2Vfd2F5cyI6IHsiYWNrIjogW10sICJjbG9zZWQiOiBbXSwgInJlY292ZXJlZCI6IFtdfSwgIm5vaXNlX3JlZHVjZV9jb25maWciOiB7InVuaXQiOiAicGVyY2VudCIsICJjb3VudCI6IDEwLCAidGltZWRlbHRhIjogNSwgImRpbWVuc2lvbnMiOiBbXSwgImlzX2VuYWJsZWQiOiBmYWxzZX19LCAiY29uZmlnX2lkIjogOTAwNiwgInJlbGF0ZV90eXBlIjogIk5PVElDRSIsICJ1c2VyX2dyb3VwcyI6IFswXX0sICJzb3VyY2UiOiAiYmttb25pdG9ydjMiLCAiYWN0aW9ucyI6IFtdLCAiZGV0ZWN0cyI6IFt7ImxldmVsIjogMywgImNvbm5lY3RvciI6ICJhbmQiLCAiZXhwcmVzc2lvbiI6ICIiLCAidHJpZ2dlcl9jb25maWciOiB7ImNvdW50IjogMSwgInVwdGltZSI6IHsiY2FsZW5kYXJzIjogW10sICJ0aW1lX3JhbmdlcyI6IFt7ImVuZCI6ICIyMzo1OSIsICJzdGFydCI6ICIwMDowMCJ9XX0sICJjaGVja193aW5kb3ciOiA1fSwgInJlY292ZXJ5X2NvbmZpZyI6IHsiY2hlY2tfd2luZG93IjogNSwgInN0YXR1c19zZXR0ZXIiOiAicmVjb3ZlcnkifX0sIHsibGV2ZWwiOiAyLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJvcyIsICJia19iaXpfaWQiOiAyMDA1MDAwMTkxLCAiZGF0YV9zb3VyY2VfdHlwZSI6ICJcdTc2ZDFcdTYzYTdcdTkxYzdcdTk2YzZcdTYzMDdcdTY4MDcifSwgImlzX2VuYWJsZWQiOiB0cnVlfQ== \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.kafka.5676.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.kafka.5676.tpl64 deleted file mode 100644 index a04b0bc31a..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.kafka.5676.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU2NzYsICJuYW1lIjogIltLYWZrYV0tXHU0ZTNiXHU2NzNhXHU3OGMxXHU3NmQ4SU9cdTUyMjlcdTc1MjhcdTczODciLCAiZGJfdHlwZSI6ICJrYWZrYSIsICJkZXRhaWxzIjogeyJhcHAiOiAiIiwgIm5hbWUiOiAiW0thZmthXS1cdTRlM2JcdTY3M2FcdTc4YzFcdTc2ZDhJT1x1NTIyOVx1NzUyOFx1NzM4NyIsICJwYXRoIjogIiIsICJ0eXBlIjogIm1vbml0b3IiLCAiaXRlbXMiOiBbeyJuYW1lIjogIkFWRyhJL09cdTRmN2ZcdTc1MjhcdTczODcpIiwgInRhcmdldCI6IFtbeyJmaWVsZCI6ICJob3N0X3RvcG9fbm9kZSIsICJ2YWx1ZSI6IFt7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgxNTV9LCB7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgxNTZ9XSwgIm1ldGhvZCI6ICJlcSJ9XV0sICJmdW5jdGlvbnMiOiBbXSwgImFsZ29yaXRobXMiOiBbeyJpZCI6IDYyMTksICJ0eXBlIjogIlRocmVzaG9sZCIsICJsZXZlbCI6IDEsICJjb25maWciOiBbW3sibWV0aG9kIjogImd0ZSIsICJ0aHJlc2hvbGQiOiA5MH1dXSwgInVuaXRfcHJlZml4IjogIiUifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICIiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNjIxNSwgIm5hbWUiOiAiSS9PXHU0ZjdmXHU3NTI4XHU3Mzg3IiwgInVuaXQiOiAicGVyY2VudHVuaXQiLCAiYWxpYXMiOiAiYSIsICJmdW5jdGlvbnMiOiBbXSwgIm1ldHJpY19pZCI6ICJia19tb25pdG9yLnN5c3RlbS5pby51dGlsIiwgImFnZ19tZXRob2QiOiAiQVZHIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAibWV0cmljX2ZpZWxkIjogInV0aWwiLCAiYWdnX2NvbmRpdGlvbiI6IFtdLCAiYWdnX2RpbWVuc2lvbiI6IFsiYmtfdGFyZ2V0X2lwIiwgImJrX3RhcmdldF9jbG91ZF9pZCIsICJkZXZpY2VfbmFtZSJdLCAiZGF0YV90eXBlX2xhYmVsIjogInRpbWVfc2VyaWVzIiwgInJlc3VsdF90YWJsZV9pZCI6ICJzeXN0ZW0uaW8iLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAiYmtfbW9uaXRvciJ9XSwgIm5vX2RhdGFfY29uZmlnIjogeyJsZXZlbCI6IDIsICJjb250aW51b3VzIjogMTAsICJpc19lbmFibGVkIjogZmFsc2UsICJhZ2dfZGltZW5zaW9uIjogWyJia190YXJnZXRfaXAiLCAiYmtfdGFyZ2V0X2Nsb3VkX2lkIl19fV0sICJsYWJlbHMiOiBbIkthZmthIiwgIkRCTV9LQUZLQSIsICJEQk1fS0FGS0EiLCAiREJNIl0sICJub3RpY2UiOiB7ImNvbmZpZyI6IHsidGVtcGxhdGUiOiBbeyJzaWduYWwiOiAiYWJub3JtYWwiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJyZWNvdmVyZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJjbG9zZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9XSwgIm5lZWRfcG9sbCI6IHRydWUsICJub3RpZnlfaW50ZXJ2YWwiOiA3MjAwLCAiaW50ZXJ2YWxfbm90aWZ5X21vZGUiOiAic3RhbmRhcmQifSwgInNpZ25hbCI6IFsiYWJub3JtYWwiLCAibm9fZGF0YSJdLCAib3B0aW9ucyI6IHsiZW5kX3RpbWUiOiAiMjM6NTk6NTkiLCAic3RhcnRfdGltZSI6ICIwMDowMDowMCIsICJhc3NpZ25fbW9kZSI6IFtdLCAidXBncmFkZV9jb25maWciOiB7fSwgImNvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAxLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzdHJhdGVneV9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiZGltZW5zaW9ucyJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifV0sICJ0aW1lZGVsdGEiOiA2MCwgImlzX2VuYWJsZWQiOiB0cnVlLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0IiwgIm5lZWRfYml6X2NvbnZlcmdlIjogdHJ1ZSwgInN1Yl9jb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMiwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifV0sICJ0aW1lZGVsdGEiOiA2MCwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdF9hbGFybSJ9fSwgImV4Y2x1ZGVfbm90aWNlX3dheXMiOiB7ImNsb3NlZCI6IFtdLCAicmVjb3ZlcmVkIjogW119LCAibm9pc2VfcmVkdWNlX2NvbmZpZyI6IHsidW5pdCI6ICJwZXJjZW50IiwgImNvdW50IjogMTAsICJ0aW1lZGVsdGEiOiA1LCAiZGltZW5zaW9ucyI6IFtdLCAiaXNfZW5hYmxlZCI6IGZhbHNlfX0sICJjb25maWdfaWQiOiA4NzMwLCAicmVsYXRlX3R5cGUiOiAiTk9USUNFIiwgInVzZXJfZ3JvdXBzIjogWzBdfSwgInNvdXJjZSI6ICJia21vbml0b3J2MyIsICJhY3Rpb25zIjogW10sICJkZXRlY3RzIjogW3sibGV2ZWwiOiAxLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJvcyIsICJia19iaXpfaWQiOiAyMDA1MDAwMTk0LCAiZGF0YV9zb3VyY2VfdHlwZSI6ICJcdTc2ZDFcdTYzYTdcdTkxYzdcdTk2YzZcdTYzMDdcdTY4MDcifSwgImlzX2VuYWJsZWQiOiB0cnVlfQ== \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.kafka.5677.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.kafka.5677.tpl64 deleted file mode 100644 index 1e973429e7..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.kafka.5677.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU2NzcsICJuYW1lIjogIltLYWZrYV0tXHU0ZTNiXHU2NzNhXHU3OGMxXHU3NmQ4XHU3YTdhXHU5NWY0XHU0ZjdmXHU3NTI4XHU3Mzg3IiwgImRiX3R5cGUiOiAia2Fma2EiLCAiZGV0YWlscyI6IHsiYXBwIjogIiIsICJuYW1lIjogIltLYWZrYV0tXHU0ZTNiXHU2NzNhXHU3OGMxXHU3NmQ4XHU3YTdhXHU5NWY0XHU0ZjdmXHU3NTI4XHU3Mzg3IiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAiQVZHKFx1NzhjMVx1NzZkOFx1N2E3YVx1OTVmNFx1NGY3Zlx1NzUyOFx1NzM4NykiLCAidGFyZ2V0IjogW1t7ImZpZWxkIjogImhvc3RfdG9wb19ub2RlIiwgInZhbHVlIjogW3siYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1NX0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1Nn1dLCAibWV0aG9kIjogImVxIn1dXSwgImZ1bmN0aW9ucyI6IFtdLCAiYWxnb3JpdGhtcyI6IFt7ImlkIjogNjIyMCwgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMSwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDg1fV1dLCAidW5pdF9wcmVmaXgiOiAiJSJ9XSwgImV4cHJlc3Npb24iOiAiYSIsICJvcmlnaW5fc3FsIjogIiIsICJxdWVyeV9jb25maWdzIjogW3siaWQiOiA2MjE2LCAibmFtZSI6ICJcdTc4YzFcdTc2ZDhcdTdhN2FcdTk1ZjRcdTRmN2ZcdTc1MjhcdTczODciLCAidW5pdCI6ICJwZXJjZW50IiwgImFsaWFzIjogImEiLCAiZnVuY3Rpb25zIjogW10sICJtZXRyaWNfaWQiOiAiYmtfbW9uaXRvci5zeXN0ZW0uZGlzay5pbl91c2UiLCAiYWdnX21ldGhvZCI6ICJBVkciLCAiYWdnX2ludGVydmFsIjogNjAsICJtZXRyaWNfZmllbGQiOiAiaW5fdXNlIiwgImFnZ19jb25kaXRpb24iOiBbXSwgImFnZ19kaW1lbnNpb24iOiBbImJrX3RhcmdldF9pcCIsICJia190YXJnZXRfY2xvdWRfaWQiLCAibW91bnRfcG9pbnQiXSwgImRhdGFfdHlwZV9sYWJlbCI6ICJ0aW1lX3NlcmllcyIsICJyZXN1bHRfdGFibGVfaWQiOiAic3lzdGVtLmRpc2siLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAiYmtfbW9uaXRvciJ9XSwgIm5vX2RhdGFfY29uZmlnIjogeyJsZXZlbCI6IDIsICJjb250aW51b3VzIjogMTAsICJpc19lbmFibGVkIjogZmFsc2UsICJhZ2dfZGltZW5zaW9uIjogWyJia190YXJnZXRfaXAiLCAiYmtfdGFyZ2V0X2Nsb3VkX2lkIl19fV0sICJsYWJlbHMiOiBbIkthZmthIiwgIkRCTV9LQUZLQSIsICJEQk1fS0FGS0EiLCAiREJNIl0sICJub3RpY2UiOiB7ImNvbmZpZyI6IHsidGVtcGxhdGUiOiBbeyJzaWduYWwiOiAiYWJub3JtYWwiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJyZWNvdmVyZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJjbG9zZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9XSwgIm5lZWRfcG9sbCI6IHRydWUsICJub3RpZnlfaW50ZXJ2YWwiOiA3MjAwLCAiaW50ZXJ2YWxfbm90aWZ5X21vZGUiOiAic3RhbmRhcmQifSwgInNpZ25hbCI6IFsiYWJub3JtYWwiLCAibm9fZGF0YSJdLCAib3B0aW9ucyI6IHsiZW5kX3RpbWUiOiAiMjM6NTk6NTkiLCAic3RhcnRfdGltZSI6ICIwMDowMDowMCIsICJhc3NpZ25fbW9kZSI6IFtdLCAidXBncmFkZV9jb25maWciOiB7fSwgImNvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAxLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzdHJhdGVneV9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiZGltZW5zaW9ucyJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifV0sICJ0aW1lZGVsdGEiOiA2MCwgImlzX2VuYWJsZWQiOiB0cnVlLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0IiwgIm5lZWRfYml6X2NvbnZlcmdlIjogdHJ1ZSwgInN1Yl9jb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMiwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifV0sICJ0aW1lZGVsdGEiOiA2MCwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdF9hbGFybSJ9fSwgImV4Y2x1ZGVfbm90aWNlX3dheXMiOiB7ImNsb3NlZCI6IFtdLCAicmVjb3ZlcmVkIjogW119LCAibm9pc2VfcmVkdWNlX2NvbmZpZyI6IHsidW5pdCI6ICJwZXJjZW50IiwgImNvdW50IjogMTAsICJ0aW1lZGVsdGEiOiA1LCAiZGltZW5zaW9ucyI6IFtdLCAiaXNfZW5hYmxlZCI6IGZhbHNlfX0sICJjb25maWdfaWQiOiA4NzMxLCAicmVsYXRlX3R5cGUiOiAiTk9USUNFIiwgInVzZXJfZ3JvdXBzIjogWzBdfSwgInNvdXJjZSI6ICJia21vbml0b3J2MyIsICJhY3Rpb25zIjogW10sICJkZXRlY3RzIjogW3sibGV2ZWwiOiAxLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJvcyIsICJia19iaXpfaWQiOiAyMDA1MDAwMTk0LCAiZGF0YV9zb3VyY2VfdHlwZSI6ICJcdTc2ZDFcdTYzYTdcdTkxYzdcdTk2YzZcdTYzMDdcdTY4MDcifSwgImlzX2VuYWJsZWQiOiB0cnVlfQ== \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.kafka.5678.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.kafka.5678.tpl64 deleted file mode 100644 index 0dfb12443f..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.kafka.5678.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU2NzgsICJuYW1lIjogIltLYWZrYV0tXHU0ZTNiXHU2NzNhXHU3ZjUxXHU3ZWRjXHU1MTY1XHU2ZDQxXHU5MWNmIiwgImRiX3R5cGUiOiAia2Fma2EiLCAiZGV0YWlscyI6IHsiYXBwIjogIiIsICJuYW1lIjogIltLYWZrYV0tXHU0ZTNiXHU2NzNhXHU3ZjUxXHU3ZWRjXHU1MTY1XHU2ZDQxXHU5MWNmIiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAiQVZHKFx1N2Y1MVx1NTM2MVx1NTE2NVx1NmQ0MVx1OTFjZikiLCAidGFyZ2V0IjogW1t7ImZpZWxkIjogImhvc3RfdG9wb19ub2RlIiwgInZhbHVlIjogW3siYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1NX0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1Nn1dLCAibWV0aG9kIjogImVxIn1dXSwgImZ1bmN0aW9ucyI6IFtdLCAiYWxnb3JpdGhtcyI6IFt7ImlkIjogNjIyMSwgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMSwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDI1MH1dXSwgInVuaXRfcHJlZml4IjogIk0ifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICIiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNjIxNywgIm5hbWUiOiAiXHU3ZjUxXHU1MzYxXHU1MTY1XHU2ZDQxXHU5MWNmIiwgInVuaXQiOiAiQnBzIiwgImFsaWFzIjogImEiLCAiZnVuY3Rpb25zIjogW10sICJtZXRyaWNfaWQiOiAiYmtfbW9uaXRvci5zeXN0ZW0ubmV0LnNwZWVkX3JlY3YiLCAiYWdnX21ldGhvZCI6ICJBVkciLCAiYWdnX2ludGVydmFsIjogNjAsICJtZXRyaWNfZmllbGQiOiAic3BlZWRfcmVjdiIsICJhZ2dfY29uZGl0aW9uIjogW10sICJhZ2dfZGltZW5zaW9uIjogWyJia190YXJnZXRfaXAiLCAiYmtfdGFyZ2V0X2Nsb3VkX2lkIiwgImRldmljZV9uYW1lIl0sICJkYXRhX3R5cGVfbGFiZWwiOiAidGltZV9zZXJpZXMiLCAicmVzdWx0X3RhYmxlX2lkIjogInN5c3RlbS5uZXQiLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAiYmtfbW9uaXRvciJ9XSwgIm5vX2RhdGFfY29uZmlnIjogeyJsZXZlbCI6IDIsICJjb250aW51b3VzIjogMTAsICJpc19lbmFibGVkIjogZmFsc2UsICJhZ2dfZGltZW5zaW9uIjogWyJia190YXJnZXRfaXAiLCAiYmtfdGFyZ2V0X2Nsb3VkX2lkIl19fV0sICJsYWJlbHMiOiBbIkthZmthIiwgIkRCTV9LQUZLQSIsICJEQk1fS0FGS0EiLCAiREJNIl0sICJub3RpY2UiOiB7ImNvbmZpZyI6IHsidGVtcGxhdGUiOiBbeyJzaWduYWwiOiAiYWJub3JtYWwiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJyZWNvdmVyZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJjbG9zZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9XSwgIm5lZWRfcG9sbCI6IHRydWUsICJub3RpZnlfaW50ZXJ2YWwiOiA3MjAwLCAiaW50ZXJ2YWxfbm90aWZ5X21vZGUiOiAic3RhbmRhcmQifSwgInNpZ25hbCI6IFsiYWJub3JtYWwiLCAibm9fZGF0YSJdLCAib3B0aW9ucyI6IHsiZW5kX3RpbWUiOiAiMjM6NTk6NTkiLCAic3RhcnRfdGltZSI6ICIwMDowMDowMCIsICJhc3NpZ25fbW9kZSI6IFtdLCAidXBncmFkZV9jb25maWciOiB7fSwgImNvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAxLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzdHJhdGVneV9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiZGltZW5zaW9ucyJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifV0sICJ0aW1lZGVsdGEiOiA2MCwgImlzX2VuYWJsZWQiOiB0cnVlLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0IiwgIm5lZWRfYml6X2NvbnZlcmdlIjogdHJ1ZSwgInN1Yl9jb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMiwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifV0sICJ0aW1lZGVsdGEiOiA2MCwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdF9hbGFybSJ9fSwgImV4Y2x1ZGVfbm90aWNlX3dheXMiOiB7ImNsb3NlZCI6IFtdLCAicmVjb3ZlcmVkIjogW119LCAibm9pc2VfcmVkdWNlX2NvbmZpZyI6IHsidW5pdCI6ICJwZXJjZW50IiwgImNvdW50IjogMTAsICJ0aW1lZGVsdGEiOiA1LCAiZGltZW5zaW9ucyI6IFtdLCAiaXNfZW5hYmxlZCI6IGZhbHNlfX0sICJjb25maWdfaWQiOiA4NzMyLCAicmVsYXRlX3R5cGUiOiAiTk9USUNFIiwgInVzZXJfZ3JvdXBzIjogWzBdfSwgInNvdXJjZSI6ICJia21vbml0b3J2MyIsICJhY3Rpb25zIjogW10sICJkZXRlY3RzIjogW3sibGV2ZWwiOiAxLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJvcyIsICJia19iaXpfaWQiOiAyMDA1MDAwMTk0LCAiZGF0YV9zb3VyY2VfdHlwZSI6ICJcdTc2ZDFcdTYzYTdcdTkxYzdcdTk2YzZcdTYzMDdcdTY4MDcifSwgImlzX2VuYWJsZWQiOiB0cnVlfQ== \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.kafka.5679.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.kafka.5679.tpl64 deleted file mode 100644 index 13910062f2..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.kafka.5679.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU2NzksICJuYW1lIjogIltLYWZrYV0tXHU0ZTNiXHU2NzNhIENQVSBcdTRmN2ZcdTc1MjhcdTczODciLCAiZGJfdHlwZSI6ICJrYWZrYSIsICJkZXRhaWxzIjogeyJhcHAiOiAiIiwgIm5hbWUiOiAiW0thZmthXS1cdTRlM2JcdTY3M2EgQ1BVIFx1NGY3Zlx1NzUyOFx1NzM4NyIsICJwYXRoIjogIiIsICJ0eXBlIjogIm1vbml0b3IiLCAiaXRlbXMiOiBbeyJuYW1lIjogIltLYWZrYV0tXHU0ZTNiXHU2NzNhIENQVSBcdTRmN2ZcdTc1MjhcdTczODciLCAidGFyZ2V0IjogW1t7ImZpZWxkIjogImhvc3RfdG9wb19ub2RlIiwgInZhbHVlIjogW3siYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1NX0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1Nn1dLCAibWV0aG9kIjogImVxIn1dXSwgImZ1bmN0aW9ucyI6IFtdLCAiYWxnb3JpdGhtcyI6IFt7ImlkIjogNjIyMiwgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMSwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDkwfV1dLCAidW5pdF9wcmVmaXgiOiAiIn1dLCAiZXhwcmVzc2lvbiI6ICJhIiwgIm9yaWdpbl9zcWwiOiAiYXZnIGJ5KGJrX3RhcmdldF9jbG91ZF9pZCwgYmtfdGFyZ2V0X2lwKSAoYXZnX292ZXJfdGltZShia21vbml0b3I6c3lzdGVtOmNwdV9zdW1tYXJ5OnVzYWdlWzFtXSkpIiwgInF1ZXJ5X2NvbmZpZ3MiOiBbeyJpZCI6IDYyMTgsICJuYW1lIjogIiIsICJhbGlhcyI6ICJhIiwgInByb21xbCI6ICJhdmcgYnkoYmtfdGFyZ2V0X2Nsb3VkX2lkLCBia190YXJnZXRfaXApIChhdmdfb3Zlcl90aW1lKGJrbW9uaXRvcjpzeXN0ZW06Y3B1X3N1bW1hcnk6dXNhZ2VbMW1dKSkiLCAiZnVuY3Rpb25zIjogW10sICJtZXRyaWNfaWQiOiAiYXZnIGJ5KGJrX3RhcmdldF9jbG91ZF9pZCwgYmtfdGFyZ2V0X2lwKSAoYXZnX292ZXJfdGltZShia21vbml0byIsICJhZ2dfaW50ZXJ2YWwiOiA2MCwgImRhdGFfdHlwZV9sYWJlbCI6ICJ0aW1lX3NlcmllcyIsICJkYXRhX3NvdXJjZV9sYWJlbCI6ICJwcm9tZXRoZXVzIn1dLCAibm9fZGF0YV9jb25maWciOiB7ImxldmVsIjogMiwgImNvbnRpbnVvdXMiOiAxMCwgImlzX2VuYWJsZWQiOiBmYWxzZSwgImFnZ19kaW1lbnNpb24iOiBbXX19XSwgImxhYmVscyI6IFsiS2Fma2EiLCAiREJNX0tBRktBIiwgIkRCTV9LQUZLQSIsICJEQk0iXSwgIm5vdGljZSI6IHsiY29uZmlnIjogeyJ0ZW1wbGF0ZSI6IFt7InNpZ25hbCI6ICJhYm5vcm1hbCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In0sIHsic2lnbmFsIjogInJlY292ZXJlZCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In0sIHsic2lnbmFsIjogImNsb3NlZCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In1dLCAibmVlZF9wb2xsIjogdHJ1ZSwgIm5vdGlmeV9pbnRlcnZhbCI6IDcyMDAsICJpbnRlcnZhbF9ub3RpZnlfbW9kZSI6ICJzdGFuZGFyZCJ9LCAic2lnbmFsIjogWyJhYm5vcm1hbCIsICJub19kYXRhIl0sICJvcHRpb25zIjogeyJlbmRfdGltZSI6ICIyMzo1OTo1OSIsICJzdGFydF90aW1lIjogIjAwOjAwOjAwIiwgImFzc2lnbl9tb2RlIjogW10sICJ1cGdyYWRlX2NvbmZpZyI6IHt9LCAiY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDEsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInN0cmF0ZWd5X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJkaW1lbnNpb25zIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJia19iaXpfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV9yZWNlaXZlciJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3dheSJ9XSwgInRpbWVkZWx0YSI6IDYwLCAiaXNfZW5hYmxlZCI6IHRydWUsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3QiLCAibmVlZF9iaXpfY29udmVyZ2UiOiB0cnVlLCAic3ViX2NvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAyLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJia19iaXpfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV9yZWNlaXZlciJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3dheSJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9XSwgInRpbWVkZWx0YSI6IDYwLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0X2FsYXJtIn19LCAiZXhjbHVkZV9ub3RpY2Vfd2F5cyI6IHsiY2xvc2VkIjogW10sICJyZWNvdmVyZWQiOiBbXX0sICJub2lzZV9yZWR1Y2VfY29uZmlnIjogeyJ1bml0IjogInBlcmNlbnQiLCAiY291bnQiOiAxMCwgInRpbWVkZWx0YSI6IDUsICJkaW1lbnNpb25zIjogW10sICJpc19lbmFibGVkIjogZmFsc2V9fSwgImNvbmZpZ19pZCI6IDg3MzMsICJyZWxhdGVfdHlwZSI6ICJOT1RJQ0UiLCAidXNlcl9ncm91cHMiOiBbMF19LCAic291cmNlIjogImJrbW9uaXRvcnYzIiwgImFjdGlvbnMiOiBbXSwgImRldGVjdHMiOiBbeyJsZXZlbCI6IDEsICJjb25uZWN0b3IiOiAiYW5kIiwgImV4cHJlc3Npb24iOiAiIiwgInRyaWdnZXJfY29uZmlnIjogeyJjb3VudCI6IDEsICJ1cHRpbWUiOiB7ImNhbGVuZGFycyI6IFtdLCAidGltZV9yYW5nZXMiOiBbeyJlbmQiOiAiMjM6NTkiLCAic3RhcnQiOiAiMDA6MDAifV19LCAiY2hlY2tfd2luZG93IjogNX0sICJyZWNvdmVyeV9jb25maWciOiB7ImNoZWNrX3dpbmRvdyI6IDUsICJzdGF0dXNfc2V0dGVyIjogInJlY292ZXJ5In19XSwgInNjZW5hcmlvIjogIm9zIiwgImJrX2Jpel9pZCI6IDIwMDUwMDAxOTQsICJkYXRhX3NvdXJjZV90eXBlIjogIlByb21ldGhldXMifSwgImlzX2VuYWJsZWQiOiB0cnVlfQ== \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.kafka.5685.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.kafka.5685.tpl64 deleted file mode 100644 index 2e66839953..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.kafka.5685.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU2ODUsICJuYW1lIjogIltLYWZrYV0tXHU2ZDg4XHU4ZDM5XHU3ZWM0XHU1ZWY2XHU4ZmRmIiwgImRiX3R5cGUiOiAia2Fma2EiLCAiZGV0YWlscyI6IHsiYXBwIjogIiIsICJuYW1lIjogIltLYWZrYV0tXHU2ZDg4XHU4ZDM5XHU3ZWM0XHU1ZWY2XHU4ZmRmIiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAiW0thZmthXS1cdTZkODhcdThkMzlcdTdlYzRcdTVlZjZcdThmZGYiLCAidGFyZ2V0IjogW1t7ImZpZWxkIjogImhvc3RfdG9wb19ub2RlIiwgInZhbHVlIjogW3siYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1NX0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1Nn1dLCAibWV0aG9kIjogImVxIn1dXSwgImZ1bmN0aW9ucyI6IFtdLCAiYWxnb3JpdGhtcyI6IFt7ImlkIjogNjIyOCwgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMiwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDEwMDAwMH1dXSwgInVuaXRfcHJlZml4IjogIiJ9XSwgImV4cHJlc3Npb24iOiAiYSIsICJvcmlnaW5fc3FsIjogImF2ZyBieShia190YXJnZXRfc2VydmljZV9pbnN0YW5jZV9pZCwgYXBwLCBjbHVzdGVyX25hbWUsIGNvbnN1bWVyZ3JvdXApIChia21vbml0b3I6ZXhwb3J0ZXJfZGJtX2thZmthX2V4cG9ydGVyOmdyb3VwMTprYWZrYV9jb25zdW1lcmdyb3VwX2xhZ19zdW0pIiwgInF1ZXJ5X2NvbmZpZ3MiOiBbeyJpZCI6IDYyMjQsICJuYW1lIjogIiIsICJhbGlhcyI6ICJhIiwgInByb21xbCI6ICJhdmcgYnkoYmtfdGFyZ2V0X3NlcnZpY2VfaW5zdGFuY2VfaWQsIGFwcCwgY2x1c3Rlcl9uYW1lLCBjb25zdW1lcmdyb3VwKSAoYmttb25pdG9yOmV4cG9ydGVyX2RibV9rYWZrYV9leHBvcnRlcjpncm91cDE6a2Fma2FfY29uc3VtZXJncm91cF9sYWdfc3VtKSIsICJmdW5jdGlvbnMiOiBbXSwgIm1ldHJpY19pZCI6ICJhdmcgYnkoYmtfdGFyZ2V0X3NlcnZpY2VfaW5zdGFuY2VfaWQsIGFwcCwgY2x1c3Rlcl9uYW1lLCBjb25zdW1lIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAiZGF0YV90eXBlX2xhYmVsIjogInRpbWVfc2VyaWVzIiwgImRhdGFfc291cmNlX2xhYmVsIjogInByb21ldGhldXMifV0sICJub19kYXRhX2NvbmZpZyI6IHsibGV2ZWwiOiAyLCAiY29udGludW91cyI6IDEwLCAiaXNfZW5hYmxlZCI6IGZhbHNlLCAiYWdnX2RpbWVuc2lvbiI6IFtdfX1dLCAibGFiZWxzIjogWyJLYWZrYSIsICJEQk1fS0FGS0EiLCAiREJNX0tBRktBIiwgIkRCTSJdLCAibm90aWNlIjogeyJjb25maWciOiB7InRlbXBsYXRlIjogW3sic2lnbmFsIjogImFibm9ybWFsIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAicmVjb3ZlcmVkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAiY2xvc2VkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifV0sICJuZWVkX3BvbGwiOiB0cnVlLCAibm90aWZ5X2ludGVydmFsIjogNzIwMCwgImludGVydmFsX25vdGlmeV9tb2RlIjogInN0YW5kYXJkIn0sICJzaWduYWwiOiBbImFibm9ybWFsIiwgIm5vX2RhdGEiXSwgIm9wdGlvbnMiOiB7ImVuZF90aW1lIjogIjIzOjU5OjU5IiwgInN0YXJ0X3RpbWUiOiAiMDA6MDA6MDAiLCAiYXNzaWduX21vZGUiOiBbXSwgInVwZ3JhZGVfY29uZmlnIjoge30sICJjb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMSwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic3RyYXRlZ3lfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImRpbWVuc2lvbnMifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In1dLCAidGltZWRlbHRhIjogNjAsICJpc19lbmFibGVkIjogdHJ1ZSwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdCIsICJuZWVkX2Jpel9jb252ZXJnZSI6IHRydWUsICJzdWJfY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDIsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn1dLCAidGltZWRlbHRhIjogNjAsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3RfYWxhcm0ifX0sICJleGNsdWRlX25vdGljZV93YXlzIjogeyJjbG9zZWQiOiBbXSwgInJlY292ZXJlZCI6IFtdfSwgIm5vaXNlX3JlZHVjZV9jb25maWciOiB7InVuaXQiOiAicGVyY2VudCIsICJjb3VudCI6IDEwLCAidGltZWRlbHRhIjogNSwgImRpbWVuc2lvbnMiOiBbXSwgImlzX2VuYWJsZWQiOiBmYWxzZX19LCAiY29uZmlnX2lkIjogODczOSwgInJlbGF0ZV90eXBlIjogIk5PVElDRSIsICJ1c2VyX2dyb3VwcyI6IFswXX0sICJzb3VyY2UiOiAiYmttb25pdG9ydjMiLCAiYWN0aW9ucyI6IFtdLCAiZGV0ZWN0cyI6IFt7ImxldmVsIjogMiwgImNvbm5lY3RvciI6ICJhbmQiLCAiZXhwcmVzc2lvbiI6ICIiLCAidHJpZ2dlcl9jb25maWciOiB7ImNvdW50IjogMSwgInVwdGltZSI6IHsiY2FsZW5kYXJzIjogW10sICJ0aW1lX3JhbmdlcyI6IFt7ImVuZCI6ICIyMzo1OSIsICJzdGFydCI6ICIwMDowMCJ9XX0sICJjaGVja193aW5kb3ciOiA1fSwgInJlY292ZXJ5X2NvbmZpZyI6IHsiY2hlY2tfd2luZG93IjogNSwgInN0YXR1c19zZXR0ZXIiOiAicmVjb3ZlcnkifX1dLCAic2NlbmFyaW8iOiAiY29tcG9uZW50IiwgImJrX2Jpel9pZCI6IDIwMDUwMDAxOTQsICJkYXRhX3NvdXJjZV90eXBlIjogIlByb21ldGhldXMifSwgImlzX2VuYWJsZWQiOiB0cnVlfQ== \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5614.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5614.tpl64 deleted file mode 100644 index 43b761674c..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5614.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU2MTQsICJuYW1lIjogIk15U1FMIFx1OGZkZVx1NjNhNVx1NjU3MFx1NGY3Zlx1NzUyOFx1NzM4NyIsICJkYl90eXBlIjogIm15c3FsIiwgImRldGFpbHMiOiB7ImFwcCI6ICIiLCAibmFtZSI6ICJNeVNRTCBcdThmZGVcdTYzYTVcdTY1NzBcdTRmN2ZcdTc1MjhcdTczODciLCAicGF0aCI6ICIiLCAidHlwZSI6ICJtb25pdG9yIiwgIml0ZW1zIjogW3sibmFtZSI6ICIxMDAgKiBBVkcobXlzcWxfZ2xvYmFsX3N0YXR1c190aHJlYWRzX2Nvbm5lY3RlZCkgLyBBVkcobXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19tYXhfY29ubmVjdGlvbnMpIiwgInRhcmdldCI6IFtbeyJmaWVsZCI6ICJob3N0X3RvcG9fbm9kZSIsICJ2YWx1ZSI6IFt7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgxNDh9LCB7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgxNDl9LCB7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgxNDl9XSwgIm1ldGhvZCI6ICJlcSJ9XV0sICJmdW5jdGlvbnMiOiBbXSwgImFsZ29yaXRobXMiOiBbeyJpZCI6IDYxNTEsICJ0eXBlIjogIlRocmVzaG9sZCIsICJsZXZlbCI6IDMsICJjb25maWciOiBbW3sibWV0aG9kIjogImd0ZSIsICJ0aHJlc2hvbGQiOiA5NX1dXSwgInVuaXRfcHJlZml4IjogIiJ9LCB7ImlkIjogNjE1MiwgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMiwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDF9XV0sICJ1bml0X3ByZWZpeCI6ICIifV0sICJleHByZXNzaW9uIjogIjEwMCAqIGEgLyBiIiwgIm9yaWdpbl9zcWwiOiAiIiwgInF1ZXJ5X2NvbmZpZ3MiOiBbeyJpZCI6IDYxNDgsICJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfdGhyZWFkc19jb25uZWN0ZWQiLCAidW5pdCI6ICIiLCAiYWxpYXMiOiAiYSIsICJmdW5jdGlvbnMiOiBbXSwgIm1ldHJpY19pZCI6ICJia19tb25pdG9yLmV4cG9ydGVyX2RibV9teXNxbGRfZXhwb3J0ZXIuZ3N0YXR1cy5teXNxbF9nbG9iYWxfc3RhIiwgImFnZ19tZXRob2QiOiAiQVZHIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAibWV0cmljX2ZpZWxkIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfdGhyZWFkc19jb25uZWN0ZWQiLCAiYWdnX2NvbmRpdGlvbiI6IFtdLCAiYWdnX2RpbWVuc2lvbiI6IFsiYmtfdGFyZ2V0X3NlcnZpY2VfaW5zdGFuY2VfaWQiLCAiYXBwIiwgImNsdXN0ZXJfZG9tYWluIiwgImluc3RhbmNlX3JvbGUiLCAiaW5zdGFuY2UiXSwgImRhdGFfdHlwZV9sYWJlbCI6ICJ0aW1lX3NlcmllcyIsICJyZXN1bHRfdGFibGVfaWQiOiAiZXhwb3J0ZXJfZGJtX215c3FsZF9leHBvcnRlci5nc3RhdHVzIiwgImRhdGFfc291cmNlX2xhYmVsIjogImJrX21vbml0b3IifSwgeyJpZCI6IDYxNDksICJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfbWF4X2Nvbm5lY3Rpb25zIiwgInVuaXQiOiAiIiwgImFsaWFzIjogImIiLCAiZnVuY3Rpb25zIjogW10sICJtZXRyaWNfaWQiOiAiYmtfbW9uaXRvci5leHBvcnRlcl9kYm1fbXlzcWxkX2V4cG9ydGVyLmd2YXJzLm15c3FsX2dsb2JhbF92YXJpYSIsICJhZ2dfbWV0aG9kIjogIkFWRyIsICJhZ2dfaW50ZXJ2YWwiOiA2MCwgIm1ldHJpY19maWVsZCI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX21heF9jb25uZWN0aW9ucyIsICJhZ2dfY29uZGl0aW9uIjogW10sICJhZ2dfZGltZW5zaW9uIjogWyJia190YXJnZXRfc2VydmljZV9pbnN0YW5jZV9pZCIsICJhcHAiLCAiY2x1c3Rlcl9kb21haW4iLCAiaW5zdGFuY2Vfcm9sZSIsICJpbnN0YW5jZSJdLCAiZGF0YV90eXBlX2xhYmVsIjogInRpbWVfc2VyaWVzIiwgInJlc3VsdF90YWJsZV9pZCI6ICJleHBvcnRlcl9kYm1fbXlzcWxkX2V4cG9ydGVyLmd2YXJzIiwgImRhdGFfc291cmNlX2xhYmVsIjogImJrX21vbml0b3IifV0sICJub19kYXRhX2NvbmZpZyI6IHsibGV2ZWwiOiAyLCAiY29udGludW91cyI6IDEwLCAiaXNfZW5hYmxlZCI6IGZhbHNlLCAiYWdnX2RpbWVuc2lvbiI6IFsiYmtfdGFyZ2V0X3NlcnZpY2VfaW5zdGFuY2VfaWQiXX19XSwgImxhYmVscyI6IFsiTXlTUUwiLCAiREJNX01ZU1FMIiwgIkRCTV9NWVNRTCIsICJEQk0iXSwgIm5vdGljZSI6IHsiY29uZmlnIjogeyJ0ZW1wbGF0ZSI6IFt7InNpZ25hbCI6ICJhYm5vcm1hbCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX0lXG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJyZWNvdmVyZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJjbG9zZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9XSwgIm5lZWRfcG9sbCI6IHRydWUsICJub3RpZnlfaW50ZXJ2YWwiOiA3MjAwLCAiaW50ZXJ2YWxfbm90aWZ5X21vZGUiOiAic3RhbmRhcmQifSwgInNpZ25hbCI6IFsiYWJub3JtYWwiLCAibm9fZGF0YSJdLCAib3B0aW9ucyI6IHsiZW5kX3RpbWUiOiAiMjM6NTk6NTkiLCAic3RhcnRfdGltZSI6ICIwMDowMDowMCIsICJhc3NpZ25fbW9kZSI6IFtdLCAidXBncmFkZV9jb25maWciOiB7fSwgImNvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAxLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzdHJhdGVneV9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiZGltZW5zaW9ucyJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifV0sICJ0aW1lZGVsdGEiOiA2MCwgImlzX2VuYWJsZWQiOiB0cnVlLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0IiwgIm5lZWRfYml6X2NvbnZlcmdlIjogdHJ1ZSwgInN1Yl9jb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMiwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifV0sICJ0aW1lZGVsdGEiOiA2MCwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdF9hbGFybSJ9fSwgImV4Y2x1ZGVfbm90aWNlX3dheXMiOiB7ImNsb3NlZCI6IFtdLCAicmVjb3ZlcmVkIjogW119LCAibm9pc2VfcmVkdWNlX2NvbmZpZyI6IHsidW5pdCI6ICJwZXJjZW50IiwgImNvdW50IjogMTAsICJ0aW1lZGVsdGEiOiA1LCAiZGltZW5zaW9ucyI6IFtdLCAiaXNfZW5hYmxlZCI6IGZhbHNlfX0sICJjb25maWdfaWQiOiA4NjY4LCAicmVsYXRlX3R5cGUiOiAiTk9USUNFIiwgInVzZXJfZ3JvdXBzIjogWzBdfSwgInNvdXJjZSI6ICJia21vbml0b3J2MyIsICJhY3Rpb25zIjogW10sICJkZXRlY3RzIjogW3sibGV2ZWwiOiAzLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiAyLCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fSwgeyJsZXZlbCI6IDIsICJjb25uZWN0b3IiOiAiYW5kIiwgImV4cHJlc3Npb24iOiAiIiwgInRyaWdnZXJfY29uZmlnIjogeyJjb3VudCI6IDEsICJ1cHRpbWUiOiB7ImNhbGVuZGFycyI6IFtdLCAidGltZV9yYW5nZXMiOiBbeyJlbmQiOiAiMjM6NTkiLCAic3RhcnQiOiAiMDA6MDAifV19LCAiY2hlY2tfd2luZG93IjogNX0sICJyZWNvdmVyeV9jb25maWciOiB7ImNoZWNrX3dpbmRvdyI6IDIsICJzdGF0dXNfc2V0dGVyIjogInJlY292ZXJ5In19XSwgInNjZW5hcmlvIjogImNvbXBvbmVudCIsICJia19iaXpfaWQiOiAyMDA1MDAwMTk0LCAiZGF0YV9zb3VyY2VfdHlwZSI6ICJcdTc2ZDFcdTYzYTdcdTkxYzdcdTk2YzZcdTYzMDdcdTY4MDcifSwgImlzX2VuYWJsZWQiOiB0cnVlfQ== \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5621.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5621.tpl64 deleted file mode 100644 index 7d9a74b517..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5621.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU2MjEsICJuYW1lIjogIk15U1FMIFx1NWI5ZVx1NGY4YiBUaHJlYWRzX3J1bm5pbmciLCAiZGJfdHlwZSI6ICJteXNxbCIsICJkZXRhaWxzIjogeyJhcHAiOiAiIiwgIm5hbWUiOiAiTXlTUUwgXHU1YjllXHU0ZjhiIFRocmVhZHNfcnVubmluZyIsICJwYXRoIjogIiIsICJ0eXBlIjogIm1vbml0b3IiLCAiaXRlbXMiOiBbeyJuYW1lIjogIkFWRyhteXNxbF9nbG9iYWxfc3RhdHVzX3RocmVhZHNfcnVubmluZykiLCAidGFyZ2V0IjogW1t7ImZpZWxkIjogImhvc3RfdG9wb19ub2RlIiwgInZhbHVlIjogW3siYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE0OH0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE0OX0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE0OX1dLCAibWV0aG9kIjogImVxIn1dXSwgImZ1bmN0aW9ucyI6IFtdLCAiYWxnb3JpdGhtcyI6IFt7ImlkIjogNjE1OSwgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMiwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDUwfV1dLCAidW5pdF9wcmVmaXgiOiAiIn0sIHsiaWQiOiA2MTYwLCAidHlwZSI6ICJUaHJlc2hvbGQiLCAibGV2ZWwiOiAxLCAiY29uZmlnIjogW1t7Im1ldGhvZCI6ICJndGUiLCAidGhyZXNob2xkIjogNTAwfV1dLCAidW5pdF9wcmVmaXgiOiAiIn1dLCAiZXhwcmVzc2lvbiI6ICJhIiwgIm9yaWdpbl9zcWwiOiAiIiwgInF1ZXJ5X2NvbmZpZ3MiOiBbeyJpZCI6IDYxNTYsICJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfdGhyZWFkc19ydW5uaW5nIiwgInVuaXQiOiAiIiwgImFsaWFzIjogImEiLCAiZnVuY3Rpb25zIjogW10sICJtZXRyaWNfaWQiOiAiYmtfbW9uaXRvci5leHBvcnRlcl9kYm1fbXlzcWxkX2V4cG9ydGVyLmdzdGF0dXMubXlzcWxfZ2xvYmFsX3N0YSIsICJhZ2dfbWV0aG9kIjogIkFWRyIsICJhZ2dfaW50ZXJ2YWwiOiA2MCwgIm1ldHJpY19maWVsZCI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX3RocmVhZHNfcnVubmluZyIsICJhZ2dfY29uZGl0aW9uIjogW10sICJhZ2dfZGltZW5zaW9uIjogWyJia190YXJnZXRfc2VydmljZV9pbnN0YW5jZV9pZCIsICJhcHAiLCAiY2x1c3Rlcl9kb21haW4iLCAiaW5zdGFuY2Vfcm9sZSIsICJpbnN0YW5jZSJdLCAiZGF0YV90eXBlX2xhYmVsIjogInRpbWVfc2VyaWVzIiwgInJlc3VsdF90YWJsZV9pZCI6ICJleHBvcnRlcl9kYm1fbXlzcWxkX2V4cG9ydGVyLmdzdGF0dXMiLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAiYmtfbW9uaXRvciJ9XSwgIm5vX2RhdGFfY29uZmlnIjogeyJsZXZlbCI6IDIsICJjb250aW51b3VzIjogMTAsICJpc19lbmFibGVkIjogZmFsc2UsICJhZ2dfZGltZW5zaW9uIjogWyJia190YXJnZXRfc2VydmljZV9pbnN0YW5jZV9pZCJdfX1dLCAibGFiZWxzIjogWyJNeVNRTCIsICJEQk1fTVlTUUwiLCAiREJNX01ZU1FMIiwgIkRCTSJdLCAibm90aWNlIjogeyJjb25maWciOiB7InRlbXBsYXRlIjogW3sic2lnbmFsIjogImFibm9ybWFsIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAicmVjb3ZlcmVkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAiY2xvc2VkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifV0sICJuZWVkX3BvbGwiOiB0cnVlLCAibm90aWZ5X2ludGVydmFsIjogNzIwMCwgImludGVydmFsX25vdGlmeV9tb2RlIjogInN0YW5kYXJkIn0sICJzaWduYWwiOiBbImFibm9ybWFsIiwgIm5vX2RhdGEiXSwgIm9wdGlvbnMiOiB7ImVuZF90aW1lIjogIjIzOjU5OjU5IiwgInN0YXJ0X3RpbWUiOiAiMDA6MDA6MDAiLCAiYXNzaWduX21vZGUiOiBbXSwgInVwZ3JhZGVfY29uZmlnIjoge30sICJjb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMSwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic3RyYXRlZ3lfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImRpbWVuc2lvbnMifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In1dLCAidGltZWRlbHRhIjogNjAsICJpc19lbmFibGVkIjogdHJ1ZSwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdCIsICJuZWVkX2Jpel9jb252ZXJnZSI6IHRydWUsICJzdWJfY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDIsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn1dLCAidGltZWRlbHRhIjogNjAsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3RfYWxhcm0ifX0sICJleGNsdWRlX25vdGljZV93YXlzIjogeyJjbG9zZWQiOiBbXSwgInJlY292ZXJlZCI6IFtdfSwgIm5vaXNlX3JlZHVjZV9jb25maWciOiB7InVuaXQiOiAicGVyY2VudCIsICJjb3VudCI6IDEwLCAidGltZWRlbHRhIjogNSwgImRpbWVuc2lvbnMiOiBbImFwcCIsICJjbHVzdGVyX2RvbWFpbiIsICJpbnN0YW5jZSIsICJpbnN0YW5jZV9yb2xlIiwgImJrX3RhcmdldF9zZXJ2aWNlX2luc3RhbmNlX2lkIl0sICJpc19lbmFibGVkIjogZmFsc2V9fSwgImNvbmZpZ19pZCI6IDg2NzUsICJyZWxhdGVfdHlwZSI6ICJOT1RJQ0UiLCAidXNlcl9ncm91cHMiOiBbMF19LCAic291cmNlIjogImJrbW9uaXRvcnYzIiwgImFjdGlvbnMiOiBbXSwgImRldGVjdHMiOiBbeyJsZXZlbCI6IDIsICJjb25uZWN0b3IiOiAiYW5kIiwgImV4cHJlc3Npb24iOiAiIiwgInRyaWdnZXJfY29uZmlnIjogeyJjb3VudCI6IDEsICJ1cHRpbWUiOiB7ImNhbGVuZGFycyI6IFtdLCAidGltZV9yYW5nZXMiOiBbeyJlbmQiOiAiMjM6NTkiLCAic3RhcnQiOiAiMDA6MDAifV19LCAiY2hlY2tfd2luZG93IjogNX0sICJyZWNvdmVyeV9jb25maWciOiB7ImNoZWNrX3dpbmRvdyI6IDUsICJzdGF0dXNfc2V0dGVyIjogInJlY292ZXJ5In19LCB7ImxldmVsIjogMSwgImNvbm5lY3RvciI6ICJhbmQiLCAiZXhwcmVzc2lvbiI6ICIiLCAidHJpZ2dlcl9jb25maWciOiB7ImNvdW50IjogMSwgInVwdGltZSI6IHsiY2FsZW5kYXJzIjogW10sICJ0aW1lX3JhbmdlcyI6IFt7ImVuZCI6ICIyMzo1OSIsICJzdGFydCI6ICIwMDowMCJ9XX0sICJjaGVja193aW5kb3ciOiA1fSwgInJlY292ZXJ5X2NvbmZpZyI6IHsiY2hlY2tfd2luZG93IjogNSwgInN0YXR1c19zZXR0ZXIiOiAicmVjb3ZlcnkifX1dLCAic2NlbmFyaW8iOiAiY29tcG9uZW50IiwgImJrX2Jpel9pZCI6IDIwMDUwMDAxOTQsICJkYXRhX3NvdXJjZV90eXBlIjogIlx1NzZkMVx1NjNhN1x1OTFjN1x1OTZjNlx1NjMwN1x1NjgwNyJ9LCAiaXNfZW5hYmxlZCI6IHRydWV9 \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5623.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5623.tpl64 deleted file mode 100644 index 8091d113ba..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5623.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU2MjMsICJuYW1lIjogIk15U1FMIFx1NGUzYlx1NjczYSBDUFUgXHU4ZDFmXHU4ZjdkIiwgImRiX3R5cGUiOiAibXlzcWwiLCAiZGV0YWlscyI6IHsiYXBwIjogIiIsICJuYW1lIjogIk15U1FMIFx1NGUzYlx1NjczYSBDUFUgXHU4ZDFmXHU4ZjdkIiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAiQVZHKENQVSB1c2FnZSkiLCAidGFyZ2V0IjogW1t7ImZpZWxkIjogImhvc3RfdG9wb19ub2RlIiwgInZhbHVlIjogW3siYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE0OH0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE0OX0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE0OX1dLCAibWV0aG9kIjogImVxIn1dXSwgImZ1bmN0aW9ucyI6IFtdLCAiYWxnb3JpdGhtcyI6IFt7ImlkIjogNjE2MywgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMywgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDkwfV1dLCAidW5pdF9wcmVmaXgiOiAiJSJ9LCB7ImlkIjogNjE2NCwgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMiwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDgwfV1dLCAidW5pdF9wcmVmaXgiOiAiJSJ9XSwgImV4cHJlc3Npb24iOiAiYSIsICJvcmlnaW5fc3FsIjogIiIsICJxdWVyeV9jb25maWdzIjogW3siaWQiOiA2MTU4LCAibmFtZSI6ICJDUFVcdTRmN2ZcdTc1MjhcdTczODciLCAidW5pdCI6ICJwZXJjZW50IiwgImFsaWFzIjogImEiLCAiZnVuY3Rpb25zIjogW10sICJtZXRyaWNfaWQiOiAiYmtfbW9uaXRvci5kYm1fc3lzdGVtLmNwdV9zdW1tYXJ5LnVzYWdlIiwgImFnZ19tZXRob2QiOiAiQVZHIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAibWV0cmljX2ZpZWxkIjogInVzYWdlIiwgImFnZ19jb25kaXRpb24iOiBbXSwgImFnZ19kaW1lbnNpb24iOiBbImJrX3RhcmdldF9jbG91ZF9pZCIsICJia190YXJnZXRfaXAiLCAiaW5zdGFuY2Vfcm9sZSIsICJhcHAiLCAiY2x1c3Rlcl9kb21haW4iXSwgImRhdGFfdHlwZV9sYWJlbCI6ICJ0aW1lX3NlcmllcyIsICJyZXN1bHRfdGFibGVfaWQiOiAiZGJtX3N5c3RlbS5jcHVfc3VtbWFyeSIsICJkYXRhX3NvdXJjZV9sYWJlbCI6ICJia19tb25pdG9yIn1dLCAibm9fZGF0YV9jb25maWciOiB7ImxldmVsIjogMiwgImNvbnRpbnVvdXMiOiAxMCwgImlzX2VuYWJsZWQiOiBmYWxzZSwgImFnZ19kaW1lbnNpb24iOiBbImJrX3RhcmdldF9pcCIsICJia190YXJnZXRfY2xvdWRfaWQiXX19XSwgImxhYmVscyI6IFsiTXlTUUwiLCAiREJNX01ZU1FMIiwgIkRCTV9NWVNRTCIsICJEQk0iXSwgIm5vdGljZSI6IHsiY29uZmlnIjogeyJ0ZW1wbGF0ZSI6IFt7InNpZ25hbCI6ICJhYm5vcm1hbCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In0sIHsic2lnbmFsIjogInJlY292ZXJlZCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In0sIHsic2lnbmFsIjogImNsb3NlZCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In1dLCAibmVlZF9wb2xsIjogdHJ1ZSwgIm5vdGlmeV9pbnRlcnZhbCI6IDcyMDAsICJpbnRlcnZhbF9ub3RpZnlfbW9kZSI6ICJzdGFuZGFyZCJ9LCAic2lnbmFsIjogWyJhYm5vcm1hbCIsICJub19kYXRhIl0sICJvcHRpb25zIjogeyJlbmRfdGltZSI6ICIyMzo1OTo1OSIsICJzdGFydF90aW1lIjogIjAwOjAwOjAwIiwgImFzc2lnbl9tb2RlIjogW10sICJ1cGdyYWRlX2NvbmZpZyI6IHt9LCAiY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDEsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInN0cmF0ZWd5X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJkaW1lbnNpb25zIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJia19iaXpfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV9yZWNlaXZlciJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3dheSJ9XSwgInRpbWVkZWx0YSI6IDYwLCAiaXNfZW5hYmxlZCI6IHRydWUsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3QiLCAibmVlZF9iaXpfY29udmVyZ2UiOiB0cnVlLCAic3ViX2NvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAyLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJia19iaXpfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV9yZWNlaXZlciJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3dheSJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9XSwgInRpbWVkZWx0YSI6IDYwLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0X2FsYXJtIn19LCAiZXhjbHVkZV9ub3RpY2Vfd2F5cyI6IHsiY2xvc2VkIjogW10sICJyZWNvdmVyZWQiOiBbXX0sICJub2lzZV9yZWR1Y2VfY29uZmlnIjogeyJ1bml0IjogInBlcmNlbnQiLCAiY291bnQiOiAxMCwgInRpbWVkZWx0YSI6IDUsICJkaW1lbnNpb25zIjogW10sICJpc19lbmFibGVkIjogZmFsc2V9fSwgImNvbmZpZ19pZCI6IDg2NzcsICJyZWxhdGVfdHlwZSI6ICJOT1RJQ0UiLCAidXNlcl9ncm91cHMiOiBbMF19LCAic291cmNlIjogImJrbW9uaXRvcnYzIiwgImFjdGlvbnMiOiBbXSwgImRldGVjdHMiOiBbeyJsZXZlbCI6IDMsICJjb25uZWN0b3IiOiAiYW5kIiwgImV4cHJlc3Npb24iOiAiIiwgInRyaWdnZXJfY29uZmlnIjogeyJjb3VudCI6IDEsICJ1cHRpbWUiOiB7ImNhbGVuZGFycyI6IFtdLCAidGltZV9yYW5nZXMiOiBbeyJlbmQiOiAiMjM6NTkiLCAic3RhcnQiOiAiMDA6MDAifV19LCAiY2hlY2tfd2luZG93IjogNX0sICJyZWNvdmVyeV9jb25maWciOiB7ImNoZWNrX3dpbmRvdyI6IDUsICJzdGF0dXNfc2V0dGVyIjogInJlY292ZXJ5In19LCB7ImxldmVsIjogMiwgImNvbm5lY3RvciI6ICJhbmQiLCAiZXhwcmVzc2lvbiI6ICIiLCAidHJpZ2dlcl9jb25maWciOiB7ImNvdW50IjogMSwgInVwdGltZSI6IHsiY2FsZW5kYXJzIjogW10sICJ0aW1lX3JhbmdlcyI6IFt7ImVuZCI6ICIyMzo1OSIsICJzdGFydCI6ICIwMDowMCJ9XX0sICJjaGVja193aW5kb3ciOiA1fSwgInJlY292ZXJ5X2NvbmZpZyI6IHsiY2hlY2tfd2luZG93IjogNSwgInN0YXR1c19zZXR0ZXIiOiAicmVjb3ZlcnkifX1dLCAic2NlbmFyaW8iOiAib3MiLCAiYmtfYml6X2lkIjogMjAwNTAwMDE5NCwgImRhdGFfc291cmNlX3R5cGUiOiAiXHU3NmQxXHU2M2E3XHU5MWM3XHU5NmM2XHU2MzA3XHU2ODA3In0sICJpc19lbmFibGVkIjogdHJ1ZX0= \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5624.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5624.tpl64 deleted file mode 100644 index 6a3a713c6e..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5624.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU2MjQsICJuYW1lIjogIk15U1FMIFx1NGUzYlx1NjczYVx1N2Y1MVx1N2VkY1x1NTFmYVx1NmQ0MVx1OTFjZiIsICJkYl90eXBlIjogIm15c3FsIiwgImRldGFpbHMiOiB7ImFwcCI6ICIiLCAibmFtZSI6ICJNeVNRTCBcdTRlM2JcdTY3M2FcdTdmNTFcdTdlZGNcdTUxZmFcdTZkNDFcdTkxY2YiLCAicGF0aCI6ICIiLCAidHlwZSI6ICJtb25pdG9yIiwgIml0ZW1zIjogW3sibmFtZSI6ICJBVkcoXHU3ZjUxXHU1MzYxXHU1MWZhXHU2ZDQxXHU5MWNmKSIsICJ0YXJnZXQiOiBbW3siZmllbGQiOiAiaG9zdF90b3BvX25vZGUiLCAidmFsdWUiOiBbeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ4fSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ5fSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ5fV0sICJtZXRob2QiOiAiZXEifV1dLCAiZnVuY3Rpb25zIjogW10sICJhbGdvcml0aG1zIjogW3siaWQiOiA2MTY1LCAidHlwZSI6ICJUaHJlc2hvbGQiLCAibGV2ZWwiOiAyLCAiY29uZmlnIjogW1t7Im1ldGhvZCI6ICJndGUiLCAidGhyZXNob2xkIjogMTAwMDAwMDAwMH1dXSwgInVuaXRfcHJlZml4IjogIiJ9XSwgImV4cHJlc3Npb24iOiAiYSIsICJvcmlnaW5fc3FsIjogIiIsICJxdWVyeV9jb25maWdzIjogW3siaWQiOiA2MTU5LCAibmFtZSI6ICJcdTdmNTFcdTUzNjFcdTUxZmFcdTZkNDFcdTkxY2YiLCAidW5pdCI6ICIiLCAiYWxpYXMiOiAiYSIsICJmdW5jdGlvbnMiOiBbeyJpZCI6ICJyYXRlIiwgInBhcmFtcyI6IFt7ImlkIjogIndpbmRvdyIsICJ2YWx1ZSI6ICIybSJ9XX1dLCAibWV0cmljX2lkIjogImJrX21vbml0b3IuZGJtX3N5c3RlbS5uZXQuc3BlZWRfc2VudCIsICJhZ2dfbWV0aG9kIjogIkFWRyIsICJhZ2dfaW50ZXJ2YWwiOiA2MCwgIm1ldHJpY19maWVsZCI6ICJzcGVlZF9zZW50IiwgImFnZ19jb25kaXRpb24iOiBbXSwgImFnZ19kaW1lbnNpb24iOiBbImJrX3RhcmdldF9jbG91ZF9pZCIsICJia190YXJnZXRfaXAiLCAiaW5zdGFuY2Vfcm9sZSIsICJhcHAiLCAiZGV2aWNlX25hbWUiLCAiY2x1c3Rlcl9kb21haW4iXSwgImRhdGFfdHlwZV9sYWJlbCI6ICJ0aW1lX3NlcmllcyIsICJyZXN1bHRfdGFibGVfaWQiOiAiZGJtX3N5c3RlbS5uZXQiLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAiYmtfbW9uaXRvciJ9XSwgIm5vX2RhdGFfY29uZmlnIjogeyJsZXZlbCI6IDIsICJjb250aW51b3VzIjogMTAsICJpc19lbmFibGVkIjogZmFsc2UsICJhZ2dfZGltZW5zaW9uIjogWyJia190YXJnZXRfaXAiLCAiYmtfdGFyZ2V0X2Nsb3VkX2lkIl19fV0sICJsYWJlbHMiOiBbIk15U1FMIiwgIkRCTV9NWVNRTCIsICJEQk1fTVlTUUwiLCAiREJNIl0sICJub3RpY2UiOiB7ImNvbmZpZyI6IHsidGVtcGxhdGUiOiBbeyJzaWduYWwiOiAiYWJub3JtYWwiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJyZWNvdmVyZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJjbG9zZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9XSwgIm5lZWRfcG9sbCI6IHRydWUsICJub3RpZnlfaW50ZXJ2YWwiOiA3MjAwLCAiaW50ZXJ2YWxfbm90aWZ5X21vZGUiOiAic3RhbmRhcmQifSwgInNpZ25hbCI6IFsiYWJub3JtYWwiLCAibm9fZGF0YSJdLCAib3B0aW9ucyI6IHsiZW5kX3RpbWUiOiAiMjM6NTk6NTkiLCAic3RhcnRfdGltZSI6ICIwMDowMDowMCIsICJhc3NpZ25fbW9kZSI6IFtdLCAidXBncmFkZV9jb25maWciOiB7fSwgImNvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAxLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzdHJhdGVneV9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiZGltZW5zaW9ucyJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifV0sICJ0aW1lZGVsdGEiOiA2MCwgImlzX2VuYWJsZWQiOiB0cnVlLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0IiwgIm5lZWRfYml6X2NvbnZlcmdlIjogdHJ1ZSwgInN1Yl9jb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMiwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifV0sICJ0aW1lZGVsdGEiOiA2MCwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdF9hbGFybSJ9fSwgImV4Y2x1ZGVfbm90aWNlX3dheXMiOiB7ImNsb3NlZCI6IFtdLCAicmVjb3ZlcmVkIjogW119LCAibm9pc2VfcmVkdWNlX2NvbmZpZyI6IHsidW5pdCI6ICJwZXJjZW50IiwgImNvdW50IjogMTAsICJ0aW1lZGVsdGEiOiA1LCAiZGltZW5zaW9ucyI6IFtdLCAiaXNfZW5hYmxlZCI6IGZhbHNlfX0sICJjb25maWdfaWQiOiA4Njc4LCAicmVsYXRlX3R5cGUiOiAiTk9USUNFIiwgInVzZXJfZ3JvdXBzIjogWzBdfSwgInNvdXJjZSI6ICJia21vbml0b3J2MyIsICJhY3Rpb25zIjogW10sICJkZXRlY3RzIjogW3sibGV2ZWwiOiAyLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJvcyIsICJia19iaXpfaWQiOiAyMDA1MDAwMTk0LCAiZGF0YV9zb3VyY2VfdHlwZSI6ICJcdTc2ZDFcdTYzYTdcdTkxYzdcdTk2YzZcdTYzMDdcdTY4MDcifSwgImlzX2VuYWJsZWQiOiB0cnVlfQ== \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5625.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5625.tpl64 deleted file mode 100644 index 508e0b9cf7..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5625.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU2MjUsICJuYW1lIjogIk15U1FMIFx1NGVjZVx1NWU5M1x1NWVmNlx1OGZkZiIsICJkYl90eXBlIjogIm15c3FsIiwgImRldGFpbHMiOiB7ImFwcCI6ICIiLCAibmFtZSI6ICJNeVNRTCBcdTRlY2VcdTVlOTNcdTVlZjZcdThmZGYiLCAicGF0aCI6ICIiLCAidHlwZSI6ICJtb25pdG9yIiwgIml0ZW1zIjogW3sibmFtZSI6ICJBVkcobXlzcWxfc2xhdmVfc3RhdHVzX3NlY29uZHNfYmVoaW5kX21hc3RlcikiLCAidGFyZ2V0IjogW1t7ImZpZWxkIjogImhvc3RfdG9wb19ub2RlIiwgInZhbHVlIjogW3siYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE0OH0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE0OX0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE0OX1dLCAibWV0aG9kIjogImVxIn1dXSwgImZ1bmN0aW9ucyI6IFtdLCAiYWxnb3JpdGhtcyI6IFt7ImlkIjogNjE2NiwgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMiwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDEyMH1dXSwgInVuaXRfcHJlZml4IjogIiJ9LCB7ImlkIjogNjE2NywgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMSwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDM2MDB9XV0sICJ1bml0X3ByZWZpeCI6ICIifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICIiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNjE2MCwgIm5hbWUiOiAibXlzcWxfc2xhdmVfc3RhdHVzX3NlY29uZHNfYmVoaW5kX21hc3RlciIsICJ1bml0IjogIiIsICJhbGlhcyI6ICJhIiwgImZ1bmN0aW9ucyI6IFtdLCAibWV0cmljX2lkIjogImJrX21vbml0b3IuZXhwb3J0ZXJfZGJtX215c3FsZF9leHBvcnRlci5zbGF2ZXN0YXQubXlzcWxfc2xhdmVfc3QiLCAiYWdnX21ldGhvZCI6ICJBVkciLCAiYWdnX2ludGVydmFsIjogNjAsICJtZXRyaWNfZmllbGQiOiAibXlzcWxfc2xhdmVfc3RhdHVzX3NlY29uZHNfYmVoaW5kX21hc3RlciIsICJhZ2dfY29uZGl0aW9uIjogW10sICJhZ2dfZGltZW5zaW9uIjogWyJia190YXJnZXRfc2VydmljZV9pbnN0YW5jZV9pZCIsICJhcHAiLCAiY2x1c3Rlcl9kb21haW4iLCAiaW5zdGFuY2Vfcm9sZSIsICJpbnN0YW5jZSJdLCAiZGF0YV90eXBlX2xhYmVsIjogInRpbWVfc2VyaWVzIiwgInJlc3VsdF90YWJsZV9pZCI6ICJleHBvcnRlcl9kYm1fbXlzcWxkX2V4cG9ydGVyLnNsYXZlc3RhdCIsICJkYXRhX3NvdXJjZV9sYWJlbCI6ICJia19tb25pdG9yIn1dLCAibm9fZGF0YV9jb25maWciOiB7ImxldmVsIjogMiwgImNvbnRpbnVvdXMiOiAxMCwgImlzX2VuYWJsZWQiOiBmYWxzZSwgImFnZ19kaW1lbnNpb24iOiBbImJrX3RhcmdldF9zZXJ2aWNlX2luc3RhbmNlX2lkIl19fV0sICJsYWJlbHMiOiBbIk15U1FMIiwgIkRCTV9NWVNRTCIsICJEQk1fTVlTUUwiLCAiREJNIl0sICJub3RpY2UiOiB7ImNvbmZpZyI6IHsidGVtcGxhdGUiOiBbeyJzaWduYWwiOiAiYWJub3JtYWwiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJyZWNvdmVyZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJjbG9zZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9XSwgIm5lZWRfcG9sbCI6IHRydWUsICJub3RpZnlfaW50ZXJ2YWwiOiA3MjAwLCAiaW50ZXJ2YWxfbm90aWZ5X21vZGUiOiAic3RhbmRhcmQifSwgInNpZ25hbCI6IFsiYWJub3JtYWwiLCAibm9fZGF0YSJdLCAib3B0aW9ucyI6IHsiZW5kX3RpbWUiOiAiMjM6NTk6NTkiLCAic3RhcnRfdGltZSI6ICIwMDowMDowMCIsICJhc3NpZ25fbW9kZSI6IFtdLCAidXBncmFkZV9jb25maWciOiB7fSwgImNvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAxLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzdHJhdGVneV9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiZGltZW5zaW9ucyJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifV0sICJ0aW1lZGVsdGEiOiA2MCwgImlzX2VuYWJsZWQiOiB0cnVlLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0IiwgIm5lZWRfYml6X2NvbnZlcmdlIjogdHJ1ZSwgInN1Yl9jb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMiwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifV0sICJ0aW1lZGVsdGEiOiA2MCwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdF9hbGFybSJ9fSwgImV4Y2x1ZGVfbm90aWNlX3dheXMiOiB7ImNsb3NlZCI6IFtdLCAicmVjb3ZlcmVkIjogW119LCAibm9pc2VfcmVkdWNlX2NvbmZpZyI6IHsidW5pdCI6ICJwZXJjZW50IiwgImNvdW50IjogMTAsICJ0aW1lZGVsdGEiOiA1LCAiZGltZW5zaW9ucyI6IFsiYXBwIiwgImNsdXN0ZXJfZG9tYWluIiwgImluc3RhbmNlIiwgImluc3RhbmNlX3JvbGUiLCAiYmtfdGFyZ2V0X3NlcnZpY2VfaW5zdGFuY2VfaWQiXSwgImlzX2VuYWJsZWQiOiBmYWxzZX19LCAiY29uZmlnX2lkIjogODY3OSwgInJlbGF0ZV90eXBlIjogIk5PVElDRSIsICJ1c2VyX2dyb3VwcyI6IFswXX0sICJzb3VyY2UiOiAiYmttb25pdG9ydjMiLCAiYWN0aW9ucyI6IFtdLCAiZGV0ZWN0cyI6IFt7ImxldmVsIjogMiwgImNvbm5lY3RvciI6ICJhbmQiLCAiZXhwcmVzc2lvbiI6ICIiLCAidHJpZ2dlcl9jb25maWciOiB7ImNvdW50IjogMSwgInVwdGltZSI6IHsiY2FsZW5kYXJzIjogW10sICJ0aW1lX3JhbmdlcyI6IFt7ImVuZCI6ICIyMzo1OSIsICJzdGFydCI6ICIwMDowMCJ9XX0sICJjaGVja193aW5kb3ciOiA1fSwgInJlY292ZXJ5X2NvbmZpZyI6IHsiY2hlY2tfd2luZG93IjogNSwgInN0YXR1c19zZXR0ZXIiOiAicmVjb3ZlcnkifX0sIHsibGV2ZWwiOiAxLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJjb21wb25lbnQiLCAiYmtfYml6X2lkIjogMjAwNTAwMDE5NCwgImRhdGFfc291cmNlX3R5cGUiOiAiXHU3NmQxXHU2M2E3XHU5MWM3XHU5NmM2XHU2MzA3XHU2ODA3In0sICJpc19lbmFibGVkIjogdHJ1ZX0= \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5626.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5626.tpl64 deleted file mode 100644 index a56fcc0133..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5626.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU2MjYsICJuYW1lIjogIk15U1FMIFx1NGVjZVx1NWU5M1x1NTQwY1x1NmI2NVx1NWYwMlx1NWUzOCIsICJkYl90eXBlIjogIm15c3FsIiwgImRldGFpbHMiOiB7ImFwcCI6ICIiLCAibmFtZSI6ICJNeVNRTCBcdTRlY2VcdTVlOTNcdTU0MGNcdTZiNjVcdTVmMDJcdTVlMzgiLCAicGF0aCI6ICIiLCAidHlwZSI6ICJtb25pdG9yIiwgIml0ZW1zIjogW3sibmFtZSI6ICJBVkcobXlzcWxfc2xhdmVfc3RhdHVzX3NsYXZlX2lvX3J1bm5pbmcpICsgQVZHKG15c3FsX3NsYXZlX3N0YXR1c19zbGF2ZV9zcWxfcnVubmluZykiLCAidGFyZ2V0IjogW1t7ImZpZWxkIjogImhvc3RfdG9wb19ub2RlIiwgInZhbHVlIjogW3siYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE0OH0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE0OX0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE0OX1dLCAibWV0aG9kIjogImVxIn1dXSwgImZ1bmN0aW9ucyI6IFtdLCAiYWxnb3JpdGhtcyI6IFt7ImlkIjogNjE2OCwgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMSwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAibHQiLCAidGhyZXNob2xkIjogMn1dXSwgInVuaXRfcHJlZml4IjogIiJ9XSwgImV4cHJlc3Npb24iOiAiYSArIGIiLCAib3JpZ2luX3NxbCI6ICIiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNjE2MSwgIm5hbWUiOiAibXlzcWxfc2xhdmVfc3RhdHVzX3NsYXZlX2lvX3J1bm5pbmciLCAidW5pdCI6ICIiLCAiYWxpYXMiOiAiYSIsICJmdW5jdGlvbnMiOiBbXSwgIm1ldHJpY19pZCI6ICJia19tb25pdG9yLmV4cG9ydGVyX2RibV9teXNxbGRfZXhwb3J0ZXIuc2xhdmVzdGF0Lm15c3FsX3NsYXZlX3N0IiwgImFnZ19tZXRob2QiOiAiQVZHIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAibWV0cmljX2ZpZWxkIjogIm15c3FsX3NsYXZlX3N0YXR1c19zbGF2ZV9pb19ydW5uaW5nIiwgImFnZ19jb25kaXRpb24iOiBbeyJrZXkiOiAiaW5zdGFuY2Vfcm9sZSIsICJ2YWx1ZSI6IFsiYmFja2VuZF9zbGF2ZSJdLCAibWV0aG9kIjogImVxIiwgImNvbmRpdGlvbiI6ICJhbmQiLCAiZGltZW5zaW9uX25hbWUiOiAiaW5zdGFuY2Vfcm9sZSJ9XSwgImFnZ19kaW1lbnNpb24iOiBbImJrX3RhcmdldF9zZXJ2aWNlX2luc3RhbmNlX2lkIiwgImFwcCIsICJjbHVzdGVyX2RvbWFpbiIsICJpbnN0YW5jZSJdLCAiZGF0YV90eXBlX2xhYmVsIjogInRpbWVfc2VyaWVzIiwgInJlc3VsdF90YWJsZV9pZCI6ICJleHBvcnRlcl9kYm1fbXlzcWxkX2V4cG9ydGVyLnNsYXZlc3RhdCIsICJkYXRhX3NvdXJjZV9sYWJlbCI6ICJia19tb25pdG9yIn0sIHsiaWQiOiA2MTYyLCAibmFtZSI6ICJteXNxbF9zbGF2ZV9zdGF0dXNfc2xhdmVfc3FsX3J1bm5pbmciLCAidW5pdCI6ICIiLCAiYWxpYXMiOiAiYiIsICJmdW5jdGlvbnMiOiBbXSwgIm1ldHJpY19pZCI6ICJia19tb25pdG9yLmV4cG9ydGVyX2RibV9teXNxbGRfZXhwb3J0ZXIuc2xhdmVzdGF0Lm15c3FsX3NsYXZlX3N0IiwgImFnZ19tZXRob2QiOiAiQVZHIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAibWV0cmljX2ZpZWxkIjogIm15c3FsX3NsYXZlX3N0YXR1c19zbGF2ZV9zcWxfcnVubmluZyIsICJhZ2dfY29uZGl0aW9uIjogW3sia2V5IjogImluc3RhbmNlX3JvbGUiLCAidmFsdWUiOiBbImJhY2tlbmRfc2xhdmUiXSwgIm1ldGhvZCI6ICJlcSIsICJjb25kaXRpb24iOiAiYW5kIiwgImRpbWVuc2lvbl9uYW1lIjogImluc3RhbmNlX3JvbGUifV0sICJhZ2dfZGltZW5zaW9uIjogWyJia190YXJnZXRfc2VydmljZV9pbnN0YW5jZV9pZCIsICJhcHAiLCAiY2x1c3Rlcl9kb21haW4iLCAiaW5zdGFuY2UiXSwgImRhdGFfdHlwZV9sYWJlbCI6ICJ0aW1lX3NlcmllcyIsICJyZXN1bHRfdGFibGVfaWQiOiAiZXhwb3J0ZXJfZGJtX215c3FsZF9leHBvcnRlci5zbGF2ZXN0YXQiLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAiYmtfbW9uaXRvciJ9XSwgIm5vX2RhdGFfY29uZmlnIjogeyJsZXZlbCI6IDIsICJjb250aW51b3VzIjogMTAsICJpc19lbmFibGVkIjogZmFsc2UsICJhZ2dfZGltZW5zaW9uIjogWyJia190YXJnZXRfc2VydmljZV9pbnN0YW5jZV9pZCJdfX1dLCAibGFiZWxzIjogWyJNeVNRTCIsICJEQk1fTVlTUUwiLCAiREJNX01ZU1FMIiwgIkRCTSJdLCAibm90aWNlIjogeyJjb25maWciOiB7InRlbXBsYXRlIjogW3sic2lnbmFsIjogImFibm9ybWFsIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAicmVjb3ZlcmVkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAiY2xvc2VkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifV0sICJuZWVkX3BvbGwiOiB0cnVlLCAibm90aWZ5X2ludGVydmFsIjogNzIwMCwgImludGVydmFsX25vdGlmeV9tb2RlIjogInN0YW5kYXJkIn0sICJzaWduYWwiOiBbImFibm9ybWFsIiwgIm5vX2RhdGEiXSwgIm9wdGlvbnMiOiB7ImVuZF90aW1lIjogIjIzOjU5OjU5IiwgInN0YXJ0X3RpbWUiOiAiMDA6MDA6MDAiLCAiYXNzaWduX21vZGUiOiBbXSwgInVwZ3JhZGVfY29uZmlnIjoge30sICJjb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMSwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic3RyYXRlZ3lfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImRpbWVuc2lvbnMifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In1dLCAidGltZWRlbHRhIjogNjAsICJpc19lbmFibGVkIjogdHJ1ZSwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdCIsICJuZWVkX2Jpel9jb252ZXJnZSI6IHRydWUsICJzdWJfY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDIsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn1dLCAidGltZWRlbHRhIjogNjAsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3RfYWxhcm0ifX0sICJleGNsdWRlX25vdGljZV93YXlzIjogeyJjbG9zZWQiOiBbXSwgInJlY292ZXJlZCI6IFtdfSwgIm5vaXNlX3JlZHVjZV9jb25maWciOiB7InVuaXQiOiAicGVyY2VudCIsICJjb3VudCI6IDEwLCAidGltZWRlbHRhIjogNSwgImRpbWVuc2lvbnMiOiBbImFwcCIsICJjbHVzdGVyX2RvbWFpbiIsICJpbnN0YW5jZSIsICJia190YXJnZXRfc2VydmljZV9pbnN0YW5jZV9pZCJdLCAiaXNfZW5hYmxlZCI6IGZhbHNlfX0sICJjb25maWdfaWQiOiA4NjgwLCAicmVsYXRlX3R5cGUiOiAiTk9USUNFIiwgInVzZXJfZ3JvdXBzIjogWzBdfSwgInNvdXJjZSI6ICJia21vbml0b3J2MyIsICJhY3Rpb25zIjogW10sICJkZXRlY3RzIjogW3sibGV2ZWwiOiAxLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJjb21wb25lbnQiLCAiYmtfYml6X2lkIjogMjAwNTAwMDE5NCwgImRhdGFfc291cmNlX3R5cGUiOiAiXHU3NmQxXHU2M2E3XHU5MWM3XHU5NmM2XHU2MzA3XHU2ODA3In0sICJpc19lbmFibGVkIjogdHJ1ZX0= \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5627.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5627.tpl64 deleted file mode 100644 index 4ef75d3aaa..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5627.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU2MjcsICJuYW1lIjogIk15U1FMICBcdThmZGJcdTdhMGJkb3duIiwgImRiX3R5cGUiOiAibXlzcWwiLCAiZGV0YWlscyI6IHsiYXBwIjogIiIsICJuYW1lIjogIk15U1FMICBcdThmZGJcdTdhMGJkb3duIiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAiQVZHKG15c3FsX3VwKSIsICJ0YXJnZXQiOiBbW3siZmllbGQiOiAiaG9zdF90b3BvX25vZGUiLCAidmFsdWUiOiBbeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ4fSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ5fSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ5fV0sICJtZXRob2QiOiAiZXEifV1dLCAiZnVuY3Rpb25zIjogW10sICJhbGdvcml0aG1zIjogW3siaWQiOiA2MTY5LCAidHlwZSI6ICJUaHJlc2hvbGQiLCAibGV2ZWwiOiAyLCAiY29uZmlnIjogW1t7Im1ldGhvZCI6ICJlcSIsICJ0aHJlc2hvbGQiOiAwfV1dLCAidW5pdF9wcmVmaXgiOiAiIn1dLCAiZXhwcmVzc2lvbiI6ICJhIiwgIm9yaWdpbl9zcWwiOiAiIiwgInF1ZXJ5X2NvbmZpZ3MiOiBbeyJpZCI6IDYxNjMsICJuYW1lIjogIm15c3FsX3VwIiwgInVuaXQiOiAiIiwgImFsaWFzIjogImEiLCAiZnVuY3Rpb25zIjogW10sICJtZXRyaWNfaWQiOiAiYmtfbW9uaXRvci5leHBvcnRlcl9kYm1fbXlzcWxkX2V4cG9ydGVyLm15c3FsdXAubXlzcWxfdXAiLCAiYWdnX21ldGhvZCI6ICJBVkciLCAiYWdnX2ludGVydmFsIjogNjAsICJtZXRyaWNfZmllbGQiOiAibXlzcWxfdXAiLCAiYWdnX2NvbmRpdGlvbiI6IFtdLCAiYWdnX2RpbWVuc2lvbiI6IFsiYmtfdGFyZ2V0X3NlcnZpY2VfaW5zdGFuY2VfaWQiLCAiYXBwIiwgImNsdXN0ZXJfZG9tYWluIiwgImluc3RhbmNlX3JvbGUiLCAiaW5zdGFuY2UiXSwgImRhdGFfdHlwZV9sYWJlbCI6ICJ0aW1lX3NlcmllcyIsICJyZXN1bHRfdGFibGVfaWQiOiAiZXhwb3J0ZXJfZGJtX215c3FsZF9leHBvcnRlci5teXNxbHVwIiwgImRhdGFfc291cmNlX2xhYmVsIjogImJrX21vbml0b3IifV0sICJub19kYXRhX2NvbmZpZyI6IHsibGV2ZWwiOiAyLCAiY29udGludW91cyI6IDEwLCAiaXNfZW5hYmxlZCI6IGZhbHNlLCAiYWdnX2RpbWVuc2lvbiI6IFsiYmtfdGFyZ2V0X3NlcnZpY2VfaW5zdGFuY2VfaWQiXX19XSwgImxhYmVscyI6IFsiTXlTUUwiLCAiREJNX01ZU1FMIiwgIkRCTV9NWVNRTCIsICJEQk0iXSwgIm5vdGljZSI6IHsiY29uZmlnIjogeyJ0ZW1wbGF0ZSI6IFt7InNpZ25hbCI6ICJhYm5vcm1hbCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In0sIHsic2lnbmFsIjogInJlY292ZXJlZCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In0sIHsic2lnbmFsIjogImNsb3NlZCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In1dLCAibmVlZF9wb2xsIjogdHJ1ZSwgIm5vdGlmeV9pbnRlcnZhbCI6IDcyMDAsICJpbnRlcnZhbF9ub3RpZnlfbW9kZSI6ICJzdGFuZGFyZCJ9LCAic2lnbmFsIjogWyJhYm5vcm1hbCIsICJub19kYXRhIl0sICJvcHRpb25zIjogeyJlbmRfdGltZSI6ICIyMzo1OTo1OSIsICJzdGFydF90aW1lIjogIjAwOjAwOjAwIiwgImFzc2lnbl9tb2RlIjogW10sICJ1cGdyYWRlX2NvbmZpZyI6IHt9LCAiY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDEsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInN0cmF0ZWd5X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJkaW1lbnNpb25zIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJia19iaXpfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV9yZWNlaXZlciJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3dheSJ9XSwgInRpbWVkZWx0YSI6IDYwLCAiaXNfZW5hYmxlZCI6IHRydWUsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3QiLCAibmVlZF9iaXpfY29udmVyZ2UiOiB0cnVlLCAic3ViX2NvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAyLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJia19iaXpfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV9yZWNlaXZlciJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3dheSJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9XSwgInRpbWVkZWx0YSI6IDYwLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0X2FsYXJtIn19LCAiZXhjbHVkZV9ub3RpY2Vfd2F5cyI6IHsiY2xvc2VkIjogW10sICJyZWNvdmVyZWQiOiBbXX0sICJub2lzZV9yZWR1Y2VfY29uZmlnIjogeyJ1bml0IjogInBlcmNlbnQiLCAiY291bnQiOiAxMCwgInRpbWVkZWx0YSI6IDUsICJkaW1lbnNpb25zIjogW10sICJpc19lbmFibGVkIjogZmFsc2V9fSwgImNvbmZpZ19pZCI6IDg2ODEsICJyZWxhdGVfdHlwZSI6ICJOT1RJQ0UiLCAidXNlcl9ncm91cHMiOiBbMF19LCAic291cmNlIjogImJrbW9uaXRvcnYzIiwgImFjdGlvbnMiOiBbXSwgImRldGVjdHMiOiBbeyJsZXZlbCI6IDIsICJjb25uZWN0b3IiOiAiYW5kIiwgImV4cHJlc3Npb24iOiAiIiwgInRyaWdnZXJfY29uZmlnIjogeyJjb3VudCI6IDEsICJ1cHRpbWUiOiB7ImNhbGVuZGFycyI6IFtdLCAidGltZV9yYW5nZXMiOiBbeyJlbmQiOiAiMjM6NTkiLCAic3RhcnQiOiAiMDA6MDAifV19LCAiY2hlY2tfd2luZG93IjogNX0sICJyZWNvdmVyeV9jb25maWciOiB7ImNoZWNrX3dpbmRvdyI6IDUsICJzdGF0dXNfc2V0dGVyIjogInJlY292ZXJ5In19XSwgInNjZW5hcmlvIjogImNvbXBvbmVudCIsICJia19iaXpfaWQiOiAyMDA1MDAwMTk0LCAiZGF0YV9zb3VyY2VfdHlwZSI6ICJcdTc2ZDFcdTYzYTdcdTkxYzdcdTk2YzZcdTYzMDdcdTY4MDcifSwgImlzX2VuYWJsZWQiOiB0cnVlfQ== \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5629.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5629.tpl64 deleted file mode 100644 index 095dfb784f..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5629.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU2MjksICJuYW1lIjogIk15U1FMIFx1NGUzYlx1NjczYVx1NzhjMVx1NzZkOFx1N2E3YVx1OTVmNFx1NGY3Zlx1NzUyOFx1NzM4NyIsICJkYl90eXBlIjogIm15c3FsIiwgImRldGFpbHMiOiB7ImFwcCI6ICIiLCAibmFtZSI6ICJNeVNRTCBcdTRlM2JcdTY3M2FcdTc4YzFcdTc2ZDhcdTdhN2FcdTk1ZjRcdTRmN2ZcdTc1MjhcdTczODciLCAicGF0aCI6ICIiLCAidHlwZSI6ICJtb25pdG9yIiwgIml0ZW1zIjogW3sibmFtZSI6ICJBVkcoRGlzayBzcGFjZSB1c2FnZSkiLCAidGFyZ2V0IjogW1t7ImZpZWxkIjogImhvc3RfdG9wb19ub2RlIiwgInZhbHVlIjogW3siYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE0OH0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE0OX0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE0OX1dLCAibWV0aG9kIjogImVxIn1dXSwgImZ1bmN0aW9ucyI6IFtdLCAiYWxnb3JpdGhtcyI6IFt7ImlkIjogNjE3MSwgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMiwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDgwfV1dLCAidW5pdF9wcmVmaXgiOiAiJSJ9LCB7ImlkIjogNjE3MiwgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMSwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDk1fV1dLCAidW5pdF9wcmVmaXgiOiAiJSJ9XSwgImV4cHJlc3Npb24iOiAiYSIsICJvcmlnaW5fc3FsIjogIiIsICJxdWVyeV9jb25maWdzIjogW3siaWQiOiA2MTY1LCAibmFtZSI6ICJcdTc4YzFcdTc2ZDhcdTdhN2FcdTk1ZjRcdTRmN2ZcdTc1MjhcdTczODciLCAidW5pdCI6ICJwZXJjZW50IiwgImFsaWFzIjogImEiLCAiZnVuY3Rpb25zIjogW10sICJtZXRyaWNfaWQiOiAiYmtfbW9uaXRvci5kYm1fc3lzdGVtLmRpc2suaW5fdXNlIiwgImFnZ19tZXRob2QiOiAiQVZHIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAibWV0cmljX2ZpZWxkIjogImluX3VzZSIsICJhZ2dfY29uZGl0aW9uIjogW10sICJhZ2dfZGltZW5zaW9uIjogWyJpbnN0YW5jZV9yb2xlIiwgImRldmljZV9uYW1lIiwgImFwcCIsICJia190YXJnZXRfaXAiLCAiY2x1c3Rlcl9kb21haW4iLCAiYmtfdGFyZ2V0X2Nsb3VkX2lkIl0sICJkYXRhX3R5cGVfbGFiZWwiOiAidGltZV9zZXJpZXMiLCAicmVzdWx0X3RhYmxlX2lkIjogImRibV9zeXN0ZW0uZGlzayIsICJkYXRhX3NvdXJjZV9sYWJlbCI6ICJia19tb25pdG9yIn1dLCAibm9fZGF0YV9jb25maWciOiB7ImxldmVsIjogMiwgImNvbnRpbnVvdXMiOiAxMCwgImlzX2VuYWJsZWQiOiBmYWxzZSwgImFnZ19kaW1lbnNpb24iOiBbImJrX3RhcmdldF9pcCIsICJia190YXJnZXRfY2xvdWRfaWQiXX19XSwgImxhYmVscyI6IFsiTXlTUUwiLCAiREJNX01ZU1FMIiwgIkRCTV9NWVNRTCIsICJEQk0iXSwgIm5vdGljZSI6IHsiY29uZmlnIjogeyJ0ZW1wbGF0ZSI6IFt7InNpZ25hbCI6ICJhYm5vcm1hbCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In0sIHsic2lnbmFsIjogInJlY292ZXJlZCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In0sIHsic2lnbmFsIjogImNsb3NlZCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In1dLCAibmVlZF9wb2xsIjogdHJ1ZSwgIm5vdGlmeV9pbnRlcnZhbCI6IDcyMDAsICJpbnRlcnZhbF9ub3RpZnlfbW9kZSI6ICJzdGFuZGFyZCJ9LCAic2lnbmFsIjogWyJhYm5vcm1hbCIsICJub19kYXRhIl0sICJvcHRpb25zIjogeyJlbmRfdGltZSI6ICIyMzo1OTo1OSIsICJzdGFydF90aW1lIjogIjAwOjAwOjAwIiwgImFzc2lnbl9tb2RlIjogW10sICJ1cGdyYWRlX2NvbmZpZyI6IHt9LCAiY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDEsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInN0cmF0ZWd5X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJkaW1lbnNpb25zIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJia19iaXpfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV9yZWNlaXZlciJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3dheSJ9XSwgInRpbWVkZWx0YSI6IDYwLCAiaXNfZW5hYmxlZCI6IHRydWUsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3QiLCAibmVlZF9iaXpfY29udmVyZ2UiOiB0cnVlLCAic3ViX2NvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAyLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJia19iaXpfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV9yZWNlaXZlciJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3dheSJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9XSwgInRpbWVkZWx0YSI6IDYwLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0X2FsYXJtIn19LCAiZXhjbHVkZV9ub3RpY2Vfd2F5cyI6IHsiY2xvc2VkIjogW10sICJyZWNvdmVyZWQiOiBbXX0sICJub2lzZV9yZWR1Y2VfY29uZmlnIjogeyJ1bml0IjogInBlcmNlbnQiLCAiY291bnQiOiAxMCwgInRpbWVkZWx0YSI6IDUsICJkaW1lbnNpb25zIjogW10sICJpc19lbmFibGVkIjogZmFsc2V9fSwgImNvbmZpZ19pZCI6IDg2ODMsICJyZWxhdGVfdHlwZSI6ICJOT1RJQ0UiLCAidXNlcl9ncm91cHMiOiBbMF19LCAic291cmNlIjogImJrbW9uaXRvcnYzIiwgImFjdGlvbnMiOiBbXSwgImRldGVjdHMiOiBbeyJsZXZlbCI6IDIsICJjb25uZWN0b3IiOiAiYW5kIiwgImV4cHJlc3Npb24iOiAiIiwgInRyaWdnZXJfY29uZmlnIjogeyJjb3VudCI6IDEsICJ1cHRpbWUiOiB7ImNhbGVuZGFycyI6IFtdLCAidGltZV9yYW5nZXMiOiBbeyJlbmQiOiAiMjM6NTkiLCAic3RhcnQiOiAiMDA6MDAifV19LCAiY2hlY2tfd2luZG93IjogNX0sICJyZWNvdmVyeV9jb25maWciOiB7ImNoZWNrX3dpbmRvdyI6IDUsICJzdGF0dXNfc2V0dGVyIjogInJlY292ZXJ5In19LCB7ImxldmVsIjogMSwgImNvbm5lY3RvciI6ICJhbmQiLCAiZXhwcmVzc2lvbiI6ICIiLCAidHJpZ2dlcl9jb25maWciOiB7ImNvdW50IjogMSwgInVwdGltZSI6IHsiY2FsZW5kYXJzIjogW10sICJ0aW1lX3JhbmdlcyI6IFt7ImVuZCI6ICIyMzo1OSIsICJzdGFydCI6ICIwMDowMCJ9XX0sICJjaGVja193aW5kb3ciOiA1fSwgInJlY292ZXJ5X2NvbmZpZyI6IHsiY2hlY2tfd2luZG93IjogNSwgInN0YXR1c19zZXR0ZXIiOiAicmVjb3ZlcnkifX1dLCAic2NlbmFyaW8iOiAib3MiLCAiYmtfYml6X2lkIjogMjAwNTAwMDE5NCwgImRhdGFfc291cmNlX3R5cGUiOiAiXHU3NmQxXHU2M2E3XHU5MWM3XHU5NmM2XHU2MzA3XHU2ODA3In0sICJpc19lbmFibGVkIjogdHJ1ZX0= \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5630.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5630.tpl64 deleted file mode 100644 index 3e51df2612..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5630.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU2MzAsICJuYW1lIjogIk15U1FMIFx1NGUzYlx1NjczYVx1NzhjMVx1NzZkOElPXHU1MjI5XHU3NTI4XHU3Mzg3IiwgImRiX3R5cGUiOiAibXlzcWwiLCAiZGV0YWlscyI6IHsiYXBwIjogIiIsICJuYW1lIjogIk15U1FMIFx1NGUzYlx1NjczYVx1NzhjMVx1NzZkOElPXHU1MjI5XHU3NTI4XHU3Mzg3IiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAiQVZHKEkvT1x1NGY3Zlx1NzUyOFx1NzM4NykiLCAidGFyZ2V0IjogW1t7ImZpZWxkIjogImhvc3RfdG9wb19ub2RlIiwgInZhbHVlIjogW3siYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE0OH0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE0OX0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE0OX1dLCAibWV0aG9kIjogImVxIn1dXSwgImZ1bmN0aW9ucyI6IFtdLCAiYWxnb3JpdGhtcyI6IFt7ImlkIjogNjE3MywgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMiwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDkwfV1dLCAidW5pdF9wcmVmaXgiOiAiJSJ9XSwgImV4cHJlc3Npb24iOiAiYSIsICJvcmlnaW5fc3FsIjogIiIsICJxdWVyeV9jb25maWdzIjogW3siaWQiOiA2MTY2LCAibmFtZSI6ICJJL09cdTRmN2ZcdTc1MjhcdTczODciLCAidW5pdCI6ICJwZXJjZW50dW5pdCIsICJhbGlhcyI6ICJhIiwgImZ1bmN0aW9ucyI6IFtdLCAibWV0cmljX2lkIjogImJrX21vbml0b3IuZGJtX3N5c3RlbS5pby51dGlsIiwgImFnZ19tZXRob2QiOiAiQVZHIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAibWV0cmljX2ZpZWxkIjogInV0aWwiLCAiYWdnX2NvbmRpdGlvbiI6IFtdLCAiYWdnX2RpbWVuc2lvbiI6IFsiaW5zdGFuY2Vfcm9sZSIsICJkZXZpY2VfbmFtZSIsICJhcHAiLCAiYmtfdGFyZ2V0X2lwIiwgImNsdXN0ZXJfZG9tYWluIiwgImJrX3RhcmdldF9jbG91ZF9pZCJdLCAiZGF0YV90eXBlX2xhYmVsIjogInRpbWVfc2VyaWVzIiwgInJlc3VsdF90YWJsZV9pZCI6ICJkYm1fc3lzdGVtLmlvIiwgImRhdGFfc291cmNlX2xhYmVsIjogImJrX21vbml0b3IifV0sICJub19kYXRhX2NvbmZpZyI6IHsibGV2ZWwiOiAyLCAiY29udGludW91cyI6IDEwLCAiaXNfZW5hYmxlZCI6IGZhbHNlLCAiYWdnX2RpbWVuc2lvbiI6IFsiYmtfdGFyZ2V0X2lwIiwgImJrX3RhcmdldF9jbG91ZF9pZCJdfX1dLCAibGFiZWxzIjogWyJNeVNRTCIsICJEQk1fTVlTUUwiLCAiREJNX01ZU1FMIiwgIkRCTSJdLCAibm90aWNlIjogeyJjb25maWciOiB7InRlbXBsYXRlIjogW3sic2lnbmFsIjogImFibm9ybWFsIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAicmVjb3ZlcmVkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAiY2xvc2VkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifV0sICJuZWVkX3BvbGwiOiB0cnVlLCAibm90aWZ5X2ludGVydmFsIjogNzIwMCwgImludGVydmFsX25vdGlmeV9tb2RlIjogInN0YW5kYXJkIn0sICJzaWduYWwiOiBbImFibm9ybWFsIiwgIm5vX2RhdGEiXSwgIm9wdGlvbnMiOiB7ImVuZF90aW1lIjogIjIzOjU5OjU5IiwgInN0YXJ0X3RpbWUiOiAiMDA6MDA6MDAiLCAiYXNzaWduX21vZGUiOiBbXSwgInVwZ3JhZGVfY29uZmlnIjoge30sICJjb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMSwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic3RyYXRlZ3lfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImRpbWVuc2lvbnMifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In1dLCAidGltZWRlbHRhIjogNjAsICJpc19lbmFibGVkIjogdHJ1ZSwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdCIsICJuZWVkX2Jpel9jb252ZXJnZSI6IHRydWUsICJzdWJfY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDIsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn1dLCAidGltZWRlbHRhIjogNjAsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3RfYWxhcm0ifX0sICJleGNsdWRlX25vdGljZV93YXlzIjogeyJjbG9zZWQiOiBbXSwgInJlY292ZXJlZCI6IFtdfSwgIm5vaXNlX3JlZHVjZV9jb25maWciOiB7InVuaXQiOiAicGVyY2VudCIsICJjb3VudCI6IDEwLCAidGltZWRlbHRhIjogNSwgImRpbWVuc2lvbnMiOiBbXSwgImlzX2VuYWJsZWQiOiBmYWxzZX19LCAiY29uZmlnX2lkIjogODY4NCwgInJlbGF0ZV90eXBlIjogIk5PVElDRSIsICJ1c2VyX2dyb3VwcyI6IFswXX0sICJzb3VyY2UiOiAiYmttb25pdG9ydjMiLCAiYWN0aW9ucyI6IFtdLCAiZGV0ZWN0cyI6IFt7ImxldmVsIjogMiwgImNvbm5lY3RvciI6ICJhbmQiLCAiZXhwcmVzc2lvbiI6ICIiLCAidHJpZ2dlcl9jb25maWciOiB7ImNvdW50IjogMSwgInVwdGltZSI6IHsiY2FsZW5kYXJzIjogW10sICJ0aW1lX3JhbmdlcyI6IFt7ImVuZCI6ICIyMzo1OSIsICJzdGFydCI6ICIwMDowMCJ9XX0sICJjaGVja193aW5kb3ciOiA1fSwgInJlY292ZXJ5X2NvbmZpZyI6IHsiY2hlY2tfd2luZG93IjogNSwgInN0YXR1c19zZXR0ZXIiOiAicmVjb3ZlcnkifX1dLCAic2NlbmFyaW8iOiAib3MiLCAiYmtfYml6X2lkIjogMjAwNTAwMDE5NCwgImRhdGFfc291cmNlX3R5cGUiOiAiXHU3NmQxXHU2M2E3XHU5MWM3XHU5NmM2XHU2MzA3XHU2ODA3In0sICJpc19lbmFibGVkIjogdHJ1ZX0= \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5703.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5703.tpl64 deleted file mode 100644 index ec3ceb1472..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5703.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU3MDMsICJuYW1lIjogIk15U1FMIElubm9EQiBsb2cgd2FpdHMiLCAiZGJfdHlwZSI6ICJteXNxbCIsICJkZXRhaWxzIjogeyJhcHAiOiAiIiwgIm5hbWUiOiAiTXlTUUwgSW5ub0RCIGxvZyB3YWl0cyIsICJwYXRoIjogIiIsICJ0eXBlIjogIm1vbml0b3IiLCAiaXRlbXMiOiBbeyJuYW1lIjogIkFWRyhteXNxbF9nbG9iYWxfc3RhdHVzX2lubm9kYl9sb2dfd2FpdHMpIiwgInRhcmdldCI6IFtbeyJmaWVsZCI6ICJob3N0X3RvcG9fbm9kZSIsICJ2YWx1ZSI6IFt7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgxNDh9LCB7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgxNDl9LCB7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgxNDl9XSwgIm1ldGhvZCI6ICJlcSJ9XV0sICJmdW5jdGlvbnMiOiBbXSwgImFsZ29yaXRobXMiOiBbeyJpZCI6IDYyNDYsICJ0eXBlIjogIlRocmVzaG9sZCIsICJsZXZlbCI6IDIsICJjb25maWciOiBbW3sibWV0aG9kIjogImd0ZSIsICJ0aHJlc2hvbGQiOiAxMH1dXSwgInVuaXRfcHJlZml4IjogIiJ9XSwgImV4cHJlc3Npb24iOiAiYSIsICJvcmlnaW5fc3FsIjogIiIsICJxdWVyeV9jb25maWdzIjogW3siaWQiOiA2MjQyLCAibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX2lubm9kYl9sb2dfd2FpdHMiLCAidW5pdCI6ICIiLCAiYWxpYXMiOiAiYSIsICJmdW5jdGlvbnMiOiBbeyJpZCI6ICJyYXRlIiwgInBhcmFtcyI6IFt7ImlkIjogIndpbmRvdyIsICJ2YWx1ZSI6ICIxMG0ifV19XSwgIm1ldHJpY19pZCI6ICJia19tb25pdG9yLmV4cG9ydGVyX2RibV9teXNxbGRfZXhwb3J0ZXIuZ3N0YXR1cy5teXNxbF9nbG9iYWxfc3RhIiwgImFnZ19tZXRob2QiOiAiQVZHIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAibWV0cmljX2ZpZWxkIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfaW5ub2RiX2xvZ193YWl0cyIsICJhZ2dfY29uZGl0aW9uIjogW10sICJhZ2dfZGltZW5zaW9uIjogWyJia190YXJnZXRfc2VydmljZV9pbnN0YW5jZV9pZCIsICJhcHAiLCAiY2x1c3Rlcl9kb21haW4iLCAiaW5zdGFuY2Vfcm9sZSIsICJpbnN0YW5jZSJdLCAiZGF0YV90eXBlX2xhYmVsIjogInRpbWVfc2VyaWVzIiwgInJlc3VsdF90YWJsZV9pZCI6ICJleHBvcnRlcl9kYm1fbXlzcWxkX2V4cG9ydGVyLmdzdGF0dXMiLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAiYmtfbW9uaXRvciJ9XSwgIm5vX2RhdGFfY29uZmlnIjogeyJsZXZlbCI6IDIsICJjb250aW51b3VzIjogMTAsICJpc19lbmFibGVkIjogZmFsc2UsICJhZ2dfZGltZW5zaW9uIjogWyJia190YXJnZXRfc2VydmljZV9pbnN0YW5jZV9pZCJdfX1dLCAibGFiZWxzIjogWyJNeVNRTCIsICJEQk1fTVlTUUwiLCAiREJNX01ZU1FMIiwgIkRCTSJdLCAibm90aWNlIjogeyJjb25maWciOiB7InRlbXBsYXRlIjogW3sic2lnbmFsIjogImFibm9ybWFsIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAicmVjb3ZlcmVkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAiY2xvc2VkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifV0sICJuZWVkX3BvbGwiOiB0cnVlLCAibm90aWZ5X2ludGVydmFsIjogNzIwMCwgImludGVydmFsX25vdGlmeV9tb2RlIjogInN0YW5kYXJkIn0sICJzaWduYWwiOiBbImFibm9ybWFsIiwgIm5vX2RhdGEiXSwgIm9wdGlvbnMiOiB7ImVuZF90aW1lIjogIjIzOjU5OjU5IiwgInN0YXJ0X3RpbWUiOiAiMDA6MDA6MDAiLCAiYXNzaWduX21vZGUiOiBbXSwgInVwZ3JhZGVfY29uZmlnIjoge30sICJjb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMSwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic3RyYXRlZ3lfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImRpbWVuc2lvbnMifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In1dLCAidGltZWRlbHRhIjogNjAsICJpc19lbmFibGVkIjogdHJ1ZSwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdCIsICJuZWVkX2Jpel9jb252ZXJnZSI6IHRydWUsICJzdWJfY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDIsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn1dLCAidGltZWRlbHRhIjogNjAsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3RfYWxhcm0ifX0sICJleGNsdWRlX25vdGljZV93YXlzIjogeyJjbG9zZWQiOiBbXSwgInJlY292ZXJlZCI6IFtdfSwgIm5vaXNlX3JlZHVjZV9jb25maWciOiB7InVuaXQiOiAicGVyY2VudCIsICJjb3VudCI6IDEwLCAidGltZWRlbHRhIjogNSwgImRpbWVuc2lvbnMiOiBbImFwcCIsICJjbHVzdGVyX2RvbWFpbiIsICJpbnN0YW5jZSIsICJpbnN0YW5jZV9yb2xlIiwgImJrX3RhcmdldF9zZXJ2aWNlX2luc3RhbmNlX2lkIl0sICJpc19lbmFibGVkIjogZmFsc2V9fSwgImNvbmZpZ19pZCI6IDg3NTcsICJyZWxhdGVfdHlwZSI6ICJOT1RJQ0UiLCAidXNlcl9ncm91cHMiOiBbMF19LCAic291cmNlIjogImJrbW9uaXRvcnYzIiwgImFjdGlvbnMiOiBbXSwgImRldGVjdHMiOiBbeyJsZXZlbCI6IDIsICJjb25uZWN0b3IiOiAiYW5kIiwgImV4cHJlc3Npb24iOiAiIiwgInRyaWdnZXJfY29uZmlnIjogeyJjb3VudCI6IDEsICJ1cHRpbWUiOiB7ImNhbGVuZGFycyI6IFtdLCAidGltZV9yYW5nZXMiOiBbeyJlbmQiOiAiMjM6NTkiLCAic3RhcnQiOiAiMDA6MDAifV19LCAiY2hlY2tfd2luZG93IjogNX0sICJyZWNvdmVyeV9jb25maWciOiB7ImNoZWNrX3dpbmRvdyI6IDUsICJzdGF0dXNfc2V0dGVyIjogInJlY292ZXJ5In19XSwgInNjZW5hcmlvIjogImNvbXBvbmVudCIsICJia19iaXpfaWQiOiAyMDA1MDAwMTk0LCAiZGF0YV9zb3VyY2VfdHlwZSI6ICJcdTc2ZDFcdTYzYTdcdTkxYzdcdTk2YzZcdTYzMDdcdTY4MDcifSwgImlzX2VuYWJsZWQiOiB0cnVlfQ== \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5704.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5704.tpl64 deleted file mode 100644 index ce9c587c31..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5704.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU3MDQsICJuYW1lIjogIk15U1FMIHJlc3RhcnRlZCIsICJkYl90eXBlIjogIm15c3FsIiwgImRldGFpbHMiOiB7ImFwcCI6ICIiLCAibmFtZSI6ICJNeVNRTCByZXN0YXJ0ZWQiLCAicGF0aCI6ICIiLCAidHlwZSI6ICJtb25pdG9yIiwgIml0ZW1zIjogW3sibmFtZSI6ICJBVkcobXlzcWxfZ2xvYmFsX3N0YXR1c191cHRpbWUpIiwgInRhcmdldCI6IFtbeyJmaWVsZCI6ICJob3N0X3RvcG9fbm9kZSIsICJ2YWx1ZSI6IFt7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgxNDh9LCB7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgxNDl9LCB7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgxNDl9XSwgIm1ldGhvZCI6ICJlcSJ9XV0sICJmdW5jdGlvbnMiOiBbXSwgImFsZ29yaXRobXMiOiBbeyJpZCI6IDYyNDcsICJ0eXBlIjogIlRocmVzaG9sZCIsICJsZXZlbCI6IDIsICJjb25maWciOiBbW3sibWV0aG9kIjogImx0IiwgInRocmVzaG9sZCI6IDYwfV1dLCAidW5pdF9wcmVmaXgiOiAiIn1dLCAiZXhwcmVzc2lvbiI6ICJhIiwgIm9yaWdpbl9zcWwiOiAiIiwgInF1ZXJ5X2NvbmZpZ3MiOiBbeyJpZCI6IDYyNDMsICJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfdXB0aW1lIiwgInVuaXQiOiAiIiwgImFsaWFzIjogImEiLCAiZnVuY3Rpb25zIjogW10sICJtZXRyaWNfaWQiOiAiYmtfbW9uaXRvci5leHBvcnRlcl9kYm1fbXlzcWxkX2V4cG9ydGVyLmdzdGF0dXMubXlzcWxfZ2xvYmFsX3N0YSIsICJhZ2dfbWV0aG9kIjogIkFWRyIsICJhZ2dfaW50ZXJ2YWwiOiA2MCwgIm1ldHJpY19maWVsZCI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX3VwdGltZSIsICJhZ2dfY29uZGl0aW9uIjogW10sICJhZ2dfZGltZW5zaW9uIjogWyJia190YXJnZXRfc2VydmljZV9pbnN0YW5jZV9pZCIsICJhcHAiLCAiY2x1c3Rlcl9kb21haW4iLCAiaW5zdGFuY2Vfcm9sZSIsICJpbnN0YW5jZSJdLCAiZGF0YV90eXBlX2xhYmVsIjogInRpbWVfc2VyaWVzIiwgInJlc3VsdF90YWJsZV9pZCI6ICJleHBvcnRlcl9kYm1fbXlzcWxkX2V4cG9ydGVyLmdzdGF0dXMiLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAiYmtfbW9uaXRvciJ9XSwgIm5vX2RhdGFfY29uZmlnIjogeyJsZXZlbCI6IDIsICJjb250aW51b3VzIjogMTAsICJpc19lbmFibGVkIjogZmFsc2UsICJhZ2dfZGltZW5zaW9uIjogWyJia190YXJnZXRfc2VydmljZV9pbnN0YW5jZV9pZCJdfX1dLCAibGFiZWxzIjogWyJNeVNRTCIsICJEQk1fTVlTUUwiLCAiREJNX01ZU1FMIiwgIkRCTSJdLCAibm90aWNlIjogeyJjb25maWciOiB7InRlbXBsYXRlIjogW3sic2lnbmFsIjogImFibm9ybWFsIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAicmVjb3ZlcmVkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAiY2xvc2VkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifV0sICJuZWVkX3BvbGwiOiB0cnVlLCAibm90aWZ5X2ludGVydmFsIjogNzIwMCwgImludGVydmFsX25vdGlmeV9tb2RlIjogInN0YW5kYXJkIn0sICJzaWduYWwiOiBbImFibm9ybWFsIiwgIm5vX2RhdGEiXSwgIm9wdGlvbnMiOiB7ImVuZF90aW1lIjogIjIzOjU5OjU5IiwgInN0YXJ0X3RpbWUiOiAiMDA6MDA6MDAiLCAiYXNzaWduX21vZGUiOiBbXSwgInVwZ3JhZGVfY29uZmlnIjoge30sICJjb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMSwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic3RyYXRlZ3lfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImRpbWVuc2lvbnMifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In1dLCAidGltZWRlbHRhIjogNjAsICJpc19lbmFibGVkIjogdHJ1ZSwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdCIsICJuZWVkX2Jpel9jb252ZXJnZSI6IHRydWUsICJzdWJfY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDIsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn1dLCAidGltZWRlbHRhIjogNjAsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3RfYWxhcm0ifX0sICJleGNsdWRlX25vdGljZV93YXlzIjogeyJjbG9zZWQiOiBbXSwgInJlY292ZXJlZCI6IFtdfSwgIm5vaXNlX3JlZHVjZV9jb25maWciOiB7InVuaXQiOiAicGVyY2VudCIsICJjb3VudCI6IDEwLCAidGltZWRlbHRhIjogNSwgImRpbWVuc2lvbnMiOiBbXSwgImlzX2VuYWJsZWQiOiBmYWxzZX19LCAiY29uZmlnX2lkIjogODc1OCwgInJlbGF0ZV90eXBlIjogIk5PVElDRSIsICJ1c2VyX2dyb3VwcyI6IFswXX0sICJzb3VyY2UiOiAiYmttb25pdG9ydjMiLCAiYWN0aW9ucyI6IFtdLCAiZGV0ZWN0cyI6IFt7ImxldmVsIjogMiwgImNvbm5lY3RvciI6ICJhbmQiLCAiZXhwcmVzc2lvbiI6ICIiLCAidHJpZ2dlcl9jb25maWciOiB7ImNvdW50IjogMSwgInVwdGltZSI6IHsiY2FsZW5kYXJzIjogW10sICJ0aW1lX3JhbmdlcyI6IFt7ImVuZCI6ICIyMzo1OSIsICJzdGFydCI6ICIwMDowMCJ9XX0sICJjaGVja193aW5kb3ciOiA1fSwgInJlY292ZXJ5X2NvbmZpZyI6IHsiY2hlY2tfd2luZG93IjogNSwgInN0YXR1c19zZXR0ZXIiOiAicmVjb3ZlcnkifX1dLCAic2NlbmFyaW8iOiAiY29tcG9uZW50IiwgImJrX2Jpel9pZCI6IDIwMDUwMDAxOTQsICJkYXRhX3NvdXJjZV90eXBlIjogIlx1NzZkMVx1NjNhN1x1OTFjN1x1OTZjNlx1NjMwN1x1NjgwNyJ9LCAiaXNfZW5hYmxlZCI6IHRydWV9 \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5758.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5758.tpl64 deleted file mode 100644 index dcd499a6c7..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5758.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU3NTgsICJuYW1lIjogIk15U1FMIGludFx1NmVhMlx1NTFmYVx1NTQ0YVx1OGI2NiIsICJkYl90eXBlIjogIm15c3FsIiwgImRldGFpbHMiOiB7ImFwcCI6ICIiLCAibmFtZSI6ICJNeVNRTCBpbnRcdTZlYTJcdTUxZmFcdTU0NGFcdThiNjYiLCAicGF0aCI6ICIiLCAidHlwZSI6ICJtb25pdG9yIiwgIml0ZW1zIjogW3sibmFtZSI6ICJBVkcobXlzcWxfaW5mb19zY2hlbWFfYXV0b19pbmNyZW1lbnRfY29sdW1uKSAvIEFWRyhteXNxbF9pbmZvX3NjaGVtYV9hdXRvX2luY3JlbWVudF9jb2x1bW5fbWF4KSAqIDEwMCIsICJ0YXJnZXQiOiBbW3siZmllbGQiOiAiaG9zdF90b3BvX25vZGUiLCAidmFsdWUiOiBbeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ4fSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ5fSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ5fV0sICJtZXRob2QiOiAiZXEifV1dLCAiZnVuY3Rpb25zIjogW10sICJhbGdvcml0aG1zIjogW3siaWQiOiA2MzAxLCAidHlwZSI6ICJUaHJlc2hvbGQiLCAibGV2ZWwiOiAyLCAiY29uZmlnIjogW1t7Im1ldGhvZCI6ICJndGUiLCAidGhyZXNob2xkIjogOTh9XV0sICJ1bml0X3ByZWZpeCI6ICIifV0sICJleHByZXNzaW9uIjogImEgLyBiICogMTAwIiwgIm9yaWdpbl9zcWwiOiAiIiwgInF1ZXJ5X2NvbmZpZ3MiOiBbeyJpZCI6IDYyOTcsICJuYW1lIjogIm15c3FsX2luZm9fc2NoZW1hX2F1dG9faW5jcmVtZW50X2NvbHVtbiIsICJ1bml0IjogIiIsICJhbGlhcyI6ICJhIiwgImZ1bmN0aW9ucyI6IFtdLCAibWV0cmljX2lkIjogImJrX21vbml0b3IuZXhwb3J0ZXJfZGJtX215c3FsZF9leHBvcnRlci50YWJsZWluY3IubXlzcWxfaW5mb19zY2giLCAiYWdnX21ldGhvZCI6ICJBVkciLCAiYWdnX2ludGVydmFsIjogNjAsICJtZXRyaWNfZmllbGQiOiAibXlzcWxfaW5mb19zY2hlbWFfYXV0b19pbmNyZW1lbnRfY29sdW1uIiwgImFnZ19jb25kaXRpb24iOiBbeyJrZXkiOiAiaW5zdGFuY2Vfcm9sZSIsICJ2YWx1ZSI6IFsiYmFja2VuZF9tYXN0ZXIiXSwgIm1ldGhvZCI6ICJlcSIsICJjb25kaXRpb24iOiAiYW5kIiwgImRpbWVuc2lvbl9uYW1lIjogImluc3RhbmNlX3JvbGUifV0sICJhZ2dfZGltZW5zaW9uIjogWyJia190YXJnZXRfc2VydmljZV9pbnN0YW5jZV9pZCIsICJhcHAiLCAiY2x1c3Rlcl9kb21haW4iLCAic2NoZW1hIiwgInRhYmxlIiwgImNvbHVtbiJdLCAiZGF0YV90eXBlX2xhYmVsIjogInRpbWVfc2VyaWVzIiwgInJlc3VsdF90YWJsZV9pZCI6ICJleHBvcnRlcl9kYm1fbXlzcWxkX2V4cG9ydGVyLnRhYmxlaW5jciIsICJkYXRhX3NvdXJjZV9sYWJlbCI6ICJia19tb25pdG9yIn0sIHsiaWQiOiA2Mjk4LCAibmFtZSI6ICJteXNxbF9pbmZvX3NjaGVtYV9hdXRvX2luY3JlbWVudF9jb2x1bW5fbWF4IiwgInVuaXQiOiAiIiwgImFsaWFzIjogImIiLCAiZnVuY3Rpb25zIjogW10sICJtZXRyaWNfaWQiOiAiYmtfbW9uaXRvci5leHBvcnRlcl9kYm1fbXlzcWxkX2V4cG9ydGVyLnRhYmxlaW5jci5teXNxbF9pbmZvX3NjaCIsICJhZ2dfbWV0aG9kIjogIkFWRyIsICJhZ2dfaW50ZXJ2YWwiOiA2MCwgIm1ldHJpY19maWVsZCI6ICJteXNxbF9pbmZvX3NjaGVtYV9hdXRvX2luY3JlbWVudF9jb2x1bW5fbWF4IiwgImFnZ19jb25kaXRpb24iOiBbeyJrZXkiOiAiaW5zdGFuY2Vfcm9sZSIsICJ2YWx1ZSI6IFsiYmFja2VuZF9tYXN0ZXIiXSwgIm1ldGhvZCI6ICJlcSIsICJjb25kaXRpb24iOiAiYW5kIiwgImRpbWVuc2lvbl9uYW1lIjogImluc3RhbmNlX3JvbGUifV0sICJhZ2dfZGltZW5zaW9uIjogWyJia190YXJnZXRfc2VydmljZV9pbnN0YW5jZV9pZCIsICJhcHAiLCAiY2x1c3Rlcl9kb21haW4iLCAic2NoZW1hIiwgInRhYmxlIiwgImNvbHVtbiJdLCAiZGF0YV90eXBlX2xhYmVsIjogInRpbWVfc2VyaWVzIiwgInJlc3VsdF90YWJsZV9pZCI6ICJleHBvcnRlcl9kYm1fbXlzcWxkX2V4cG9ydGVyLnRhYmxlaW5jciIsICJkYXRhX3NvdXJjZV9sYWJlbCI6ICJia19tb25pdG9yIn1dLCAibm9fZGF0YV9jb25maWciOiB7ImxldmVsIjogMiwgImNvbnRpbnVvdXMiOiAxMCwgImlzX2VuYWJsZWQiOiBmYWxzZSwgImFnZ19kaW1lbnNpb24iOiBbImJrX3RhcmdldF9zZXJ2aWNlX2luc3RhbmNlX2lkIl19fV0sICJsYWJlbHMiOiBbIk15U1FMIiwgIkRCTV9NWVNRTCIsICJEQk1fTVlTUUwiLCAiREJNIl0sICJub3RpY2UiOiB7ImNvbmZpZyI6IHsidGVtcGxhdGUiOiBbeyJzaWduYWwiOiAiYWJub3JtYWwiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJyZWNvdmVyZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJjbG9zZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9XSwgIm5lZWRfcG9sbCI6IHRydWUsICJub3RpZnlfaW50ZXJ2YWwiOiA3MjAwLCAiaW50ZXJ2YWxfbm90aWZ5X21vZGUiOiAic3RhbmRhcmQifSwgInNpZ25hbCI6IFsiYWJub3JtYWwiLCAibm9fZGF0YSJdLCAib3B0aW9ucyI6IHsiZW5kX3RpbWUiOiAiMjM6NTk6NTkiLCAic3RhcnRfdGltZSI6ICIwMDowMDowMCIsICJhc3NpZ25fbW9kZSI6IFtdLCAidXBncmFkZV9jb25maWciOiB7fSwgImNvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAxLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzdHJhdGVneV9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiZGltZW5zaW9ucyJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifV0sICJ0aW1lZGVsdGEiOiA2MCwgImlzX2VuYWJsZWQiOiB0cnVlLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0IiwgIm5lZWRfYml6X2NvbnZlcmdlIjogdHJ1ZSwgInN1Yl9jb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMiwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifV0sICJ0aW1lZGVsdGEiOiA2MCwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdF9hbGFybSJ9fSwgImV4Y2x1ZGVfbm90aWNlX3dheXMiOiB7ImNsb3NlZCI6IFtdLCAicmVjb3ZlcmVkIjogW119LCAibm9pc2VfcmVkdWNlX2NvbmZpZyI6IHsidW5pdCI6ICJwZXJjZW50IiwgImNvdW50IjogMTAsICJ0aW1lZGVsdGEiOiA1LCAiZGltZW5zaW9ucyI6IFtdLCAiaXNfZW5hYmxlZCI6IGZhbHNlfX0sICJjb25maWdfaWQiOiA4ODEyLCAicmVsYXRlX3R5cGUiOiAiTk9USUNFIiwgInVzZXJfZ3JvdXBzIjogWzBdfSwgInNvdXJjZSI6ICJia21vbml0b3J2MyIsICJhY3Rpb25zIjogW10sICJkZXRlY3RzIjogW3sibGV2ZWwiOiAyLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJjb21wb25lbnQiLCAiYmtfYml6X2lkIjogMjAwNTAwMDE5NCwgImRhdGFfc291cmNlX3R5cGUiOiAiXHU3NmQxXHU2M2E3XHU5MWM3XHU5NmM2XHU2MzA3XHU2ODA3In0sICJpc19lbmFibGVkIjogdHJ1ZX0= \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5762.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5762.tpl64 deleted file mode 100644 index 5b9ab34980..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5762.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU3NjIsICJuYW1lIjogIk15U1FMIFx1OTU3Zlx1N2E3YVx1OTVmMlx1NGU4Ylx1NTJhMVx1NjcyYVx1NTE3M1x1OTVlZCIsICJkYl90eXBlIjogIm15c3FsIiwgImRldGFpbHMiOiB7ImFwcCI6ICIiLCAibmFtZSI6ICJNeVNRTCBcdTk1N2ZcdTdhN2FcdTk1ZjJcdTRlOGJcdTUyYTFcdTY3MmFcdTUxNzNcdTk1ZWQiLCAicGF0aCI6ICIiLCAidHlwZSI6ICJtb25pdG9yIiwgIml0ZW1zIjogW3sibmFtZSI6ICJBVkcobXlzcWxfZW5naW5lX2lubm9kYl90cnhfaWRsZV90aW1lX21heCkiLCAidGFyZ2V0IjogW1t7ImZpZWxkIjogImhvc3RfdG9wb19ub2RlIiwgInZhbHVlIjogW3siYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE0OH0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE0OX0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE0OX1dLCAibWV0aG9kIjogImVxIn1dXSwgImZ1bmN0aW9ucyI6IFtdLCAiYWxnb3JpdGhtcyI6IFt7ImlkIjogNjMwNSwgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMiwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDM2MDB9XV0sICJ1bml0X3ByZWZpeCI6ICIifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICIiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNjMwMiwgIm5hbWUiOiAibXlzcWxfZW5naW5lX2lubm9kYl90cnhfaWRsZV90aW1lX21heCIsICJ1bml0IjogIiIsICJhbGlhcyI6ICJhIiwgImZ1bmN0aW9ucyI6IFtdLCAibWV0cmljX2lkIjogImJrX21vbml0b3IuZXhwb3J0ZXJfZGJtX215c3FsZF9leHBvcnRlci5pbm5vZGJ0cngubXlzcWxfZW5naW5lX2kiLCAiYWdnX21ldGhvZCI6ICJBVkciLCAiYWdnX2ludGVydmFsIjogNjAsICJtZXRyaWNfZmllbGQiOiAibXlzcWxfZW5naW5lX2lubm9kYl90cnhfaWRsZV90aW1lX21heCIsICJhZ2dfY29uZGl0aW9uIjogW10sICJhZ2dfZGltZW5zaW9uIjogWyJia190YXJnZXRfc2VydmljZV9pbnN0YW5jZV9pZCIsICJhcHAiLCAiY2x1c3Rlcl9kb21haW4iLCAiaW5zdGFuY2UiLCAiaW5zdGFuY2Vfcm9sZSJdLCAiZGF0YV90eXBlX2xhYmVsIjogInRpbWVfc2VyaWVzIiwgInJlc3VsdF90YWJsZV9pZCI6ICJleHBvcnRlcl9kYm1fbXlzcWxkX2V4cG9ydGVyLmlubm9kYnRyeCIsICJkYXRhX3NvdXJjZV9sYWJlbCI6ICJia19tb25pdG9yIn1dLCAibm9fZGF0YV9jb25maWciOiB7ImxldmVsIjogMiwgImNvbnRpbnVvdXMiOiAxMCwgImlzX2VuYWJsZWQiOiBmYWxzZSwgImFnZ19kaW1lbnNpb24iOiBbImJrX3RhcmdldF9zZXJ2aWNlX2luc3RhbmNlX2lkIl19fV0sICJsYWJlbHMiOiBbIk15U1FMIiwgIkRCTV9NWVNRTCIsICJEQk1fTVlTUUwiLCAiREJNIl0sICJub3RpY2UiOiB7ImNvbmZpZyI6IHsidGVtcGxhdGUiOiBbeyJzaWduYWwiOiAiYWJub3JtYWwiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJyZWNvdmVyZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJjbG9zZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9XSwgIm5lZWRfcG9sbCI6IHRydWUsICJub3RpZnlfaW50ZXJ2YWwiOiA3MjAwLCAiaW50ZXJ2YWxfbm90aWZ5X21vZGUiOiAic3RhbmRhcmQifSwgInNpZ25hbCI6IFsiYWJub3JtYWwiLCAibm9fZGF0YSJdLCAib3B0aW9ucyI6IHsiZW5kX3RpbWUiOiAiMjM6NTk6NTkiLCAic3RhcnRfdGltZSI6ICIwMDowMDowMCIsICJhc3NpZ25fbW9kZSI6IFtdLCAidXBncmFkZV9jb25maWciOiB7fSwgImNvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAxLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzdHJhdGVneV9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiZGltZW5zaW9ucyJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifV0sICJ0aW1lZGVsdGEiOiA2MCwgImlzX2VuYWJsZWQiOiB0cnVlLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0IiwgIm5lZWRfYml6X2NvbnZlcmdlIjogdHJ1ZSwgInN1Yl9jb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMiwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifV0sICJ0aW1lZGVsdGEiOiA2MCwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdF9hbGFybSJ9fSwgImV4Y2x1ZGVfbm90aWNlX3dheXMiOiB7ImNsb3NlZCI6IFtdLCAicmVjb3ZlcmVkIjogW119LCAibm9pc2VfcmVkdWNlX2NvbmZpZyI6IHsidW5pdCI6ICJwZXJjZW50IiwgImNvdW50IjogMTAsICJ0aW1lZGVsdGEiOiA1LCAiZGltZW5zaW9ucyI6IFtdLCAiaXNfZW5hYmxlZCI6IGZhbHNlfX0sICJjb25maWdfaWQiOiA4ODE2LCAicmVsYXRlX3R5cGUiOiAiTk9USUNFIiwgInVzZXJfZ3JvdXBzIjogWzBdfSwgInNvdXJjZSI6ICJia21vbml0b3J2MyIsICJhY3Rpb25zIjogW10sICJkZXRlY3RzIjogW3sibGV2ZWwiOiAyLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJjb21wb25lbnQiLCAiYmtfYml6X2lkIjogMjAwNTAwMDE5NCwgImRhdGFfc291cmNlX3R5cGUiOiAiXHU3NmQxXHU2M2E3XHU5MWM3XHU5NmM2XHU2MzA3XHU2ODA3In0sICJpc19lbmFibGVkIjogdHJ1ZX0= \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5763.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5763.tpl64 deleted file mode 100644 index a339db83b8..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.5763.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU3NjMsICJuYW1lIjogIk15U1FMIHByb3h5XHU4ZmRiXHU3YTBiZG93biIsICJkYl90eXBlIjogIm15c3FsIiwgImRldGFpbHMiOiB7ImFwcCI6ICIiLCAibmFtZSI6ICJNeVNRTCBwcm94eVx1OGZkYlx1N2EwYmRvd24iLCAicGF0aCI6ICIiLCAidHlwZSI6ICJtb25pdG9yIiwgIml0ZW1zIjogW3sibmFtZSI6ICJBVkcobXlzcWxwcm94eV91cCkiLCAidGFyZ2V0IjogW1t7ImZpZWxkIjogImhvc3RfdG9wb19ub2RlIiwgInZhbHVlIjogW3siYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE0OH0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE0OX0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE0OX1dLCAibWV0aG9kIjogImVxIn1dXSwgImZ1bmN0aW9ucyI6IFtdLCAiYWxnb3JpdGhtcyI6IFt7ImlkIjogNjMwNiwgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMiwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZXEiLCAidGhyZXNob2xkIjogMH1dXSwgInVuaXRfcHJlZml4IjogIiJ9XSwgImV4cHJlc3Npb24iOiAiYSIsICJvcmlnaW5fc3FsIjogIiIsICJxdWVyeV9jb25maWdzIjogW3siaWQiOiA2MzAzLCAibmFtZSI6ICJteXNxbHByb3h5X3VwIiwgInVuaXQiOiAiIiwgImFsaWFzIjogImEiLCAiZnVuY3Rpb25zIjogW10sICJtZXRyaWNfaWQiOiAiYmtfbW9uaXRvci5leHBvcnRlcl9kYm1fbXlzcWxwcm94eV9leHBvcnRlci5tcHJveHkubXlzcWxwcm94eV91cCIsICJhZ2dfbWV0aG9kIjogIkFWRyIsICJhZ2dfaW50ZXJ2YWwiOiA2MCwgIm1ldHJpY19maWVsZCI6ICJteXNxbHByb3h5X3VwIiwgImFnZ19jb25kaXRpb24iOiBbXSwgImFnZ19kaW1lbnNpb24iOiBbImJrX3RhcmdldF9zZXJ2aWNlX2luc3RhbmNlX2lkIiwgImFwcCIsICJjbHVzdGVyX2RvbWFpbiIsICJpbnN0YW5jZSJdLCAiZGF0YV90eXBlX2xhYmVsIjogInRpbWVfc2VyaWVzIiwgInJlc3VsdF90YWJsZV9pZCI6ICJleHBvcnRlcl9kYm1fbXlzcWxwcm94eV9leHBvcnRlci5tcHJveHkiLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAiYmtfbW9uaXRvciJ9XSwgIm5vX2RhdGFfY29uZmlnIjogeyJsZXZlbCI6IDIsICJjb250aW51b3VzIjogMTAsICJpc19lbmFibGVkIjogZmFsc2UsICJhZ2dfZGltZW5zaW9uIjogWyJia190YXJnZXRfc2VydmljZV9pbnN0YW5jZV9pZCJdfX1dLCAibGFiZWxzIjogWyJNeVNRTCIsICJEQk1fTVlTUUwiLCAiREJNX01ZU1FMIiwgIkRCTSJdLCAibm90aWNlIjogeyJjb25maWciOiB7InRlbXBsYXRlIjogW3sic2lnbmFsIjogImFibm9ybWFsIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAicmVjb3ZlcmVkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAiY2xvc2VkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifV0sICJuZWVkX3BvbGwiOiB0cnVlLCAibm90aWZ5X2ludGVydmFsIjogNzIwMCwgImludGVydmFsX25vdGlmeV9tb2RlIjogInN0YW5kYXJkIn0sICJzaWduYWwiOiBbImFibm9ybWFsIiwgIm5vX2RhdGEiXSwgIm9wdGlvbnMiOiB7ImVuZF90aW1lIjogIjIzOjU5OjU5IiwgInN0YXJ0X3RpbWUiOiAiMDA6MDA6MDAiLCAiYXNzaWduX21vZGUiOiBbXSwgInVwZ3JhZGVfY29uZmlnIjoge30sICJjb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMSwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic3RyYXRlZ3lfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImRpbWVuc2lvbnMifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In1dLCAidGltZWRlbHRhIjogNjAsICJpc19lbmFibGVkIjogdHJ1ZSwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdCIsICJuZWVkX2Jpel9jb252ZXJnZSI6IHRydWUsICJzdWJfY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDIsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn1dLCAidGltZWRlbHRhIjogNjAsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3RfYWxhcm0ifX0sICJleGNsdWRlX25vdGljZV93YXlzIjogeyJjbG9zZWQiOiBbXSwgInJlY292ZXJlZCI6IFtdfSwgIm5vaXNlX3JlZHVjZV9jb25maWciOiB7InVuaXQiOiAicGVyY2VudCIsICJjb3VudCI6IDEwLCAidGltZWRlbHRhIjogNSwgImRpbWVuc2lvbnMiOiBbXSwgImlzX2VuYWJsZWQiOiBmYWxzZX19LCAiY29uZmlnX2lkIjogODgxNywgInJlbGF0ZV90eXBlIjogIk5PVElDRSIsICJ1c2VyX2dyb3VwcyI6IFswXX0sICJzb3VyY2UiOiAiYmttb25pdG9ydjMiLCAiYWN0aW9ucyI6IFtdLCAiZGV0ZWN0cyI6IFt7ImxldmVsIjogMiwgImNvbm5lY3RvciI6ICJhbmQiLCAiZXhwcmVzc2lvbiI6ICIiLCAidHJpZ2dlcl9jb25maWciOiB7ImNvdW50IjogMSwgInVwdGltZSI6IHsiY2FsZW5kYXJzIjogW10sICJ0aW1lX3JhbmdlcyI6IFt7ImVuZCI6ICIyMzo1OSIsICJzdGFydCI6ICIwMDowMCJ9XX0sICJjaGVja193aW5kb3ciOiA1fSwgInJlY292ZXJ5X2NvbmZpZyI6IHsiY2hlY2tfd2luZG93IjogNSwgInN0YXR1c19zZXR0ZXIiOiAicmVjb3ZlcnkifX1dLCAic2NlbmFyaW8iOiAiY29tcG9uZW50IiwgImJrX2Jpel9pZCI6IDIwMDUwMDAxOTQsICJkYXRhX3NvdXJjZV90eXBlIjogIlx1NzZkMVx1NjNhN1x1OTFjN1x1OTZjNlx1NjMwN1x1NjgwNyJ9LCAiaXNfZW5hYmxlZCI6IHRydWV9 \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73363.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73363.tpl64 deleted file mode 100644 index b291396020..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73363.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDczMzYzLCAibmFtZSI6ICJNeVNRTCBcdThmZGVcdTYzYTVcdTU5MzFcdThkMjUiLCAiZGJfdHlwZSI6ICJteXNxbCIsICJkZXRhaWxzIjogeyJhcHAiOiAiIiwgIm5hbWUiOiAiTXlTUUwgXHU4ZmRlXHU2M2E1XHU1OTMxXHU4ZDI1IiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAiQ09VTlQoZGItdXApIiwgInRhcmdldCI6IFtbeyJmaWVsZCI6ICJob3N0X3RvcG9fbm9kZSIsICJ2YWx1ZSI6IFt7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgxNDh9LCB7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgxNDl9LCB7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgxNDl9XSwgIm1ldGhvZCI6ICJlcSJ9XV0sICJmdW5jdGlvbnMiOiBbXSwgImFsZ29yaXRobXMiOiBbeyJpZCI6IDc4Njk1LCAidHlwZSI6ICJUaHJlc2hvbGQiLCAibGV2ZWwiOiAxLCAiY29uZmlnIjogW1t7Im1ldGhvZCI6ICJndGUiLCAidGhyZXNob2xkIjogMX1dXSwgInVuaXRfcHJlZml4IjogIiJ9XSwgImV4cHJlc3Npb24iOiAiYSIsICJvcmlnaW5fc3FsIjogIiIsICJxdWVyeV9jb25maWdzIjogW3siaWQiOiA3Nzc5NSwgIm5hbWUiOiAiZGItdXAiLCAiYWxpYXMiOiAiYSIsICJmdW5jdGlvbnMiOiBbXSwgIm1ldHJpY19pZCI6ICJjdXN0b20uZXZlbnQue2JrX2Jpel9pZH1fYmttb25pdG9yX2V2ZW50X3tldmVudF9kYXRhX2lkfS5kYi11cCIsICJhZ2dfbWV0aG9kIjogIkNPVU5UIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAiYWdnX2NvbmRpdGlvbiI6IFtdLCAiYWdnX2RpbWVuc2lvbiI6IFsic2VydmVyX2lwIiwgInBvcnQiLCAiaW1tdXRlX2RvbWFpbiIsICJia19jbG91ZF9pZCIsICJtYWNoaW5lX3R5cGUiLCAicm9sZSIsICJhcHBfaWQiXSwgImRhdGFfdHlwZV9sYWJlbCI6ICJldmVudCIsICJyZXN1bHRfdGFibGVfaWQiOiAie2JrX2Jpel9pZH1fYmttb25pdG9yX2V2ZW50X3tldmVudF9kYXRhX2lkfSIsICJjdXN0b21fZXZlbnRfbmFtZSI6ICJkYi11cCIsICJkYXRhX3NvdXJjZV9sYWJlbCI6ICJjdXN0b20ifV0sICJub19kYXRhX2NvbmZpZyI6IHsibGV2ZWwiOiAyLCAiY29udGludW91cyI6IDEwLCAiaXNfZW5hYmxlZCI6IGZhbHNlLCAiYWdnX2RpbWVuc2lvbiI6IFtdfX1dLCAibGFiZWxzIjogWyJEQk1fTVlTUUwiLCAiREJNIl0sICJub3RpY2UiOiB7ImNvbmZpZyI6IHsidGVtcGxhdGUiOiBbeyJzaWduYWwiOiAiYWJub3JtYWwiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJyZWNvdmVyZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJjbG9zZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9XSwgIm5lZWRfcG9sbCI6IHRydWUsICJub3RpZnlfaW50ZXJ2YWwiOiA3MjAwLCAiaW50ZXJ2YWxfbm90aWZ5X21vZGUiOiAic3RhbmRhcmQifSwgInNpZ25hbCI6IFsiYWJub3JtYWwiLCAibm9fZGF0YSJdLCAib3B0aW9ucyI6IHsiZW5kX3RpbWUiOiAiMjM6NTk6NTkiLCAic3RhcnRfdGltZSI6ICIwMDowMDowMCIsICJhc3NpZ25fbW9kZSI6IFsib25seV9ub3RpY2UiLCAiYnlfcnVsZSJdLCAiY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDEsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInN0cmF0ZWd5X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJkaW1lbnNpb25zIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJia19iaXpfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV9yZWNlaXZlciJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3dheSJ9XSwgInRpbWVkZWx0YSI6IDYwLCAiaXNfZW5hYmxlZCI6IHRydWUsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3QiLCAibmVlZF9iaXpfY29udmVyZ2UiOiB0cnVlLCAic3ViX2NvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAyLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJia19iaXpfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV9yZWNlaXZlciJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3dheSJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9XSwgInRpbWVkZWx0YSI6IDYwLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0X2FsYXJtIn19LCAiY2hhcnRfaW1hZ2VfZW5hYmxlZCI6IHRydWUsICJleGNsdWRlX25vdGljZV93YXlzIjogeyJjbG9zZWQiOiBbXSwgInJlY292ZXJlZCI6IFtdfSwgIm5vaXNlX3JlZHVjZV9jb25maWciOiB7InVuaXQiOiAicGVyY2VudCIsICJjb3VudCI6IDEwLCAidGltZWRlbHRhIjogNSwgImRpbWVuc2lvbnMiOiBbXSwgImlzX2VuYWJsZWQiOiBmYWxzZX19LCAiY29uZmlnX2lkIjogNzIwMjAsICJyZWxhdGVfdHlwZSI6ICJOT1RJQ0UiLCAidXNlcl9ncm91cHMiOiBbMF19LCAic291cmNlIjogImJrbW9uaXRvcnYzIiwgImFjdGlvbnMiOiBbXSwgImRldGVjdHMiOiBbeyJsZXZlbCI6IDEsICJjb25uZWN0b3IiOiAiYW5kIiwgImV4cHJlc3Npb24iOiAiIiwgInRyaWdnZXJfY29uZmlnIjogeyJjb3VudCI6IDEsICJ1cHRpbWUiOiB7ImNhbGVuZGFycyI6IFtdLCAidGltZV9yYW5nZXMiOiBbeyJlbmQiOiAiMjM6NTkiLCAic3RhcnQiOiAiMDA6MDAifV19LCAiY2hlY2tfd2luZG93IjogNX0sICJyZWNvdmVyeV9jb25maWciOiB7ImNoZWNrX3dpbmRvdyI6IDUsICJzdGF0dXNfc2V0dGVyIjogInJlY292ZXJ5In19XSwgInNjZW5hcmlvIjogIm90aGVyX3J0IiwgImJrX2Jpel9pZCI6IDEwMDQ2NSwgImRhdGFfc291cmNlX3R5cGUiOiAiXHU4MWVhXHU1YjlhXHU0ZTQ5XHU0ZThiXHU0ZWY2In0sICJpc19lbmFibGVkIjogdHJ1ZX0= \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73370.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73370.tpl64 deleted file mode 100644 index 0b1d9c5d1c..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73370.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDczMzcwLCAibmFtZSI6ICJNeVNRTCBTbGF2ZSBcdTU0MGNcdTZiNjVcdTVmMDJcdTVlMzgiLCAiZGJfdHlwZSI6ICJteXNxbCIsICJkZXRhaWxzIjogeyJhcHAiOiAiIiwgIm5hbWUiOiAiTXlTUUwgU2xhdmUgXHU1NDBjXHU2YjY1XHU1ZjAyXHU1ZTM4IiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAiQ09VTlQoc2xhdmUtc3RhdHVzKSIsICJ0YXJnZXQiOiBbW3siZmllbGQiOiAiaG9zdF90b3BvX25vZGUiLCAidmFsdWUiOiBbeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ4fSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ5fSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ5fV0sICJtZXRob2QiOiAiZXEifV1dLCAiZnVuY3Rpb25zIjogW10sICJhbGdvcml0aG1zIjogW3siaWQiOiA3ODcwNSwgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMSwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDF9XV0sICJ1bml0X3ByZWZpeCI6ICIifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICIiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNzc4MDIsICJuYW1lIjogInNsYXZlLXN0YXR1cyIsICJhbGlhcyI6ICJhIiwgImZ1bmN0aW9ucyI6IFtdLCAibWV0cmljX2lkIjogImN1c3RvbS5ldmVudC57YmtfYml6X2lkfV9ia21vbml0b3JfZXZlbnRfe2V2ZW50X2RhdGFfaWR9LnNsYXZlLXN0YXR1cyIsICJhZ2dfbWV0aG9kIjogIkNPVU5UIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAiYWdnX2NvbmRpdGlvbiI6IFtdLCAiYWdnX2RpbWVuc2lvbiI6IFsiYXBwX2lkIiwgImJrX2Nsb3VkX2lkIiwgImltbXV0ZV9kb21haW4iLCAibWFjaGluZV90eXBlIiwgInBvcnQiLCAicm9sZSIsICJzZXJ2ZXJfaXAiXSwgImRhdGFfdHlwZV9sYWJlbCI6ICJldmVudCIsICJyZXN1bHRfdGFibGVfaWQiOiAie2JrX2Jpel9pZH1fYmttb25pdG9yX2V2ZW50X3tldmVudF9kYXRhX2lkfSIsICJjdXN0b21fZXZlbnRfbmFtZSI6ICJzbGF2ZS1zdGF0dXMiLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAiY3VzdG9tIn1dLCAibm9fZGF0YV9jb25maWciOiB7ImxldmVsIjogMiwgImNvbnRpbnVvdXMiOiAxMCwgImlzX2VuYWJsZWQiOiBmYWxzZSwgImFnZ19kaW1lbnNpb24iOiBbXX19XSwgImxhYmVscyI6IFsiREJNX01ZU1FMIiwgIkRCTSJdLCAibm90aWNlIjogeyJjb25maWciOiB7InRlbXBsYXRlIjogW3sic2lnbmFsIjogImFibm9ybWFsIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAicmVjb3ZlcmVkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAiY2xvc2VkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifV0sICJuZWVkX3BvbGwiOiB0cnVlLCAibm90aWZ5X2ludGVydmFsIjogNzIwMCwgImludGVydmFsX25vdGlmeV9tb2RlIjogInN0YW5kYXJkIn0sICJzaWduYWwiOiBbImFibm9ybWFsIiwgIm5vX2RhdGEiXSwgIm9wdGlvbnMiOiB7ImVuZF90aW1lIjogIjIzOjU5OjU5IiwgInN0YXJ0X3RpbWUiOiAiMDA6MDA6MDAiLCAiYXNzaWduX21vZGUiOiBbIm9ubHlfbm90aWNlIiwgImJ5X3J1bGUiXSwgImNvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAxLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzdHJhdGVneV9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiZGltZW5zaW9ucyJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifV0sICJ0aW1lZGVsdGEiOiA2MCwgImlzX2VuYWJsZWQiOiB0cnVlLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0IiwgIm5lZWRfYml6X2NvbnZlcmdlIjogdHJ1ZSwgInN1Yl9jb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMiwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifV0sICJ0aW1lZGVsdGEiOiA2MCwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdF9hbGFybSJ9fSwgImNoYXJ0X2ltYWdlX2VuYWJsZWQiOiB0cnVlLCAiZXhjbHVkZV9ub3RpY2Vfd2F5cyI6IHsiY2xvc2VkIjogW10sICJyZWNvdmVyZWQiOiBbXX0sICJub2lzZV9yZWR1Y2VfY29uZmlnIjogeyJ1bml0IjogInBlcmNlbnQiLCAiY291bnQiOiAxMCwgInRpbWVkZWx0YSI6IDUsICJkaW1lbnNpb25zIjogW10sICJpc19lbmFibGVkIjogZmFsc2V9fSwgImNvbmZpZ19pZCI6IDcyMDM0LCAicmVsYXRlX3R5cGUiOiAiTk9USUNFIiwgInVzZXJfZ3JvdXBzIjogWzBdfSwgInNvdXJjZSI6ICJia21vbml0b3J2MyIsICJhY3Rpb25zIjogW10sICJkZXRlY3RzIjogW3sibGV2ZWwiOiAxLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJvdGhlcl9ydCIsICJia19iaXpfaWQiOiAxMDA0NjUsICJkYXRhX3NvdXJjZV90eXBlIjogIlx1ODFlYVx1NWI5YVx1NGU0OVx1NGU4Ylx1NGVmNiJ9LCAiaXNfZW5hYmxlZCI6IHRydWV9 \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73376.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73376.tpl64 deleted file mode 100644 index bacdbd636e..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73376.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDczMzc2LCAibmFtZSI6ICJNeVNRTCBNb25pdG9yIFx1NjI2N1x1ODg0Y1x1NWYwMlx1NWUzOCIsICJkYl90eXBlIjogIm15c3FsIiwgImRldGFpbHMiOiB7ImFwcCI6ICIiLCAibmFtZSI6ICJNeVNRTCBNb25pdG9yIFx1NjI2N1x1ODg0Y1x1NWYwMlx1NWUzOCIsICJwYXRoIjogIiIsICJ0eXBlIjogIm1vbml0b3IiLCAiaXRlbXMiOiBbeyJuYW1lIjogIkNPVU5UKG1vbml0b3ItaW50ZXJuYWwtZXJyb3IpIiwgInRhcmdldCI6IFtbeyJmaWVsZCI6ICJob3N0X3RvcG9fbm9kZSIsICJ2YWx1ZSI6IFt7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgxNDh9LCB7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgxNDl9LCB7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgxNDl9XSwgIm1ldGhvZCI6ICJlcSJ9XV0sICJmdW5jdGlvbnMiOiBbXSwgImFsZ29yaXRobXMiOiBbeyJpZCI6IDc4NzExLCAidHlwZSI6ICJUaHJlc2hvbGQiLCAibGV2ZWwiOiAyLCAiY29uZmlnIjogW1t7Im1ldGhvZCI6ICJndGUiLCAidGhyZXNob2xkIjogMX1dXSwgInVuaXRfcHJlZml4IjogIiJ9XSwgImV4cHJlc3Npb24iOiAiYSIsICJvcmlnaW5fc3FsIjogIiIsICJxdWVyeV9jb25maWdzIjogW3siaWQiOiA3NzgwOSwgIm5hbWUiOiAibW9uaXRvci1pbnRlcm5hbC1lcnJvciIsICJhbGlhcyI6ICJhIiwgImZ1bmN0aW9ucyI6IFtdLCAibWV0cmljX2lkIjogImN1c3RvbS5ldmVudC57YmtfYml6X2lkfV9ia21vbml0b3JfZXZlbnRfe2V2ZW50X2RhdGFfaWR9Lm1vbml0b3ItaW50ZXJuYWwtZXJyb3IiLCAiYWdnX21ldGhvZCI6ICJDT1VOVCIsICJhZ2dfaW50ZXJ2YWwiOiA2MCwgImFnZ19jb25kaXRpb24iOiBbXSwgImFnZ19kaW1lbnNpb24iOiBbImFwcF9pZCIsICJia19jbG91ZF9pZCIsICJpbW11dGVfZG9tYWluIiwgIm1hY2hpbmVfdHlwZSIsICJwb3J0IiwgInJvbGUiLCAic2VydmVyX2lwIl0sICJkYXRhX3R5cGVfbGFiZWwiOiAiZXZlbnQiLCAicmVzdWx0X3RhYmxlX2lkIjogIntia19iaXpfaWR9X2JrbW9uaXRvcl9ldmVudF97ZXZlbnRfZGF0YV9pZH0iLCAiY3VzdG9tX2V2ZW50X25hbWUiOiAibW9uaXRvci1pbnRlcm5hbC1lcnJvciIsICJkYXRhX3NvdXJjZV9sYWJlbCI6ICJjdXN0b20ifV0sICJub19kYXRhX2NvbmZpZyI6IHsibGV2ZWwiOiAyLCAiY29udGludW91cyI6IDEwLCAiaXNfZW5hYmxlZCI6IGZhbHNlLCAiYWdnX2RpbWVuc2lvbiI6IFtdfX1dLCAibGFiZWxzIjogWyJEQk1fTVlTUUwiLCAiREJNIl0sICJub3RpY2UiOiB7ImNvbmZpZyI6IHsidGVtcGxhdGUiOiBbeyJzaWduYWwiOiAiYWJub3JtYWwiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJyZWNvdmVyZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJjbG9zZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9XSwgIm5lZWRfcG9sbCI6IHRydWUsICJub3RpZnlfaW50ZXJ2YWwiOiA3MjAwLCAiaW50ZXJ2YWxfbm90aWZ5X21vZGUiOiAic3RhbmRhcmQifSwgInNpZ25hbCI6IFsiYWJub3JtYWwiLCAibm9fZGF0YSJdLCAib3B0aW9ucyI6IHsiZW5kX3RpbWUiOiAiMjM6NTk6NTkiLCAic3RhcnRfdGltZSI6ICIwMDowMDowMCIsICJhc3NpZ25fbW9kZSI6IFsib25seV9ub3RpY2UiLCAiYnlfcnVsZSJdLCAiY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDEsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInN0cmF0ZWd5X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJkaW1lbnNpb25zIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJia19iaXpfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV9yZWNlaXZlciJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3dheSJ9XSwgInRpbWVkZWx0YSI6IDYwLCAiaXNfZW5hYmxlZCI6IHRydWUsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3QiLCAibmVlZF9iaXpfY29udmVyZ2UiOiB0cnVlLCAic3ViX2NvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAyLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJia19iaXpfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV9yZWNlaXZlciJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3dheSJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9XSwgInRpbWVkZWx0YSI6IDYwLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0X2FsYXJtIn19LCAiY2hhcnRfaW1hZ2VfZW5hYmxlZCI6IHRydWUsICJleGNsdWRlX25vdGljZV93YXlzIjogeyJjbG9zZWQiOiBbXSwgInJlY292ZXJlZCI6IFtdfSwgIm5vaXNlX3JlZHVjZV9jb25maWciOiB7InVuaXQiOiAicGVyY2VudCIsICJjb3VudCI6IDEwLCAidGltZWRlbHRhIjogNSwgImRpbWVuc2lvbnMiOiBbXSwgImlzX2VuYWJsZWQiOiBmYWxzZX19LCAiY29uZmlnX2lkIjogNzIwNDEsICJyZWxhdGVfdHlwZSI6ICJOT1RJQ0UiLCAidXNlcl9ncm91cHMiOiBbMF19LCAic291cmNlIjogImJrbW9uaXRvcnYzIiwgImFjdGlvbnMiOiBbXSwgImRldGVjdHMiOiBbeyJsZXZlbCI6IDIsICJjb25uZWN0b3IiOiAiYW5kIiwgImV4cHJlc3Npb24iOiAiIiwgInRyaWdnZXJfY29uZmlnIjogeyJjb3VudCI6IDEsICJ1cHRpbWUiOiB7ImNhbGVuZGFycyI6IFtdLCAidGltZV9yYW5nZXMiOiBbeyJlbmQiOiAiMjM6NTkiLCAic3RhcnQiOiAiMDA6MDAifV19LCAiY2hlY2tfd2luZG93IjogNX0sICJyZWNvdmVyeV9jb25maWciOiB7ImNoZWNrX3dpbmRvdyI6IDUsICJzdGF0dXNfc2V0dGVyIjogInJlY292ZXJ5In19XSwgInNjZW5hcmlvIjogIm90aGVyX3J0IiwgImJrX2Jpel9pZCI6IDEwMDQ2NSwgImRhdGFfc291cmNlX3R5cGUiOiAiXHU4MWVhXHU1YjlhXHU0ZTQ5XHU0ZThiXHU0ZWY2In0sICJpc19lbmFibGVkIjogdHJ1ZX0= \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73377.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73377.tpl64 deleted file mode 100644 index 6c4c4ee48b..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73377.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDczMzc3LCAibmFtZSI6ICJNeVNRTCBcdTkxNGRcdTdmNmVcdTVmMDJcdTVlMzgiLCAiZGJfdHlwZSI6ICJteXNxbCIsICJkZXRhaWxzIjogeyJhcHAiOiAiIiwgIm5hbWUiOiAiTXlTUUwgXHU5MTRkXHU3ZjZlXHU1ZjAyXHU1ZTM4IiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAiQ09VTlQobXlzcWwtY29uZmlnLWRpZmYpIiwgInRhcmdldCI6IFtbeyJmaWVsZCI6ICJob3N0X3RvcG9fbm9kZSIsICJ2YWx1ZSI6IFt7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgxNDh9LCB7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgxNDl9LCB7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgxNDl9XSwgIm1ldGhvZCI6ICJlcSJ9XV0sICJmdW5jdGlvbnMiOiBbXSwgImFsZ29yaXRobXMiOiBbeyJpZCI6IDc4NzEyLCAidHlwZSI6ICJUaHJlc2hvbGQiLCAibGV2ZWwiOiAyLCAiY29uZmlnIjogW1t7Im1ldGhvZCI6ICJndGUiLCAidGhyZXNob2xkIjogMX1dXSwgInVuaXRfcHJlZml4IjogIiJ9XSwgImV4cHJlc3Npb24iOiAiYSIsICJvcmlnaW5fc3FsIjogIiIsICJxdWVyeV9jb25maWdzIjogW3siaWQiOiA3NzgxMCwgIm5hbWUiOiAibXlzcWwtY29uZmlnLWRpZmYiLCAiYWxpYXMiOiAiYSIsICJmdW5jdGlvbnMiOiBbXSwgIm1ldHJpY19pZCI6ICJjdXN0b20uZXZlbnQue2JrX2Jpel9pZH1fYmttb25pdG9yX2V2ZW50X3tldmVudF9kYXRhX2lkfS5teXNxbC1jb25maWctZGlmZiIsICJhZ2dfbWV0aG9kIjogIkNPVU5UIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAiYWdnX2NvbmRpdGlvbiI6IFtdLCAiYWdnX2RpbWVuc2lvbiI6IFsiYmtfY2xvdWRfaWQiLCAicG9ydCIsICJhcHBfaWQiLCAic2VydmVyX2lwIiwgInJvbGUiLCAiaW1tdXRlX2RvbWFpbiIsICJtYWNoaW5lX3R5cGUiXSwgImRhdGFfdHlwZV9sYWJlbCI6ICJldmVudCIsICJyZXN1bHRfdGFibGVfaWQiOiAie2JrX2Jpel9pZH1fYmttb25pdG9yX2V2ZW50X3tldmVudF9kYXRhX2lkfSIsICJjdXN0b21fZXZlbnRfbmFtZSI6ICJteXNxbC1jb25maWctZGlmZiIsICJkYXRhX3NvdXJjZV9sYWJlbCI6ICJjdXN0b20ifV0sICJub19kYXRhX2NvbmZpZyI6IHsibGV2ZWwiOiAyLCAiY29udGludW91cyI6IDEwLCAiaXNfZW5hYmxlZCI6IGZhbHNlLCAiYWdnX2RpbWVuc2lvbiI6IFtdfX1dLCAibGFiZWxzIjogWyJEQk1fTVlTUUwiLCAiREJNIl0sICJub3RpY2UiOiB7ImNvbmZpZyI6IHsidGVtcGxhdGUiOiBbeyJzaWduYWwiOiAiYWJub3JtYWwiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJyZWNvdmVyZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJjbG9zZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9XSwgIm5lZWRfcG9sbCI6IHRydWUsICJub3RpZnlfaW50ZXJ2YWwiOiA3MjAwLCAiaW50ZXJ2YWxfbm90aWZ5X21vZGUiOiAic3RhbmRhcmQifSwgInNpZ25hbCI6IFsibm9fZGF0YSIsICJhYm5vcm1hbCJdLCAib3B0aW9ucyI6IHsiZW5kX3RpbWUiOiAiMjM6NTk6NTkiLCAic3RhcnRfdGltZSI6ICIwMDowMDowMCIsICJhc3NpZ25fbW9kZSI6IFsib25seV9ub3RpY2UiLCAiYnlfcnVsZSJdLCAiY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDEsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInN0cmF0ZWd5X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJkaW1lbnNpb25zIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJia19iaXpfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV9yZWNlaXZlciJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3dheSJ9XSwgInRpbWVkZWx0YSI6IDYwLCAiaXNfZW5hYmxlZCI6IHRydWUsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3QiLCAibmVlZF9iaXpfY29udmVyZ2UiOiB0cnVlLCAic3ViX2NvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAyLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJia19iaXpfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV9yZWNlaXZlciJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3dheSJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9XSwgInRpbWVkZWx0YSI6IDYwLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0X2FsYXJtIn19LCAiY2hhcnRfaW1hZ2VfZW5hYmxlZCI6IHRydWUsICJleGNsdWRlX25vdGljZV93YXlzIjogeyJjbG9zZWQiOiBbXSwgInJlY292ZXJlZCI6IFtdfSwgIm5vaXNlX3JlZHVjZV9jb25maWciOiB7InVuaXQiOiAicGVyY2VudCIsICJjb3VudCI6IDEwLCAidGltZWRlbHRhIjogNSwgImRpbWVuc2lvbnMiOiBbXSwgImlzX2VuYWJsZWQiOiBmYWxzZX19LCAiY29uZmlnX2lkIjogNzIwNDIsICJyZWxhdGVfdHlwZSI6ICJOT1RJQ0UiLCAidXNlcl9ncm91cHMiOiBbMF19LCAic291cmNlIjogImJrbW9uaXRvcnYzIiwgImFjdGlvbnMiOiBbXSwgImRldGVjdHMiOiBbeyJsZXZlbCI6IDIsICJjb25uZWN0b3IiOiAiYW5kIiwgImV4cHJlc3Npb24iOiAiIiwgInRyaWdnZXJfY29uZmlnIjogeyJjb3VudCI6IDEsICJ1cHRpbWUiOiB7ImNhbGVuZGFycyI6IFtdLCAidGltZV9yYW5nZXMiOiBbeyJlbmQiOiAiMjM6NTkiLCAic3RhcnQiOiAiMDA6MDAifV19LCAiY2hlY2tfd2luZG93IjogNX0sICJyZWNvdmVyeV9jb25maWciOiB7ImNoZWNrX3dpbmRvdyI6IDUsICJzdGF0dXNfc2V0dGVyIjogInJlY292ZXJ5In19XSwgInNjZW5hcmlvIjogIm90aGVyX3J0IiwgImJrX2Jpel9pZCI6IDEwMDQ2NSwgImRhdGFfc291cmNlX3R5cGUiOiAiXHU4MWVhXHU1YjlhXHU0ZTQ5XHU0ZThiXHU0ZWY2In0sICJpc19lbmFibGVkIjogdHJ1ZX0= \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73378.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73378.tpl64 deleted file mode 100644 index 343dbdecc0..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73378.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDczMzc4LCAibmFtZSI6ICJNeVNRTCBcdThmZGVcdTYzYTVcdTY1ZTVcdTVmZDdcdThmYzdcdTU5MWEiLCAiZGJfdHlwZSI6ICJteXNxbCIsICJkZXRhaWxzIjogeyJhcHAiOiAiIiwgIm5hbWUiOiAiTXlTUUwgXHU4ZmRlXHU2M2E1XHU2NWU1XHU1ZmQ3XHU4ZmM3XHU1OTFhIiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAiQ09VTlQobXlzcWwtY29ubmxvZy1zaXplKSIsICJ0YXJnZXQiOiBbW3siZmllbGQiOiAiaG9zdF90b3BvX25vZGUiLCAidmFsdWUiOiBbeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ4fSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ5fSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ5fV0sICJtZXRob2QiOiAiZXEifV1dLCAiZnVuY3Rpb25zIjogW10sICJhbGdvcml0aG1zIjogW3siaWQiOiA3ODcxMywgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMiwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDF9XV0sICJ1bml0X3ByZWZpeCI6ICIifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICIiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNzc4MTEsICJuYW1lIjogIm15c3FsLWNvbm5sb2ctc2l6ZSIsICJhbGlhcyI6ICJhIiwgImZ1bmN0aW9ucyI6IFtdLCAibWV0cmljX2lkIjogImN1c3RvbS5ldmVudC57YmtfYml6X2lkfV9ia21vbml0b3JfZXZlbnRfe2V2ZW50X2RhdGFfaWR9Lm15c3FsLWNvbm5sb2ctc2l6ZSIsICJhZ2dfbWV0aG9kIjogIkNPVU5UIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAiYWdnX2NvbmRpdGlvbiI6IFtdLCAiYWdnX2RpbWVuc2lvbiI6IFsiYmtfY2xvdWRfaWQiLCAicG9ydCIsICJhcHBfaWQiLCAic2VydmVyX2lwIiwgInJvbGUiLCAiaW1tdXRlX2RvbWFpbiIsICJtYWNoaW5lX3R5cGUiXSwgImRhdGFfdHlwZV9sYWJlbCI6ICJldmVudCIsICJyZXN1bHRfdGFibGVfaWQiOiAie2JrX2Jpel9pZH1fYmttb25pdG9yX2V2ZW50X3tldmVudF9kYXRhX2lkfSIsICJjdXN0b21fZXZlbnRfbmFtZSI6ICJteXNxbC1jb25ubG9nLXNpemUiLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAiY3VzdG9tIn1dLCAibm9fZGF0YV9jb25maWciOiB7ImxldmVsIjogMiwgImNvbnRpbnVvdXMiOiAxMCwgImlzX2VuYWJsZWQiOiBmYWxzZSwgImFnZ19kaW1lbnNpb24iOiBbXX19XSwgImxhYmVscyI6IFsiREJNX01ZU1FMIiwgIkRCTSJdLCAibm90aWNlIjogeyJjb25maWciOiB7InRlbXBsYXRlIjogW3sic2lnbmFsIjogImFibm9ybWFsIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAicmVjb3ZlcmVkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAiY2xvc2VkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifV0sICJuZWVkX3BvbGwiOiB0cnVlLCAibm90aWZ5X2ludGVydmFsIjogNzIwMCwgImludGVydmFsX25vdGlmeV9tb2RlIjogInN0YW5kYXJkIn0sICJzaWduYWwiOiBbIm5vX2RhdGEiLCAiYWJub3JtYWwiXSwgIm9wdGlvbnMiOiB7ImVuZF90aW1lIjogIjIzOjU5OjU5IiwgInN0YXJ0X3RpbWUiOiAiMDA6MDA6MDAiLCAiYXNzaWduX21vZGUiOiBbIm9ubHlfbm90aWNlIiwgImJ5X3J1bGUiXSwgImNvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAxLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzdHJhdGVneV9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiZGltZW5zaW9ucyJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifV0sICJ0aW1lZGVsdGEiOiA2MCwgImlzX2VuYWJsZWQiOiB0cnVlLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0IiwgIm5lZWRfYml6X2NvbnZlcmdlIjogdHJ1ZSwgInN1Yl9jb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMiwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifV0sICJ0aW1lZGVsdGEiOiA2MCwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdF9hbGFybSJ9fSwgImNoYXJ0X2ltYWdlX2VuYWJsZWQiOiB0cnVlLCAiZXhjbHVkZV9ub3RpY2Vfd2F5cyI6IHsiY2xvc2VkIjogW10sICJyZWNvdmVyZWQiOiBbXX0sICJub2lzZV9yZWR1Y2VfY29uZmlnIjogeyJ1bml0IjogInBlcmNlbnQiLCAiY291bnQiOiAxMCwgInRpbWVkZWx0YSI6IDUsICJkaW1lbnNpb25zIjogW10sICJpc19lbmFibGVkIjogZmFsc2V9fSwgImNvbmZpZ19pZCI6IDcyMDQzLCAicmVsYXRlX3R5cGUiOiAiTk9USUNFIiwgInVzZXJfZ3JvdXBzIjogWzBdfSwgInNvdXJjZSI6ICJia21vbml0b3J2MyIsICJhY3Rpb25zIjogW10sICJkZXRlY3RzIjogW3sibGV2ZWwiOiAyLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJvdGhlcl9ydCIsICJia19iaXpfaWQiOiAxMDA0NjUsICJkYXRhX3NvdXJjZV90eXBlIjogIlx1ODFlYVx1NWI5YVx1NGU0OVx1NGU4Ylx1NGVmNiJ9LCAiaXNfZW5hYmxlZCI6IHRydWV9 \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73379.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73379.tpl64 deleted file mode 100644 index 85b39c246a..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73379.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDczMzc5LCAibmFtZSI6ICJNeVNRTCBcdTk1MDFcdTg4NjgiLCAiZGJfdHlwZSI6ICJteXNxbCIsICJkZXRhaWxzIjogeyJhcHAiOiAiIiwgIm5hbWUiOiAiTXlTUUwgXHU5NTAxXHU4ODY4IiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAiQ09VTlQobXlzcWwtbG9jaykiLCAidGFyZ2V0IjogW1t7ImZpZWxkIjogImhvc3RfdG9wb19ub2RlIiwgInZhbHVlIjogW3siYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE0OH0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE0OX0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE0OX1dLCAibWV0aG9kIjogImVxIn1dXSwgImZ1bmN0aW9ucyI6IFtdLCAiYWxnb3JpdGhtcyI6IFt7ImlkIjogNzg3MTQsICJ0eXBlIjogIlRocmVzaG9sZCIsICJsZXZlbCI6IDEsICJjb25maWciOiBbW3sibWV0aG9kIjogImd0ZSIsICJ0aHJlc2hvbGQiOiAxfV1dLCAidW5pdF9wcmVmaXgiOiAiIn1dLCAiZXhwcmVzc2lvbiI6ICJhIiwgIm9yaWdpbl9zcWwiOiAiIiwgInF1ZXJ5X2NvbmZpZ3MiOiBbeyJpZCI6IDc3ODEyLCAibmFtZSI6ICJteXNxbC1sb2NrIiwgImFsaWFzIjogImEiLCAiZnVuY3Rpb25zIjogW10sICJtZXRyaWNfaWQiOiAiY3VzdG9tLmV2ZW50Lntia19iaXpfaWR9X2JrbW9uaXRvcl9ldmVudF97ZXZlbnRfZGF0YV9pZH0ubXlzcWwtbG9jayIsICJhZ2dfbWV0aG9kIjogIkNPVU5UIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAiYWdnX2NvbmRpdGlvbiI6IFtdLCAiYWdnX2RpbWVuc2lvbiI6IFsiYmtfY2xvdWRfaWQiLCAic2VydmVyX2lwIiwgInJvbGUiLCAibWFjaGluZV90eXBlIiwgImltbXV0ZV9kb21haW4iLCAicG9ydCIsICJhcHBfaWQiXSwgImRhdGFfdHlwZV9sYWJlbCI6ICJldmVudCIsICJyZXN1bHRfdGFibGVfaWQiOiAie2JrX2Jpel9pZH1fYmttb25pdG9yX2V2ZW50X3tldmVudF9kYXRhX2lkfSIsICJjdXN0b21fZXZlbnRfbmFtZSI6ICJteXNxbC1sb2NrIiwgImRhdGFfc291cmNlX2xhYmVsIjogImN1c3RvbSJ9XSwgIm5vX2RhdGFfY29uZmlnIjogeyJsZXZlbCI6IDIsICJjb250aW51b3VzIjogMTAsICJpc19lbmFibGVkIjogZmFsc2UsICJhZ2dfZGltZW5zaW9uIjogW119fV0sICJsYWJlbHMiOiBbIkRCTV9NWVNRTCIsICJEQk0iXSwgIm5vdGljZSI6IHsiY29uZmlnIjogeyJ0ZW1wbGF0ZSI6IFt7InNpZ25hbCI6ICJhYm5vcm1hbCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In0sIHsic2lnbmFsIjogInJlY292ZXJlZCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In0sIHsic2lnbmFsIjogImNsb3NlZCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In1dLCAibmVlZF9wb2xsIjogdHJ1ZSwgIm5vdGlmeV9pbnRlcnZhbCI6IDcyMDAsICJpbnRlcnZhbF9ub3RpZnlfbW9kZSI6ICJzdGFuZGFyZCJ9LCAic2lnbmFsIjogWyJub19kYXRhIiwgImFibm9ybWFsIl0sICJvcHRpb25zIjogeyJlbmRfdGltZSI6ICIyMzo1OTo1OSIsICJzdGFydF90aW1lIjogIjAwOjAwOjAwIiwgImFzc2lnbl9tb2RlIjogWyJvbmx5X25vdGljZSIsICJieV9ydWxlIl0sICJjb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMSwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic3RyYXRlZ3lfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImRpbWVuc2lvbnMifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In1dLCAidGltZWRlbHRhIjogNjAsICJpc19lbmFibGVkIjogdHJ1ZSwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdCIsICJuZWVkX2Jpel9jb252ZXJnZSI6IHRydWUsICJzdWJfY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDIsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn1dLCAidGltZWRlbHRhIjogNjAsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3RfYWxhcm0ifX0sICJjaGFydF9pbWFnZV9lbmFibGVkIjogdHJ1ZSwgImV4Y2x1ZGVfbm90aWNlX3dheXMiOiB7ImNsb3NlZCI6IFtdLCAicmVjb3ZlcmVkIjogW119LCAibm9pc2VfcmVkdWNlX2NvbmZpZyI6IHsidW5pdCI6ICJwZXJjZW50IiwgImNvdW50IjogMTAsICJ0aW1lZGVsdGEiOiA1LCAiZGltZW5zaW9ucyI6IFtdLCAiaXNfZW5hYmxlZCI6IGZhbHNlfX0sICJjb25maWdfaWQiOiA3MjA0NCwgInJlbGF0ZV90eXBlIjogIk5PVElDRSIsICJ1c2VyX2dyb3VwcyI6IFswXX0sICJzb3VyY2UiOiAiYmttb25pdG9ydjMiLCAiYWN0aW9ucyI6IFtdLCAiZGV0ZWN0cyI6IFt7ImxldmVsIjogMSwgImNvbm5lY3RvciI6ICJhbmQiLCAiZXhwcmVzc2lvbiI6ICIiLCAidHJpZ2dlcl9jb25maWciOiB7ImNvdW50IjogMSwgInVwdGltZSI6IHsiY2FsZW5kYXJzIjogW10sICJ0aW1lX3JhbmdlcyI6IFt7ImVuZCI6ICIyMzo1OSIsICJzdGFydCI6ICIwMDowMCJ9XX0sICJjaGVja193aW5kb3ciOiA1fSwgInJlY292ZXJ5X2NvbmZpZyI6IHsiY2hlY2tfd2luZG93IjogNSwgInN0YXR1c19zZXR0ZXIiOiAicmVjb3ZlcnkifX1dLCAic2NlbmFyaW8iOiAib3RoZXJfcnQiLCAiYmtfYml6X2lkIjogMTAwNDY1LCAiZGF0YV9zb3VyY2VfdHlwZSI6ICJcdTgxZWFcdTViOWFcdTRlNDlcdTRlOGJcdTRlZjYifSwgImlzX2VuYWJsZWQiOiB0cnVlfQ== \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73380.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73380.tpl64 deleted file mode 100644 index 8f7cad4901..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73380.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDczMzgwLCAibmFtZSI6ICJNeVNRTCBcdTZjZThcdTUxNjUiLCAiZGJfdHlwZSI6ICJteXNxbCIsICJkZXRhaWxzIjogeyJhcHAiOiAiIiwgIm5hbWUiOiAiTXlTUUwgXHU2Y2U4XHU1MTY1IiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAiQ09VTlQobXlzcWwtaW5qZWN0KSIsICJ0YXJnZXQiOiBbW3siZmllbGQiOiAiaG9zdF90b3BvX25vZGUiLCAidmFsdWUiOiBbeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ4fSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ5fSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ5fV0sICJtZXRob2QiOiAiZXEifV1dLCAiZnVuY3Rpb25zIjogW10sICJhbGdvcml0aG1zIjogW3siaWQiOiA3ODcxNSwgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMSwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDF9XV0sICJ1bml0X3ByZWZpeCI6ICIifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICIiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNzc4MTMsICJuYW1lIjogIm15c3FsLWluamVjdCIsICJhbGlhcyI6ICJhIiwgImZ1bmN0aW9ucyI6IFtdLCAibWV0cmljX2lkIjogImN1c3RvbS5ldmVudC57YmtfYml6X2lkfV9ia21vbml0b3JfZXZlbnRfe2V2ZW50X2RhdGFfaWR9Lm15c3FsLWluamVjdCIsICJhZ2dfbWV0aG9kIjogIkNPVU5UIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAiYWdnX2NvbmRpdGlvbiI6IFtdLCAiYWdnX2RpbWVuc2lvbiI6IFsiYmtfY2xvdWRfaWQiLCAic2VydmVyX2lwIiwgInJvbGUiLCAibWFjaGluZV90eXBlIiwgImltbXV0ZV9kb21haW4iLCAicG9ydCIsICJhcHBfaWQiXSwgImRhdGFfdHlwZV9sYWJlbCI6ICJldmVudCIsICJyZXN1bHRfdGFibGVfaWQiOiAie2JrX2Jpel9pZH1fYmttb25pdG9yX2V2ZW50X3tldmVudF9kYXRhX2lkfSIsICJjdXN0b21fZXZlbnRfbmFtZSI6ICJteXNxbC1pbmplY3QiLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAiY3VzdG9tIn1dLCAibm9fZGF0YV9jb25maWciOiB7ImxldmVsIjogMiwgImNvbnRpbnVvdXMiOiAxMCwgImlzX2VuYWJsZWQiOiBmYWxzZSwgImFnZ19kaW1lbnNpb24iOiBbXX19XSwgImxhYmVscyI6IFsiREJNX01ZU1FMIiwgIkRCTSJdLCAibm90aWNlIjogeyJjb25maWciOiB7InRlbXBsYXRlIjogW3sic2lnbmFsIjogImFibm9ybWFsIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAicmVjb3ZlcmVkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAiY2xvc2VkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifV0sICJuZWVkX3BvbGwiOiB0cnVlLCAibm90aWZ5X2ludGVydmFsIjogNzIwMCwgImludGVydmFsX25vdGlmeV9tb2RlIjogInN0YW5kYXJkIn0sICJzaWduYWwiOiBbImFibm9ybWFsIiwgIm5vX2RhdGEiXSwgIm9wdGlvbnMiOiB7ImVuZF90aW1lIjogIjIzOjU5OjU5IiwgInN0YXJ0X3RpbWUiOiAiMDA6MDA6MDAiLCAiYXNzaWduX21vZGUiOiBbIm9ubHlfbm90aWNlIiwgImJ5X3J1bGUiXSwgImNvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAxLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzdHJhdGVneV9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiZGltZW5zaW9ucyJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifV0sICJ0aW1lZGVsdGEiOiA2MCwgImlzX2VuYWJsZWQiOiB0cnVlLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0IiwgIm5lZWRfYml6X2NvbnZlcmdlIjogdHJ1ZSwgInN1Yl9jb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMiwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifV0sICJ0aW1lZGVsdGEiOiA2MCwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdF9hbGFybSJ9fSwgImNoYXJ0X2ltYWdlX2VuYWJsZWQiOiB0cnVlLCAiZXhjbHVkZV9ub3RpY2Vfd2F5cyI6IHsiY2xvc2VkIjogW10sICJyZWNvdmVyZWQiOiBbXX0sICJub2lzZV9yZWR1Y2VfY29uZmlnIjogeyJ1bml0IjogInBlcmNlbnQiLCAiY291bnQiOiAxMCwgInRpbWVkZWx0YSI6IDUsICJkaW1lbnNpb25zIjogW10sICJpc19lbmFibGVkIjogZmFsc2V9fSwgImNvbmZpZ19pZCI6IDcyMDQ1LCAicmVsYXRlX3R5cGUiOiAiTk9USUNFIiwgInVzZXJfZ3JvdXBzIjogWzBdfSwgInNvdXJjZSI6ICJia21vbml0b3J2MyIsICJhY3Rpb25zIjogW10sICJkZXRlY3RzIjogW3sibGV2ZWwiOiAxLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJvdGhlcl9ydCIsICJia19iaXpfaWQiOiAxMDA0NjUsICJkYXRhX3NvdXJjZV90eXBlIjogIlx1ODFlYVx1NWI5YVx1NGU0OVx1NGU4Ylx1NGVmNiJ9LCAiaXNfZW5hYmxlZCI6IHRydWV9 \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73381.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73381.tpl64 deleted file mode 100644 index 6c69e35358..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73381.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDczMzgxLCAibmFtZSI6ICJNeVNRTCBcdTY1ZTVcdTVmZDdcdTc2ZDFcdTYzYTctXHU4MWY0XHU1NDdkIiwgImRiX3R5cGUiOiAibXlzcWwiLCAiZGV0YWlscyI6IHsiYXBwIjogIiIsICJuYW1lIjogIk15U1FMIFx1NjVlNVx1NWZkN1x1NzZkMVx1NjNhNy1cdTgxZjRcdTU0N2QiLCAicGF0aCI6ICIiLCAidHlwZSI6ICJtb25pdG9yIiwgIml0ZW1zIjogW3sibmFtZSI6ICJDT1VOVChteXNxbC1lcnItY3JpdGljYWwpIiwgInRhcmdldCI6IFtbeyJmaWVsZCI6ICJob3N0X3RvcG9fbm9kZSIsICJ2YWx1ZSI6IFt7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgxNDh9LCB7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgxNDl9LCB7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgxNDl9XSwgIm1ldGhvZCI6ICJlcSJ9XV0sICJmdW5jdGlvbnMiOiBbXSwgImFsZ29yaXRobXMiOiBbeyJpZCI6IDc4NzE2LCAidHlwZSI6ICJUaHJlc2hvbGQiLCAibGV2ZWwiOiAxLCAiY29uZmlnIjogW1t7Im1ldGhvZCI6ICJndGUiLCAidGhyZXNob2xkIjogMX1dXSwgInVuaXRfcHJlZml4IjogIiJ9XSwgImV4cHJlc3Npb24iOiAiYSIsICJvcmlnaW5fc3FsIjogIiIsICJxdWVyeV9jb25maWdzIjogW3siaWQiOiA3NzgxNCwgIm5hbWUiOiAibXlzcWwtZXJyLWNyaXRpY2FsIiwgImFsaWFzIjogImEiLCAiZnVuY3Rpb25zIjogW10sICJtZXRyaWNfaWQiOiAiY3VzdG9tLmV2ZW50Lntia19iaXpfaWR9X2JrbW9uaXRvcl9ldmVudF97ZXZlbnRfZGF0YV9pZH0ubXlzcWwtZXJyLWNyaXRpY2FsIiwgImFnZ19tZXRob2QiOiAiQ09VTlQiLCAiYWdnX2ludGVydmFsIjogNjAsICJhZ2dfY29uZGl0aW9uIjogW10sICJhZ2dfZGltZW5zaW9uIjogWyJia19jbG91ZF9pZCIsICJzZXJ2ZXJfaXAiLCAicm9sZSIsICJtYWNoaW5lX3R5cGUiLCAiaW1tdXRlX2RvbWFpbiIsICJhcHBfaWQiLCAicG9ydCJdLCAiZGF0YV90eXBlX2xhYmVsIjogImV2ZW50IiwgInJlc3VsdF90YWJsZV9pZCI6ICJ7YmtfYml6X2lkfV9ia21vbml0b3JfZXZlbnRfe2V2ZW50X2RhdGFfaWR9IiwgImN1c3RvbV9ldmVudF9uYW1lIjogIm15c3FsLWVyci1jcml0aWNhbCIsICJkYXRhX3NvdXJjZV9sYWJlbCI6ICJjdXN0b20ifV0sICJub19kYXRhX2NvbmZpZyI6IHsibGV2ZWwiOiAyLCAiY29udGludW91cyI6IDEwLCAiaXNfZW5hYmxlZCI6IGZhbHNlLCAiYWdnX2RpbWVuc2lvbiI6IFtdfX1dLCAibGFiZWxzIjogWyJEQk1fTVlTUUwiLCAiREJNIl0sICJub3RpY2UiOiB7ImNvbmZpZyI6IHsidGVtcGxhdGUiOiBbeyJzaWduYWwiOiAiYWJub3JtYWwiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJyZWNvdmVyZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJjbG9zZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9XSwgIm5lZWRfcG9sbCI6IHRydWUsICJub3RpZnlfaW50ZXJ2YWwiOiA3MjAwLCAiaW50ZXJ2YWxfbm90aWZ5X21vZGUiOiAic3RhbmRhcmQifSwgInNpZ25hbCI6IFsibm9fZGF0YSIsICJhYm5vcm1hbCJdLCAib3B0aW9ucyI6IHsiZW5kX3RpbWUiOiAiMjM6NTk6NTkiLCAic3RhcnRfdGltZSI6ICIwMDowMDowMCIsICJhc3NpZ25fbW9kZSI6IFsib25seV9ub3RpY2UiLCAiYnlfcnVsZSJdLCAiY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDEsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInN0cmF0ZWd5X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJkaW1lbnNpb25zIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJia19iaXpfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV9yZWNlaXZlciJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3dheSJ9XSwgInRpbWVkZWx0YSI6IDYwLCAiaXNfZW5hYmxlZCI6IHRydWUsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3QiLCAibmVlZF9iaXpfY29udmVyZ2UiOiB0cnVlLCAic3ViX2NvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAyLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJia19iaXpfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV9yZWNlaXZlciJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3dheSJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9XSwgInRpbWVkZWx0YSI6IDYwLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0X2FsYXJtIn19LCAiY2hhcnRfaW1hZ2VfZW5hYmxlZCI6IHRydWUsICJleGNsdWRlX25vdGljZV93YXlzIjogeyJjbG9zZWQiOiBbXSwgInJlY292ZXJlZCI6IFtdfSwgIm5vaXNlX3JlZHVjZV9jb25maWciOiB7InVuaXQiOiAicGVyY2VudCIsICJjb3VudCI6IDEwLCAidGltZWRlbHRhIjogNSwgImRpbWVuc2lvbnMiOiBbXSwgImlzX2VuYWJsZWQiOiBmYWxzZX19LCAiY29uZmlnX2lkIjogNzIwNDYsICJyZWxhdGVfdHlwZSI6ICJOT1RJQ0UiLCAidXNlcl9ncm91cHMiOiBbMF19LCAic291cmNlIjogImJrbW9uaXRvcnYzIiwgImFjdGlvbnMiOiBbXSwgImRldGVjdHMiOiBbeyJsZXZlbCI6IDEsICJjb25uZWN0b3IiOiAiYW5kIiwgImV4cHJlc3Npb24iOiAiIiwgInRyaWdnZXJfY29uZmlnIjogeyJjb3VudCI6IDEsICJ1cHRpbWUiOiB7ImNhbGVuZGFycyI6IFtdLCAidGltZV9yYW5nZXMiOiBbeyJlbmQiOiAiMjM6NTkiLCAic3RhcnQiOiAiMDA6MDAifV19LCAiY2hlY2tfd2luZG93IjogNX0sICJyZWNvdmVyeV9jb25maWciOiB7ImNoZWNrX3dpbmRvdyI6IDUsICJzdGF0dXNfc2V0dGVyIjogInJlY292ZXJ5In19XSwgInNjZW5hcmlvIjogIm90aGVyX3J0IiwgImJrX2Jpel9pZCI6IDEwMDQ2NSwgImRhdGFfc291cmNlX3R5cGUiOiAiXHU4MWVhXHU1YjlhXHU0ZTQ5XHU0ZThiXHU0ZWY2In0sICJpc19lbmFibGVkIjogdHJ1ZX0= \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73382.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73382.tpl64 deleted file mode 100644 index 140a1661d4..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73382.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDczMzgyLCAibmFtZSI6ICJNeVNRTCBcdTY1ZTVcdTVmZDdcdTc2ZDFcdTYzYTctXHU5ODg0XHU4YjY2IiwgImRiX3R5cGUiOiAibXlzcWwiLCAiZGV0YWlscyI6IHsiYXBwIjogIiIsICJuYW1lIjogIk15U1FMIFx1NjVlNVx1NWZkN1x1NzZkMVx1NjNhNy1cdTk4ODRcdThiNjYiLCAicGF0aCI6ICIiLCAidHlwZSI6ICJtb25pdG9yIiwgIml0ZW1zIjogW3sibmFtZSI6ICJDT1VOVChteXNxbC1lcnItbm90aWNlKSIsICJ0YXJnZXQiOiBbW3siZmllbGQiOiAiaG9zdF90b3BvX25vZGUiLCAidmFsdWUiOiBbeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ4fSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ5fSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ5fV0sICJtZXRob2QiOiAiZXEifV1dLCAiZnVuY3Rpb25zIjogW10sICJhbGdvcml0aG1zIjogW3siaWQiOiA3ODcxNywgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMiwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDF9XV0sICJ1bml0X3ByZWZpeCI6ICIifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICIiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNzc4MTUsICJuYW1lIjogIm15c3FsLWVyci1ub3RpY2UiLCAiYWxpYXMiOiAiYSIsICJmdW5jdGlvbnMiOiBbXSwgIm1ldHJpY19pZCI6ICJjdXN0b20uZXZlbnQue2JrX2Jpel9pZH1fYmttb25pdG9yX2V2ZW50X3tldmVudF9kYXRhX2lkfS5teXNxbC1lcnItbm90aWNlIiwgImFnZ19tZXRob2QiOiAiQ09VTlQiLCAiYWdnX2ludGVydmFsIjogNjAsICJhZ2dfY29uZGl0aW9uIjogW10sICJhZ2dfZGltZW5zaW9uIjogWyJia19jbG91ZF9pZCIsICJzZXJ2ZXJfaXAiLCAicm9sZSIsICJtYWNoaW5lX3R5cGUiLCAiaW1tdXRlX2RvbWFpbiIsICJhcHBfaWQiLCAicG9ydCJdLCAiZGF0YV90eXBlX2xhYmVsIjogImV2ZW50IiwgInJlc3VsdF90YWJsZV9pZCI6ICJ7YmtfYml6X2lkfV9ia21vbml0b3JfZXZlbnRfe2V2ZW50X2RhdGFfaWR9IiwgImN1c3RvbV9ldmVudF9uYW1lIjogIm15c3FsLWVyci1ub3RpY2UiLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAiY3VzdG9tIn1dLCAibm9fZGF0YV9jb25maWciOiB7ImxldmVsIjogMiwgImNvbnRpbnVvdXMiOiAxMCwgImlzX2VuYWJsZWQiOiBmYWxzZSwgImFnZ19kaW1lbnNpb24iOiBbXX19XSwgImxhYmVscyI6IFsiREJNX01ZU1FMIiwgIkRCTSJdLCAibm90aWNlIjogeyJjb25maWciOiB7InRlbXBsYXRlIjogW3sic2lnbmFsIjogImFibm9ybWFsIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAicmVjb3ZlcmVkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAiY2xvc2VkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifV0sICJuZWVkX3BvbGwiOiB0cnVlLCAibm90aWZ5X2ludGVydmFsIjogNzIwMCwgImludGVydmFsX25vdGlmeV9tb2RlIjogInN0YW5kYXJkIn0sICJzaWduYWwiOiBbImFibm9ybWFsIiwgIm5vX2RhdGEiXSwgIm9wdGlvbnMiOiB7ImVuZF90aW1lIjogIjIzOjU5OjU5IiwgInN0YXJ0X3RpbWUiOiAiMDA6MDA6MDAiLCAiYXNzaWduX21vZGUiOiBbIm9ubHlfbm90aWNlIiwgImJ5X3J1bGUiXSwgImNvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAxLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzdHJhdGVneV9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiZGltZW5zaW9ucyJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifV0sICJ0aW1lZGVsdGEiOiA2MCwgImlzX2VuYWJsZWQiOiB0cnVlLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0IiwgIm5lZWRfYml6X2NvbnZlcmdlIjogdHJ1ZSwgInN1Yl9jb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMiwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifV0sICJ0aW1lZGVsdGEiOiA2MCwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdF9hbGFybSJ9fSwgImNoYXJ0X2ltYWdlX2VuYWJsZWQiOiB0cnVlLCAiZXhjbHVkZV9ub3RpY2Vfd2F5cyI6IHsiY2xvc2VkIjogW10sICJyZWNvdmVyZWQiOiBbXX0sICJub2lzZV9yZWR1Y2VfY29uZmlnIjogeyJ1bml0IjogInBlcmNlbnQiLCAiY291bnQiOiAxMCwgInRpbWVkZWx0YSI6IDUsICJkaW1lbnNpb25zIjogW10sICJpc19lbmFibGVkIjogZmFsc2V9fSwgImNvbmZpZ19pZCI6IDcyMDQ3LCAicmVsYXRlX3R5cGUiOiAiTk9USUNFIiwgInVzZXJfZ3JvdXBzIjogWzBdfSwgInNvdXJjZSI6ICJia21vbml0b3J2MyIsICJhY3Rpb25zIjogW10sICJkZXRlY3RzIjogW3sibGV2ZWwiOiAyLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJvdGhlcl9ydCIsICJia19iaXpfaWQiOiAxMDA0NjUsICJkYXRhX3NvdXJjZV90eXBlIjogIlx1ODFlYVx1NWI5YVx1NGU0OVx1NGU4Ylx1NGVmNiJ9LCAiaXNfZW5hYmxlZCI6IHRydWV9 \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73418.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73418.tpl64 deleted file mode 100644 index 815e947387..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73418.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDczNDE4LCAibmFtZSI6ICJNeVNRTCBcdTViNThcdTUwYThcdTVmMTVcdTY0Y2VcdTY4YzBcdTY3ZTUiLCAiZGJfdHlwZSI6ICJteXNxbCIsICJkZXRhaWxzIjogeyJhcHAiOiAiIiwgIm5hbWUiOiAiTXlTUUwgXHU1YjU4XHU1MGE4XHU1ZjE1XHU2NGNlXHU2OGMwXHU2N2U1IiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAiQ09VTlQoZW5naW5lKSIsICJ0YXJnZXQiOiBbW3siZmllbGQiOiAiaG9zdF90b3BvX25vZGUiLCAidmFsdWUiOiBbeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ4fSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ5fSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ5fV0sICJtZXRob2QiOiAiZXEifV1dLCAiZnVuY3Rpb25zIjogW10sICJhbGdvcml0aG1zIjogW3siaWQiOiA3ODc2NiwgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMiwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDF9XV0sICJ1bml0X3ByZWZpeCI6ICIifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICIiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNzc4NjcsICJuYW1lIjogImVuZ2luZSIsICJhbGlhcyI6ICJhIiwgImZ1bmN0aW9ucyI6IFtdLCAibWV0cmljX2lkIjogImN1c3RvbS5ldmVudC57YmtfYml6X2lkfV9ia21vbml0b3JfZXZlbnRfe2V2ZW50X2RhdGFfaWR9LmVuZ2luZSIsICJhZ2dfbWV0aG9kIjogIkNPVU5UIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAiYWdnX2NvbmRpdGlvbiI6IFtdLCAiYWdnX2RpbWVuc2lvbiI6IFsiYmtfY2xvdWRfaWQiLCAic2VydmVyX2lwIiwgInJvbGUiLCAiaW1tdXRlX2RvbWFpbiIsICJtYWNoaW5lX3R5cGUiLCAiYXBwX2lkIiwgInBvcnQiXSwgImRhdGFfdHlwZV9sYWJlbCI6ICJldmVudCIsICJyZXN1bHRfdGFibGVfaWQiOiAie2JrX2Jpel9pZH1fYmttb25pdG9yX2V2ZW50X3tldmVudF9kYXRhX2lkfSIsICJjdXN0b21fZXZlbnRfbmFtZSI6ICJlbmdpbmUiLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAiY3VzdG9tIn1dLCAibm9fZGF0YV9jb25maWciOiB7ImxldmVsIjogMiwgImNvbnRpbnVvdXMiOiAxMCwgImlzX2VuYWJsZWQiOiBmYWxzZSwgImFnZ19kaW1lbnNpb24iOiBbXX19XSwgImxhYmVscyI6IFsiREJNX01ZU1FMIiwgIkRCTSJdLCAibm90aWNlIjogeyJjb25maWciOiB7InRlbXBsYXRlIjogW3sic2lnbmFsIjogImFibm9ybWFsIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAicmVjb3ZlcmVkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAiY2xvc2VkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifV0sICJuZWVkX3BvbGwiOiB0cnVlLCAibm90aWZ5X2ludGVydmFsIjogNzIwMCwgImludGVydmFsX25vdGlmeV9tb2RlIjogInN0YW5kYXJkIn0sICJzaWduYWwiOiBbIm5vX2RhdGEiLCAiYWJub3JtYWwiXSwgIm9wdGlvbnMiOiB7ImVuZF90aW1lIjogIjIzOjU5OjU5IiwgInN0YXJ0X3RpbWUiOiAiMDA6MDA6MDAiLCAiYXNzaWduX21vZGUiOiBbIm9ubHlfbm90aWNlIiwgImJ5X3J1bGUiXSwgImNvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAxLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzdHJhdGVneV9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiZGltZW5zaW9ucyJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifV0sICJ0aW1lZGVsdGEiOiA2MCwgImlzX2VuYWJsZWQiOiB0cnVlLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0IiwgIm5lZWRfYml6X2NvbnZlcmdlIjogdHJ1ZSwgInN1Yl9jb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMiwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifV0sICJ0aW1lZGVsdGEiOiA2MCwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdF9hbGFybSJ9fSwgImNoYXJ0X2ltYWdlX2VuYWJsZWQiOiB0cnVlLCAiZXhjbHVkZV9ub3RpY2Vfd2F5cyI6IHsiY2xvc2VkIjogW10sICJyZWNvdmVyZWQiOiBbXX0sICJub2lzZV9yZWR1Y2VfY29uZmlnIjogeyJ1bml0IjogInBlcmNlbnQiLCAiY291bnQiOiAxMCwgInRpbWVkZWx0YSI6IDUsICJkaW1lbnNpb25zIjogW10sICJpc19lbmFibGVkIjogZmFsc2V9fSwgImNvbmZpZ19pZCI6IDcyMDgzLCAicmVsYXRlX3R5cGUiOiAiTk9USUNFIiwgInVzZXJfZ3JvdXBzIjogWzBdfSwgInNvdXJjZSI6ICJia21vbml0b3J2MyIsICJhY3Rpb25zIjogW10sICJkZXRlY3RzIjogW3sibGV2ZWwiOiAyLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJvdGhlcl9ydCIsICJia19iaXpfaWQiOiAxMDA0NjUsICJkYXRhX3NvdXJjZV90eXBlIjogIlx1ODFlYVx1NWI5YVx1NGU0OVx1NGU4Ylx1NGVmNiJ9LCAiaXNfZW5hYmxlZCI6IHRydWV9 \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73456.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73456.tpl64 deleted file mode 100644 index f830ca5d46..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73456.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDczNDU2LCAibmFtZSI6ICJNeVNRTCBFeHQzIFx1NjU4N1x1NGVmNlx1OGZjN1x1NTkyNyIsICJkYl90eXBlIjogIm15c3FsIiwgImRldGFpbHMiOiB7ImFwcCI6ICIiLCAibmFtZSI6ICJNeVNRTCBFeHQzIFx1NjU4N1x1NGVmNlx1OGZjN1x1NTkyNyIsICJwYXRoIjogIiIsICJ0eXBlIjogIm1vbml0b3IiLCAiaXRlbXMiOiBbeyJuYW1lIjogIkNPVU5UKGV4dDMtY2hlY2spIiwgInRhcmdldCI6IFtbeyJmaWVsZCI6ICJob3N0X3RvcG9fbm9kZSIsICJ2YWx1ZSI6IFt7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgxNDh9LCB7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgxNDl9LCB7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgxNDl9XSwgIm1ldGhvZCI6ICJlcSJ9XV0sICJmdW5jdGlvbnMiOiBbXSwgImFsZ29yaXRobXMiOiBbeyJpZCI6IDc4ODA4LCAidHlwZSI6ICJUaHJlc2hvbGQiLCAibGV2ZWwiOiAxLCAiY29uZmlnIjogW1t7Im1ldGhvZCI6ICJndGUiLCAidGhyZXNob2xkIjogMX1dXSwgInVuaXRfcHJlZml4IjogIiJ9XSwgImV4cHJlc3Npb24iOiAiYSIsICJvcmlnaW5fc3FsIjogIiIsICJxdWVyeV9jb25maWdzIjogW3siaWQiOiA3NzkwOSwgIm5hbWUiOiAiZXh0My1jaGVjayIsICJhbGlhcyI6ICJhIiwgImZ1bmN0aW9ucyI6IFtdLCAibWV0cmljX2lkIjogImN1c3RvbS5ldmVudC57YmtfYml6X2lkfV9ia21vbml0b3JfZXZlbnRfe2V2ZW50X2RhdGFfaWR9LmV4dDMtY2hlY2siLCAiYWdnX21ldGhvZCI6ICJDT1VOVCIsICJhZ2dfaW50ZXJ2YWwiOiA2MCwgImFnZ19jb25kaXRpb24iOiBbXSwgImFnZ19kaW1lbnNpb24iOiBbImFwcF9pZCIsICJia19jbG91ZF9pZCIsICJpbW11dGVfZG9tYWluIiwgInJvbGUiLCAicG9ydCIsICJtYWNoaW5lX3R5cGUiLCAic2VydmVyX2lwIl0sICJkYXRhX3R5cGVfbGFiZWwiOiAiZXZlbnQiLCAicmVzdWx0X3RhYmxlX2lkIjogIntia19iaXpfaWR9X2JrbW9uaXRvcl9ldmVudF97ZXZlbnRfZGF0YV9pZH0iLCAiY3VzdG9tX2V2ZW50X25hbWUiOiAiZXh0My1jaGVjayIsICJkYXRhX3NvdXJjZV9sYWJlbCI6ICJjdXN0b20ifV0sICJub19kYXRhX2NvbmZpZyI6IHsibGV2ZWwiOiAyLCAiY29udGludW91cyI6IDEwLCAiaXNfZW5hYmxlZCI6IGZhbHNlLCAiYWdnX2RpbWVuc2lvbiI6IFtdfX1dLCAibGFiZWxzIjogWyJEQk1fTVlTUUwiLCAiREJNIl0sICJub3RpY2UiOiB7ImNvbmZpZyI6IHsidGVtcGxhdGUiOiBbeyJzaWduYWwiOiAiYWJub3JtYWwiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJyZWNvdmVyZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJjbG9zZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9XSwgIm5lZWRfcG9sbCI6IHRydWUsICJub3RpZnlfaW50ZXJ2YWwiOiA3MjAwLCAiaW50ZXJ2YWxfbm90aWZ5X21vZGUiOiAic3RhbmRhcmQifSwgInNpZ25hbCI6IFsiYWJub3JtYWwiLCAibm9fZGF0YSJdLCAib3B0aW9ucyI6IHsiZW5kX3RpbWUiOiAiMjM6NTk6NTkiLCAic3RhcnRfdGltZSI6ICIwMDowMDowMCIsICJhc3NpZ25fbW9kZSI6IFsib25seV9ub3RpY2UiLCAiYnlfcnVsZSJdLCAiY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDEsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInN0cmF0ZWd5X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJkaW1lbnNpb25zIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJia19iaXpfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV9yZWNlaXZlciJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3dheSJ9XSwgInRpbWVkZWx0YSI6IDYwLCAiaXNfZW5hYmxlZCI6IHRydWUsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3QiLCAibmVlZF9iaXpfY29udmVyZ2UiOiB0cnVlLCAic3ViX2NvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAyLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJia19iaXpfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV9yZWNlaXZlciJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3dheSJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9XSwgInRpbWVkZWx0YSI6IDYwLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0X2FsYXJtIn19LCAiY2hhcnRfaW1hZ2VfZW5hYmxlZCI6IHRydWUsICJleGNsdWRlX25vdGljZV93YXlzIjogeyJjbG9zZWQiOiBbXSwgInJlY292ZXJlZCI6IFtdfSwgIm5vaXNlX3JlZHVjZV9jb25maWciOiB7InVuaXQiOiAicGVyY2VudCIsICJjb3VudCI6IDEwLCAidGltZWRlbHRhIjogNSwgImRpbWVuc2lvbnMiOiBbXSwgImlzX2VuYWJsZWQiOiBmYWxzZX19LCAiY29uZmlnX2lkIjogNzIxMzMsICJyZWxhdGVfdHlwZSI6ICJOT1RJQ0UiLCAidXNlcl9ncm91cHMiOiBbMF19LCAic291cmNlIjogImJrbW9uaXRvcnYzIiwgImFjdGlvbnMiOiBbXSwgImRldGVjdHMiOiBbeyJsZXZlbCI6IDEsICJjb25uZWN0b3IiOiAiYW5kIiwgImV4cHJlc3Npb24iOiAiIiwgInRyaWdnZXJfY29uZmlnIjogeyJjb3VudCI6IDEsICJ1cHRpbWUiOiB7ImNhbGVuZGFycyI6IFtdLCAidGltZV9yYW5nZXMiOiBbeyJlbmQiOiAiMjM6NTkiLCAic3RhcnQiOiAiMDA6MDAifV19LCAiY2hlY2tfd2luZG93IjogNX0sICJyZWNvdmVyeV9jb25maWciOiB7ImNoZWNrX3dpbmRvdyI6IDUsICJzdGF0dXNfc2V0dGVyIjogInJlY292ZXJ5In19XSwgInNjZW5hcmlvIjogIm90aGVyX3J0IiwgImJrX2Jpel9pZCI6IDEwMDQ2NSwgImRhdGFfc291cmNlX3R5cGUiOiAiXHU4MWVhXHU1YjlhXHU0ZTQ5XHU0ZThiXHU0ZWY2In0sICJpc19lbmFibGVkIjogdHJ1ZX0= \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73471.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73471.tpl64 deleted file mode 100644 index 65e778eb3b..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73471.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDczNDcxLCAibmFtZSI6ICJNeVNRTCBUcmlnZ2VyIERlZmluZXIiLCAiZGJfdHlwZSI6ICJteXNxbCIsICJkZXRhaWxzIjogeyJhcHAiOiAiIiwgIm5hbWUiOiAiTXlTUUwgVHJpZ2dlciBEZWZpbmVyIiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAiQ09VTlQodHJpZ2dlci1kZWZpbmVyKSIsICJ0YXJnZXQiOiBbW3siZmllbGQiOiAiaG9zdF90b3BvX25vZGUiLCAidmFsdWUiOiBbeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ4fSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ5fSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ5fV0sICJtZXRob2QiOiAiZXEifV1dLCAiZnVuY3Rpb25zIjogW10sICJhbGdvcml0aG1zIjogW3siaWQiOiA3ODgyMywgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMiwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDF9XV0sICJ1bml0X3ByZWZpeCI6ICIifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICIiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNzc5MjQsICJuYW1lIjogInRyaWdnZXItZGVmaW5lciIsICJhbGlhcyI6ICJhIiwgImZ1bmN0aW9ucyI6IFtdLCAibWV0cmljX2lkIjogImN1c3RvbS5ldmVudC57YmtfYml6X2lkfV9ia21vbml0b3JfZXZlbnRfe2V2ZW50X2RhdGFfaWR9LnRyaWdnZXItZGVmaW5lciIsICJhZ2dfbWV0aG9kIjogIkNPVU5UIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAiYWdnX2NvbmRpdGlvbiI6IFtdLCAiYWdnX2RpbWVuc2lvbiI6IFsiYXBwX2lkIiwgImJrX2Nsb3VkX2lkIiwgIm1hY2hpbmVfdHlwZSIsICJyb2xlIiwgInBvcnQiLCAiaW1tdXRlX2RvbWFpbiIsICJzZXJ2ZXJfaXAiXSwgImRhdGFfdHlwZV9sYWJlbCI6ICJldmVudCIsICJyZXN1bHRfdGFibGVfaWQiOiAie2JrX2Jpel9pZH1fYmttb25pdG9yX2V2ZW50X3tldmVudF9kYXRhX2lkfSIsICJjdXN0b21fZXZlbnRfbmFtZSI6ICJ0cmlnZ2VyLWRlZmluZXIiLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAiY3VzdG9tIn1dLCAibm9fZGF0YV9jb25maWciOiB7ImxldmVsIjogMiwgImNvbnRpbnVvdXMiOiAxMCwgImlzX2VuYWJsZWQiOiBmYWxzZSwgImFnZ19kaW1lbnNpb24iOiBbXX19XSwgImxhYmVscyI6IFsiREJNX01ZU1FMIiwgIkRCTSJdLCAibm90aWNlIjogeyJjb25maWciOiB7InRlbXBsYXRlIjogW3sic2lnbmFsIjogImFibm9ybWFsIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAicmVjb3ZlcmVkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAiY2xvc2VkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifV0sICJuZWVkX3BvbGwiOiB0cnVlLCAibm90aWZ5X2ludGVydmFsIjogNzIwMCwgImludGVydmFsX25vdGlmeV9tb2RlIjogInN0YW5kYXJkIn0sICJzaWduYWwiOiBbImFibm9ybWFsIiwgIm5vX2RhdGEiXSwgIm9wdGlvbnMiOiB7ImVuZF90aW1lIjogIjIzOjU5OjU5IiwgInN0YXJ0X3RpbWUiOiAiMDA6MDA6MDAiLCAiYXNzaWduX21vZGUiOiBbIm9ubHlfbm90aWNlIiwgImJ5X3J1bGUiXSwgImNvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAxLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzdHJhdGVneV9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiZGltZW5zaW9ucyJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifV0sICJ0aW1lZGVsdGEiOiA2MCwgImlzX2VuYWJsZWQiOiB0cnVlLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0IiwgIm5lZWRfYml6X2NvbnZlcmdlIjogdHJ1ZSwgInN1Yl9jb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMiwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifV0sICJ0aW1lZGVsdGEiOiA2MCwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdF9hbGFybSJ9fSwgImNoYXJ0X2ltYWdlX2VuYWJsZWQiOiB0cnVlLCAiZXhjbHVkZV9ub3RpY2Vfd2F5cyI6IHsiY2xvc2VkIjogW10sICJyZWNvdmVyZWQiOiBbXX0sICJub2lzZV9yZWR1Y2VfY29uZmlnIjogeyJ1bml0IjogInBlcmNlbnQiLCAiY291bnQiOiAxMCwgInRpbWVkZWx0YSI6IDUsICJkaW1lbnNpb25zIjogW10sICJpc19lbmFibGVkIjogZmFsc2V9fSwgImNvbmZpZ19pZCI6IDcyMTU3LCAicmVsYXRlX3R5cGUiOiAiTk9USUNFIiwgInVzZXJfZ3JvdXBzIjogWzBdfSwgInNvdXJjZSI6ICJia21vbml0b3J2MyIsICJhY3Rpb25zIjogW10sICJkZXRlY3RzIjogW3sibGV2ZWwiOiAyLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJvdGhlcl9ydCIsICJia19iaXpfaWQiOiAxMDA0NjUsICJkYXRhX3NvdXJjZV90eXBlIjogIlx1ODFlYVx1NWI5YVx1NGU0OVx1NGU4Ylx1NGVmNiJ9LCAiaXNfZW5hYmxlZCI6IHRydWV9 \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73472.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73472.tpl64 deleted file mode 100644 index 07e7a93ee2..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73472.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDczNDcyLCAibmFtZSI6ICJNeVNRTCBWaWV3IERlZmluZXIiLCAiZGJfdHlwZSI6ICJteXNxbCIsICJkZXRhaWxzIjogeyJhcHAiOiAiIiwgIm5hbWUiOiAiTXlTUUwgVmlldyBEZWZpbmVyIiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAiQ09VTlQodmlldy1kZWZpbmVyKSIsICJ0YXJnZXQiOiBbW3siZmllbGQiOiAiaG9zdF90b3BvX25vZGUiLCAidmFsdWUiOiBbeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ4fSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ5fSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ5fV0sICJtZXRob2QiOiAiZXEifV1dLCAiZnVuY3Rpb25zIjogW10sICJhbGdvcml0aG1zIjogW3siaWQiOiA3ODgyNCwgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMiwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDF9XV0sICJ1bml0X3ByZWZpeCI6ICIifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICIiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNzc5MjUsICJuYW1lIjogInZpZXctZGVmaW5lciIsICJhbGlhcyI6ICJhIiwgImZ1bmN0aW9ucyI6IFtdLCAibWV0cmljX2lkIjogImN1c3RvbS5ldmVudC57YmtfYml6X2lkfV9ia21vbml0b3JfZXZlbnRfe2V2ZW50X2RhdGFfaWR9LnZpZXctZGVmaW5lciIsICJhZ2dfbWV0aG9kIjogIkNPVU5UIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAiYWdnX2NvbmRpdGlvbiI6IFtdLCAiYWdnX2RpbWVuc2lvbiI6IFsiYXBwX2lkIiwgImJrX2Nsb3VkX2lkIiwgIm1hY2hpbmVfdHlwZSIsICJyb2xlIiwgInBvcnQiLCAiaW1tdXRlX2RvbWFpbiIsICJzZXJ2ZXJfaXAiXSwgImRhdGFfdHlwZV9sYWJlbCI6ICJldmVudCIsICJyZXN1bHRfdGFibGVfaWQiOiAie2JrX2Jpel9pZH1fYmttb25pdG9yX2V2ZW50X3tldmVudF9kYXRhX2lkfSIsICJjdXN0b21fZXZlbnRfbmFtZSI6ICJ2aWV3LWRlZmluZXIiLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAiY3VzdG9tIn1dLCAibm9fZGF0YV9jb25maWciOiB7ImxldmVsIjogMiwgImNvbnRpbnVvdXMiOiAxMCwgImlzX2VuYWJsZWQiOiBmYWxzZSwgImFnZ19kaW1lbnNpb24iOiBbXX19XSwgImxhYmVscyI6IFsiREJNX01ZU1FMIiwgIkRCTSJdLCAibm90aWNlIjogeyJjb25maWciOiB7InRlbXBsYXRlIjogW3sic2lnbmFsIjogImFibm9ybWFsIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAicmVjb3ZlcmVkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAiY2xvc2VkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifV0sICJuZWVkX3BvbGwiOiB0cnVlLCAibm90aWZ5X2ludGVydmFsIjogNzIwMCwgImludGVydmFsX25vdGlmeV9tb2RlIjogInN0YW5kYXJkIn0sICJzaWduYWwiOiBbImFibm9ybWFsIiwgIm5vX2RhdGEiXSwgIm9wdGlvbnMiOiB7ImVuZF90aW1lIjogIjIzOjU5OjU5IiwgInN0YXJ0X3RpbWUiOiAiMDA6MDA6MDAiLCAiYXNzaWduX21vZGUiOiBbIm9ubHlfbm90aWNlIiwgImJ5X3J1bGUiXSwgImNvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAxLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzdHJhdGVneV9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiZGltZW5zaW9ucyJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifV0sICJ0aW1lZGVsdGEiOiA2MCwgImlzX2VuYWJsZWQiOiB0cnVlLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0IiwgIm5lZWRfYml6X2NvbnZlcmdlIjogdHJ1ZSwgInN1Yl9jb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMiwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifV0sICJ0aW1lZGVsdGEiOiA2MCwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdF9hbGFybSJ9fSwgImNoYXJ0X2ltYWdlX2VuYWJsZWQiOiB0cnVlLCAiZXhjbHVkZV9ub3RpY2Vfd2F5cyI6IHsiY2xvc2VkIjogW10sICJyZWNvdmVyZWQiOiBbXX0sICJub2lzZV9yZWR1Y2VfY29uZmlnIjogeyJ1bml0IjogInBlcmNlbnQiLCAiY291bnQiOiAxMCwgInRpbWVkZWx0YSI6IDUsICJkaW1lbnNpb25zIjogW10sICJpc19lbmFibGVkIjogZmFsc2V9fSwgImNvbmZpZ19pZCI6IDcyMTU4LCAicmVsYXRlX3R5cGUiOiAiTk9USUNFIiwgInVzZXJfZ3JvdXBzIjogWzBdfSwgInNvdXJjZSI6ICJia21vbml0b3J2MyIsICJhY3Rpb25zIjogW10sICJkZXRlY3RzIjogW3sibGV2ZWwiOiAyLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJvdGhlcl9ydCIsICJia19iaXpfaWQiOiAxMDA0NjUsICJkYXRhX3NvdXJjZV90eXBlIjogIlx1ODFlYVx1NWI5YVx1NGU0OVx1NGU4Ylx1NGVmNiJ9LCAiaXNfZW5hYmxlZCI6IHRydWV9 \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73473.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73473.tpl64 deleted file mode 100644 index 7cc0462d36..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73473.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDczNDczLCAibmFtZSI6ICJNeVNRTCBSb3V0aW5lIERlZmluZXIiLCAiZGJfdHlwZSI6ICJteXNxbCIsICJkZXRhaWxzIjogeyJhcHAiOiAiIiwgIm5hbWUiOiAiTXlTUUwgUm91dGluZSBEZWZpbmVyIiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAiQ09VTlQocm91dGluZS1kZWZpbmVyKSIsICJ0YXJnZXQiOiBbW3siZmllbGQiOiAiaG9zdF90b3BvX25vZGUiLCAidmFsdWUiOiBbeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ4fSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ5fSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ5fV0sICJtZXRob2QiOiAiZXEifV1dLCAiZnVuY3Rpb25zIjogW10sICJhbGdvcml0aG1zIjogW3siaWQiOiA3ODgyNSwgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMiwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDF9XV0sICJ1bml0X3ByZWZpeCI6ICIifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICIiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNzc5MjYsICJuYW1lIjogInJvdXRpbmUtZGVmaW5lciIsICJhbGlhcyI6ICJhIiwgImZ1bmN0aW9ucyI6IFtdLCAibWV0cmljX2lkIjogImN1c3RvbS5ldmVudC57YmtfYml6X2lkfV9ia21vbml0b3JfZXZlbnRfe2V2ZW50X2RhdGFfaWR9LnJvdXRpbmUtZGVmaW5lciIsICJhZ2dfbWV0aG9kIjogIkNPVU5UIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAiYWdnX2NvbmRpdGlvbiI6IFtdLCAiYWdnX2RpbWVuc2lvbiI6IFsiYXBwX2lkIiwgInJvbGUiLCAicG9ydCIsICJtYWNoaW5lX3R5cGUiLCAiaW1tdXRlX2RvbWFpbiIsICJia19jbG91ZF9pZCIsICJzZXJ2ZXJfaXAiXSwgImRhdGFfdHlwZV9sYWJlbCI6ICJldmVudCIsICJyZXN1bHRfdGFibGVfaWQiOiAie2JrX2Jpel9pZH1fYmttb25pdG9yX2V2ZW50X3tldmVudF9kYXRhX2lkfSIsICJjdXN0b21fZXZlbnRfbmFtZSI6ICJyb3V0aW5lLWRlZmluZXIiLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAiY3VzdG9tIn1dLCAibm9fZGF0YV9jb25maWciOiB7ImxldmVsIjogMiwgImNvbnRpbnVvdXMiOiAxMCwgImlzX2VuYWJsZWQiOiBmYWxzZSwgImFnZ19kaW1lbnNpb24iOiBbXX19XSwgImxhYmVscyI6IFsiREJNX01ZU1FMIiwgIkRCTSJdLCAibm90aWNlIjogeyJjb25maWciOiB7InRlbXBsYXRlIjogW3sic2lnbmFsIjogImFibm9ybWFsIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAicmVjb3ZlcmVkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAiY2xvc2VkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifV0sICJuZWVkX3BvbGwiOiB0cnVlLCAibm90aWZ5X2ludGVydmFsIjogNzIwMCwgImludGVydmFsX25vdGlmeV9tb2RlIjogInN0YW5kYXJkIn0sICJzaWduYWwiOiBbImFibm9ybWFsIiwgIm5vX2RhdGEiXSwgIm9wdGlvbnMiOiB7ImVuZF90aW1lIjogIjIzOjU5OjU5IiwgInN0YXJ0X3RpbWUiOiAiMDA6MDA6MDAiLCAiYXNzaWduX21vZGUiOiBbIm9ubHlfbm90aWNlIiwgImJ5X3J1bGUiXSwgImNvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAxLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzdHJhdGVneV9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiZGltZW5zaW9ucyJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifV0sICJ0aW1lZGVsdGEiOiA2MCwgImlzX2VuYWJsZWQiOiB0cnVlLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0IiwgIm5lZWRfYml6X2NvbnZlcmdlIjogdHJ1ZSwgInN1Yl9jb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMiwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifV0sICJ0aW1lZGVsdGEiOiA2MCwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdF9hbGFybSJ9fSwgImNoYXJ0X2ltYWdlX2VuYWJsZWQiOiB0cnVlLCAiZXhjbHVkZV9ub3RpY2Vfd2F5cyI6IHsiY2xvc2VkIjogW10sICJyZWNvdmVyZWQiOiBbXX0sICJub2lzZV9yZWR1Y2VfY29uZmlnIjogeyJ1bml0IjogInBlcmNlbnQiLCAiY291bnQiOiAxMCwgInRpbWVkZWx0YSI6IDUsICJkaW1lbnNpb25zIjogW10sICJpc19lbmFibGVkIjogZmFsc2V9fSwgImNvbmZpZ19pZCI6IDcyMTU5LCAicmVsYXRlX3R5cGUiOiAiTk9USUNFIiwgInVzZXJfZ3JvdXBzIjogWzBdfSwgInNvdXJjZSI6ICJia21vbml0b3J2MyIsICJhY3Rpb25zIjogW10sICJkZXRlY3RzIjogW3sibGV2ZWwiOiAyLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJvdGhlcl9ydCIsICJia19iaXpfaWQiOiAxMDA0NjUsICJkYXRhX3NvdXJjZV90eXBlIjogIlx1ODFlYVx1NWI5YVx1NGU0OVx1NGU4Ylx1NGVmNiJ9LCAiaXNfZW5hYmxlZCI6IHRydWV9 \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73474.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73474.tpl64 deleted file mode 100644 index 2bf5e4c0ff..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73474.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDczNDc0LCAibmFtZSI6ICJNeVNRTCBcdTVlOTNcdTViNTdcdTdiMjZcdTk2YzYiLCAiZGJfdHlwZSI6ICJteXNxbCIsICJkZXRhaWxzIjogeyJhcHAiOiAiIiwgIm5hbWUiOiAiTXlTUUwgXHU1ZTkzXHU1YjU3XHU3YjI2XHU5NmM2IiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAiQ09VTlQoY2hhcmFjdGVyLWNvbnNpc3RlbmN5KSIsICJ0YXJnZXQiOiBbW3siZmllbGQiOiAiaG9zdF90b3BvX25vZGUiLCAidmFsdWUiOiBbeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ4fSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ5fSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ5fV0sICJtZXRob2QiOiAiZXEifV1dLCAiZnVuY3Rpb25zIjogW10sICJhbGdvcml0aG1zIjogW3siaWQiOiA3ODgyNiwgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMiwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDF9XV0sICJ1bml0X3ByZWZpeCI6ICIifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICIiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNzc5MjcsICJuYW1lIjogImNoYXJhY3Rlci1jb25zaXN0ZW5jeSIsICJhbGlhcyI6ICJhIiwgImZ1bmN0aW9ucyI6IFtdLCAibWV0cmljX2lkIjogImN1c3RvbS5ldmVudC57YmtfYml6X2lkfV9ia21vbml0b3JfZXZlbnRfe2V2ZW50X2RhdGFfaWR9LmNoYXJhY3Rlci1jb25zaXN0ZW5jeSIsICJhZ2dfbWV0aG9kIjogIkNPVU5UIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAiYWdnX2NvbmRpdGlvbiI6IFtdLCAiYWdnX2RpbWVuc2lvbiI6IFsiYXBwX2lkIiwgIm1hY2hpbmVfdHlwZSIsICJwb3J0IiwgImJrX2Nsb3VkX2lkIiwgImltbXV0ZV9kb21haW4iLCAic2VydmVyX2lwIiwgInJvbGUiXSwgImRhdGFfdHlwZV9sYWJlbCI6ICJldmVudCIsICJyZXN1bHRfdGFibGVfaWQiOiAie2JrX2Jpel9pZH1fYmttb25pdG9yX2V2ZW50X3tldmVudF9kYXRhX2lkfSIsICJjdXN0b21fZXZlbnRfbmFtZSI6ICJjaGFyYWN0ZXItY29uc2lzdGVuY3kiLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAiY3VzdG9tIn1dLCAibm9fZGF0YV9jb25maWciOiB7ImxldmVsIjogMiwgImNvbnRpbnVvdXMiOiAxMCwgImlzX2VuYWJsZWQiOiBmYWxzZSwgImFnZ19kaW1lbnNpb24iOiBbXX19XSwgImxhYmVscyI6IFsiREJNX01ZU1FMIiwgIkRCTSJdLCAibm90aWNlIjogeyJjb25maWciOiB7InRlbXBsYXRlIjogW3sic2lnbmFsIjogImFibm9ybWFsIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAicmVjb3ZlcmVkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAiY2xvc2VkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifV0sICJuZWVkX3BvbGwiOiB0cnVlLCAibm90aWZ5X2ludGVydmFsIjogNzIwMCwgImludGVydmFsX25vdGlmeV9tb2RlIjogInN0YW5kYXJkIn0sICJzaWduYWwiOiBbImFibm9ybWFsIiwgIm5vX2RhdGEiXSwgIm9wdGlvbnMiOiB7ImVuZF90aW1lIjogIjIzOjU5OjU5IiwgInN0YXJ0X3RpbWUiOiAiMDA6MDA6MDAiLCAiYXNzaWduX21vZGUiOiBbIm9ubHlfbm90aWNlIiwgImJ5X3J1bGUiXSwgImNvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAxLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzdHJhdGVneV9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiZGltZW5zaW9ucyJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifV0sICJ0aW1lZGVsdGEiOiA2MCwgImlzX2VuYWJsZWQiOiB0cnVlLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0IiwgIm5lZWRfYml6X2NvbnZlcmdlIjogdHJ1ZSwgInN1Yl9jb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMiwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifV0sICJ0aW1lZGVsdGEiOiA2MCwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdF9hbGFybSJ9fSwgImNoYXJ0X2ltYWdlX2VuYWJsZWQiOiB0cnVlLCAiZXhjbHVkZV9ub3RpY2Vfd2F5cyI6IHsiY2xvc2VkIjogW10sICJyZWNvdmVyZWQiOiBbXX0sICJub2lzZV9yZWR1Y2VfY29uZmlnIjogeyJ1bml0IjogInBlcmNlbnQiLCAiY291bnQiOiAxMCwgInRpbWVkZWx0YSI6IDUsICJkaW1lbnNpb25zIjogW10sICJpc19lbmFibGVkIjogZmFsc2V9fSwgImNvbmZpZ19pZCI6IDcyMTYwLCAicmVsYXRlX3R5cGUiOiAiTk9USUNFIiwgInVzZXJfZ3JvdXBzIjogWzBdfSwgInNvdXJjZSI6ICJia21vbml0b3J2MyIsICJhY3Rpb25zIjogW10sICJkZXRlY3RzIjogW3sibGV2ZWwiOiAyLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJvdGhlcl9ydCIsICJia19iaXpfaWQiOiAxMDA0NjUsICJkYXRhX3NvdXJjZV90eXBlIjogIlx1ODFlYVx1NWI5YVx1NGU0OVx1NGU4Ylx1NGVmNiJ9LCAiaXNfZW5hYmxlZCI6IHRydWV9 \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73669.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73669.tpl64 deleted file mode 100644 index fab81fd146..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73669.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDczNjY5LCAibmFtZSI6ICJQcm94eSBcdTU0MGVcdTdhZWYiLCAiZGJfdHlwZSI6ICJteXNxbCIsICJkZXRhaWxzIjogeyJhcHAiOiAiIiwgIm5hbWUiOiAiUHJveHkgXHU1NDBlXHU3YWVmIiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAiQ09VTlQocHJveHktYmFja2VuZCkiLCAidGFyZ2V0IjogW1t7ImZpZWxkIjogImhvc3RfdG9wb19ub2RlIiwgInZhbHVlIjogW3siYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE0OH0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE0OX0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE0OX1dLCAibWV0aG9kIjogImVxIn1dXSwgImZ1bmN0aW9ucyI6IFtdLCAiYWxnb3JpdGhtcyI6IFt7ImlkIjogNzkwMzYsICJ0eXBlIjogIlRocmVzaG9sZCIsICJsZXZlbCI6IDEsICJjb25maWciOiBbW3sibWV0aG9kIjogImd0ZSIsICJ0aHJlc2hvbGQiOiAxfV1dLCAidW5pdF9wcmVmaXgiOiAiIn1dLCAiZXhwcmVzc2lvbiI6ICJhIiwgIm9yaWdpbl9zcWwiOiAiIiwgInF1ZXJ5X2NvbmZpZ3MiOiBbeyJpZCI6IDc4MTI3LCAibmFtZSI6ICJwcm94eS1iYWNrZW5kIiwgImFsaWFzIjogImEiLCAiZnVuY3Rpb25zIjogW10sICJtZXRyaWNfaWQiOiAiY3VzdG9tLmV2ZW50Lntia19iaXpfaWR9X2JrbW9uaXRvcl9ldmVudF97ZXZlbnRfZGF0YV9pZH0ucHJveHktYmFja2VuZCIsICJhZ2dfbWV0aG9kIjogIkNPVU5UIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAiYWdnX2NvbmRpdGlvbiI6IFtdLCAiYWdnX2RpbWVuc2lvbiI6IFsiYXBwX2lkIiwgInJvbGUiLCAicG9ydCIsICJtYWNoaW5lX3R5cGUiLCAiaW1tdXRlX2RvbWFpbiIsICJia19jbG91ZF9pZCIsICJzZXJ2ZXJfaXAiXSwgImRhdGFfdHlwZV9sYWJlbCI6ICJldmVudCIsICJyZXN1bHRfdGFibGVfaWQiOiAie2JrX2Jpel9pZH1fYmttb25pdG9yX2V2ZW50X3tldmVudF9kYXRhX2lkfSIsICJjdXN0b21fZXZlbnRfbmFtZSI6ICJwcm94eS1iYWNrZW5kIiwgImRhdGFfc291cmNlX2xhYmVsIjogImN1c3RvbSJ9XSwgIm5vX2RhdGFfY29uZmlnIjogeyJsZXZlbCI6IDIsICJjb250aW51b3VzIjogMTAsICJpc19lbmFibGVkIjogZmFsc2UsICJhZ2dfZGltZW5zaW9uIjogW119fV0sICJsYWJlbHMiOiBbIkRCTV9NWVNRTCIsICJEQk0iXSwgIm5vdGljZSI6IHsiY29uZmlnIjogeyJ0ZW1wbGF0ZSI6IFt7InNpZ25hbCI6ICJhYm5vcm1hbCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In0sIHsic2lnbmFsIjogInJlY292ZXJlZCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In0sIHsic2lnbmFsIjogImNsb3NlZCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In1dLCAibmVlZF9wb2xsIjogdHJ1ZSwgIm5vdGlmeV9pbnRlcnZhbCI6IDcyMDAsICJpbnRlcnZhbF9ub3RpZnlfbW9kZSI6ICJzdGFuZGFyZCJ9LCAic2lnbmFsIjogWyJhYm5vcm1hbCIsICJub19kYXRhIl0sICJvcHRpb25zIjogeyJlbmRfdGltZSI6ICIyMzo1OTo1OSIsICJzdGFydF90aW1lIjogIjAwOjAwOjAwIiwgImFzc2lnbl9tb2RlIjogWyJvbmx5X25vdGljZSIsICJieV9ydWxlIl0sICJjb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMSwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic3RyYXRlZ3lfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImRpbWVuc2lvbnMifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In1dLCAidGltZWRlbHRhIjogNjAsICJpc19lbmFibGVkIjogdHJ1ZSwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdCIsICJuZWVkX2Jpel9jb252ZXJnZSI6IHRydWUsICJzdWJfY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDIsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn1dLCAidGltZWRlbHRhIjogNjAsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3RfYWxhcm0ifX0sICJjaGFydF9pbWFnZV9lbmFibGVkIjogdHJ1ZSwgImV4Y2x1ZGVfbm90aWNlX3dheXMiOiB7ImNsb3NlZCI6IFtdLCAicmVjb3ZlcmVkIjogW119LCAibm9pc2VfcmVkdWNlX2NvbmZpZyI6IHsidW5pdCI6ICJwZXJjZW50IiwgImNvdW50IjogMTAsICJ0aW1lZGVsdGEiOiA1LCAiZGltZW5zaW9ucyI6IFtdLCAiaXNfZW5hYmxlZCI6IGZhbHNlfX0sICJjb25maWdfaWQiOiA3MjM3MiwgInJlbGF0ZV90eXBlIjogIk5PVElDRSIsICJ1c2VyX2dyb3VwcyI6IFswXX0sICJzb3VyY2UiOiAiYmttb25pdG9ydjMiLCAiYWN0aW9ucyI6IFtdLCAiZGV0ZWN0cyI6IFt7ImxldmVsIjogMSwgImNvbm5lY3RvciI6ICJhbmQiLCAiZXhwcmVzc2lvbiI6ICIiLCAidHJpZ2dlcl9jb25maWciOiB7ImNvdW50IjogMSwgInVwdGltZSI6IHsiY2FsZW5kYXJzIjogW10sICJ0aW1lX3JhbmdlcyI6IFt7ImVuZCI6ICIyMzo1OSIsICJzdGFydCI6ICIwMDowMCJ9XX0sICJjaGVja193aW5kb3ciOiA1fSwgInJlY292ZXJ5X2NvbmZpZyI6IHsiY2hlY2tfd2luZG93IjogNSwgInN0YXR1c19zZXR0ZXIiOiAicmVjb3ZlcnkifX1dLCAic2NlbmFyaW8iOiAib3RoZXJfcnQiLCAiYmtfYml6X2lkIjogMTAwNDY1LCAiZGF0YV9zb3VyY2VfdHlwZSI6ICJcdTgxZWFcdTViOWFcdTRlNDlcdTRlOGJcdTRlZjYifSwgImlzX2VuYWJsZWQiOiB0cnVlfQ== \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73670.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73670.tpl64 deleted file mode 100644 index 7ed9bc9fd3..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.mysql.73670.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDczNjcwLCAibmFtZSI6ICJQcm94eSBcdTc2N2RcdTU0MGRcdTUzNTUiLCAiZGJfdHlwZSI6ICJteXNxbCIsICJkZXRhaWxzIjogeyJhcHAiOiAiIiwgIm5hbWUiOiAiUHJveHkgXHU3NjdkXHU1NDBkXHU1MzU1IiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAiQ09VTlQocHJveHktdXNlci1saXN0KSIsICJ0YXJnZXQiOiBbW3siZmllbGQiOiAiaG9zdF90b3BvX25vZGUiLCAidmFsdWUiOiBbeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ4fSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ5fSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTQ5fV0sICJtZXRob2QiOiAiZXEifV1dLCAiZnVuY3Rpb25zIjogW10sICJhbGdvcml0aG1zIjogW3siaWQiOiA3OTAzNywgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMSwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDF9XV0sICJ1bml0X3ByZWZpeCI6ICIifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICIiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNzgxMjgsICJuYW1lIjogInByb3h5LXVzZXItbGlzdCIsICJhbGlhcyI6ICJhIiwgImZ1bmN0aW9ucyI6IFtdLCAibWV0cmljX2lkIjogImN1c3RvbS5ldmVudC57YmtfYml6X2lkfV9ia21vbml0b3JfZXZlbnRfe2V2ZW50X2RhdGFfaWR9LnByb3h5LXVzZXItbGlzdCIsICJhZ2dfbWV0aG9kIjogIkNPVU5UIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAiYWdnX2NvbmRpdGlvbiI6IFtdLCAiYWdnX2RpbWVuc2lvbiI6IFsiYXBwX2lkIiwgInJvbGUiLCAicG9ydCIsICJtYWNoaW5lX3R5cGUiLCAiaW1tdXRlX2RvbWFpbiIsICJia19jbG91ZF9pZCIsICJzZXJ2ZXJfaXAiXSwgImRhdGFfdHlwZV9sYWJlbCI6ICJldmVudCIsICJyZXN1bHRfdGFibGVfaWQiOiAie2JrX2Jpel9pZH1fYmttb25pdG9yX2V2ZW50X3tldmVudF9kYXRhX2lkfSIsICJjdXN0b21fZXZlbnRfbmFtZSI6ICJwcm94eS11c2VyLWxpc3QiLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAiY3VzdG9tIn1dLCAibm9fZGF0YV9jb25maWciOiB7ImxldmVsIjogMiwgImNvbnRpbnVvdXMiOiAxMCwgImlzX2VuYWJsZWQiOiBmYWxzZSwgImFnZ19kaW1lbnNpb24iOiBbXX19XSwgImxhYmVscyI6IFsiREJNX01ZU1FMIiwgIkRCTSJdLCAibm90aWNlIjogeyJjb25maWciOiB7InRlbXBsYXRlIjogW3sic2lnbmFsIjogImFibm9ybWFsIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAicmVjb3ZlcmVkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAiY2xvc2VkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifV0sICJuZWVkX3BvbGwiOiB0cnVlLCAibm90aWZ5X2ludGVydmFsIjogNzIwMCwgImludGVydmFsX25vdGlmeV9tb2RlIjogInN0YW5kYXJkIn0sICJzaWduYWwiOiBbImFibm9ybWFsIiwgIm5vX2RhdGEiXSwgIm9wdGlvbnMiOiB7ImVuZF90aW1lIjogIjIzOjU5OjU5IiwgInN0YXJ0X3RpbWUiOiAiMDA6MDA6MDAiLCAiYXNzaWduX21vZGUiOiBbIm9ubHlfbm90aWNlIiwgImJ5X3J1bGUiXSwgImNvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAxLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzdHJhdGVneV9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiZGltZW5zaW9ucyJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifV0sICJ0aW1lZGVsdGEiOiA2MCwgImlzX2VuYWJsZWQiOiB0cnVlLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0IiwgIm5lZWRfYml6X2NvbnZlcmdlIjogdHJ1ZSwgInN1Yl9jb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMiwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifV0sICJ0aW1lZGVsdGEiOiA2MCwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdF9hbGFybSJ9fSwgImNoYXJ0X2ltYWdlX2VuYWJsZWQiOiB0cnVlLCAiZXhjbHVkZV9ub3RpY2Vfd2F5cyI6IHsiY2xvc2VkIjogW10sICJyZWNvdmVyZWQiOiBbXX0sICJub2lzZV9yZWR1Y2VfY29uZmlnIjogeyJ1bml0IjogInBlcmNlbnQiLCAiY291bnQiOiAxMCwgInRpbWVkZWx0YSI6IDUsICJkaW1lbnNpb25zIjogW10sICJpc19lbmFibGVkIjogZmFsc2V9fSwgImNvbmZpZ19pZCI6IDcyMzczLCAicmVsYXRlX3R5cGUiOiAiTk9USUNFIiwgInVzZXJfZ3JvdXBzIjogWzBdfSwgInNvdXJjZSI6ICJia21vbml0b3J2MyIsICJhY3Rpb25zIjogW10sICJkZXRlY3RzIjogW3sibGV2ZWwiOiAxLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJvdGhlcl9ydCIsICJia19iaXpfaWQiOiAxMDA0NjUsICJkYXRhX3NvdXJjZV90eXBlIjogIlx1ODFlYVx1NWI5YVx1NGU0OVx1NGU4Ylx1NGVmNiJ9LCAiaXNfZW5hYmxlZCI6IHRydWV9 \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.pulsar.5680.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.pulsar.5680.tpl64 deleted file mode 100644 index 71fc18fcb2..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.pulsar.5680.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU2ODAsICJuYW1lIjogIltQdWxzYXJdLVx1NGUzYlx1NjczYVx1NzhjMVx1NzZkOElPXHU1MjI5XHU3NTI4XHU3Mzg3IiwgImRiX3R5cGUiOiAicHVsc2FyIiwgImRldGFpbHMiOiB7ImFwcCI6ICIiLCAibmFtZSI6ICJbUHVsc2FyXS1cdTRlM2JcdTY3M2FcdTc4YzFcdTc2ZDhJT1x1NTIyOVx1NzUyOFx1NzM4NyIsICJwYXRoIjogIiIsICJ0eXBlIjogIm1vbml0b3IiLCAiaXRlbXMiOiBbeyJuYW1lIjogIkFWRyhJL09cdTRmN2ZcdTc1MjhcdTczODcpIiwgInRhcmdldCI6IFtbeyJmaWVsZCI6ICJob3N0X3RvcG9fbm9kZSIsICJ2YWx1ZSI6IFt7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgyMzF9LCB7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgyMzJ9LCB7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgyMzN9XSwgIm1ldGhvZCI6ICJlcSJ9XV0sICJmdW5jdGlvbnMiOiBbXSwgImFsZ29yaXRobXMiOiBbeyJpZCI6IDYyMjMsICJ0eXBlIjogIlRocmVzaG9sZCIsICJsZXZlbCI6IDEsICJjb25maWciOiBbW3sibWV0aG9kIjogImd0ZSIsICJ0aHJlc2hvbGQiOiA5MH1dXSwgInVuaXRfcHJlZml4IjogIiUifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICIiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNjIxOSwgIm5hbWUiOiAiSS9PXHU0ZjdmXHU3NTI4XHU3Mzg3IiwgInVuaXQiOiAicGVyY2VudHVuaXQiLCAiYWxpYXMiOiAiYSIsICJmdW5jdGlvbnMiOiBbXSwgIm1ldHJpY19pZCI6ICJia19tb25pdG9yLnN5c3RlbS5pby51dGlsIiwgImFnZ19tZXRob2QiOiAiQVZHIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAibWV0cmljX2ZpZWxkIjogInV0aWwiLCAiYWdnX2NvbmRpdGlvbiI6IFtdLCAiYWdnX2RpbWVuc2lvbiI6IFsiYmtfdGFyZ2V0X2lwIiwgImJrX3RhcmdldF9jbG91ZF9pZCIsICJkZXZpY2VfbmFtZSJdLCAiZGF0YV90eXBlX2xhYmVsIjogInRpbWVfc2VyaWVzIiwgInJlc3VsdF90YWJsZV9pZCI6ICJzeXN0ZW0uaW8iLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAiYmtfbW9uaXRvciJ9XSwgIm5vX2RhdGFfY29uZmlnIjogeyJsZXZlbCI6IDIsICJjb250aW51b3VzIjogMTAsICJpc19lbmFibGVkIjogZmFsc2UsICJhZ2dfZGltZW5zaW9uIjogWyJia190YXJnZXRfaXAiLCAiYmtfdGFyZ2V0X2Nsb3VkX2lkIl19fV0sICJsYWJlbHMiOiBbIlB1bHNhciIsICJEQk1fUFVMU0FSIiwgIkRCTSJdLCAibm90aWNlIjogeyJjb25maWciOiB7InRlbXBsYXRlIjogW3sic2lnbmFsIjogImFibm9ybWFsIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAicmVjb3ZlcmVkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAiY2xvc2VkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifV0sICJuZWVkX3BvbGwiOiB0cnVlLCAibm90aWZ5X2ludGVydmFsIjogNzIwMCwgImludGVydmFsX25vdGlmeV9tb2RlIjogInN0YW5kYXJkIn0sICJzaWduYWwiOiBbImFibm9ybWFsIiwgIm5vX2RhdGEiXSwgIm9wdGlvbnMiOiB7ImVuZF90aW1lIjogIjIzOjU5OjU5IiwgInN0YXJ0X3RpbWUiOiAiMDA6MDA6MDAiLCAiYXNzaWduX21vZGUiOiBbIm9ubHlfbm90aWNlIiwgImJ5X3J1bGUiXSwgImNvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAxLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzdHJhdGVneV9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiZGltZW5zaW9ucyJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifV0sICJ0aW1lZGVsdGEiOiA2MCwgImlzX2VuYWJsZWQiOiB0cnVlLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0IiwgIm5lZWRfYml6X2NvbnZlcmdlIjogdHJ1ZSwgInN1Yl9jb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMiwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifV0sICJ0aW1lZGVsdGEiOiA2MCwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdF9hbGFybSJ9fSwgImV4Y2x1ZGVfbm90aWNlX3dheXMiOiB7ImNsb3NlZCI6IFtdLCAicmVjb3ZlcmVkIjogW119LCAibm9pc2VfcmVkdWNlX2NvbmZpZyI6IHsidW5pdCI6ICJwZXJjZW50IiwgImNvdW50IjogMTAsICJ0aW1lZGVsdGEiOiA1LCAiZGltZW5zaW9ucyI6IFtdLCAiaXNfZW5hYmxlZCI6IGZhbHNlfX0sICJjb25maWdfaWQiOiA4NzM0LCAicmVsYXRlX3R5cGUiOiAiTk9USUNFIiwgInVzZXJfZ3JvdXBzIjogWzBdfSwgInNvdXJjZSI6ICJia21vbml0b3J2MyIsICJhY3Rpb25zIjogW10sICJkZXRlY3RzIjogW3sibGV2ZWwiOiAxLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJvcyIsICJia19iaXpfaWQiOiAyMDA1MDAwMTk0LCAiZGF0YV9zb3VyY2VfdHlwZSI6ICJcdTc2ZDFcdTYzYTdcdTkxYzdcdTk2YzZcdTYzMDdcdTY4MDcifSwgImlzX2VuYWJsZWQiOiB0cnVlfQ== \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.pulsar.5681.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.pulsar.5681.tpl64 deleted file mode 100644 index aaad29fe07..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.pulsar.5681.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU2ODEsICJuYW1lIjogIltQdWxzYXJdLVx1NGUzYlx1NjczYVx1NzhjMVx1NzZkOFx1N2E3YVx1OTVmNFx1NGY3Zlx1NzUyOFx1NzM4NyIsICJkYl90eXBlIjogInB1bHNhciIsICJkZXRhaWxzIjogeyJhcHAiOiAiIiwgIm5hbWUiOiAiW1B1bHNhcl0tXHU0ZTNiXHU2NzNhXHU3OGMxXHU3NmQ4XHU3YTdhXHU5NWY0XHU0ZjdmXHU3NTI4XHU3Mzg3IiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAiQVZHKFx1NzhjMVx1NzZkOFx1N2E3YVx1OTVmNFx1NGY3Zlx1NzUyOFx1NzM4NykiLCAidGFyZ2V0IjogW1t7ImZpZWxkIjogImhvc3RfdG9wb19ub2RlIiwgInZhbHVlIjogW3siYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODIzMX0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODIzMn0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODIzM31dLCAibWV0aG9kIjogImVxIn1dXSwgImZ1bmN0aW9ucyI6IFtdLCAiYWxnb3JpdGhtcyI6IFt7ImlkIjogNjIyNCwgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMSwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDg1fV1dLCAidW5pdF9wcmVmaXgiOiAiJSJ9XSwgImV4cHJlc3Npb24iOiAiYSIsICJvcmlnaW5fc3FsIjogIiIsICJxdWVyeV9jb25maWdzIjogW3siaWQiOiA2MjIwLCAibmFtZSI6ICJcdTc4YzFcdTc2ZDhcdTdhN2FcdTk1ZjRcdTRmN2ZcdTc1MjhcdTczODciLCAidW5pdCI6ICJwZXJjZW50IiwgImFsaWFzIjogImEiLCAiZnVuY3Rpb25zIjogW10sICJtZXRyaWNfaWQiOiAiYmtfbW9uaXRvci5zeXN0ZW0uZGlzay5pbl91c2UiLCAiYWdnX21ldGhvZCI6ICJBVkciLCAiYWdnX2ludGVydmFsIjogNjAsICJtZXRyaWNfZmllbGQiOiAiaW5fdXNlIiwgImFnZ19jb25kaXRpb24iOiBbXSwgImFnZ19kaW1lbnNpb24iOiBbImJrX3RhcmdldF9pcCIsICJia190YXJnZXRfY2xvdWRfaWQiLCAibW91bnRfcG9pbnQiXSwgImRhdGFfdHlwZV9sYWJlbCI6ICJ0aW1lX3NlcmllcyIsICJyZXN1bHRfdGFibGVfaWQiOiAic3lzdGVtLmRpc2siLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAiYmtfbW9uaXRvciJ9XSwgIm5vX2RhdGFfY29uZmlnIjogeyJsZXZlbCI6IDIsICJjb250aW51b3VzIjogMTAsICJpc19lbmFibGVkIjogZmFsc2UsICJhZ2dfZGltZW5zaW9uIjogWyJia190YXJnZXRfaXAiLCAiYmtfdGFyZ2V0X2Nsb3VkX2lkIl19fV0sICJsYWJlbHMiOiBbIlB1bHNhciIsICJEQk1fUFVMU0FSIiwgIkRCTSJdLCAibm90aWNlIjogeyJjb25maWciOiB7InRlbXBsYXRlIjogW3sic2lnbmFsIjogImFibm9ybWFsIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAicmVjb3ZlcmVkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAiY2xvc2VkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifV0sICJuZWVkX3BvbGwiOiB0cnVlLCAibm90aWZ5X2ludGVydmFsIjogNzIwMCwgImludGVydmFsX25vdGlmeV9tb2RlIjogInN0YW5kYXJkIn0sICJzaWduYWwiOiBbImFibm9ybWFsIiwgIm5vX2RhdGEiXSwgIm9wdGlvbnMiOiB7ImVuZF90aW1lIjogIjIzOjU5OjU5IiwgInN0YXJ0X3RpbWUiOiAiMDA6MDA6MDAiLCAiYXNzaWduX21vZGUiOiBbIm9ubHlfbm90aWNlIiwgImJ5X3J1bGUiXSwgImNvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAxLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzdHJhdGVneV9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiZGltZW5zaW9ucyJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifV0sICJ0aW1lZGVsdGEiOiA2MCwgImlzX2VuYWJsZWQiOiB0cnVlLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0IiwgIm5lZWRfYml6X2NvbnZlcmdlIjogdHJ1ZSwgInN1Yl9jb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMiwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifV0sICJ0aW1lZGVsdGEiOiA2MCwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdF9hbGFybSJ9fSwgImV4Y2x1ZGVfbm90aWNlX3dheXMiOiB7ImNsb3NlZCI6IFtdLCAicmVjb3ZlcmVkIjogW119LCAibm9pc2VfcmVkdWNlX2NvbmZpZyI6IHsidW5pdCI6ICJwZXJjZW50IiwgImNvdW50IjogMTAsICJ0aW1lZGVsdGEiOiA1LCAiZGltZW5zaW9ucyI6IFtdLCAiaXNfZW5hYmxlZCI6IGZhbHNlfX0sICJjb25maWdfaWQiOiA4NzM1LCAicmVsYXRlX3R5cGUiOiAiTk9USUNFIiwgInVzZXJfZ3JvdXBzIjogWzBdfSwgInNvdXJjZSI6ICJia21vbml0b3J2MyIsICJhY3Rpb25zIjogW10sICJkZXRlY3RzIjogW3sibGV2ZWwiOiAxLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJvcyIsICJia19iaXpfaWQiOiAyMDA1MDAwMTk0LCAiZGF0YV9zb3VyY2VfdHlwZSI6ICJcdTc2ZDFcdTYzYTdcdTkxYzdcdTk2YzZcdTYzMDdcdTY4MDcifSwgImlzX2VuYWJsZWQiOiB0cnVlfQ== \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.pulsar.5682.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.pulsar.5682.tpl64 deleted file mode 100644 index 13e1c6dd5a..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.pulsar.5682.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU2ODIsICJuYW1lIjogIltQdWxzYXJdLVx1NGUzYlx1NjczYVx1N2Y1MVx1N2VkY1x1NTE2NVx1NmQ0MVx1OTFjZiIsICJkYl90eXBlIjogInB1bHNhciIsICJkZXRhaWxzIjogeyJhcHAiOiAiIiwgIm5hbWUiOiAiW1B1bHNhcl0tXHU0ZTNiXHU2NzNhXHU3ZjUxXHU3ZWRjXHU1MTY1XHU2ZDQxXHU5MWNmIiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAiQVZHKFx1N2Y1MVx1NTM2MVx1NTE2NVx1NmQ0MVx1OTFjZikiLCAidGFyZ2V0IjogW1t7ImZpZWxkIjogImhvc3RfdG9wb19ub2RlIiwgInZhbHVlIjogW3siYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODIzMX0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODIzMn0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODIzM31dLCAibWV0aG9kIjogImVxIn1dXSwgImZ1bmN0aW9ucyI6IFtdLCAiYWxnb3JpdGhtcyI6IFt7ImlkIjogNjIyNSwgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMSwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDI1MH1dXSwgInVuaXRfcHJlZml4IjogIk0ifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICIiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNjIyMSwgIm5hbWUiOiAiXHU3ZjUxXHU1MzYxXHU1MTY1XHU2ZDQxXHU5MWNmIiwgInVuaXQiOiAiQnBzIiwgImFsaWFzIjogImEiLCAiZnVuY3Rpb25zIjogW10sICJtZXRyaWNfaWQiOiAiYmtfbW9uaXRvci5zeXN0ZW0ubmV0LnNwZWVkX3JlY3YiLCAiYWdnX21ldGhvZCI6ICJBVkciLCAiYWdnX2ludGVydmFsIjogNjAsICJtZXRyaWNfZmllbGQiOiAic3BlZWRfcmVjdiIsICJhZ2dfY29uZGl0aW9uIjogW10sICJhZ2dfZGltZW5zaW9uIjogWyJia190YXJnZXRfaXAiLCAiYmtfdGFyZ2V0X2Nsb3VkX2lkIiwgImRldmljZV9uYW1lIl0sICJkYXRhX3R5cGVfbGFiZWwiOiAidGltZV9zZXJpZXMiLCAicmVzdWx0X3RhYmxlX2lkIjogInN5c3RlbS5uZXQiLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAiYmtfbW9uaXRvciJ9XSwgIm5vX2RhdGFfY29uZmlnIjogeyJsZXZlbCI6IDIsICJjb250aW51b3VzIjogMTAsICJpc19lbmFibGVkIjogZmFsc2UsICJhZ2dfZGltZW5zaW9uIjogWyJia190YXJnZXRfaXAiLCAiYmtfdGFyZ2V0X2Nsb3VkX2lkIl19fV0sICJsYWJlbHMiOiBbIlB1bHNhciIsICJEQk1fUFVMU0FSIiwgIkRCTSJdLCAibm90aWNlIjogeyJjb25maWciOiB7InRlbXBsYXRlIjogW3sic2lnbmFsIjogImFibm9ybWFsIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAicmVjb3ZlcmVkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAiY2xvc2VkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifV0sICJuZWVkX3BvbGwiOiB0cnVlLCAibm90aWZ5X2ludGVydmFsIjogNzIwMCwgImludGVydmFsX25vdGlmeV9tb2RlIjogInN0YW5kYXJkIn0sICJzaWduYWwiOiBbImFibm9ybWFsIiwgIm5vX2RhdGEiXSwgIm9wdGlvbnMiOiB7ImVuZF90aW1lIjogIjIzOjU5OjU5IiwgInN0YXJ0X3RpbWUiOiAiMDA6MDA6MDAiLCAiYXNzaWduX21vZGUiOiBbIm9ubHlfbm90aWNlIiwgImJ5X3J1bGUiXSwgImNvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAxLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzdHJhdGVneV9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiZGltZW5zaW9ucyJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifV0sICJ0aW1lZGVsdGEiOiA2MCwgImlzX2VuYWJsZWQiOiB0cnVlLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0IiwgIm5lZWRfYml6X2NvbnZlcmdlIjogdHJ1ZSwgInN1Yl9jb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMiwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifV0sICJ0aW1lZGVsdGEiOiA2MCwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdF9hbGFybSJ9fSwgImV4Y2x1ZGVfbm90aWNlX3dheXMiOiB7ImNsb3NlZCI6IFtdLCAicmVjb3ZlcmVkIjogW119LCAibm9pc2VfcmVkdWNlX2NvbmZpZyI6IHsidW5pdCI6ICJwZXJjZW50IiwgImNvdW50IjogMTAsICJ0aW1lZGVsdGEiOiA1LCAiZGltZW5zaW9ucyI6IFtdLCAiaXNfZW5hYmxlZCI6IGZhbHNlfX0sICJjb25maWdfaWQiOiA4NzM2LCAicmVsYXRlX3R5cGUiOiAiTk9USUNFIiwgInVzZXJfZ3JvdXBzIjogWzBdfSwgInNvdXJjZSI6ICJia21vbml0b3J2MyIsICJhY3Rpb25zIjogW10sICJkZXRlY3RzIjogW3sibGV2ZWwiOiAxLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJvcyIsICJia19iaXpfaWQiOiAyMDA1MDAwMTk0LCAiZGF0YV9zb3VyY2VfdHlwZSI6ICJcdTc2ZDFcdTYzYTdcdTkxYzdcdTk2YzZcdTYzMDdcdTY4MDcifSwgImlzX2VuYWJsZWQiOiB0cnVlfQ== \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.pulsar.5683.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.pulsar.5683.tpl64 deleted file mode 100644 index 8eb7f40134..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.pulsar.5683.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU2ODMsICJuYW1lIjogIltQdWxzYXJdLVx1NGUzYlx1NjczYSBDUFUgXHU0ZjdmXHU3NTI4XHU3Mzg3IiwgImRiX3R5cGUiOiAicHVsc2FyIiwgImRldGFpbHMiOiB7ImFwcCI6ICIiLCAibmFtZSI6ICJbUHVsc2FyXS1cdTRlM2JcdTY3M2EgQ1BVIFx1NGY3Zlx1NzUyOFx1NzM4NyIsICJwYXRoIjogIiIsICJ0eXBlIjogIm1vbml0b3IiLCAiaXRlbXMiOiBbeyJuYW1lIjogIkFWRyhDUFVcdTRmN2ZcdTc1MjhcdTczODcpIiwgInRhcmdldCI6IFtbeyJmaWVsZCI6ICJob3N0X3RvcG9fbm9kZSIsICJ2YWx1ZSI6IFt7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgyMzF9LCB7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgyMzJ9LCB7ImJrX29ial9pZCI6ICJzZXQiLCAiYmtfaW5zdF9pZCI6IDIwMDAwMDgyMzN9XSwgIm1ldGhvZCI6ICJlcSJ9XV0sICJmdW5jdGlvbnMiOiBbXSwgImFsZ29yaXRobXMiOiBbeyJpZCI6IDYyMjYsICJ0eXBlIjogIlRocmVzaG9sZCIsICJsZXZlbCI6IDEsICJjb25maWciOiBbW3sibWV0aG9kIjogImd0ZSIsICJ0aHJlc2hvbGQiOiA5MH1dXSwgInVuaXRfcHJlZml4IjogIiUifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICIiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNjIyMiwgIm5hbWUiOiAiQ1BVXHU0ZjdmXHU3NTI4XHU3Mzg3IiwgInVuaXQiOiAicGVyY2VudCIsICJhbGlhcyI6ICJhIiwgImZ1bmN0aW9ucyI6IFtdLCAibWV0cmljX2lkIjogImJrX21vbml0b3Iuc3lzdGVtLmNwdV9zdW1tYXJ5LnVzYWdlIiwgImFnZ19tZXRob2QiOiAiQVZHIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAibWV0cmljX2ZpZWxkIjogInVzYWdlIiwgImFnZ19jb25kaXRpb24iOiBbXSwgImFnZ19kaW1lbnNpb24iOiBbImJrX3RhcmdldF9jbG91ZF9pZCIsICJia190YXJnZXRfaXAiXSwgImRhdGFfdHlwZV9sYWJlbCI6ICJ0aW1lX3NlcmllcyIsICJyZXN1bHRfdGFibGVfaWQiOiAic3lzdGVtLmNwdV9zdW1tYXJ5IiwgImRhdGFfc291cmNlX2xhYmVsIjogImJrX21vbml0b3IifV0sICJub19kYXRhX2NvbmZpZyI6IHsibGV2ZWwiOiAyLCAiY29udGludW91cyI6IDEwLCAiaXNfZW5hYmxlZCI6IGZhbHNlLCAiYWdnX2RpbWVuc2lvbiI6IFsiYmtfdGFyZ2V0X2lwIiwgImJrX3RhcmdldF9jbG91ZF9pZCJdfX1dLCAibGFiZWxzIjogWyJQdWxzYXIiLCAiREJNX1BVTFNBUiIsICJEQk0iXSwgIm5vdGljZSI6IHsiY29uZmlnIjogeyJ0ZW1wbGF0ZSI6IFt7InNpZ25hbCI6ICJhYm5vcm1hbCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In0sIHsic2lnbmFsIjogInJlY292ZXJlZCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In0sIHsic2lnbmFsIjogImNsb3NlZCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In1dLCAibmVlZF9wb2xsIjogdHJ1ZSwgIm5vdGlmeV9pbnRlcnZhbCI6IDcyMDAsICJpbnRlcnZhbF9ub3RpZnlfbW9kZSI6ICJzdGFuZGFyZCJ9LCAic2lnbmFsIjogWyJhYm5vcm1hbCIsICJub19kYXRhIl0sICJvcHRpb25zIjogeyJlbmRfdGltZSI6ICIyMzo1OTo1OSIsICJzdGFydF90aW1lIjogIjAwOjAwOjAwIiwgImFzc2lnbl9tb2RlIjogWyJvbmx5X25vdGljZSIsICJieV9ydWxlIl0sICJjb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMSwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic3RyYXRlZ3lfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImRpbWVuc2lvbnMifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In1dLCAidGltZWRlbHRhIjogNjAsICJpc19lbmFibGVkIjogdHJ1ZSwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdCIsICJuZWVkX2Jpel9jb252ZXJnZSI6IHRydWUsICJzdWJfY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDIsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn1dLCAidGltZWRlbHRhIjogNjAsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3RfYWxhcm0ifX0sICJleGNsdWRlX25vdGljZV93YXlzIjogeyJjbG9zZWQiOiBbXSwgInJlY292ZXJlZCI6IFtdfSwgIm5vaXNlX3JlZHVjZV9jb25maWciOiB7InVuaXQiOiAicGVyY2VudCIsICJjb3VudCI6IDEwLCAidGltZWRlbHRhIjogNSwgImRpbWVuc2lvbnMiOiBbXSwgImlzX2VuYWJsZWQiOiBmYWxzZX19LCAiY29uZmlnX2lkIjogODczNywgInJlbGF0ZV90eXBlIjogIk5PVElDRSIsICJ1c2VyX2dyb3VwcyI6IFswXX0sICJzb3VyY2UiOiAiYmttb25pdG9ydjMiLCAiYWN0aW9ucyI6IFtdLCAiZGV0ZWN0cyI6IFt7ImxldmVsIjogMSwgImNvbm5lY3RvciI6ICJhbmQiLCAiZXhwcmVzc2lvbiI6ICIiLCAidHJpZ2dlcl9jb25maWciOiB7ImNvdW50IjogMSwgInVwdGltZSI6IHsiY2FsZW5kYXJzIjogW10sICJ0aW1lX3JhbmdlcyI6IFt7ImVuZCI6ICIyMzo1OSIsICJzdGFydCI6ICIwMDowMCJ9XX0sICJjaGVja193aW5kb3ciOiA1fSwgInJlY292ZXJ5X2NvbmZpZyI6IHsiY2hlY2tfd2luZG93IjogNSwgInN0YXR1c19zZXR0ZXIiOiAicmVjb3ZlcnkifX1dLCAic2NlbmFyaW8iOiAib3MiLCAiYmtfYml6X2lkIjogMjAwNTAwMDE5NCwgImRhdGFfc291cmNlX3R5cGUiOiAiXHU3NmQxXHU2M2E3XHU5MWM3XHU5NmM2XHU2MzA3XHU2ODA3In0sICJpc19lbmFibGVkIjogdHJ1ZX0= \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.5765.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.5765.tpl64 deleted file mode 100644 index 11d714ee61..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.5765.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU3NjUsICJuYW1lIjogInJlZGlzXHU0ZTNiXHU2NzNhQ1BVXHU0ZjdmXHU3NTI4XHU3Mzg3XHU1NDRhXHU4YjY2IiwgImRiX3R5cGUiOiAicmVkaXMiLCAiZGV0YWlscyI6IHsiYXBwIjogIiIsICJuYW1lIjogInJlZGlzXHU0ZTNiXHU2NzNhQ1BVXHU0ZjdmXHU3NTI4XHU3Mzg3XHU1NDRhXHU4YjY2IiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAicmVkaXNcdTRlM2JcdTY3M2FDUFVcdTRmN2ZcdTc1MjhcdTczODdcdTU0NGFcdThiNjYiLCAidGFyZ2V0IjogW1t7ImZpZWxkIjogImhvc3RfdG9wb19ub2RlIiwgInZhbHVlIjogW3siYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1MH0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1MX0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1Mn0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1M30sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODIzMH1dLCAibWV0aG9kIjogImVxIn1dXSwgImZ1bmN0aW9ucyI6IFtdLCAiYWxnb3JpdGhtcyI6IFt7ImlkIjogNjMwOSwgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMSwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDkwfV1dLCAidW5pdF9wcmVmaXgiOiAiIn0sIHsiaWQiOiA2MzEwLCAidHlwZSI6ICJUaHJlc2hvbGQiLCAibGV2ZWwiOiAyLCAiY29uZmlnIjogW1t7Im1ldGhvZCI6ICJndGUiLCAidGhyZXNob2xkIjogODB9XV0sICJ1bml0X3ByZWZpeCI6ICIifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICJtaW4obWluX292ZXJfdGltZShia21vbml0b3I6ZGJtX3N5c3RlbTpjcHVfc3VtbWFyeTp1c2FnZXtpbnN0YW5jZV9yb2xlPVwicmVkaXNfbWFzdGVyXCJ9WzFtXSkpIGJ5IChia190YXJnZXRfY2xvdWRfaWQsYXBwLGNsdXN0ZXJfZG9tYWluLGluc3RhbmNlX3JvbGUsY2x1c3Rlcl90eXBlLGJrX3RhcmdldF9pcCkiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNjMwNSwgImFsaWFzIjogImEiLCAicHJvbXFsIjogIm1pbihtaW5fb3Zlcl90aW1lKGJrbW9uaXRvcjpkYm1fc3lzdGVtOmNwdV9zdW1tYXJ5OnVzYWdle2luc3RhbmNlX3JvbGU9XCJyZWRpc19tYXN0ZXJcIn1bMW1dKSkgYnkgKGJrX3RhcmdldF9jbG91ZF9pZCxhcHAsY2x1c3Rlcl9kb21haW4saW5zdGFuY2Vfcm9sZSxjbHVzdGVyX3R5cGUsYmtfdGFyZ2V0X2lwKSIsICJmdW5jdGlvbnMiOiBbXSwgIm1ldHJpY19pZCI6ICJtaW4obWluX292ZXJfdGltZShia21vbml0b3I6ZGJtX3N5c3RlbTpjcHVfc3VtbWFyeTp1c2FnZXtpbnN0YW5jIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAiZGF0YV90eXBlX2xhYmVsIjogInRpbWVfc2VyaWVzIiwgImRhdGFfc291cmNlX2xhYmVsIjogInByb21ldGhldXMifV0sICJub19kYXRhX2NvbmZpZyI6IHsibGV2ZWwiOiAyLCAiY29udGludW91cyI6IDEwLCAiaXNfZW5hYmxlZCI6IGZhbHNlLCAiYWdnX2RpbWVuc2lvbiI6IFtdfX1dLCAibGFiZWxzIjogWyJSRURJUyIsICJEQk1fUkVESVMiLCAiREJNX1JFRElTIiwgIkRCTSJdLCAibm90aWNlIjogeyJjb25maWciOiB7InRlbXBsYXRlIjogW3sic2lnbmFsIjogImFibm9ybWFsIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAicmVjb3ZlcmVkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAiY2xvc2VkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifV0sICJuZWVkX3BvbGwiOiB0cnVlLCAibm90aWZ5X2ludGVydmFsIjogMTgwMCwgImludGVydmFsX25vdGlmeV9tb2RlIjogInN0YW5kYXJkIn0sICJzaWduYWwiOiBbImFibm9ybWFsIiwgIm5vX2RhdGEiXSwgIm9wdGlvbnMiOiB7ImVuZF90aW1lIjogIjIzOjU5OjU5IiwgInN0YXJ0X3RpbWUiOiAiMDA6MDA6MDAiLCAiYXNzaWduX21vZGUiOiBbXSwgInVwZ3JhZGVfY29uZmlnIjoge30sICJjb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMSwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic3RyYXRlZ3lfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImRpbWVuc2lvbnMifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In1dLCAidGltZWRlbHRhIjogNjAsICJpc19lbmFibGVkIjogdHJ1ZSwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdCIsICJuZWVkX2Jpel9jb252ZXJnZSI6IHRydWUsICJzdWJfY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDIsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn1dLCAidGltZWRlbHRhIjogNjAsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3RfYWxhcm0ifX0sICJleGNsdWRlX25vdGljZV93YXlzIjogeyJjbG9zZWQiOiBbXSwgInJlY292ZXJlZCI6IFtdfSwgIm5vaXNlX3JlZHVjZV9jb25maWciOiB7InVuaXQiOiAicGVyY2VudCIsICJjb3VudCI6IDEwLCAidGltZWRlbHRhIjogNSwgImRpbWVuc2lvbnMiOiBbXSwgImlzX2VuYWJsZWQiOiBmYWxzZX19LCAiY29uZmlnX2lkIjogODgxOSwgInJlbGF0ZV90eXBlIjogIk5PVElDRSIsICJ1c2VyX2dyb3VwcyI6IFswXX0sICJzb3VyY2UiOiAiYmttb25pdG9ydjMiLCAiYWN0aW9ucyI6IFtdLCAiZGV0ZWN0cyI6IFt7ImxldmVsIjogMSwgImNvbm5lY3RvciI6ICJhbmQiLCAiZXhwcmVzc2lvbiI6ICIiLCAidHJpZ2dlcl9jb25maWciOiB7ImNvdW50IjogMiwgInVwdGltZSI6IHsiY2FsZW5kYXJzIjogW10sICJ0aW1lX3JhbmdlcyI6IFt7ImVuZCI6ICIyMzo1OSIsICJzdGFydCI6ICIwMDowMCJ9XX0sICJjaGVja193aW5kb3ciOiA1fSwgInJlY292ZXJ5X2NvbmZpZyI6IHsiY2hlY2tfd2luZG93IjogNSwgInN0YXR1c19zZXR0ZXIiOiAicmVjb3ZlcnkifX0sIHsibGV2ZWwiOiAyLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAyLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJvcyIsICJia19iaXpfaWQiOiAyMDA1MDAwMTk0LCAiZGF0YV9zb3VyY2VfdHlwZSI6ICJQcm9tZXRoZXVzIn0sICJpc19lbmFibGVkIjogdHJ1ZX0= \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.5779.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.5779.tpl64 deleted file mode 100644 index 5b1ed1b208..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.5779.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU3NzksICJuYW1lIjogInJlZGlzXHU0ZTNiXHU2NzNhXHU1MTg1XHU1YjU4XHU0ZjdmXHU3NTI4XHU3Mzg3IiwgImRiX3R5cGUiOiAicmVkaXMiLCAiZGV0YWlscyI6IHsiYXBwIjogIiIsICJuYW1lIjogInJlZGlzXHU0ZTNiXHU2NzNhXHU1MTg1XHU1YjU4XHU0ZjdmXHU3NTI4XHU3Mzg3IiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAicmVkaXNcdTRlM2JcdTY3M2FcdTUxODVcdTViNThcdTRmN2ZcdTc1MjhcdTczODciLCAidGFyZ2V0IjogW1t7ImZpZWxkIjogImhvc3RfdG9wb19ub2RlIiwgInZhbHVlIjogW3siYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1MH0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1MX0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1Mn0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1M30sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODIzMH1dLCAibWV0aG9kIjogImVxIn1dXSwgImZ1bmN0aW9ucyI6IFtdLCAiYWxnb3JpdGhtcyI6IFt7ImlkIjogNjMyNywgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMSwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDkwfV1dLCAidW5pdF9wcmVmaXgiOiAiIn0sIHsiaWQiOiA2MzI4LCAidHlwZSI6ICJUaHJlc2hvbGQiLCAibGV2ZWwiOiAyLCAiY29uZmlnIjogW1t7Im1ldGhvZCI6ICJndGUiLCAidGhyZXNob2xkIjogODB9XV0sICJ1bml0X3ByZWZpeCI6ICIifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICJtaW4obWluX292ZXJfdGltZShia21vbml0b3I6ZGJtX3N5c3RlbTptZW06cGN0X3VzZWR7aW5zdGFuY2Vfcm9sZT1cInJlZGlzX21hc3RlclwifVsxbV0pKSAgXG5ieSAoYmtfdGFyZ2V0X2Nsb3VkX2lkLGFwcCxjbHVzdGVyX2RvbWFpbixpbnN0YW5jZV9yb2xlLGNsdXN0ZXJfdHlwZSxia190YXJnZXRfaXApIiwgInF1ZXJ5X2NvbmZpZ3MiOiBbeyJpZCI6IDYzMTksICJhbGlhcyI6ICJhIiwgInByb21xbCI6ICJtaW4obWluX292ZXJfdGltZShia21vbml0b3I6ZGJtX3N5c3RlbTptZW06cGN0X3VzZWR7aW5zdGFuY2Vfcm9sZT1cInJlZGlzX21hc3RlclwifVsxbV0pKSAgXG5ieSAoYmtfdGFyZ2V0X2Nsb3VkX2lkLGFwcCxjbHVzdGVyX2RvbWFpbixpbnN0YW5jZV9yb2xlLGNsdXN0ZXJfdHlwZSxia190YXJnZXRfaXApIiwgImZ1bmN0aW9ucyI6IFtdLCAibWV0cmljX2lkIjogIm1pbihtaW5fb3Zlcl90aW1lKGJrbW9uaXRvcjpkYm1fc3lzdGVtOm1lbTpwY3RfdXNlZHtpbnN0YW5jZV9yb2wiLCAiYWdnX2ludGVydmFsIjogNjAsICJkYXRhX3R5cGVfbGFiZWwiOiAidGltZV9zZXJpZXMiLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAicHJvbWV0aGV1cyJ9XSwgIm5vX2RhdGFfY29uZmlnIjogeyJsZXZlbCI6IDIsICJjb250aW51b3VzIjogMTAsICJpc19lbmFibGVkIjogZmFsc2UsICJhZ2dfZGltZW5zaW9uIjogW119fV0sICJsYWJlbHMiOiBbIlJFRElTIiwgIkRCTV9SRURJUyIsICJEQk1fUkVESVMiLCAiREJNIl0sICJub3RpY2UiOiB7ImNvbmZpZyI6IHsidGVtcGxhdGUiOiBbeyJzaWduYWwiOiAiYWJub3JtYWwiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fVxue3thY3Rpb25faW5zdGFuY2UuYXNzaWduZWVzfX0ifSwgeyJzaWduYWwiOiAicmVjb3ZlcmVkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAiY2xvc2VkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifV0sICJuZWVkX3BvbGwiOiB0cnVlLCAibm90aWZ5X2ludGVydmFsIjogMTgwMCwgImludGVydmFsX25vdGlmeV9tb2RlIjogInN0YW5kYXJkIn0sICJzaWduYWwiOiBbIm5vX2RhdGEiLCAiYWJub3JtYWwiXSwgIm9wdGlvbnMiOiB7ImVuZF90aW1lIjogIjIzOjU5OjU5IiwgInN0YXJ0X3RpbWUiOiAiMDA6MDA6MDAiLCAiYXNzaWduX21vZGUiOiBbXSwgInVwZ3JhZGVfY29uZmlnIjoge30sICJjb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMSwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic3RyYXRlZ3lfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImRpbWVuc2lvbnMifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In1dLCAidGltZWRlbHRhIjogNjAsICJpc19lbmFibGVkIjogdHJ1ZSwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdCIsICJuZWVkX2Jpel9jb252ZXJnZSI6IHRydWUsICJzdWJfY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDIsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn1dLCAidGltZWRlbHRhIjogNjAsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3RfYWxhcm0ifX0sICJleGNsdWRlX25vdGljZV93YXlzIjogeyJjbG9zZWQiOiBbXSwgInJlY292ZXJlZCI6IFtdfSwgIm5vaXNlX3JlZHVjZV9jb25maWciOiB7InVuaXQiOiAicGVyY2VudCIsICJjb3VudCI6IDEwLCAidGltZWRlbHRhIjogNSwgImRpbWVuc2lvbnMiOiBbXSwgImlzX2VuYWJsZWQiOiBmYWxzZX19LCAiY29uZmlnX2lkIjogODgzMywgInJlbGF0ZV90eXBlIjogIk5PVElDRSIsICJ1c2VyX2dyb3VwcyI6IFswXX0sICJzb3VyY2UiOiAiYmttb25pdG9ydjMiLCAiYWN0aW9ucyI6IFtdLCAiZGV0ZWN0cyI6IFt7ImxldmVsIjogMSwgImNvbm5lY3RvciI6ICJhbmQiLCAiZXhwcmVzc2lvbiI6ICIiLCAidHJpZ2dlcl9jb25maWciOiB7ImNvdW50IjogMiwgInVwdGltZSI6IHsiY2FsZW5kYXJzIjogW10sICJ0aW1lX3JhbmdlcyI6IFt7ImVuZCI6ICIyMzo1OSIsICJzdGFydCI6ICIwMDowMCJ9XX0sICJjaGVja193aW5kb3ciOiA1fSwgInJlY292ZXJ5X2NvbmZpZyI6IHsiY2hlY2tfd2luZG93IjogNSwgInN0YXR1c19zZXR0ZXIiOiAicmVjb3ZlcnkifX0sIHsibGV2ZWwiOiAyLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAyLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJvcyIsICJia19iaXpfaWQiOiAyMDA1MDAwMTk0LCAiZGF0YV9zb3VyY2VfdHlwZSI6ICJQcm9tZXRoZXVzIn0sICJpc19lbmFibGVkIjogdHJ1ZX0= \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.5780.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.5780.tpl64 deleted file mode 100644 index c88871b729..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.5780.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU3ODAsICJuYW1lIjogInJlZGlzXHU0ZTNiXHU2NzNhXHU3OGMxXHU3NmQ4XHU1YmI5XHU5MWNmXHU0ZjdmXHU3NTI4XHU3Mzg3IiwgImRiX3R5cGUiOiAicmVkaXMiLCAiZGV0YWlscyI6IHsiYXBwIjogIiIsICJuYW1lIjogInJlZGlzXHU0ZTNiXHU2NzNhXHU3OGMxXHU3NmQ4XHU1YmI5XHU5MWNmXHU0ZjdmXHU3NTI4XHU3Mzg3IiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAicmVkaXNcdTRlM2JcdTY3M2FcdTc4YzFcdTc2ZDhcdTViYjlcdTkxY2ZcdTRmN2ZcdTc1MjhcdTczODciLCAidGFyZ2V0IjogW1t7ImZpZWxkIjogImhvc3RfdG9wb19ub2RlIiwgInZhbHVlIjogW3siYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1MH0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1MX0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1Mn0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1M30sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODIzMH1dLCAibWV0aG9kIjogImVxIn1dXSwgImZ1bmN0aW9ucyI6IFtdLCAiYWxnb3JpdGhtcyI6IFt7ImlkIjogNjMyOSwgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMSwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDkwfV1dLCAidW5pdF9wcmVmaXgiOiAiIn0sIHsiaWQiOiA2MzMwLCAidHlwZSI6ICJUaHJlc2hvbGQiLCAibGV2ZWwiOiAyLCAiY29uZmlnIjogW1t7Im1ldGhvZCI6ICJndGUiLCAidGhyZXNob2xkIjogODB9XV0sICJ1bml0X3ByZWZpeCI6ICIifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICJtaW4obWluX292ZXJfdGltZShia21vbml0b3I6ZGJtX3N5c3RlbTpkaXNrOmluX3VzZXtpbnN0YW5jZV9yb2xlPVwicmVkaXNfbWFzdGVyXCJ9WzFtXSkpICBcbmJ5IChia190YXJnZXRfY2xvdWRfaWQsYXBwLGNsdXN0ZXJfZG9tYWluLGluc3RhbmNlX3JvbGUsY2x1c3Rlcl90eXBlLGJrX3RhcmdldF9pcCkiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNjMyMCwgImFsaWFzIjogImEiLCAicHJvbXFsIjogIm1pbihtaW5fb3Zlcl90aW1lKGJrbW9uaXRvcjpkYm1fc3lzdGVtOmRpc2s6aW5fdXNle2luc3RhbmNlX3JvbGU9XCJyZWRpc19tYXN0ZXJcIn1bMW1dKSkgIFxuYnkgKGJrX3RhcmdldF9jbG91ZF9pZCxhcHAsY2x1c3Rlcl9kb21haW4saW5zdGFuY2Vfcm9sZSxjbHVzdGVyX3R5cGUsYmtfdGFyZ2V0X2lwKSIsICJmdW5jdGlvbnMiOiBbXSwgIm1ldHJpY19pZCI6ICJtaW4obWluX292ZXJfdGltZShia21vbml0b3I6ZGJtX3N5c3RlbTpkaXNrOmluX3VzZXtpbnN0YW5jZV9yb2xlIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAiZGF0YV90eXBlX2xhYmVsIjogInRpbWVfc2VyaWVzIiwgImRhdGFfc291cmNlX2xhYmVsIjogInByb21ldGhldXMifV0sICJub19kYXRhX2NvbmZpZyI6IHsibGV2ZWwiOiAyLCAiY29udGludW91cyI6IDEwLCAiaXNfZW5hYmxlZCI6IGZhbHNlLCAiYWdnX2RpbWVuc2lvbiI6IFtdfX1dLCAibGFiZWxzIjogWyJSRURJUyIsICJEQk1fUkVESVMiLCAiREJNX1JFRElTIiwgIkRCTSJdLCAibm90aWNlIjogeyJjb25maWciOiB7InRlbXBsYXRlIjogW3sic2lnbmFsIjogImFibm9ybWFsIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAicmVjb3ZlcmVkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAiY2xvc2VkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifV0sICJuZWVkX3BvbGwiOiB0cnVlLCAibm90aWZ5X2ludGVydmFsIjogMTgwMCwgImludGVydmFsX25vdGlmeV9tb2RlIjogInN0YW5kYXJkIn0sICJzaWduYWwiOiBbImFibm9ybWFsIiwgIm5vX2RhdGEiXSwgIm9wdGlvbnMiOiB7ImVuZF90aW1lIjogIjIzOjU5OjU5IiwgInN0YXJ0X3RpbWUiOiAiMDA6MDA6MDAiLCAiYXNzaWduX21vZGUiOiBbXSwgInVwZ3JhZGVfY29uZmlnIjoge30sICJjb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMSwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic3RyYXRlZ3lfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImRpbWVuc2lvbnMifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In1dLCAidGltZWRlbHRhIjogNjAsICJpc19lbmFibGVkIjogdHJ1ZSwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdCIsICJuZWVkX2Jpel9jb252ZXJnZSI6IHRydWUsICJzdWJfY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDIsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn1dLCAidGltZWRlbHRhIjogNjAsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3RfYWxhcm0ifX0sICJleGNsdWRlX25vdGljZV93YXlzIjogeyJjbG9zZWQiOiBbXSwgInJlY292ZXJlZCI6IFtdfSwgIm5vaXNlX3JlZHVjZV9jb25maWciOiB7InVuaXQiOiAicGVyY2VudCIsICJjb3VudCI6IDEwLCAidGltZWRlbHRhIjogNSwgImRpbWVuc2lvbnMiOiBbXSwgImlzX2VuYWJsZWQiOiBmYWxzZX19LCAiY29uZmlnX2lkIjogODgzNCwgInJlbGF0ZV90eXBlIjogIk5PVElDRSIsICJ1c2VyX2dyb3VwcyI6IFswXX0sICJzb3VyY2UiOiAiYmttb25pdG9ydjMiLCAiYWN0aW9ucyI6IFtdLCAiZGV0ZWN0cyI6IFt7ImxldmVsIjogMSwgImNvbm5lY3RvciI6ICJhbmQiLCAiZXhwcmVzc2lvbiI6ICIiLCAidHJpZ2dlcl9jb25maWciOiB7ImNvdW50IjogMiwgInVwdGltZSI6IHsiY2FsZW5kYXJzIjogW10sICJ0aW1lX3JhbmdlcyI6IFt7ImVuZCI6ICIyMzo1OSIsICJzdGFydCI6ICIwMDowMCJ9XX0sICJjaGVja193aW5kb3ciOiA1fSwgInJlY292ZXJ5X2NvbmZpZyI6IHsiY2hlY2tfd2luZG93IjogNSwgInN0YXR1c19zZXR0ZXIiOiAicmVjb3ZlcnkifX0sIHsibGV2ZWwiOiAyLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAyLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJvcyIsICJia19iaXpfaWQiOiAyMDA1MDAwMTk0LCAiZGF0YV9zb3VyY2VfdHlwZSI6ICJQcm9tZXRoZXVzIn0sICJpc19lbmFibGVkIjogdHJ1ZX0= \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.5781.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.5781.tpl64 deleted file mode 100644 index 37f5d6e77a..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.5781.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU3ODEsICJuYW1lIjogInJlZGlzXHU0ZTNiXHU2NzNhXHU3OGMxXHU3NmQ4SU9cdTRmN2ZcdTc1MjhcdTczODciLCAiZGJfdHlwZSI6ICJyZWRpcyIsICJkZXRhaWxzIjogeyJhcHAiOiAiIiwgIm5hbWUiOiAicmVkaXNcdTRlM2JcdTY3M2FcdTc4YzFcdTc2ZDhJT1x1NGY3Zlx1NzUyOFx1NzM4NyIsICJwYXRoIjogIiIsICJ0eXBlIjogIm1vbml0b3IiLCAiaXRlbXMiOiBbeyJuYW1lIjogInJlZGlzXHU0ZTNiXHU2NzNhXHU3OGMxXHU3NmQ4SU9cdTRmN2ZcdTc1MjhcdTczODciLCAidGFyZ2V0IjogW1t7ImZpZWxkIjogImhvc3RfdG9wb19ub2RlIiwgInZhbHVlIjogW3siYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1MH0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1MX0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1Mn0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1M30sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODIzMH1dLCAibWV0aG9kIjogImVxIn1dXSwgImZ1bmN0aW9ucyI6IFtdLCAiYWxnb3JpdGhtcyI6IFt7ImlkIjogNjMzMSwgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMSwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDkwfV1dLCAidW5pdF9wcmVmaXgiOiAiIn0sIHsiaWQiOiA2MzMyLCAidHlwZSI6ICJUaHJlc2hvbGQiLCAibGV2ZWwiOiAyLCAiY29uZmlnIjogW1t7Im1ldGhvZCI6ICJndGUiLCAidGhyZXNob2xkIjogODB9XV0sICJ1bml0X3ByZWZpeCI6ICIifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICJtaW4obWluX292ZXJfdGltZShia21vbml0b3I6ZGJtX3N5c3RlbTppbzp1dGlse2luc3RhbmNlX3JvbGU9XCJyZWRpc19tYXN0ZXJcIn1bMW1dKSkgIFxuYnkgKGJrX3RhcmdldF9jbG91ZF9pZCxhcHAsY2x1c3Rlcl9kb21haW4saW5zdGFuY2Vfcm9sZSxjbHVzdGVyX3R5cGUsYmtfdGFyZ2V0X2lwKSIsICJxdWVyeV9jb25maWdzIjogW3siaWQiOiA2MzIxLCAiYWxpYXMiOiAiYSIsICJwcm9tcWwiOiAibWluKG1pbl9vdmVyX3RpbWUoYmttb25pdG9yOmRibV9zeXN0ZW06aW86dXRpbHtpbnN0YW5jZV9yb2xlPVwicmVkaXNfbWFzdGVyXCJ9WzFtXSkpICBcbmJ5IChia190YXJnZXRfY2xvdWRfaWQsYXBwLGNsdXN0ZXJfZG9tYWluLGluc3RhbmNlX3JvbGUsY2x1c3Rlcl90eXBlLGJrX3RhcmdldF9pcCkiLCAiZnVuY3Rpb25zIjogW10sICJtZXRyaWNfaWQiOiAibWluKG1pbl9vdmVyX3RpbWUoYmttb25pdG9yOmRibV9zeXN0ZW06aW86dXRpbHtpbnN0YW5jZV9yb2xlPVwicmUiLCAiYWdnX2ludGVydmFsIjogNjAsICJkYXRhX3R5cGVfbGFiZWwiOiAidGltZV9zZXJpZXMiLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAicHJvbWV0aGV1cyJ9XSwgIm5vX2RhdGFfY29uZmlnIjogeyJsZXZlbCI6IDIsICJjb250aW51b3VzIjogMTAsICJpc19lbmFibGVkIjogZmFsc2UsICJhZ2dfZGltZW5zaW9uIjogW119fV0sICJsYWJlbHMiOiBbIlJFRElTIiwgIkRCTV9SRURJUyIsICJEQk1fUkVESVMiLCAiREJNIl0sICJub3RpY2UiOiB7ImNvbmZpZyI6IHsidGVtcGxhdGUiOiBbeyJzaWduYWwiOiAiYWJub3JtYWwiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJyZWNvdmVyZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJjbG9zZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9XSwgIm5lZWRfcG9sbCI6IHRydWUsICJub3RpZnlfaW50ZXJ2YWwiOiAxODAwLCAiaW50ZXJ2YWxfbm90aWZ5X21vZGUiOiAic3RhbmRhcmQifSwgInNpZ25hbCI6IFsiYWJub3JtYWwiLCAibm9fZGF0YSJdLCAib3B0aW9ucyI6IHsiZW5kX3RpbWUiOiAiMjM6NTk6NTkiLCAic3RhcnRfdGltZSI6ICIwMDowMDowMCIsICJhc3NpZ25fbW9kZSI6IFtdLCAidXBncmFkZV9jb25maWciOiB7fSwgImNvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAxLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzdHJhdGVneV9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiZGltZW5zaW9ucyJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifV0sICJ0aW1lZGVsdGEiOiA2MCwgImlzX2VuYWJsZWQiOiB0cnVlLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0IiwgIm5lZWRfYml6X2NvbnZlcmdlIjogdHJ1ZSwgInN1Yl9jb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMiwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifV0sICJ0aW1lZGVsdGEiOiA2MCwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdF9hbGFybSJ9fSwgImV4Y2x1ZGVfbm90aWNlX3dheXMiOiB7ImNsb3NlZCI6IFtdLCAicmVjb3ZlcmVkIjogW119LCAibm9pc2VfcmVkdWNlX2NvbmZpZyI6IHsidW5pdCI6ICJwZXJjZW50IiwgImNvdW50IjogMTAsICJ0aW1lZGVsdGEiOiA1LCAiZGltZW5zaW9ucyI6IFtdLCAiaXNfZW5hYmxlZCI6IGZhbHNlfX0sICJjb25maWdfaWQiOiA4ODM1LCAicmVsYXRlX3R5cGUiOiAiTk9USUNFIiwgInVzZXJfZ3JvdXBzIjogWzBdfSwgInNvdXJjZSI6ICJia21vbml0b3J2MyIsICJhY3Rpb25zIjogW10sICJkZXRlY3RzIjogW3sibGV2ZWwiOiAxLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAyLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fSwgeyJsZXZlbCI6IDIsICJjb25uZWN0b3IiOiAiYW5kIiwgImV4cHJlc3Npb24iOiAiIiwgInRyaWdnZXJfY29uZmlnIjogeyJjb3VudCI6IDIsICJ1cHRpbWUiOiB7ImNhbGVuZGFycyI6IFtdLCAidGltZV9yYW5nZXMiOiBbeyJlbmQiOiAiMjM6NTkiLCAic3RhcnQiOiAiMDA6MDAifV19LCAiY2hlY2tfd2luZG93IjogNX0sICJyZWNvdmVyeV9jb25maWciOiB7ImNoZWNrX3dpbmRvdyI6IDUsICJzdGF0dXNfc2V0dGVyIjogInJlY292ZXJ5In19XSwgInNjZW5hcmlvIjogIm9zIiwgImJrX2Jpel9pZCI6IDIwMDUwMDAxOTQsICJkYXRhX3NvdXJjZV90eXBlIjogIlByb21ldGhldXMifSwgImlzX2VuYWJsZWQiOiB0cnVlfQ== \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.5782.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.5782.tpl64 deleted file mode 100644 index 955ba80704..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.5782.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDU3ODIsICJuYW1lIjogInJlZGlzXHU0ZTNiXHU2NzNhXHU1MzU1XHU2ODM4Q1BVXHU0ZjdmXHU3NTI4XHU3Mzg3IiwgImRiX3R5cGUiOiAicmVkaXMiLCAiZGV0YWlscyI6IHsiYXBwIjogIiIsICJuYW1lIjogInJlZGlzXHU0ZTNiXHU2NzNhXHU1MzU1XHU2ODM4Q1BVXHU0ZjdmXHU3NTI4XHU3Mzg3IiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAicmVkaXNcdTRlM2JcdTY3M2FcdTUzNTVcdTY4MzhDUFVcdTRmN2ZcdTc1MjhcdTczODciLCAidGFyZ2V0IjogW1t7ImZpZWxkIjogImhvc3RfdG9wb19ub2RlIiwgInZhbHVlIjogW3siYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1MH0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1MX0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1Mn0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1M30sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODIzMH1dLCAibWV0aG9kIjogImVxIn1dXSwgImZ1bmN0aW9ucyI6IFtdLCAiYWxnb3JpdGhtcyI6IFt7ImlkIjogNjMzMywgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMSwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDkwfV1dLCAidW5pdF9wcmVmaXgiOiAiIn0sIHsiaWQiOiA2MzM0LCAidHlwZSI6ICJUaHJlc2hvbGQiLCAibGV2ZWwiOiAyLCAiY29uZmlnIjogW1t7Im1ldGhvZCI6ICJndGUiLCAidGhyZXNob2xkIjogODB9XV0sICJ1bml0X3ByZWZpeCI6ICIifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICJtaW4obWF4X292ZXJfdGltZShia21vbml0b3I6ZGJtX3N5c3RlbTpjcHVfZGV0YWlsOnVzYWdle2luc3RhbmNlX3JvbGU9XCJyZWRpc19tYXN0ZXJcIn1bMW1dKSkgIFxuYnkgKGJrX3RhcmdldF9jbG91ZF9pZCxhcHAsY2x1c3Rlcl9kb21haW4saW5zdGFuY2Vfcm9sZSxjbHVzdGVyX3R5cGUsYmtfdGFyZ2V0X2lwKSIsICJxdWVyeV9jb25maWdzIjogW3siaWQiOiA2MzIyLCAiYWxpYXMiOiAiYSIsICJwcm9tcWwiOiAibWluKG1heF9vdmVyX3RpbWUoYmttb25pdG9yOmRibV9zeXN0ZW06Y3B1X2RldGFpbDp1c2FnZXtpbnN0YW5jZV9yb2xlPVwicmVkaXNfbWFzdGVyXCJ9WzFtXSkpICBcbmJ5IChia190YXJnZXRfY2xvdWRfaWQsYXBwLGNsdXN0ZXJfZG9tYWluLGluc3RhbmNlX3JvbGUsY2x1c3Rlcl90eXBlLGJrX3RhcmdldF9pcCkiLCAiZnVuY3Rpb25zIjogW10sICJtZXRyaWNfaWQiOiAibWluKG1heF9vdmVyX3RpbWUoYmttb25pdG9yOmRibV9zeXN0ZW06Y3B1X2RldGFpbDp1c2FnZXtpbnN0YW5jZSIsICJhZ2dfaW50ZXJ2YWwiOiA2MCwgImRhdGFfdHlwZV9sYWJlbCI6ICJ0aW1lX3NlcmllcyIsICJkYXRhX3NvdXJjZV9sYWJlbCI6ICJwcm9tZXRoZXVzIn1dLCAibm9fZGF0YV9jb25maWciOiB7ImxldmVsIjogMiwgImNvbnRpbnVvdXMiOiAxMCwgImlzX2VuYWJsZWQiOiBmYWxzZSwgImFnZ19kaW1lbnNpb24iOiBbXX19XSwgImxhYmVscyI6IFsiUkVESVMiLCAiREJNX1JFRElTIiwgIkRCTV9SRURJUyIsICJEQk0iXSwgIm5vdGljZSI6IHsiY29uZmlnIjogeyJ0ZW1wbGF0ZSI6IFt7InNpZ25hbCI6ICJhYm5vcm1hbCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In0sIHsic2lnbmFsIjogInJlY292ZXJlZCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In0sIHsic2lnbmFsIjogImNsb3NlZCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In1dLCAibmVlZF9wb2xsIjogdHJ1ZSwgIm5vdGlmeV9pbnRlcnZhbCI6IDE4MDAsICJpbnRlcnZhbF9ub3RpZnlfbW9kZSI6ICJzdGFuZGFyZCJ9LCAic2lnbmFsIjogWyJhYm5vcm1hbCIsICJub19kYXRhIl0sICJvcHRpb25zIjogeyJlbmRfdGltZSI6ICIyMzo1OTo1OSIsICJzdGFydF90aW1lIjogIjAwOjAwOjAwIiwgImFzc2lnbl9tb2RlIjogW10sICJ1cGdyYWRlX2NvbmZpZyI6IHt9LCAiY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDEsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInN0cmF0ZWd5X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJkaW1lbnNpb25zIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJia19iaXpfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV9yZWNlaXZlciJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3dheSJ9XSwgInRpbWVkZWx0YSI6IDYwLCAiaXNfZW5hYmxlZCI6IHRydWUsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3QiLCAibmVlZF9iaXpfY29udmVyZ2UiOiB0cnVlLCAic3ViX2NvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAyLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJia19iaXpfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV9yZWNlaXZlciJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3dheSJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9XSwgInRpbWVkZWx0YSI6IDYwLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0X2FsYXJtIn19LCAiZXhjbHVkZV9ub3RpY2Vfd2F5cyI6IHsiY2xvc2VkIjogW10sICJyZWNvdmVyZWQiOiBbXX0sICJub2lzZV9yZWR1Y2VfY29uZmlnIjogeyJ1bml0IjogInBlcmNlbnQiLCAiY291bnQiOiAxMCwgInRpbWVkZWx0YSI6IDUsICJkaW1lbnNpb25zIjogW10sICJpc19lbmFibGVkIjogZmFsc2V9fSwgImNvbmZpZ19pZCI6IDg4MzYsICJyZWxhdGVfdHlwZSI6ICJOT1RJQ0UiLCAidXNlcl9ncm91cHMiOiBbMF19LCAic291cmNlIjogImJrbW9uaXRvcnYzIiwgImFjdGlvbnMiOiBbXSwgImRldGVjdHMiOiBbeyJsZXZlbCI6IDEsICJjb25uZWN0b3IiOiAiYW5kIiwgImV4cHJlc3Npb24iOiAiIiwgInRyaWdnZXJfY29uZmlnIjogeyJjb3VudCI6IDIsICJ1cHRpbWUiOiB7ImNhbGVuZGFycyI6IFtdLCAidGltZV9yYW5nZXMiOiBbeyJlbmQiOiAiMjM6NTkiLCAic3RhcnQiOiAiMDA6MDAifV19LCAiY2hlY2tfd2luZG93IjogNX0sICJyZWNvdmVyeV9jb25maWciOiB7ImNoZWNrX3dpbmRvdyI6IDUsICJzdGF0dXNfc2V0dGVyIjogInJlY292ZXJ5In19LCB7ImxldmVsIjogMiwgImNvbm5lY3RvciI6ICJhbmQiLCAiZXhwcmVzc2lvbiI6ICIiLCAidHJpZ2dlcl9jb25maWciOiB7ImNvdW50IjogMiwgInVwdGltZSI6IHsiY2FsZW5kYXJzIjogW10sICJ0aW1lX3JhbmdlcyI6IFt7ImVuZCI6ICIyMzo1OSIsICJzdGFydCI6ICIwMDowMCJ9XX0sICJjaGVja193aW5kb3ciOiA1fSwgInJlY292ZXJ5X2NvbmZpZyI6IHsiY2hlY2tfd2luZG93IjogNSwgInN0YXR1c19zZXR0ZXIiOiAicmVjb3ZlcnkifX1dLCAic2NlbmFyaW8iOiAib3MiLCAiYmtfYml6X2lkIjogMjAwNTAwMDE5NCwgImRhdGFfc291cmNlX3R5cGUiOiAiUHJvbWV0aGV1cyJ9LCAiaXNfZW5hYmxlZCI6IHRydWV9 \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.73741.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.73741.tpl64 deleted file mode 100644 index 99b3d3a8d3..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.73741.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDczNzQxLCAibmFtZSI6ICJyZWRpc19sb2dpblx1NGU4Ylx1NGVmNlx1NTQ0YVx1OGI2NiIsICJkYl90eXBlIjogInJlZGlzIiwgImRldGFpbHMiOiB7ImFwcCI6ICIiLCAibmFtZSI6ICJyZWRpc19sb2dpblx1NGU4Ylx1NGVmNlx1NTQ0YVx1OGI2NiIsICJwYXRoIjogIiIsICJ0eXBlIjogIm1vbml0b3IiLCAiaXRlbXMiOiBbeyJuYW1lIjogIkNPVU5UKFJFRElTX0xPR0lOKSIsICJ0YXJnZXQiOiBbW3siZmllbGQiOiAiaG9zdF90b3BvX25vZGUiLCAidmFsdWUiOiBbeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTUwfSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTUxfSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTUyfSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTUzfSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MjMwfV0sICJtZXRob2QiOiAiZXEifV1dLCAiZnVuY3Rpb25zIjogW10sICJhbGdvcml0aG1zIjogW3siaWQiOiA3OTEyMCwgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMSwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDF9XV0sICJ1bml0X3ByZWZpeCI6ICIifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICIiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNzgxOTksICJuYW1lIjogIlJFRElTX0xPR0lOIiwgImFsaWFzIjogImEiLCAiZnVuY3Rpb25zIjogW10sICJtZXRyaWNfaWQiOiAiY3VzdG9tLmV2ZW50Lntia19iaXpfaWR9X2JrbW9uaXRvcl9ldmVudF97ZXZlbnRfZGF0YV9pZH0uUkVESVNfTE9HSU4iLCAiYWdnX21ldGhvZCI6ICJDT1VOVCIsICJhZ2dfaW50ZXJ2YWwiOiA2MCwgImFnZ19jb25kaXRpb24iOiBbXSwgImFnZ19kaW1lbnNpb24iOiBbImJrX2Nsb3VkX2lkIiwgImFwcF9pZCIsICJkb21haW4iLCAicm9sZSIsICJzZXJ2ZXJfaXAiLCAic2VydmVyX3BvcnQiLCAidGFyZ2V0Il0sICJkYXRhX3R5cGVfbGFiZWwiOiAiZXZlbnQiLCAicmVzdWx0X3RhYmxlX2lkIjogIntia19iaXpfaWR9X2JrbW9uaXRvcl9ldmVudF97ZXZlbnRfZGF0YV9pZH0iLCAiY3VzdG9tX2V2ZW50X25hbWUiOiAiUkVESVNfTE9HSU4iLCAiZGF0YV9zb3VyY2VfbGFiZWwiOiAiY3VzdG9tIn1dLCAibm9fZGF0YV9jb25maWciOiB7ImxldmVsIjogMiwgImNvbnRpbnVvdXMiOiAxMCwgImlzX2VuYWJsZWQiOiBmYWxzZSwgImFnZ19kaW1lbnNpb24iOiBbXX19XSwgImxhYmVscyI6IFsiUkVESVMiLCAiREJNX1JFRElTIiwgIkRCTSJdLCAibm90aWNlIjogeyJjb25maWciOiB7InRlbXBsYXRlIjogW3sic2lnbmFsIjogImFibm9ybWFsIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAicmVjb3ZlcmVkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifSwgeyJzaWduYWwiOiAiY2xvc2VkIiwgInRpdGxlX3RtcGwiOiAie3tidXNpbmVzcy5ia19iaXpfbmFtZX19IC0ge3thbGFybS5uYW1lfX17e2FsYXJtLmRpc3BsYXlfdHlwZX19IiwgIm1lc3NhZ2VfdG1wbCI6ICJ7e2NvbnRlbnQubGV2ZWx9fVxue3tjb250ZW50LmJlZ2luX3RpbWV9fVxue3tjb250ZW50LnRpbWV9fVxue3tjb250ZW50LmR1cmF0aW9ufX1cbnt7Y29udGVudC50YXJnZXRfdHlwZX19XG57e2NvbnRlbnQuZGF0YV9zb3VyY2V9fVxue3tjb250ZW50LmNvbnRlbnR9fVxue3tjb250ZW50LmN1cnJlbnRfdmFsdWV9fVxue3tjb250ZW50LmJpen19XG57e2NvbnRlbnQudGFyZ2V0fX1cbnt7Y29udGVudC5kaW1lbnNpb259fVxue3tjb250ZW50LmRldGFpbH19XG57e2NvbnRlbnQucmVsYXRlZF9pbmZvfX0ifV0sICJuZWVkX3BvbGwiOiB0cnVlLCAibm90aWZ5X2ludGVydmFsIjogNzIwMCwgImludGVydmFsX25vdGlmeV9tb2RlIjogInN0YW5kYXJkIn0sICJzaWduYWwiOiBbImFibm9ybWFsIiwgIm5vX2RhdGEiXSwgIm9wdGlvbnMiOiB7ImVuZF90aW1lIjogIjIzOjU5OjU5IiwgInN0YXJ0X3RpbWUiOiAiMDA6MDA6MDAiLCAiYXNzaWduX21vZGUiOiBbIm9ubHlfbm90aWNlIiwgImJ5X3J1bGUiXSwgImNvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAxLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzdHJhdGVneV9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiZGltZW5zaW9ucyJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifV0sICJ0aW1lZGVsdGEiOiA2MCwgImlzX2VuYWJsZWQiOiB0cnVlLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0IiwgIm5lZWRfYml6X2NvbnZlcmdlIjogdHJ1ZSwgInN1Yl9jb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMiwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYmtfYml6X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2VfcmVjZWl2ZXIifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV93YXkifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifV0sICJ0aW1lZGVsdGEiOiA2MCwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdF9hbGFybSJ9fSwgImNoYXJ0X2ltYWdlX2VuYWJsZWQiOiB0cnVlLCAiZXhjbHVkZV9ub3RpY2Vfd2F5cyI6IHsiY2xvc2VkIjogW10sICJyZWNvdmVyZWQiOiBbXX0sICJub2lzZV9yZWR1Y2VfY29uZmlnIjogeyJ1bml0IjogInBlcmNlbnQiLCAiY291bnQiOiAxMCwgInRpbWVkZWx0YSI6IDUsICJkaW1lbnNpb25zIjogW10sICJpc19lbmFibGVkIjogZmFsc2V9fSwgImNvbmZpZ19pZCI6IDcyNDQ4LCAicmVsYXRlX3R5cGUiOiAiTk9USUNFIiwgInVzZXJfZ3JvdXBzIjogWzBdfSwgInNvdXJjZSI6ICJia21vbml0b3J2MyIsICJhY3Rpb25zIjogW10sICJkZXRlY3RzIjogW3sibGV2ZWwiOiAxLCAiY29ubmVjdG9yIjogImFuZCIsICJleHByZXNzaW9uIjogIiIsICJ0cmlnZ2VyX2NvbmZpZyI6IHsiY291bnQiOiAxLCAidXB0aW1lIjogeyJjYWxlbmRhcnMiOiBbXSwgInRpbWVfcmFuZ2VzIjogW3siZW5kIjogIjIzOjU5IiwgInN0YXJ0IjogIjAwOjAwIn1dfSwgImNoZWNrX3dpbmRvdyI6IDV9LCAicmVjb3ZlcnlfY29uZmlnIjogeyJjaGVja193aW5kb3ciOiA1LCAic3RhdHVzX3NldHRlciI6ICJyZWNvdmVyeSJ9fV0sICJzY2VuYXJpbyI6ICJvdGhlcl9ydCIsICJia19iaXpfaWQiOiAxMDA0NjUsICJkYXRhX3NvdXJjZV90eXBlIjogIlx1ODFlYVx1NWI5YVx1NGU0OVx1NGU4Ylx1NGVmNiJ9LCAiaXNfZW5hYmxlZCI6IHRydWV9 \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.73742.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.73742.tpl64 deleted file mode 100644 index 8c6b740ba8..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.73742.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDczNzQyLCAibmFtZSI6ICJwcmVkaXh5X2xvZ2luXHU0ZThiXHU0ZWY2XHU1NDRhXHU4YjY2IiwgImRiX3R5cGUiOiAicmVkaXMiLCAiZGV0YWlscyI6IHsiYXBwIjogIiIsICJuYW1lIjogInByZWRpeHlfbG9naW5cdTRlOGJcdTRlZjZcdTU0NGFcdThiNjYiLCAicGF0aCI6ICIiLCAidHlwZSI6ICJtb25pdG9yIiwgIml0ZW1zIjogW3sibmFtZSI6ICJDT1VOVChwcmVkaXh5X2xvZ2luKSIsICJ0YXJnZXQiOiBbW3siZmllbGQiOiAiaG9zdF90b3BvX25vZGUiLCAidmFsdWUiOiBbeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTUwfSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTUxfSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTUyfSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTUzfSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MjMwfV0sICJtZXRob2QiOiAiZXEifV1dLCAiZnVuY3Rpb25zIjogW10sICJhbGdvcml0aG1zIjogW3siaWQiOiA3OTEyMSwgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMSwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDF9XV0sICJ1bml0X3ByZWZpeCI6ICIifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICIiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNzgyMDAsICJuYW1lIjogInByZWRpeHlfbG9naW4iLCAiYWxpYXMiOiAiYSIsICJmdW5jdGlvbnMiOiBbXSwgIm1ldHJpY19pZCI6ICJjdXN0b20uZXZlbnQue2JrX2Jpel9pZH1fYmttb25pdG9yX2V2ZW50X3tldmVudF9kYXRhX2lkfS5wcmVkaXh5X2xvZ2luIiwgImFnZ19tZXRob2QiOiAiQ09VTlQiLCAiYWdnX2ludGVydmFsIjogNjAsICJhZ2dfY29uZGl0aW9uIjogW10sICJhZ2dfZGltZW5zaW9uIjogWyJia19jbG91ZF9pZCIsICJzZXJ2ZXJfcG9ydCIsICJkb21haW4iLCAicm9sZSIsICJzZXJ2ZXJfaXAiLCAidGFyZ2V0IiwgIndhcm5fbGV2ZWwiXSwgImRhdGFfdHlwZV9sYWJlbCI6ICJldmVudCIsICJyZXN1bHRfdGFibGVfaWQiOiAie2JrX2Jpel9pZH1fYmttb25pdG9yX2V2ZW50X3tldmVudF9kYXRhX2lkfSIsICJjdXN0b21fZXZlbnRfbmFtZSI6ICJwcmVkaXh5X2xvZ2luIiwgImRhdGFfc291cmNlX2xhYmVsIjogImN1c3RvbSJ9XSwgIm5vX2RhdGFfY29uZmlnIjogeyJsZXZlbCI6IDIsICJjb250aW51b3VzIjogMTAsICJpc19lbmFibGVkIjogZmFsc2UsICJhZ2dfZGltZW5zaW9uIjogW119fV0sICJsYWJlbHMiOiBbIlJFRElTIiwgIkRCTV9SRURJUyIsICJEQk0iXSwgIm5vdGljZSI6IHsiY29uZmlnIjogeyJ0ZW1wbGF0ZSI6IFt7InNpZ25hbCI6ICJhYm5vcm1hbCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In0sIHsic2lnbmFsIjogInJlY292ZXJlZCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In0sIHsic2lnbmFsIjogImNsb3NlZCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In1dLCAibmVlZF9wb2xsIjogdHJ1ZSwgIm5vdGlmeV9pbnRlcnZhbCI6IDcyMDAsICJpbnRlcnZhbF9ub3RpZnlfbW9kZSI6ICJzdGFuZGFyZCJ9LCAic2lnbmFsIjogWyJub19kYXRhIiwgImFibm9ybWFsIl0sICJvcHRpb25zIjogeyJlbmRfdGltZSI6ICIyMzo1OTo1OSIsICJzdGFydF90aW1lIjogIjAwOjAwOjAwIiwgImFzc2lnbl9tb2RlIjogWyJvbmx5X25vdGljZSIsICJieV9ydWxlIl0sICJjb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMSwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic3RyYXRlZ3lfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImRpbWVuc2lvbnMifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In1dLCAidGltZWRlbHRhIjogNjAsICJpc19lbmFibGVkIjogdHJ1ZSwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdCIsICJuZWVkX2Jpel9jb252ZXJnZSI6IHRydWUsICJzdWJfY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDIsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn1dLCAidGltZWRlbHRhIjogNjAsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3RfYWxhcm0ifX0sICJjaGFydF9pbWFnZV9lbmFibGVkIjogdHJ1ZSwgImV4Y2x1ZGVfbm90aWNlX3dheXMiOiB7ImNsb3NlZCI6IFtdLCAicmVjb3ZlcmVkIjogW119LCAibm9pc2VfcmVkdWNlX2NvbmZpZyI6IHsidW5pdCI6ICJwZXJjZW50IiwgImNvdW50IjogMTAsICJ0aW1lZGVsdGEiOiA1LCAiZGltZW5zaW9ucyI6IFtdLCAiaXNfZW5hYmxlZCI6IGZhbHNlfX0sICJjb25maWdfaWQiOiA3MjQ0OSwgInJlbGF0ZV90eXBlIjogIk5PVElDRSIsICJ1c2VyX2dyb3VwcyI6IFswXX0sICJzb3VyY2UiOiAiYmttb25pdG9ydjMiLCAiYWN0aW9ucyI6IFtdLCAiZGV0ZWN0cyI6IFt7ImxldmVsIjogMSwgImNvbm5lY3RvciI6ICJhbmQiLCAiZXhwcmVzc2lvbiI6ICIiLCAidHJpZ2dlcl9jb25maWciOiB7ImNvdW50IjogMSwgInVwdGltZSI6IHsiY2FsZW5kYXJzIjogW10sICJ0aW1lX3JhbmdlcyI6IFt7ImVuZCI6ICIyMzo1OSIsICJzdGFydCI6ICIwMDowMCJ9XX0sICJjaGVja193aW5kb3ciOiA1fSwgInJlY292ZXJ5X2NvbmZpZyI6IHsiY2hlY2tfd2luZG93IjogNSwgInN0YXR1c19zZXR0ZXIiOiAicmVjb3ZlcnkifX1dLCAic2NlbmFyaW8iOiAib3RoZXJfcnQiLCAiYmtfYml6X2lkIjogMTAwNDY1LCAiZGF0YV9zb3VyY2VfdHlwZSI6ICJcdTgxZWFcdTViOWFcdTRlNDlcdTRlOGJcdTRlZjYifSwgImlzX2VuYWJsZWQiOiB0cnVlfQ== \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.73756.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.73756.tpl64 deleted file mode 100644 index 70d88ebad0..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.73756.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDczNzU2LCAibmFtZSI6ICJyZWRpc19zeW5jIHdhcm5pbmdcdTRlOGJcdTRlZjZcdTU0NGFcdThiNjYiLCAiZGJfdHlwZSI6ICJyZWRpcyIsICJkZXRhaWxzIjogeyJhcHAiOiAiIiwgIm5hbWUiOiAicmVkaXNfc3luYyB3YXJuaW5nXHU0ZThiXHU0ZWY2XHU1NDRhXHU4YjY2IiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAiQ09VTlQocmVkaXNfc3luYykiLCAidGFyZ2V0IjogW1t7ImZpZWxkIjogImhvc3RfdG9wb19ub2RlIiwgInZhbHVlIjogW3siYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1MH0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1MX0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1Mn0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1M30sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODIzMH1dLCAibWV0aG9kIjogImVxIn1dXSwgImZ1bmN0aW9ucyI6IFtdLCAiYWxnb3JpdGhtcyI6IFt7ImlkIjogNzkxMzUsICJ0eXBlIjogIlRocmVzaG9sZCIsICJsZXZlbCI6IDIsICJjb25maWciOiBbW3sibWV0aG9kIjogImd0ZSIsICJ0aHJlc2hvbGQiOiAxfV1dLCAidW5pdF9wcmVmaXgiOiAiIn1dLCAiZXhwcmVzc2lvbiI6ICJhIiwgIm9yaWdpbl9zcWwiOiAiIiwgInF1ZXJ5X2NvbmZpZ3MiOiBbeyJpZCI6IDc4MjE0LCAibmFtZSI6ICJyZWRpc19zeW5jIiwgImFsaWFzIjogImEiLCAiZnVuY3Rpb25zIjogW10sICJtZXRyaWNfaWQiOiAiY3VzdG9tLmV2ZW50Lntia19iaXpfaWR9X2JrbW9uaXRvcl9ldmVudF97ZXZlbnRfZGF0YV9pZH0ucmVkaXNfc3luYyIsICJhZ2dfbWV0aG9kIjogIkNPVU5UIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAiYWdnX2NvbmRpdGlvbiI6IFt7ImtleSI6ICJ3YXJuX2xldmVsIiwgInZhbHVlIjogWyJ3YXJuaW5nIl0sICJtZXRob2QiOiAiZXEiLCAiY29uZGl0aW9uIjogImFuZCIsICJkaW1lbnNpb25fbmFtZSI6ICJ3YXJuX2xldmVsIn1dLCAiYWdnX2RpbWVuc2lvbiI6IFsiYmtfY2xvdWRfaWQiLCAicm9sZSIsICJzZXJ2ZXJfaXAiLCAic2VydmVyX3BvcnQiLCAiZG9tYWluIiwgIndhcm5fbGV2ZWwiXSwgImRhdGFfdHlwZV9sYWJlbCI6ICJldmVudCIsICJyZXN1bHRfdGFibGVfaWQiOiAie2JrX2Jpel9pZH1fYmttb25pdG9yX2V2ZW50X3tldmVudF9kYXRhX2lkfSIsICJjdXN0b21fZXZlbnRfbmFtZSI6ICJyZWRpc19zeW5jIiwgImRhdGFfc291cmNlX2xhYmVsIjogImN1c3RvbSJ9XSwgIm5vX2RhdGFfY29uZmlnIjogeyJsZXZlbCI6IDIsICJjb250aW51b3VzIjogMTAsICJpc19lbmFibGVkIjogZmFsc2UsICJhZ2dfZGltZW5zaW9uIjogW119fV0sICJsYWJlbHMiOiBbIlJFRElTIiwgIkRCTV9SRURJUyIsICJEQk0iXSwgIm5vdGljZSI6IHsiY29uZmlnIjogeyJ0ZW1wbGF0ZSI6IFt7InNpZ25hbCI6ICJhYm5vcm1hbCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In0sIHsic2lnbmFsIjogInJlY292ZXJlZCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In0sIHsic2lnbmFsIjogImNsb3NlZCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In1dLCAibmVlZF9wb2xsIjogdHJ1ZSwgIm5vdGlmeV9pbnRlcnZhbCI6IDcyMDAsICJpbnRlcnZhbF9ub3RpZnlfbW9kZSI6ICJzdGFuZGFyZCJ9LCAic2lnbmFsIjogWyJhYm5vcm1hbCIsICJub19kYXRhIl0sICJvcHRpb25zIjogeyJlbmRfdGltZSI6ICIyMzo1OTo1OSIsICJzdGFydF90aW1lIjogIjAwOjAwOjAwIiwgImFzc2lnbl9tb2RlIjogWyJvbmx5X25vdGljZSIsICJieV9ydWxlIl0sICJjb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMSwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic3RyYXRlZ3lfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImRpbWVuc2lvbnMifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In1dLCAidGltZWRlbHRhIjogNjAsICJpc19lbmFibGVkIjogdHJ1ZSwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdCIsICJuZWVkX2Jpel9jb252ZXJnZSI6IHRydWUsICJzdWJfY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDIsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn1dLCAidGltZWRlbHRhIjogNjAsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3RfYWxhcm0ifX0sICJjaGFydF9pbWFnZV9lbmFibGVkIjogdHJ1ZSwgImV4Y2x1ZGVfbm90aWNlX3dheXMiOiB7ImNsb3NlZCI6IFtdLCAicmVjb3ZlcmVkIjogW119LCAibm9pc2VfcmVkdWNlX2NvbmZpZyI6IHsidW5pdCI6ICJwZXJjZW50IiwgImNvdW50IjogMTAsICJ0aW1lZGVsdGEiOiA1LCAiZGltZW5zaW9ucyI6IFtdLCAiaXNfZW5hYmxlZCI6IGZhbHNlfX0sICJjb25maWdfaWQiOiA3MjQ2MywgInJlbGF0ZV90eXBlIjogIk5PVElDRSIsICJ1c2VyX2dyb3VwcyI6IFswXX0sICJzb3VyY2UiOiAiYmttb25pdG9ydjMiLCAiYWN0aW9ucyI6IFtdLCAiZGV0ZWN0cyI6IFt7ImxldmVsIjogMiwgImNvbm5lY3RvciI6ICJhbmQiLCAiZXhwcmVzc2lvbiI6ICIiLCAidHJpZ2dlcl9jb25maWciOiB7ImNvdW50IjogMSwgInVwdGltZSI6IHsiY2FsZW5kYXJzIjogW10sICJ0aW1lX3JhbmdlcyI6IFt7ImVuZCI6ICIyMzo1OSIsICJzdGFydCI6ICIwMDowMCJ9XX0sICJjaGVja193aW5kb3ciOiA1fSwgInJlY292ZXJ5X2NvbmZpZyI6IHsiY2hlY2tfd2luZG93IjogNSwgInN0YXR1c19zZXR0ZXIiOiAicmVjb3ZlcnkifX1dLCAic2NlbmFyaW8iOiAib3RoZXJfcnQiLCAiYmtfYml6X2lkIjogMTAwNDY1LCAiZGF0YV9zb3VyY2VfdHlwZSI6ICJcdTgxZWFcdTViOWFcdTRlNDlcdTRlOGJcdTRlZjYifSwgImlzX2VuYWJsZWQiOiB0cnVlfQ== \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.73757.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.73757.tpl64 deleted file mode 100644 index 3b81bd489c..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.73757.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDczNzU3LCAibmFtZSI6ICJyZWRpc19zeW5jIGVycm9yXHU0ZThiXHU0ZWY2XHU1NDRhXHU4YjY2IiwgImRiX3R5cGUiOiAicmVkaXMiLCAiZGV0YWlscyI6IHsiYXBwIjogIiIsICJuYW1lIjogInJlZGlzX3N5bmMgZXJyb3JcdTRlOGJcdTRlZjZcdTU0NGFcdThiNjYiLCAicGF0aCI6ICIiLCAidHlwZSI6ICJtb25pdG9yIiwgIml0ZW1zIjogW3sibmFtZSI6ICJDT1VOVChyZWRpc19zeW5jKSIsICJ0YXJnZXQiOiBbW3siZmllbGQiOiAiaG9zdF90b3BvX25vZGUiLCAidmFsdWUiOiBbeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTUwfSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTUxfSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTUyfSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTUzfSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MjMwfV0sICJtZXRob2QiOiAiZXEifV1dLCAiZnVuY3Rpb25zIjogW10sICJhbGdvcml0aG1zIjogW3siaWQiOiA3OTEzNiwgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMSwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDF9XV0sICJ1bml0X3ByZWZpeCI6ICIifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICIiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNzgyMTUsICJuYW1lIjogInJlZGlzX3N5bmMiLCAiYWxpYXMiOiAiYSIsICJmdW5jdGlvbnMiOiBbXSwgIm1ldHJpY19pZCI6ICJjdXN0b20uZXZlbnQue2JrX2Jpel9pZH1fYmttb25pdG9yX2V2ZW50X3tldmVudF9kYXRhX2lkfS5yZWRpc19zeW5jIiwgImFnZ19tZXRob2QiOiAiQ09VTlQiLCAiYWdnX2ludGVydmFsIjogNjAsICJhZ2dfY29uZGl0aW9uIjogW3sia2V5IjogIndhcm5fbGV2ZWwiLCAidmFsdWUiOiBbImVycm9yIl0sICJtZXRob2QiOiAiZXEiLCAiY29uZGl0aW9uIjogImFuZCIsICJkaW1lbnNpb25fbmFtZSI6ICJ3YXJuX2xldmVsIn1dLCAiYWdnX2RpbWVuc2lvbiI6IFsiYmtfY2xvdWRfaWQiLCAicm9sZSIsICJzZXJ2ZXJfaXAiLCAic2VydmVyX3BvcnQiLCAiZG9tYWluIiwgIndhcm5fbGV2ZWwiXSwgImRhdGFfdHlwZV9sYWJlbCI6ICJldmVudCIsICJyZXN1bHRfdGFibGVfaWQiOiAie2JrX2Jpel9pZH1fYmttb25pdG9yX2V2ZW50X3tldmVudF9kYXRhX2lkfSIsICJjdXN0b21fZXZlbnRfbmFtZSI6ICJyZWRpc19zeW5jIiwgImRhdGFfc291cmNlX2xhYmVsIjogImN1c3RvbSJ9XSwgIm5vX2RhdGFfY29uZmlnIjogeyJsZXZlbCI6IDIsICJjb250aW51b3VzIjogMTAsICJpc19lbmFibGVkIjogZmFsc2UsICJhZ2dfZGltZW5zaW9uIjogW119fV0sICJsYWJlbHMiOiBbIlJFRElTIiwgIkRCTV9SRURJUyIsICJEQk0iXSwgIm5vdGljZSI6IHsiY29uZmlnIjogeyJ0ZW1wbGF0ZSI6IFt7InNpZ25hbCI6ICJhYm5vcm1hbCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In0sIHsic2lnbmFsIjogInJlY292ZXJlZCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In0sIHsic2lnbmFsIjogImNsb3NlZCIsICJ0aXRsZV90bXBsIjogInt7YnVzaW5lc3MuYmtfYml6X25hbWV9fSAtIHt7YWxhcm0ubmFtZX19e3thbGFybS5kaXNwbGF5X3R5cGV9fSIsICJtZXNzYWdlX3RtcGwiOiAie3tjb250ZW50LmxldmVsfX1cbnt7Y29udGVudC5iZWdpbl90aW1lfX1cbnt7Y29udGVudC50aW1lfX1cbnt7Y29udGVudC5kdXJhdGlvbn19XG57e2NvbnRlbnQudGFyZ2V0X3R5cGV9fVxue3tjb250ZW50LmRhdGFfc291cmNlfX1cbnt7Y29udGVudC5jb250ZW50fX1cbnt7Y29udGVudC5jdXJyZW50X3ZhbHVlfX1cbnt7Y29udGVudC5iaXp9fVxue3tjb250ZW50LnRhcmdldH19XG57e2NvbnRlbnQuZGltZW5zaW9ufX1cbnt7Y29udGVudC5kZXRhaWx9fVxue3tjb250ZW50LnJlbGF0ZWRfaW5mb319In1dLCAibmVlZF9wb2xsIjogdHJ1ZSwgIm5vdGlmeV9pbnRlcnZhbCI6IDcyMDAsICJpbnRlcnZhbF9ub3RpZnlfbW9kZSI6ICJzdGFuZGFyZCJ9LCAic2lnbmFsIjogWyJub19kYXRhIiwgImFibm9ybWFsIl0sICJvcHRpb25zIjogeyJlbmRfdGltZSI6ICIyMzo1OTo1OSIsICJzdGFydF90aW1lIjogIjAwOjAwOjAwIiwgImFzc2lnbl9tb2RlIjogWyJvbmx5X25vdGljZSIsICJieV9ydWxlIl0sICJjb252ZXJnZV9jb25maWciOiB7ImNvdW50IjogMSwgImNvbmRpdGlvbiI6IFt7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic3RyYXRlZ3lfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImRpbWVuc2lvbnMifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImFsZXJ0X2xldmVsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJzaWduYWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In1dLCAidGltZWRlbHRhIjogNjAsICJpc19lbmFibGVkIjogdHJ1ZSwgImNvbnZlcmdlX2Z1bmMiOiAiY29sbGVjdCIsICJuZWVkX2Jpel9jb252ZXJnZSI6IHRydWUsICJzdWJfY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDIsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogImJrX2Jpel9pZCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3JlY2VpdmVyIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJub3RpY2Vfd2F5In0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn1dLCAidGltZWRlbHRhIjogNjAsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3RfYWxhcm0ifX0sICJjaGFydF9pbWFnZV9lbmFibGVkIjogdHJ1ZSwgImV4Y2x1ZGVfbm90aWNlX3dheXMiOiB7ImNsb3NlZCI6IFtdLCAicmVjb3ZlcmVkIjogW119LCAibm9pc2VfcmVkdWNlX2NvbmZpZyI6IHsidW5pdCI6ICJwZXJjZW50IiwgImNvdW50IjogMTAsICJ0aW1lZGVsdGEiOiA1LCAiZGltZW5zaW9ucyI6IFtdLCAiaXNfZW5hYmxlZCI6IGZhbHNlfX0sICJjb25maWdfaWQiOiA3MjQ2NCwgInJlbGF0ZV90eXBlIjogIk5PVElDRSIsICJ1c2VyX2dyb3VwcyI6IFswXX0sICJzb3VyY2UiOiAiYmttb25pdG9ydjMiLCAiYWN0aW9ucyI6IFtdLCAiZGV0ZWN0cyI6IFt7ImxldmVsIjogMSwgImNvbm5lY3RvciI6ICJhbmQiLCAiZXhwcmVzc2lvbiI6ICIiLCAidHJpZ2dlcl9jb25maWciOiB7ImNvdW50IjogMSwgInVwdGltZSI6IHsiY2FsZW5kYXJzIjogW10sICJ0aW1lX3JhbmdlcyI6IFt7ImVuZCI6ICIyMzo1OSIsICJzdGFydCI6ICIwMDowMCJ9XX0sICJjaGVja193aW5kb3ciOiA1fSwgInJlY292ZXJ5X2NvbmZpZyI6IHsiY2hlY2tfd2luZG93IjogNSwgInN0YXR1c19zZXR0ZXIiOiAicmVjb3ZlcnkifX1dLCAic2NlbmFyaW8iOiAib3RoZXJfcnQiLCAiYmtfYml6X2lkIjogMTAwNDY1LCAiZGF0YV9zb3VyY2VfdHlwZSI6ICJcdTgxZWFcdTViOWFcdTRlNDlcdTRlOGJcdTRlZjYifSwgImlzX2VuYWJsZWQiOiB0cnVlfQ== \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.73759.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.73759.tpl64 deleted file mode 100644 index dc7e251b34..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.73759.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDczNzU5LCAibmFtZSI6ICJyZWRpc19wZXJzaXN0IGVycm9yXHU0ZThiXHU0ZWY2XHU1NDRhXHU4YjY2IiwgImRiX3R5cGUiOiAicmVkaXMiLCAiZGV0YWlscyI6IHsiYXBwIjogIiIsICJuYW1lIjogInJlZGlzX3BlcnNpc3QgZXJyb3JcdTRlOGJcdTRlZjZcdTU0NGFcdThiNjYiLCAicGF0aCI6ICIiLCAidHlwZSI6ICJtb25pdG9yIiwgIml0ZW1zIjogW3sibmFtZSI6ICJDT1VOVChyZWRpc19wZXJzaXN0KSIsICJ0YXJnZXQiOiBbW3siZmllbGQiOiAiaG9zdF90b3BvX25vZGUiLCAidmFsdWUiOiBbeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTUwfSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTUxfSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTUyfSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTUzfSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MjMwfV0sICJtZXRob2QiOiAiZXEifV1dLCAiZnVuY3Rpb25zIjogW10sICJhbGdvcml0aG1zIjogW3siaWQiOiA3OTE0MSwgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMSwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDF9XV0sICJ1bml0X3ByZWZpeCI6ICIifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICIiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNzgyMTcsICJuYW1lIjogInJlZGlzX3BlcnNpc3QiLCAiYWxpYXMiOiAiYSIsICJmdW5jdGlvbnMiOiBbXSwgIm1ldHJpY19pZCI6ICJjdXN0b20uZXZlbnQue2JrX2Jpel9pZH1fYmttb25pdG9yX2V2ZW50X3tldmVudF9kYXRhX2lkfS5yZWRpc19wZXJzaXN0IiwgImFnZ19tZXRob2QiOiAiQ09VTlQiLCAiYWdnX2ludGVydmFsIjogNjAsICJhZ2dfY29uZGl0aW9uIjogW3sia2V5IjogIndhcm5fbGV2ZWwiLCAidmFsdWUiOiBbImVycm9yIl0sICJtZXRob2QiOiAiZXEiLCAiY29uZGl0aW9uIjogImFuZCIsICJkaW1lbnNpb25fbmFtZSI6ICJ3YXJuX2xldmVsIn1dLCAiYWdnX2RpbWVuc2lvbiI6IFsiYmtfY2xvdWRfaWQiLCAic2VydmVyX3BvcnQiLCAid2Fybl9sZXZlbCIsICJ0YXJnZXQiLCAic2VydmVyX2lwIiwgInJvbGUiLCAiZG9tYWluIl0sICJkYXRhX3R5cGVfbGFiZWwiOiAiZXZlbnQiLCAicmVzdWx0X3RhYmxlX2lkIjogIntia19iaXpfaWR9X2JrbW9uaXRvcl9ldmVudF97ZXZlbnRfZGF0YV9pZH0iLCAiY3VzdG9tX2V2ZW50X25hbWUiOiAicmVkaXNfcGVyc2lzdCIsICJkYXRhX3NvdXJjZV9sYWJlbCI6ICJjdXN0b20ifV0sICJub19kYXRhX2NvbmZpZyI6IHsibGV2ZWwiOiAyLCAiY29udGludW91cyI6IDEwLCAiaXNfZW5hYmxlZCI6IGZhbHNlLCAiYWdnX2RpbWVuc2lvbiI6IFtdfX1dLCAibGFiZWxzIjogWyJSRURJUyIsICJEQk1fUkVESVMiLCAiREJNIl0sICJub3RpY2UiOiB7ImNvbmZpZyI6IHsidGVtcGxhdGUiOiBbeyJzaWduYWwiOiAiYWJub3JtYWwiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJyZWNvdmVyZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJjbG9zZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9XSwgIm5lZWRfcG9sbCI6IHRydWUsICJub3RpZnlfaW50ZXJ2YWwiOiA3MjAwLCAiaW50ZXJ2YWxfbm90aWZ5X21vZGUiOiAic3RhbmRhcmQifSwgInNpZ25hbCI6IFsibm9fZGF0YSIsICJhYm5vcm1hbCJdLCAib3B0aW9ucyI6IHsiZW5kX3RpbWUiOiAiMjM6NTk6NTkiLCAic3RhcnRfdGltZSI6ICIwMDowMDowMCIsICJhc3NpZ25fbW9kZSI6IFsib25seV9ub3RpY2UiLCAiYnlfcnVsZSJdLCAiY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDEsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInN0cmF0ZWd5X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJkaW1lbnNpb25zIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJia19iaXpfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV9yZWNlaXZlciJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3dheSJ9XSwgInRpbWVkZWx0YSI6IDYwLCAiaXNfZW5hYmxlZCI6IHRydWUsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3QiLCAibmVlZF9iaXpfY29udmVyZ2UiOiB0cnVlLCAic3ViX2NvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAyLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJia19iaXpfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV9yZWNlaXZlciJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3dheSJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9XSwgInRpbWVkZWx0YSI6IDYwLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0X2FsYXJtIn19LCAiY2hhcnRfaW1hZ2VfZW5hYmxlZCI6IHRydWUsICJleGNsdWRlX25vdGljZV93YXlzIjogeyJjbG9zZWQiOiBbXSwgInJlY292ZXJlZCI6IFtdfSwgIm5vaXNlX3JlZHVjZV9jb25maWciOiB7InVuaXQiOiAicGVyY2VudCIsICJjb3VudCI6IDEwLCAidGltZWRlbHRhIjogNSwgImRpbWVuc2lvbnMiOiBbXSwgImlzX2VuYWJsZWQiOiBmYWxzZX19LCAiY29uZmlnX2lkIjogNzI0NjYsICJyZWxhdGVfdHlwZSI6ICJOT1RJQ0UiLCAidXNlcl9ncm91cHMiOiBbMF19LCAic291cmNlIjogImJrbW9uaXRvcnYzIiwgImFjdGlvbnMiOiBbXSwgImRldGVjdHMiOiBbeyJsZXZlbCI6IDEsICJjb25uZWN0b3IiOiAiYW5kIiwgImV4cHJlc3Npb24iOiAiIiwgInRyaWdnZXJfY29uZmlnIjogeyJjb3VudCI6IDEsICJ1cHRpbWUiOiB7ImNhbGVuZGFycyI6IFtdLCAidGltZV9yYW5nZXMiOiBbeyJlbmQiOiAiMjM6NTkiLCAic3RhcnQiOiAiMDA6MDAifV19LCAiY2hlY2tfd2luZG93IjogNX0sICJyZWNvdmVyeV9jb25maWciOiB7ImNoZWNrX3dpbmRvdyI6IDUsICJzdGF0dXNfc2V0dGVyIjogInJlY292ZXJ5In19XSwgInNjZW5hcmlvIjogIm90aGVyX3J0IiwgImJrX2Jpel9pZCI6IDEwMDQ2NSwgImRhdGFfc291cmNlX3R5cGUiOiAiXHU4MWVhXHU1YjlhXHU0ZTQ5XHU0ZThiXHU0ZWY2In0sICJpc19lbmFibGVkIjogdHJ1ZX0= \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.73760.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.73760.tpl64 deleted file mode 100644 index d835070668..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.73760.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDczNzYwLCAibmFtZSI6ICJyZWRpc19wZXJzaXN0IHdhcm5pbmdcdTRlOGJcdTRlZjZcdTU0NGFcdThiNjYiLCAiZGJfdHlwZSI6ICJyZWRpcyIsICJkZXRhaWxzIjogeyJhcHAiOiAiIiwgIm5hbWUiOiAicmVkaXNfcGVyc2lzdCB3YXJuaW5nXHU0ZThiXHU0ZWY2XHU1NDRhXHU4YjY2IiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAiQ09VTlQocmVkaXNfcGVyc2lzdCkiLCAidGFyZ2V0IjogW1t7ImZpZWxkIjogImhvc3RfdG9wb19ub2RlIiwgInZhbHVlIjogW3siYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1MH0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1MX0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1Mn0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1M30sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODIzMH1dLCAibWV0aG9kIjogImVxIn1dXSwgImZ1bmN0aW9ucyI6IFtdLCAiYWxnb3JpdGhtcyI6IFt7ImlkIjogNzkxNDIsICJ0eXBlIjogIlRocmVzaG9sZCIsICJsZXZlbCI6IDIsICJjb25maWciOiBbW3sibWV0aG9kIjogImd0ZSIsICJ0aHJlc2hvbGQiOiAxfV1dLCAidW5pdF9wcmVmaXgiOiAiIn1dLCAiZXhwcmVzc2lvbiI6ICJhIiwgIm9yaWdpbl9zcWwiOiAiIiwgInF1ZXJ5X2NvbmZpZ3MiOiBbeyJpZCI6IDc4MjE4LCAibmFtZSI6ICJyZWRpc19wZXJzaXN0IiwgImFsaWFzIjogImEiLCAiZnVuY3Rpb25zIjogW10sICJtZXRyaWNfaWQiOiAiY3VzdG9tLmV2ZW50Lntia19iaXpfaWR9X2JrbW9uaXRvcl9ldmVudF97ZXZlbnRfZGF0YV9pZH0ucmVkaXNfcGVyc2lzdCIsICJhZ2dfbWV0aG9kIjogIkNPVU5UIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAiYWdnX2NvbmRpdGlvbiI6IFt7ImtleSI6ICJ3YXJuX2xldmVsIiwgInZhbHVlIjogWyJ3YXJuaW5nIl0sICJtZXRob2QiOiAiZXEiLCAiY29uZGl0aW9uIjogImFuZCIsICJkaW1lbnNpb25fbmFtZSI6ICJ3YXJuX2xldmVsIn1dLCAiYWdnX2RpbWVuc2lvbiI6IFsiYmtfY2xvdWRfaWQiLCAic2VydmVyX3BvcnQiLCAid2Fybl9sZXZlbCIsICJ0YXJnZXQiLCAic2VydmVyX2lwIiwgInJvbGUiLCAiZG9tYWluIl0sICJkYXRhX3R5cGVfbGFiZWwiOiAiZXZlbnQiLCAicmVzdWx0X3RhYmxlX2lkIjogIntia19iaXpfaWR9X2JrbW9uaXRvcl9ldmVudF97ZXZlbnRfZGF0YV9pZH0iLCAiY3VzdG9tX2V2ZW50X25hbWUiOiAicmVkaXNfcGVyc2lzdCIsICJkYXRhX3NvdXJjZV9sYWJlbCI6ICJjdXN0b20ifV0sICJub19kYXRhX2NvbmZpZyI6IHsibGV2ZWwiOiAyLCAiY29udGludW91cyI6IDEwLCAiaXNfZW5hYmxlZCI6IGZhbHNlLCAiYWdnX2RpbWVuc2lvbiI6IFtdfX1dLCAibGFiZWxzIjogWyJSRURJUyIsICJEQk1fUkVESVMiLCAiREJNIl0sICJub3RpY2UiOiB7ImNvbmZpZyI6IHsidGVtcGxhdGUiOiBbeyJzaWduYWwiOiAiYWJub3JtYWwiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJyZWNvdmVyZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJjbG9zZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9XSwgIm5lZWRfcG9sbCI6IHRydWUsICJub3RpZnlfaW50ZXJ2YWwiOiA3MjAwLCAiaW50ZXJ2YWxfbm90aWZ5X21vZGUiOiAic3RhbmRhcmQifSwgInNpZ25hbCI6IFsiYWJub3JtYWwiLCAibm9fZGF0YSJdLCAib3B0aW9ucyI6IHsiZW5kX3RpbWUiOiAiMjM6NTk6NTkiLCAic3RhcnRfdGltZSI6ICIwMDowMDowMCIsICJhc3NpZ25fbW9kZSI6IFsib25seV9ub3RpY2UiLCAiYnlfcnVsZSJdLCAiY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDEsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInN0cmF0ZWd5X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJkaW1lbnNpb25zIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJia19iaXpfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV9yZWNlaXZlciJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3dheSJ9XSwgInRpbWVkZWx0YSI6IDYwLCAiaXNfZW5hYmxlZCI6IHRydWUsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3QiLCAibmVlZF9iaXpfY29udmVyZ2UiOiB0cnVlLCAic3ViX2NvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAyLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJia19iaXpfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV9yZWNlaXZlciJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3dheSJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9XSwgInRpbWVkZWx0YSI6IDYwLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0X2FsYXJtIn19LCAiY2hhcnRfaW1hZ2VfZW5hYmxlZCI6IHRydWUsICJleGNsdWRlX25vdGljZV93YXlzIjogeyJjbG9zZWQiOiBbXSwgInJlY292ZXJlZCI6IFtdfSwgIm5vaXNlX3JlZHVjZV9jb25maWciOiB7InVuaXQiOiAicGVyY2VudCIsICJjb3VudCI6IDEwLCAidGltZWRlbHRhIjogNSwgImRpbWVuc2lvbnMiOiBbXSwgImlzX2VuYWJsZWQiOiBmYWxzZX19LCAiY29uZmlnX2lkIjogNzI0NjcsICJyZWxhdGVfdHlwZSI6ICJOT1RJQ0UiLCAidXNlcl9ncm91cHMiOiBbMF19LCAic291cmNlIjogImJrbW9uaXRvcnYzIiwgImFjdGlvbnMiOiBbXSwgImRldGVjdHMiOiBbeyJsZXZlbCI6IDIsICJjb25uZWN0b3IiOiAiYW5kIiwgImV4cHJlc3Npb24iOiAiIiwgInRyaWdnZXJfY29uZmlnIjogeyJjb3VudCI6IDEsICJ1cHRpbWUiOiB7ImNhbGVuZGFycyI6IFtdLCAidGltZV9yYW5nZXMiOiBbeyJlbmQiOiAiMjM6NTkiLCAic3RhcnQiOiAiMDA6MDAifV19LCAiY2hlY2tfd2luZG93IjogNX0sICJyZWNvdmVyeV9jb25maWciOiB7ImNoZWNrX3dpbmRvdyI6IDUsICJzdGF0dXNfc2V0dGVyIjogInJlY292ZXJ5In19XSwgInNjZW5hcmlvIjogIm90aGVyX3J0IiwgImJrX2Jpel9pZCI6IDEwMDQ2NSwgImRhdGFfc291cmNlX3R5cGUiOiAiXHU4MWVhXHU1YjlhXHU0ZTQ5XHU0ZThiXHU0ZWY2In0sICJpc19lbmFibGVkIjogdHJ1ZX0= \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.73761.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.73761.tpl64 deleted file mode 100644 index e3c37191f9..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.73761.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDczNzYxLCAibmFtZSI6ICJyZWRpc19jbHVzdGVyX3N0YXRlIGVycm9yXHU0ZThiXHU0ZWY2XHU1NDRhXHU4YjY2IiwgImRiX3R5cGUiOiAicmVkaXMiLCAiZGV0YWlscyI6IHsiYXBwIjogIiIsICJuYW1lIjogInJlZGlzX2NsdXN0ZXJfc3RhdGUgZXJyb3JcdTRlOGJcdTRlZjZcdTU0NGFcdThiNjYiLCAicGF0aCI6ICIiLCAidHlwZSI6ICJtb25pdG9yIiwgIml0ZW1zIjogW3sibmFtZSI6ICJDT1VOVChyZWRpc19jbHVzdGVyX3N0YXRlKSIsICJ0YXJnZXQiOiBbW3siZmllbGQiOiAiaG9zdF90b3BvX25vZGUiLCAidmFsdWUiOiBbeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTUwfSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTUxfSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTUyfSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MTUzfSwgeyJia19vYmpfaWQiOiAic2V0IiwgImJrX2luc3RfaWQiOiAyMDAwMDA4MjMwfV0sICJtZXRob2QiOiAiZXEifV1dLCAiZnVuY3Rpb25zIjogW10sICJhbGdvcml0aG1zIjogW3siaWQiOiA3OTE0MywgInR5cGUiOiAiVGhyZXNob2xkIiwgImxldmVsIjogMSwgImNvbmZpZyI6IFtbeyJtZXRob2QiOiAiZ3RlIiwgInRocmVzaG9sZCI6IDF9XV0sICJ1bml0X3ByZWZpeCI6ICIifV0sICJleHByZXNzaW9uIjogImEiLCAib3JpZ2luX3NxbCI6ICIiLCAicXVlcnlfY29uZmlncyI6IFt7ImlkIjogNzgyMTksICJuYW1lIjogInJlZGlzX2NsdXN0ZXJfc3RhdGUiLCAiYWxpYXMiOiAiYSIsICJmdW5jdGlvbnMiOiBbXSwgIm1ldHJpY19pZCI6ICJjdXN0b20uZXZlbnQue2JrX2Jpel9pZH1fYmttb25pdG9yX2V2ZW50X3tldmVudF9kYXRhX2lkfS5yZWRpc19jbHVzdGVyX3N0YXRlIiwgImFnZ19tZXRob2QiOiAiQ09VTlQiLCAiYWdnX2ludGVydmFsIjogNjAsICJhZ2dfY29uZGl0aW9uIjogW3sia2V5IjogIndhcm5fbGV2ZWwiLCAidmFsdWUiOiBbImVycm9yIl0sICJtZXRob2QiOiAiZXEiLCAiY29uZGl0aW9uIjogImFuZCIsICJkaW1lbnNpb25fbmFtZSI6ICJ3YXJuX2xldmVsIn1dLCAiYWdnX2RpbWVuc2lvbiI6IFsiYmtfY2xvdWRfaWQiLCAiZG9tYWluIiwgInJvbGUiLCAic2VydmVyX2lwIiwgInNlcnZlcl9wb3J0IiwgIndhcm5fbGV2ZWwiLCAidGFyZ2V0Il0sICJkYXRhX3R5cGVfbGFiZWwiOiAiZXZlbnQiLCAicmVzdWx0X3RhYmxlX2lkIjogIntia19iaXpfaWR9X2JrbW9uaXRvcl9ldmVudF97ZXZlbnRfZGF0YV9pZH0iLCAiY3VzdG9tX2V2ZW50X25hbWUiOiAicmVkaXNfY2x1c3Rlcl9zdGF0ZSIsICJkYXRhX3NvdXJjZV9sYWJlbCI6ICJjdXN0b20ifV0sICJub19kYXRhX2NvbmZpZyI6IHsibGV2ZWwiOiAyLCAiY29udGludW91cyI6IDEwLCAiaXNfZW5hYmxlZCI6IGZhbHNlLCAiYWdnX2RpbWVuc2lvbiI6IFtdfX1dLCAibGFiZWxzIjogWyJSRURJUyIsICJEQk1fUkVESVMiLCAiREJNIl0sICJub3RpY2UiOiB7ImNvbmZpZyI6IHsidGVtcGxhdGUiOiBbeyJzaWduYWwiOiAiYWJub3JtYWwiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJyZWNvdmVyZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJjbG9zZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9XSwgIm5lZWRfcG9sbCI6IHRydWUsICJub3RpZnlfaW50ZXJ2YWwiOiA3MjAwLCAiaW50ZXJ2YWxfbm90aWZ5X21vZGUiOiAic3RhbmRhcmQifSwgInNpZ25hbCI6IFsiYWJub3JtYWwiLCAibm9fZGF0YSJdLCAib3B0aW9ucyI6IHsiZW5kX3RpbWUiOiAiMjM6NTk6NTkiLCAic3RhcnRfdGltZSI6ICIwMDowMDowMCIsICJhc3NpZ25fbW9kZSI6IFsib25seV9ub3RpY2UiLCAiYnlfcnVsZSJdLCAiY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDEsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInN0cmF0ZWd5X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJkaW1lbnNpb25zIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJia19iaXpfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV9yZWNlaXZlciJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3dheSJ9XSwgInRpbWVkZWx0YSI6IDYwLCAiaXNfZW5hYmxlZCI6IHRydWUsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3QiLCAibmVlZF9iaXpfY29udmVyZ2UiOiB0cnVlLCAic3ViX2NvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAyLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJia19iaXpfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV9yZWNlaXZlciJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3dheSJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9XSwgInRpbWVkZWx0YSI6IDYwLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0X2FsYXJtIn19LCAiY2hhcnRfaW1hZ2VfZW5hYmxlZCI6IHRydWUsICJleGNsdWRlX25vdGljZV93YXlzIjogeyJjbG9zZWQiOiBbXSwgInJlY292ZXJlZCI6IFtdfSwgIm5vaXNlX3JlZHVjZV9jb25maWciOiB7InVuaXQiOiAicGVyY2VudCIsICJjb3VudCI6IDEwLCAidGltZWRlbHRhIjogNSwgImRpbWVuc2lvbnMiOiBbXSwgImlzX2VuYWJsZWQiOiBmYWxzZX19LCAiY29uZmlnX2lkIjogNzI0NjgsICJyZWxhdGVfdHlwZSI6ICJOT1RJQ0UiLCAidXNlcl9ncm91cHMiOiBbMF19LCAic291cmNlIjogImJrbW9uaXRvcnYzIiwgImFjdGlvbnMiOiBbXSwgImRldGVjdHMiOiBbeyJsZXZlbCI6IDEsICJjb25uZWN0b3IiOiAiYW5kIiwgImV4cHJlc3Npb24iOiAiIiwgInRyaWdnZXJfY29uZmlnIjogeyJjb3VudCI6IDEsICJ1cHRpbWUiOiB7ImNhbGVuZGFycyI6IFtdLCAidGltZV9yYW5nZXMiOiBbeyJlbmQiOiAiMjM6NTkiLCAic3RhcnQiOiAiMDA6MDAifV19LCAiY2hlY2tfd2luZG93IjogNX0sICJyZWNvdmVyeV9jb25maWciOiB7ImNoZWNrX3dpbmRvdyI6IDUsICJzdGF0dXNfc2V0dGVyIjogInJlY292ZXJ5In19XSwgInNjZW5hcmlvIjogIm90aGVyX3J0IiwgImJrX2Jpel9pZCI6IDEwMDQ2NSwgImRhdGFfc291cmNlX3R5cGUiOiAiXHU4MWVhXHU1YjlhXHU0ZTQ5XHU0ZThiXHU0ZWY2In0sICJpc19lbmFibGVkIjogdHJ1ZX0= \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.73762.tpl64 b/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.73762.tpl64 deleted file mode 100644 index 638554384a..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/alarm/0.redis.73762.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAibW9uaXRvcl9zdHJhdGVneV9pZCI6IDczNzYyLCAibmFtZSI6ICJyZWRpc19jbHVzdGVyX3N0YXRlIHdhcm5cdTRlOGJcdTRlZjZcdTU0NGFcdThiNjYiLCAiZGJfdHlwZSI6ICJyZWRpcyIsICJkZXRhaWxzIjogeyJhcHAiOiAiIiwgIm5hbWUiOiAicmVkaXNfY2x1c3Rlcl9zdGF0ZSB3YXJuXHU0ZThiXHU0ZWY2XHU1NDRhXHU4YjY2IiwgInBhdGgiOiAiIiwgInR5cGUiOiAibW9uaXRvciIsICJpdGVtcyI6IFt7Im5hbWUiOiAiQ09VTlQocmVkaXNfY2x1c3Rlcl9zdGF0ZSkiLCAidGFyZ2V0IjogW1t7ImZpZWxkIjogImhvc3RfdG9wb19ub2RlIiwgInZhbHVlIjogW3siYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1MH0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1MX0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1Mn0sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODE1M30sIHsiYmtfb2JqX2lkIjogInNldCIsICJia19pbnN0X2lkIjogMjAwMDAwODIzMH1dLCAibWV0aG9kIjogImVxIn1dXSwgImZ1bmN0aW9ucyI6IFtdLCAiYWxnb3JpdGhtcyI6IFt7ImlkIjogNzkxNDQsICJ0eXBlIjogIlRocmVzaG9sZCIsICJsZXZlbCI6IDIsICJjb25maWciOiBbW3sibWV0aG9kIjogImd0ZSIsICJ0aHJlc2hvbGQiOiAxfV1dLCAidW5pdF9wcmVmaXgiOiAiIn1dLCAiZXhwcmVzc2lvbiI6ICJhIiwgIm9yaWdpbl9zcWwiOiAiIiwgInF1ZXJ5X2NvbmZpZ3MiOiBbeyJpZCI6IDc4MjIwLCAibmFtZSI6ICJyZWRpc19jbHVzdGVyX3N0YXRlIiwgImFsaWFzIjogImEiLCAiZnVuY3Rpb25zIjogW10sICJtZXRyaWNfaWQiOiAiY3VzdG9tLmV2ZW50Lntia19iaXpfaWR9X2JrbW9uaXRvcl9ldmVudF97ZXZlbnRfZGF0YV9pZH0ucmVkaXNfY2x1c3Rlcl9zdGF0ZSIsICJhZ2dfbWV0aG9kIjogIkNPVU5UIiwgImFnZ19pbnRlcnZhbCI6IDYwLCAiYWdnX2NvbmRpdGlvbiI6IFt7ImtleSI6ICJ3YXJuX2xldmVsIiwgInZhbHVlIjogWyJ3YXJuaW5nIl0sICJtZXRob2QiOiAiZXEiLCAiY29uZGl0aW9uIjogImFuZCIsICJkaW1lbnNpb25fbmFtZSI6ICJ3YXJuX2xldmVsIn1dLCAiYWdnX2RpbWVuc2lvbiI6IFsiYmtfY2xvdWRfaWQiLCAiZG9tYWluIiwgInJvbGUiLCAic2VydmVyX2lwIiwgInNlcnZlcl9wb3J0IiwgIndhcm5fbGV2ZWwiLCAidGFyZ2V0Il0sICJkYXRhX3R5cGVfbGFiZWwiOiAiZXZlbnQiLCAicmVzdWx0X3RhYmxlX2lkIjogIntia19iaXpfaWR9X2JrbW9uaXRvcl9ldmVudF97ZXZlbnRfZGF0YV9pZH0iLCAiY3VzdG9tX2V2ZW50X25hbWUiOiAicmVkaXNfY2x1c3Rlcl9zdGF0ZSIsICJkYXRhX3NvdXJjZV9sYWJlbCI6ICJjdXN0b20ifV0sICJub19kYXRhX2NvbmZpZyI6IHsibGV2ZWwiOiAyLCAiY29udGludW91cyI6IDEwLCAiaXNfZW5hYmxlZCI6IGZhbHNlLCAiYWdnX2RpbWVuc2lvbiI6IFtdfX1dLCAibGFiZWxzIjogWyJSRURJUyIsICJEQk1fUkVESVMiLCAiREJNIl0sICJub3RpY2UiOiB7ImNvbmZpZyI6IHsidGVtcGxhdGUiOiBbeyJzaWduYWwiOiAiYWJub3JtYWwiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJyZWNvdmVyZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9LCB7InNpZ25hbCI6ICJjbG9zZWQiLCAidGl0bGVfdG1wbCI6ICJ7e2J1c2luZXNzLmJrX2Jpel9uYW1lfX0gLSB7e2FsYXJtLm5hbWV9fXt7YWxhcm0uZGlzcGxheV90eXBlfX0iLCAibWVzc2FnZV90bXBsIjogInt7Y29udGVudC5sZXZlbH19XG57e2NvbnRlbnQuYmVnaW5fdGltZX19XG57e2NvbnRlbnQudGltZX19XG57e2NvbnRlbnQuZHVyYXRpb259fVxue3tjb250ZW50LnRhcmdldF90eXBlfX1cbnt7Y29udGVudC5kYXRhX3NvdXJjZX19XG57e2NvbnRlbnQuY29udGVudH19XG57e2NvbnRlbnQuY3VycmVudF92YWx1ZX19XG57e2NvbnRlbnQuYml6fX1cbnt7Y29udGVudC50YXJnZXR9fVxue3tjb250ZW50LmRpbWVuc2lvbn19XG57e2NvbnRlbnQuZGV0YWlsfX1cbnt7Y29udGVudC5yZWxhdGVkX2luZm99fSJ9XSwgIm5lZWRfcG9sbCI6IHRydWUsICJub3RpZnlfaW50ZXJ2YWwiOiA3MjAwLCAiaW50ZXJ2YWxfbm90aWZ5X21vZGUiOiAic3RhbmRhcmQifSwgInNpZ25hbCI6IFsiYWJub3JtYWwiLCAibm9fZGF0YSJdLCAib3B0aW9ucyI6IHsiZW5kX3RpbWUiOiAiMjM6NTk6NTkiLCAic3RhcnRfdGltZSI6ICIwMDowMDowMCIsICJhc3NpZ25fbW9kZSI6IFsib25seV9ub3RpY2UiLCAiYnlfcnVsZSJdLCAiY29udmVyZ2VfY29uZmlnIjogeyJjb3VudCI6IDEsICJjb25kaXRpb24iOiBbeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInN0cmF0ZWd5X2lkIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJkaW1lbnNpb25zIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJhbGVydF9sZXZlbCJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAic2lnbmFsIn0sIHsidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJia19iaXpfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV9yZWNlaXZlciJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3dheSJ9XSwgInRpbWVkZWx0YSI6IDYwLCAiaXNfZW5hYmxlZCI6IHRydWUsICJjb252ZXJnZV9mdW5jIjogImNvbGxlY3QiLCAibmVlZF9iaXpfY29udmVyZ2UiOiB0cnVlLCAic3ViX2NvbnZlcmdlX2NvbmZpZyI6IHsiY291bnQiOiAyLCAiY29uZGl0aW9uIjogW3sidmFsdWUiOiBbInNlbGYiXSwgImRpbWVuc2lvbiI6ICJia19iaXpfaWQifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogIm5vdGljZV9yZWNlaXZlciJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAibm90aWNlX3dheSJ9LCB7InZhbHVlIjogWyJzZWxmIl0sICJkaW1lbnNpb24iOiAiYWxlcnRfbGV2ZWwifSwgeyJ2YWx1ZSI6IFsic2VsZiJdLCAiZGltZW5zaW9uIjogInNpZ25hbCJ9XSwgInRpbWVkZWx0YSI6IDYwLCAiY29udmVyZ2VfZnVuYyI6ICJjb2xsZWN0X2FsYXJtIn19LCAiY2hhcnRfaW1hZ2VfZW5hYmxlZCI6IHRydWUsICJleGNsdWRlX25vdGljZV93YXlzIjogeyJjbG9zZWQiOiBbXSwgInJlY292ZXJlZCI6IFtdfSwgIm5vaXNlX3JlZHVjZV9jb25maWciOiB7InVuaXQiOiAicGVyY2VudCIsICJjb3VudCI6IDEwLCAidGltZWRlbHRhIjogNSwgImRpbWVuc2lvbnMiOiBbXSwgImlzX2VuYWJsZWQiOiBmYWxzZX19LCAiY29uZmlnX2lkIjogNzI0NjksICJyZWxhdGVfdHlwZSI6ICJOT1RJQ0UiLCAidXNlcl9ncm91cHMiOiBbMF19LCAic291cmNlIjogImJrbW9uaXRvcnYzIiwgImFjdGlvbnMiOiBbXSwgImRldGVjdHMiOiBbeyJsZXZlbCI6IDIsICJjb25uZWN0b3IiOiAiYW5kIiwgImV4cHJlc3Npb24iOiAiIiwgInRyaWdnZXJfY29uZmlnIjogeyJjb3VudCI6IDEsICJ1cHRpbWUiOiB7ImNhbGVuZGFycyI6IFtdLCAidGltZV9yYW5nZXMiOiBbeyJlbmQiOiAiMjM6NTkiLCAic3RhcnQiOiAiMDA6MDAifV19LCAiY2hlY2tfd2luZG93IjogNX0sICJyZWNvdmVyeV9jb25maWciOiB7ImNoZWNrX3dpbmRvdyI6IDUsICJzdGF0dXNfc2V0dGVyIjogInJlY292ZXJ5In19XSwgInNjZW5hcmlvIjogIm90aGVyX3J0IiwgImJrX2Jpel9pZCI6IDEwMDQ2NSwgImRhdGFfc291cmNlX3R5cGUiOiAiXHU4MWVhXHU1YjlhXHU0ZTQ5XHU0ZThiXHU0ZWY2In0sICJpc19lbmFibGVkIjogdHJ1ZX0= \ No newline at end of file diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/InfluxDB CPU \344\275\277\347\224\250\347\216\207\345\221\212\350\255\246.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/InfluxDB CPU \344\275\277\347\224\250\347\216\207\345\221\212\350\255\246.json" new file mode 100644 index 0000000000..adb7ba89e4 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/InfluxDB CPU \344\275\277\347\224\250\347\216\207\345\221\212\350\255\246.json" @@ -0,0 +1,296 @@ +{ + "bk_biz_id": 0, + "name": "InfluxDB CPU 使用率告警", + "db_type": "influxdb", + "details": { + "app": "", + "name": "InfluxDB CPU 使用率告警", + "path": "", + "type": "monitor", + "items": [ + { + "name": "AVG(CPU使用率)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008245 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6533, + "type": "Threshold", + "level": 3, + "config": [ + [ + { + "method": "gte", + "threshold": 60 + } + ] + ], + "unit_prefix": "%" + }, + { + "id": 6534, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 80 + } + ] + ], + "unit_prefix": "%" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 6499, + "name": "CPU使用率", + "unit": "percent", + "alias": "a", + "functions": [], + "metric_id": "bk_monitor.system.cpu_summary.usage", + "agg_method": "AVG", + "agg_interval": 60, + "metric_field": "usage", + "agg_condition": [], + "agg_dimension": [ + "bk_target_ip", + "bk_target_cloud_id" + ], + "data_type_label": "time_series", + "result_table_id": "system.cpu_summary", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_ip", + "bk_target_cloud_id" + ] + } + } + ], + "labels": [ + "InfluxDB", + "DBM_INFLUXDB", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.assign_detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.assign_detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.assign_detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "by_rule", + "only_notice" + ], + "upgrade_config": { + "is_enabled": false, + "user_groups": [], + "upgrade_interval": 86400 + }, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "ack": [], + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 9006, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 3, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + }, + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "os", + "bk_biz_id": 2005000191, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/InfluxDB Series \345\242\236\351\225\277\351\207\217.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/InfluxDB Series \345\242\236\351\225\277\351\207\217.json" new file mode 100644 index 0000000000..1d62bc5d44 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/InfluxDB Series \345\242\236\351\225\277\351\207\217.json" @@ -0,0 +1,269 @@ +{ + "bk_biz_id": 0, + "name": "InfluxDB Series 增长量", + "db_type": "influxdb", + "details": { + "app": "", + "name": "InfluxDB Series 增长量", + "path": "", + "type": "monitor", + "items": [ + { + "name": "AVG(influxdb_database_numSeries)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008245 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6530, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 200000 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 6497, + "name": "influxdb_database_numSeries", + "unit": "", + "alias": "a", + "functions": [ + { + "id": "increase", + "params": [ + { + "id": "window", + "value": "10m" + } + ] + } + ], + "metric_id": "bk_monitor.pushgateway_dbm_influxdb_bkpull.group1.influxdb_database_numSeries", + "agg_method": "AVG", + "agg_interval": 300, + "metric_field": "influxdb_database_numSeries", + "agg_condition": [], + "agg_dimension": [ + "bk_target_service_instance_id", + "_database" + ], + "data_type_label": "time_series", + "result_table_id": "pushgateway_dbm_influxdb_bkpull.group1", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_service_instance_id" + ] + } + } + ], + "labels": [ + "InfluxDB", + "DBM_INFLUXDB", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.assign_detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.assign_detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.assign_detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "by_rule", + "only_notice" + ], + "upgrade_config": { + "is_enabled": false, + "user_groups": [], + "upgrade_interval": 86400 + }, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "ack": [], + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 9004, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "component", + "bk_biz_id": 2005000191, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/InfluxDB \345\206\205\345\255\230\344\275\277\347\224\250\345\221\212\350\255\246.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/InfluxDB \345\206\205\345\255\230\344\275\277\347\224\250\345\221\212\350\255\246.json" new file mode 100644 index 0000000000..926016b0c2 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/InfluxDB \345\206\205\345\255\230\344\275\277\347\224\250\345\221\212\350\255\246.json" @@ -0,0 +1,296 @@ +{ + "bk_biz_id": 0, + "name": "InfluxDB 内存使用告警", + "db_type": "influxdb", + "details": { + "app": "", + "name": "InfluxDB 内存使用告警", + "path": "", + "type": "monitor", + "items": [ + { + "name": "AVG(应用程序内存使用占比)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008245 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6524, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 80 + } + ] + ], + "unit_prefix": "%" + }, + { + "id": 6525, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 95 + } + ] + ], + "unit_prefix": "%" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 6493, + "name": "应用程序内存使用占比", + "unit": "percent", + "alias": "a", + "functions": [], + "metric_id": "bk_monitor.system.mem.pct_used", + "agg_method": "AVG", + "agg_interval": 60, + "metric_field": "pct_used", + "agg_condition": [], + "agg_dimension": [ + "bk_target_ip", + "bk_target_cloud_id" + ], + "data_type_label": "time_series", + "result_table_id": "system.mem", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_ip", + "bk_target_cloud_id" + ] + } + } + ], + "labels": [ + "InfluxDB", + "DBM_INFLUXDB", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.assign_detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.assign_detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.assign_detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "by_rule", + "only_notice" + ], + "upgrade_config": { + "is_enabled": false, + "user_groups": [], + "upgrade_interval": 86400 + }, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "ack": [], + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 9000, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + }, + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "os", + "bk_biz_id": 2005000191, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/InfluxDB \345\206\231\345\205\245\345\274\202\345\270\270\345\221\212\350\255\246.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/InfluxDB \345\206\231\345\205\245\345\274\202\345\270\270\345\221\212\350\255\246.json" new file mode 100644 index 0000000000..5f53c8d274 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/InfluxDB \345\206\231\345\205\245\345\274\202\345\270\270\345\221\212\350\255\246.json" @@ -0,0 +1,269 @@ +{ + "bk_biz_id": 0, + "name": "InfluxDB 写入异常告警", + "db_type": "influxdb", + "details": { + "app": "", + "name": "InfluxDB 写入异常告警", + "path": "", + "type": "monitor", + "items": [ + { + "name": "AVG(influxdb_httpd_pointsWrittenFail)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008245 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6528, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 10000 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 6495, + "name": "influxdb_httpd_pointsWrittenFail", + "unit": "", + "alias": "a", + "functions": [ + { + "id": "rate", + "params": [ + { + "id": "window", + "value": "10m" + } + ] + } + ], + "metric_id": "bk_monitor.pushgateway_dbm_influxdb_bkpull.group6.influxdb_httpd_pointsWrittenFail", + "agg_method": "AVG", + "agg_interval": 120, + "metric_field": "influxdb_httpd_pointsWrittenFail", + "agg_condition": [], + "agg_dimension": [ + "bk_target_service_instance_id", + "ip" + ], + "data_type_label": "time_series", + "result_table_id": "pushgateway_dbm_influxdb_bkpull.group6", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_service_instance_id" + ] + } + } + ], + "labels": [ + "InfluxDB", + "DBM_INFLUXDB", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.assign_detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.assign_detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.assign_detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "by_rule", + "only_notice" + ], + "upgrade_config": { + "is_enabled": false, + "user_groups": [], + "upgrade_interval": 86400 + }, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "ack": [], + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 9002, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "component", + "bk_biz_id": 2005000191, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/InfluxDB \345\256\236\344\276\213\347\253\257\345\217\243\345\274\202\345\270\270.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/InfluxDB \345\256\236\344\276\213\347\253\257\345\217\243\345\274\202\345\270\270.json" new file mode 100644 index 0000000000..b2bdd86362 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/InfluxDB \345\256\236\344\276\213\347\253\257\345\217\243\345\274\202\345\270\270.json" @@ -0,0 +1,259 @@ +{ + "bk_biz_id": 0, + "name": "InfluxDB 实例端口异常", + "db_type": "influxdb", + "details": { + "app": "", + "name": "InfluxDB 实例端口异常", + "path": "", + "type": "monitor", + "items": [ + { + "name": "MAX(http_response_result_code)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008245 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6529, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "eq", + "threshold": 3 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 6496, + "name": "http_response_result_code", + "unit": "", + "alias": "a", + "functions": [], + "metric_id": "bk_monitor.pushgateway_dbm_influxdb_bkpull.group2.http_response_result_code", + "agg_method": "MAX", + "agg_interval": 60, + "metric_field": "http_response_result_code", + "agg_condition": [], + "agg_dimension": [ + "bk_target_service_instance_id", + "ip" + ], + "data_type_label": "time_series", + "result_table_id": "pushgateway_dbm_influxdb_bkpull.group2", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_service_instance_id" + ] + } + } + ], + "labels": [ + "InfluxDB", + "DBM_INFLUXDB", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.assign_detail}}\n{{content.related_info}} : connection_failed" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.assign_detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.assign_detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "by_rule", + "only_notice" + ], + "upgrade_config": { + "is_enabled": false, + "user_groups": [], + "upgrade_interval": 86400 + }, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "ack": [], + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 9003, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "component", + "bk_biz_id": 2005000191, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/InfluxDB \347\243\201\347\233\230 IO \345\210\251\347\224\250\347\216\207.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/InfluxDB \347\243\201\347\233\230 IO \345\210\251\347\224\250\347\216\207.json" new file mode 100644 index 0000000000..d1b4c6e7bb --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/InfluxDB \347\243\201\347\233\230 IO \345\210\251\347\224\250\347\216\207.json" @@ -0,0 +1,297 @@ +{ + "bk_biz_id": 0, + "name": "InfluxDB 磁盘 IO 利用率", + "db_type": "influxdb", + "details": { + "app": "", + "name": "InfluxDB 磁盘 IO 利用率", + "path": "", + "type": "monitor", + "items": [ + { + "name": "AVG(I/O使用率)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008245 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6531, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 80 + } + ] + ], + "unit_prefix": "%" + }, + { + "id": 6532, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 99 + } + ] + ], + "unit_prefix": "%" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 6498, + "name": "I/O使用率", + "unit": "percentunit", + "alias": "a", + "functions": [], + "metric_id": "bk_monitor.system.io.util", + "agg_method": "AVG", + "agg_interval": 60, + "metric_field": "util", + "agg_condition": [], + "agg_dimension": [ + "bk_target_ip", + "bk_target_cloud_id", + "device_name" + ], + "data_type_label": "time_series", + "result_table_id": "system.io", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_ip", + "bk_target_cloud_id" + ] + } + } + ], + "labels": [ + "InfluxDB", + "DBM_INFLUXDB", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.assign_detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.assign_detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.assign_detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "by_rule", + "only_notice" + ], + "upgrade_config": { + "is_enabled": false, + "user_groups": [], + "upgrade_interval": 86400 + }, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "ack": [], + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 9005, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + }, + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "os", + "bk_biz_id": 2005000191, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/InfluxDB \347\243\201\347\233\230\347\251\272\351\227\264\345\221\212\350\255\246.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/InfluxDB \347\243\201\347\233\230\347\251\272\351\227\264\345\221\212\350\255\246.json" new file mode 100644 index 0000000000..ea9cc72793 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/InfluxDB \347\243\201\347\233\230\347\251\272\351\227\264\345\221\212\350\255\246.json" @@ -0,0 +1,297 @@ +{ + "bk_biz_id": 0, + "name": "InfluxDB 磁盘空间告警", + "db_type": "influxdb", + "details": { + "app": "", + "name": "InfluxDB 磁盘空间告警", + "path": "", + "type": "monitor", + "items": [ + { + "name": "MAX(磁盘空间使用率)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008245 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6526, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 80 + } + ] + ], + "unit_prefix": "%" + }, + { + "id": 6527, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 90 + } + ] + ], + "unit_prefix": "%" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 6494, + "name": "磁盘空间使用率", + "unit": "percent", + "alias": "a", + "functions": [], + "metric_id": "bk_monitor.system.disk.in_use", + "agg_method": "MAX", + "agg_interval": 60, + "metric_field": "in_use", + "agg_condition": [], + "agg_dimension": [ + "bk_target_ip", + "bk_target_cloud_id", + "mount_point" + ], + "data_type_label": "time_series", + "result_table_id": "system.disk", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_ip", + "bk_target_cloud_id" + ] + } + } + ], + "labels": [ + "InfluxDB", + "DBM_INFLUXDB", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.assign_detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.assign_detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.assign_detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "by_rule", + "only_notice" + ], + "upgrade_config": { + "is_enabled": false, + "user_groups": [], + "upgrade_interval": 86400 + }, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "ack": [], + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 9001, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + }, + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "os", + "bk_biz_id": 2005000191, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \350\277\233\347\250\213down.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \350\277\233\347\250\213down.json" new file mode 100644 index 0000000000..b48e1de819 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \350\277\233\347\250\213down.json" @@ -0,0 +1,263 @@ +{ + "bk_biz_id": 0, + "name": "MySQL 进程down", + "db_type": "mysql", + "details": { + "app": "", + "name": "MySQL 进程down", + "path": "", + "type": "monitor", + "items": [ + { + "name": "AVG(mysql_up)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008148 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6169, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "eq", + "threshold": 0 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 6163, + "name": "mysql_up", + "unit": "", + "alias": "a", + "functions": [], + "metric_id": "bk_monitor.exporter_dbm_mysqld_exporter.mysqlup.mysql_up", + "agg_method": "AVG", + "agg_interval": 60, + "metric_field": "mysql_up", + "agg_condition": [], + "agg_dimension": [ + "bk_target_service_instance_id", + "app", + "cluster_domain", + "instance_role", + "instance" + ], + "data_type_label": "time_series", + "result_table_id": "exporter_dbm_mysqld_exporter.mysqlup", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_service_instance_id" + ] + } + } + ], + "labels": [ + "MySQL", + "DBM_MYSQL", + "DBM_MYSQL", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 8681, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "component", + "bk_biz_id": 2005000194, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/MySQL Ext3 \346\226\207\344\273\266\350\277\207\345\244\247.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL Ext3 \346\226\207\344\273\266\350\277\207\345\244\247.json" new file mode 100644 index 0000000000..49e7ce0a51 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL Ext3 \346\226\207\344\273\266\350\277\207\345\244\247.json" @@ -0,0 +1,263 @@ +{ + "bk_biz_id": 0, + "name": "MySQL Ext3 文件过大", + "db_type": "mysql", + "details": { + "app": "", + "name": "MySQL Ext3 文件过大", + "path": "", + "type": "monitor", + "items": [ + { + "name": "COUNT(ext3-check)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008148 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 78808, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 1 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 77909, + "name": "ext3-check", + "alias": "a", + "functions": [], + "metric_id": "custom.event.{bk_biz_id}_bkmonitor_event_{event_data_id}.ext3-check", + "agg_method": "COUNT", + "agg_interval": 60, + "agg_condition": [], + "agg_dimension": [ + "app_id", + "bk_cloud_id", + "immute_domain", + "role", + "port", + "machine_type", + "server_ip" + ], + "data_type_label": "event", + "result_table_id": "{bk_biz_id}_bkmonitor_event_{event_data_id}", + "custom_event_name": "ext3-check", + "data_source_label": "custom" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "DBM_MYSQL", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "only_notice", + "by_rule" + ], + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "chart_image_enabled": true, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 72133, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "other_rt", + "bk_biz_id": 100465, + "data_source_type": "自定义事件" + }, + "is_enabled": true +} diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/MySQL InnoDB log waits.json b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL InnoDB log waits.json new file mode 100644 index 0000000000..9f6e6073b7 --- /dev/null +++ b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL InnoDB log waits.json @@ -0,0 +1,279 @@ +{ + "bk_biz_id": 0, + "name": "MySQL InnoDB log waits", + "db_type": "mysql", + "details": { + "app": "", + "name": "MySQL InnoDB log waits", + "path": "", + "type": "monitor", + "items": [ + { + "name": "AVG(mysql_global_status_innodb_log_waits)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008148 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6246, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 10 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 6242, + "name": "mysql_global_status_innodb_log_waits", + "unit": "", + "alias": "a", + "functions": [ + { + "id": "rate", + "params": [ + { + "id": "window", + "value": "10m" + } + ] + } + ], + "metric_id": "bk_monitor.exporter_dbm_mysqld_exporter.gstatus.mysql_global_sta", + "agg_method": "AVG", + "agg_interval": 60, + "metric_field": "mysql_global_status_innodb_log_waits", + "agg_condition": [], + "agg_dimension": [ + "bk_target_service_instance_id", + "app", + "cluster_domain", + "instance_role", + "instance" + ], + "data_type_label": "time_series", + "result_table_id": "exporter_dbm_mysqld_exporter.gstatus", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_service_instance_id" + ] + } + } + ], + "labels": [ + "MySQL", + "DBM_MYSQL", + "DBM_MYSQL", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [ + "app", + "cluster_domain", + "instance", + "instance_role", + "bk_target_service_instance_id" + ], + "is_enabled": false + } + }, + "config_id": 8757, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "component", + "bk_biz_id": 2005000194, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/MySQL Monitor \346\211\247\350\241\214\345\274\202\345\270\270.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL Monitor \346\211\247\350\241\214\345\274\202\345\270\270.json" new file mode 100644 index 0000000000..771fc7de8e --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL Monitor \346\211\247\350\241\214\345\274\202\345\270\270.json" @@ -0,0 +1,263 @@ +{ + "bk_biz_id": 0, + "name": "MySQL Monitor 执行异常", + "db_type": "mysql", + "details": { + "app": "", + "name": "MySQL Monitor 执行异常", + "path": "", + "type": "monitor", + "items": [ + { + "name": "COUNT(monitor-internal-error)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008148 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 78711, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 1 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 77809, + "name": "monitor-internal-error", + "alias": "a", + "functions": [], + "metric_id": "custom.event.{bk_biz_id}_bkmonitor_event_{event_data_id}.monitor-internal-error", + "agg_method": "COUNT", + "agg_interval": 60, + "agg_condition": [], + "agg_dimension": [ + "app_id", + "bk_cloud_id", + "immute_domain", + "machine_type", + "port", + "role", + "server_ip" + ], + "data_type_label": "event", + "result_table_id": "{bk_biz_id}_bkmonitor_event_{event_data_id}", + "custom_event_name": "monitor-internal-error", + "data_source_label": "custom" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "DBM_MYSQL", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "only_notice", + "by_rule" + ], + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "chart_image_enabled": true, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 72041, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "other_rt", + "bk_biz_id": 100465, + "data_source_type": "自定义事件" + }, + "is_enabled": true +} diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/MySQL Routine Definer.json b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL Routine Definer.json new file mode 100644 index 0000000000..8eb89d9791 --- /dev/null +++ b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL Routine Definer.json @@ -0,0 +1,263 @@ +{ + "bk_biz_id": 0, + "name": "MySQL Routine Definer", + "db_type": "mysql", + "details": { + "app": "", + "name": "MySQL Routine Definer", + "path": "", + "type": "monitor", + "items": [ + { + "name": "COUNT(routine-definer)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008148 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 78825, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 1 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 77926, + "name": "routine-definer", + "alias": "a", + "functions": [], + "metric_id": "custom.event.{bk_biz_id}_bkmonitor_event_{event_data_id}.routine-definer", + "agg_method": "COUNT", + "agg_interval": 60, + "agg_condition": [], + "agg_dimension": [ + "app_id", + "role", + "port", + "machine_type", + "immute_domain", + "bk_cloud_id", + "server_ip" + ], + "data_type_label": "event", + "result_table_id": "{bk_biz_id}_bkmonitor_event_{event_data_id}", + "custom_event_name": "routine-definer", + "data_source_label": "custom" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "DBM_MYSQL", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "only_notice", + "by_rule" + ], + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "chart_image_enabled": true, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 72159, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "other_rt", + "bk_biz_id": 100465, + "data_source_type": "自定义事件" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/MySQL Slave \345\220\214\346\255\245\345\274\202\345\270\270.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL Slave \345\220\214\346\255\245\345\274\202\345\270\270.json" new file mode 100644 index 0000000000..5bcec584aa --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL Slave \345\220\214\346\255\245\345\274\202\345\270\270.json" @@ -0,0 +1,263 @@ +{ + "bk_biz_id": 0, + "name": "MySQL Slave 同步异常", + "db_type": "mysql", + "details": { + "app": "", + "name": "MySQL Slave 同步异常", + "path": "", + "type": "monitor", + "items": [ + { + "name": "COUNT(slave-status)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008148 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 78705, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 1 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 77802, + "name": "slave-status", + "alias": "a", + "functions": [], + "metric_id": "custom.event.{bk_biz_id}_bkmonitor_event_{event_data_id}.slave-status", + "agg_method": "COUNT", + "agg_interval": 60, + "agg_condition": [], + "agg_dimension": [ + "app_id", + "bk_cloud_id", + "immute_domain", + "machine_type", + "port", + "role", + "server_ip" + ], + "data_type_label": "event", + "result_table_id": "{bk_biz_id}_bkmonitor_event_{event_data_id}", + "custom_event_name": "slave-status", + "data_source_label": "custom" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "DBM_MYSQL", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "only_notice", + "by_rule" + ], + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "chart_image_enabled": true, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 72034, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "other_rt", + "bk_biz_id": 100465, + "data_source_type": "自定义事件" + }, + "is_enabled": true +} diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/MySQL Trigger Definer.json b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL Trigger Definer.json new file mode 100644 index 0000000000..c95551e729 --- /dev/null +++ b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL Trigger Definer.json @@ -0,0 +1,263 @@ +{ + "bk_biz_id": 0, + "name": "MySQL Trigger Definer", + "db_type": "mysql", + "details": { + "app": "", + "name": "MySQL Trigger Definer", + "path": "", + "type": "monitor", + "items": [ + { + "name": "COUNT(trigger-definer)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008148 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 78823, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 1 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 77924, + "name": "trigger-definer", + "alias": "a", + "functions": [], + "metric_id": "custom.event.{bk_biz_id}_bkmonitor_event_{event_data_id}.trigger-definer", + "agg_method": "COUNT", + "agg_interval": 60, + "agg_condition": [], + "agg_dimension": [ + "app_id", + "bk_cloud_id", + "machine_type", + "role", + "port", + "immute_domain", + "server_ip" + ], + "data_type_label": "event", + "result_table_id": "{bk_biz_id}_bkmonitor_event_{event_data_id}", + "custom_event_name": "trigger-definer", + "data_source_label": "custom" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "DBM_MYSQL", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "only_notice", + "by_rule" + ], + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "chart_image_enabled": true, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 72157, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "other_rt", + "bk_biz_id": 100465, + "data_source_type": "自定义事件" + }, + "is_enabled": true +} diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/MySQL View Definer.json b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL View Definer.json new file mode 100644 index 0000000000..22ca35515c --- /dev/null +++ b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL View Definer.json @@ -0,0 +1,263 @@ +{ + "bk_biz_id": 0, + "name": "MySQL View Definer", + "db_type": "mysql", + "details": { + "app": "", + "name": "MySQL View Definer", + "path": "", + "type": "monitor", + "items": [ + { + "name": "COUNT(view-definer)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008148 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 78824, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 1 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 77925, + "name": "view-definer", + "alias": "a", + "functions": [], + "metric_id": "custom.event.{bk_biz_id}_bkmonitor_event_{event_data_id}.view-definer", + "agg_method": "COUNT", + "agg_interval": 60, + "agg_condition": [], + "agg_dimension": [ + "app_id", + "bk_cloud_id", + "machine_type", + "role", + "port", + "immute_domain", + "server_ip" + ], + "data_type_label": "event", + "result_table_id": "{bk_biz_id}_bkmonitor_event_{event_data_id}", + "custom_event_name": "view-definer", + "data_source_label": "custom" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "DBM_MYSQL", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "only_notice", + "by_rule" + ], + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "chart_image_enabled": true, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 72158, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "other_rt", + "bk_biz_id": 100465, + "data_source_type": "自定义事件" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/MySQL int\346\272\242\345\207\272\345\221\212\350\255\246.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL int\346\272\242\345\207\272\345\221\212\350\255\246.json" new file mode 100644 index 0000000000..5264c3e305 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL int\346\272\242\345\207\272\345\221\212\350\255\246.json" @@ -0,0 +1,307 @@ +{ + "bk_biz_id": 0, + "name": "MySQL int溢出告警", + "db_type": "mysql", + "details": { + "app": "", + "name": "MySQL int溢出告警", + "path": "", + "type": "monitor", + "items": [ + { + "name": "AVG(mysql_info_schema_auto_increment_column) / AVG(mysql_info_schema_auto_increment_column_max) * 100", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008148 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6301, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 98 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a / b * 100", + "origin_sql": "", + "query_configs": [ + { + "id": 6297, + "name": "mysql_info_schema_auto_increment_column", + "unit": "", + "alias": "a", + "functions": [], + "metric_id": "bk_monitor.exporter_dbm_mysqld_exporter.tableincr.mysql_info_sch", + "agg_method": "AVG", + "agg_interval": 60, + "metric_field": "mysql_info_schema_auto_increment_column", + "agg_condition": [ + { + "key": "instance_role", + "value": [ + "backend_master" + ], + "method": "eq", + "condition": "and", + "dimension_name": "instance_role" + } + ], + "agg_dimension": [ + "bk_target_service_instance_id", + "app", + "cluster_domain", + "schema", + "table", + "column" + ], + "data_type_label": "time_series", + "result_table_id": "exporter_dbm_mysqld_exporter.tableincr", + "data_source_label": "bk_monitor" + }, + { + "id": 6298, + "name": "mysql_info_schema_auto_increment_column_max", + "unit": "", + "alias": "b", + "functions": [], + "metric_id": "bk_monitor.exporter_dbm_mysqld_exporter.tableincr.mysql_info_sch", + "agg_method": "AVG", + "agg_interval": 60, + "metric_field": "mysql_info_schema_auto_increment_column_max", + "agg_condition": [ + { + "key": "instance_role", + "value": [ + "backend_master" + ], + "method": "eq", + "condition": "and", + "dimension_name": "instance_role" + } + ], + "agg_dimension": [ + "bk_target_service_instance_id", + "app", + "cluster_domain", + "schema", + "table", + "column" + ], + "data_type_label": "time_series", + "result_table_id": "exporter_dbm_mysqld_exporter.tableincr", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_service_instance_id" + ] + } + } + ], + "labels": [ + "MySQL", + "DBM_MYSQL", + "DBM_MYSQL", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 8812, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "component", + "bk_biz_id": 2005000194, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/MySQL proxy\350\277\233\347\250\213down.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL proxy\350\277\233\347\250\213down.json" new file mode 100644 index 0000000000..b25923fef5 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL proxy\350\277\233\347\250\213down.json" @@ -0,0 +1,262 @@ +{ + "bk_biz_id": 0, + "name": "MySQL proxy进程down", + "db_type": "mysql", + "details": { + "app": "", + "name": "MySQL proxy进程down", + "path": "", + "type": "monitor", + "items": [ + { + "name": "AVG(mysqlproxy_up)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008148 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6306, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "eq", + "threshold": 0 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 6303, + "name": "mysqlproxy_up", + "unit": "", + "alias": "a", + "functions": [], + "metric_id": "bk_monitor.exporter_dbm_mysqlproxy_exporter.mproxy.mysqlproxy_up", + "agg_method": "AVG", + "agg_interval": 60, + "metric_field": "mysqlproxy_up", + "agg_condition": [], + "agg_dimension": [ + "bk_target_service_instance_id", + "app", + "cluster_domain", + "instance" + ], + "data_type_label": "time_series", + "result_table_id": "exporter_dbm_mysqlproxy_exporter.mproxy", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_service_instance_id" + ] + } + } + ], + "labels": [ + "MySQL", + "DBM_MYSQL", + "DBM_MYSQL", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 8817, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "component", + "bk_biz_id": 2005000194, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/MySQL restarted.json b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL restarted.json new file mode 100644 index 0000000000..aa64051802 --- /dev/null +++ b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL restarted.json @@ -0,0 +1,263 @@ +{ + "bk_biz_id": 0, + "name": "MySQL restarted", + "db_type": "mysql", + "details": { + "app": "", + "name": "MySQL restarted", + "path": "", + "type": "monitor", + "items": [ + { + "name": "AVG(mysql_global_status_uptime)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008148 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6247, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "lt", + "threshold": 60 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 6243, + "name": "mysql_global_status_uptime", + "unit": "", + "alias": "a", + "functions": [], + "metric_id": "bk_monitor.exporter_dbm_mysqld_exporter.gstatus.mysql_global_sta", + "agg_method": "AVG", + "agg_interval": 60, + "metric_field": "mysql_global_status_uptime", + "agg_condition": [], + "agg_dimension": [ + "bk_target_service_instance_id", + "app", + "cluster_domain", + "instance_role", + "instance" + ], + "data_type_label": "time_series", + "result_table_id": "exporter_dbm_mysqld_exporter.gstatus", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_service_instance_id" + ] + } + } + ], + "labels": [ + "MySQL", + "DBM_MYSQL", + "DBM_MYSQL", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 8758, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "component", + "bk_biz_id": 2005000194, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \344\270\273\346\234\272 CPU \350\264\237\350\275\275.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \344\270\273\346\234\272 CPU \350\264\237\350\275\275.json" new file mode 100644 index 0000000000..3c337562d1 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \344\270\273\346\234\272 CPU \350\264\237\350\275\275.json" @@ -0,0 +1,300 @@ +{ + "bk_biz_id": 0, + "name": "MySQL 主机 CPU 负载", + "db_type": "mysql", + "details": { + "app": "", + "name": "MySQL 主机 CPU 负载", + "path": "", + "type": "monitor", + "items": [ + { + "name": "AVG(CPU usage)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008148 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6163, + "type": "Threshold", + "level": 3, + "config": [ + [ + { + "method": "gte", + "threshold": 90 + } + ] + ], + "unit_prefix": "%" + }, + { + "id": 6164, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 80 + } + ] + ], + "unit_prefix": "%" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 6158, + "name": "CPU使用率", + "unit": "percent", + "alias": "a", + "functions": [], + "metric_id": "bk_monitor.dbm_system.cpu_summary.usage", + "agg_method": "AVG", + "agg_interval": 60, + "metric_field": "usage", + "agg_condition": [], + "agg_dimension": [ + "bk_target_cloud_id", + "bk_target_ip", + "instance_role", + "app", + "cluster_domain" + ], + "data_type_label": "time_series", + "result_table_id": "dbm_system.cpu_summary", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_ip", + "bk_target_cloud_id" + ] + } + } + ], + "labels": [ + "MySQL", + "DBM_MYSQL", + "DBM_MYSQL", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 8677, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 3, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + }, + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "os", + "bk_biz_id": 2005000194, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \344\270\273\346\234\272\347\243\201\347\233\230IO\345\210\251\347\224\250\347\216\207.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \344\270\273\346\234\272\347\243\201\347\233\230IO\345\210\251\347\224\250\347\216\207.json" new file mode 100644 index 0000000000..608911033d --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \344\270\273\346\234\272\347\243\201\347\233\230IO\345\210\251\347\224\250\347\216\207.json" @@ -0,0 +1,265 @@ +{ + "bk_biz_id": 0, + "name": "MySQL 主机磁盘IO利用率", + "db_type": "mysql", + "details": { + "app": "", + "name": "MySQL 主机磁盘IO利用率", + "path": "", + "type": "monitor", + "items": [ + { + "name": "AVG(I/O使用率)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008148 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6173, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 90 + } + ] + ], + "unit_prefix": "%" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 6166, + "name": "I/O使用率", + "unit": "percentunit", + "alias": "a", + "functions": [], + "metric_id": "bk_monitor.dbm_system.io.util", + "agg_method": "AVG", + "agg_interval": 60, + "metric_field": "util", + "agg_condition": [], + "agg_dimension": [ + "instance_role", + "device_name", + "app", + "bk_target_ip", + "cluster_domain", + "bk_target_cloud_id" + ], + "data_type_label": "time_series", + "result_table_id": "dbm_system.io", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_ip", + "bk_target_cloud_id" + ] + } + } + ], + "labels": [ + "MySQL", + "DBM_MYSQL", + "DBM_MYSQL", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 8684, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "os", + "bk_biz_id": 2005000194, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \344\270\273\346\234\272\347\243\201\347\233\230\347\251\272\351\227\264\344\275\277\347\224\250\347\216\207.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \344\270\273\346\234\272\347\243\201\347\233\230\347\251\272\351\227\264\344\275\277\347\224\250\347\216\207.json" new file mode 100644 index 0000000000..56af158714 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \344\270\273\346\234\272\347\243\201\347\233\230\347\251\272\351\227\264\344\275\277\347\224\250\347\216\207.json" @@ -0,0 +1,301 @@ +{ + "bk_biz_id": 0, + "name": "MySQL 主机磁盘空间使用率", + "db_type": "mysql", + "details": { + "app": "", + "name": "MySQL 主机磁盘空间使用率", + "path": "", + "type": "monitor", + "items": [ + { + "name": "AVG(Disk space usage)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008148 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6171, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 80 + } + ] + ], + "unit_prefix": "%" + }, + { + "id": 6172, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 95 + } + ] + ], + "unit_prefix": "%" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 6165, + "name": "磁盘空间使用率", + "unit": "percent", + "alias": "a", + "functions": [], + "metric_id": "bk_monitor.dbm_system.disk.in_use", + "agg_method": "AVG", + "agg_interval": 60, + "metric_field": "in_use", + "agg_condition": [], + "agg_dimension": [ + "instance_role", + "device_name", + "app", + "bk_target_ip", + "cluster_domain", + "bk_target_cloud_id" + ], + "data_type_label": "time_series", + "result_table_id": "dbm_system.disk", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_ip", + "bk_target_cloud_id" + ] + } + } + ], + "labels": [ + "MySQL", + "DBM_MYSQL", + "DBM_MYSQL", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 8683, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + }, + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "os", + "bk_biz_id": 2005000194, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \344\270\273\346\234\272\347\275\221\347\273\234\345\207\272\346\265\201\351\207\217.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \344\270\273\346\234\272\347\275\221\347\273\234\345\207\272\346\265\201\351\207\217.json" new file mode 100644 index 0000000000..1088786c09 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \344\270\273\346\234\272\347\275\221\347\273\234\345\207\272\346\265\201\351\207\217.json" @@ -0,0 +1,275 @@ +{ + "bk_biz_id": 0, + "name": "MySQL 主机网络出流量", + "db_type": "mysql", + "details": { + "app": "", + "name": "MySQL 主机网络出流量", + "path": "", + "type": "monitor", + "items": [ + { + "name": "AVG(网卡出流量)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008148 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6165, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 1000000000 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 6159, + "name": "网卡出流量", + "unit": "", + "alias": "a", + "functions": [ + { + "id": "rate", + "params": [ + { + "id": "window", + "value": "2m" + } + ] + } + ], + "metric_id": "bk_monitor.dbm_system.net.speed_sent", + "agg_method": "AVG", + "agg_interval": 60, + "metric_field": "speed_sent", + "agg_condition": [], + "agg_dimension": [ + "bk_target_cloud_id", + "bk_target_ip", + "instance_role", + "app", + "device_name", + "cluster_domain" + ], + "data_type_label": "time_series", + "result_table_id": "dbm_system.net", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_ip", + "bk_target_cloud_id" + ] + } + } + ], + "labels": [ + "MySQL", + "DBM_MYSQL", + "DBM_MYSQL", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 8678, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "os", + "bk_biz_id": 2005000194, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \344\273\216\345\272\223\345\220\214\346\255\245\345\274\202\345\270\270.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \344\273\216\345\272\223\345\220\214\346\255\245\345\274\202\345\270\270.json" new file mode 100644 index 0000000000..ab616006af --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \344\273\216\345\272\223\345\220\214\346\255\245\345\274\202\345\270\270.json" @@ -0,0 +1,308 @@ +{ + "bk_biz_id": 0, + "name": "MySQL 从库同步异常", + "db_type": "mysql", + "details": { + "app": "", + "name": "MySQL 从库同步异常", + "path": "", + "type": "monitor", + "items": [ + { + "name": "AVG(mysql_slave_status_slave_io_running) + AVG(mysql_slave_status_slave_sql_running)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008148 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6168, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "lt", + "threshold": 2 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a + b", + "origin_sql": "", + "query_configs": [ + { + "id": 6161, + "name": "mysql_slave_status_slave_io_running", + "unit": "", + "alias": "a", + "functions": [], + "metric_id": "bk_monitor.exporter_dbm_mysqld_exporter.slavestat.mysql_slave_st", + "agg_method": "AVG", + "agg_interval": 60, + "metric_field": "mysql_slave_status_slave_io_running", + "agg_condition": [ + { + "key": "instance_role", + "value": [ + "backend_slave" + ], + "method": "eq", + "condition": "and", + "dimension_name": "instance_role" + } + ], + "agg_dimension": [ + "bk_target_service_instance_id", + "app", + "cluster_domain", + "instance" + ], + "data_type_label": "time_series", + "result_table_id": "exporter_dbm_mysqld_exporter.slavestat", + "data_source_label": "bk_monitor" + }, + { + "id": 6162, + "name": "mysql_slave_status_slave_sql_running", + "unit": "", + "alias": "b", + "functions": [], + "metric_id": "bk_monitor.exporter_dbm_mysqld_exporter.slavestat.mysql_slave_st", + "agg_method": "AVG", + "agg_interval": 60, + "metric_field": "mysql_slave_status_slave_sql_running", + "agg_condition": [ + { + "key": "instance_role", + "value": [ + "backend_slave" + ], + "method": "eq", + "condition": "and", + "dimension_name": "instance_role" + } + ], + "agg_dimension": [ + "bk_target_service_instance_id", + "app", + "cluster_domain", + "instance" + ], + "data_type_label": "time_series", + "result_table_id": "exporter_dbm_mysqld_exporter.slavestat", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_service_instance_id" + ] + } + } + ], + "labels": [ + "MySQL", + "DBM_MYSQL", + "DBM_MYSQL", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [ + "app", + "cluster_domain", + "instance", + "bk_target_service_instance_id" + ], + "is_enabled": false + } + }, + "config_id": 8680, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "component", + "bk_biz_id": 2005000194, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \344\273\216\345\272\223\345\273\266\350\277\237.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \344\273\216\345\272\223\345\273\266\350\277\237.json" new file mode 100644 index 0000000000..a4eda6b1d2 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \344\273\216\345\272\223\345\273\266\350\277\237.json" @@ -0,0 +1,305 @@ +{ + "bk_biz_id": 0, + "name": "MySQL 从库延迟", + "db_type": "mysql", + "details": { + "app": "", + "name": "MySQL 从库延迟", + "path": "", + "type": "monitor", + "items": [ + { + "name": "AVG(mysql_slave_status_seconds_behind_master)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008148 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6166, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 120 + } + ] + ], + "unit_prefix": "" + }, + { + "id": 6167, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 3600 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 6160, + "name": "mysql_slave_status_seconds_behind_master", + "unit": "", + "alias": "a", + "functions": [], + "metric_id": "bk_monitor.exporter_dbm_mysqld_exporter.slavestat.mysql_slave_st", + "agg_method": "AVG", + "agg_interval": 60, + "metric_field": "mysql_slave_status_seconds_behind_master", + "agg_condition": [], + "agg_dimension": [ + "bk_target_service_instance_id", + "app", + "cluster_domain", + "instance_role", + "instance" + ], + "data_type_label": "time_series", + "result_table_id": "exporter_dbm_mysqld_exporter.slavestat", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_service_instance_id" + ] + } + } + ], + "labels": [ + "MySQL", + "DBM_MYSQL", + "DBM_MYSQL", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [ + "app", + "cluster_domain", + "instance", + "instance_role", + "bk_target_service_instance_id" + ], + "is_enabled": false + } + }, + "config_id": 8679, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + }, + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "component", + "bk_biz_id": 2005000194, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \345\255\230\345\202\250\345\274\225\346\223\216\346\243\200\346\237\245.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \345\255\230\345\202\250\345\274\225\346\223\216\346\243\200\346\237\245.json" new file mode 100644 index 0000000000..9b25c662a9 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \345\255\230\345\202\250\345\274\225\346\223\216\346\243\200\346\237\245.json" @@ -0,0 +1,263 @@ +{ + "bk_biz_id": 0, + "name": "MySQL 存储引擎检查", + "db_type": "mysql", + "details": { + "app": "", + "name": "MySQL 存储引擎检查", + "path": "", + "type": "monitor", + "items": [ + { + "name": "COUNT(engine)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008148 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 78766, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 1 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 77867, + "name": "engine", + "alias": "a", + "functions": [], + "metric_id": "custom.event.{bk_biz_id}_bkmonitor_event_{event_data_id}.engine", + "agg_method": "COUNT", + "agg_interval": 60, + "agg_condition": [], + "agg_dimension": [ + "bk_cloud_id", + "server_ip", + "role", + "immute_domain", + "machine_type", + "app_id", + "port" + ], + "data_type_label": "event", + "result_table_id": "{bk_biz_id}_bkmonitor_event_{event_data_id}", + "custom_event_name": "engine", + "data_source_label": "custom" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "DBM_MYSQL", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "no_data", + "abnormal" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "only_notice", + "by_rule" + ], + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "chart_image_enabled": true, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 72083, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "other_rt", + "bk_biz_id": 100465, + "data_source_type": "自定义事件" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \345\256\236\344\276\213 Threads_running.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \345\256\236\344\276\213 Threads_running.json" new file mode 100644 index 0000000000..fe59171a0e --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \345\256\236\344\276\213 Threads_running.json" @@ -0,0 +1,305 @@ +{ + "bk_biz_id": 0, + "name": "MySQL 实例 Threads_running", + "db_type": "mysql", + "details": { + "app": "", + "name": "MySQL 实例 Threads_running", + "path": "", + "type": "monitor", + "items": [ + { + "name": "AVG(mysql_global_status_threads_running)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008148 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6159, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 50 + } + ] + ], + "unit_prefix": "" + }, + { + "id": 6160, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 500 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 6156, + "name": "mysql_global_status_threads_running", + "unit": "", + "alias": "a", + "functions": [], + "metric_id": "bk_monitor.exporter_dbm_mysqld_exporter.gstatus.mysql_global_sta", + "agg_method": "AVG", + "agg_interval": 60, + "metric_field": "mysql_global_status_threads_running", + "agg_condition": [], + "agg_dimension": [ + "bk_target_service_instance_id", + "app", + "cluster_domain", + "instance_role", + "instance" + ], + "data_type_label": "time_series", + "result_table_id": "exporter_dbm_mysqld_exporter.gstatus", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_service_instance_id" + ] + } + } + ], + "labels": [ + "MySQL", + "DBM_MYSQL", + "DBM_MYSQL", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [ + "app", + "cluster_domain", + "instance", + "instance_role", + "bk_target_service_instance_id" + ], + "is_enabled": false + } + }, + "config_id": 8675, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + }, + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "component", + "bk_biz_id": 2005000194, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \345\272\223\345\255\227\347\254\246\351\233\206.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \345\272\223\345\255\227\347\254\246\351\233\206.json" new file mode 100644 index 0000000000..a37825480a --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \345\272\223\345\255\227\347\254\246\351\233\206.json" @@ -0,0 +1,263 @@ +{ + "bk_biz_id": 0, + "name": "MySQL 库字符集", + "db_type": "mysql", + "details": { + "app": "", + "name": "MySQL 库字符集", + "path": "", + "type": "monitor", + "items": [ + { + "name": "COUNT(character-consistency)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008148 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 78826, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 1 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 77927, + "name": "character-consistency", + "alias": "a", + "functions": [], + "metric_id": "custom.event.{bk_biz_id}_bkmonitor_event_{event_data_id}.character-consistency", + "agg_method": "COUNT", + "agg_interval": 60, + "agg_condition": [], + "agg_dimension": [ + "app_id", + "machine_type", + "port", + "bk_cloud_id", + "immute_domain", + "server_ip", + "role" + ], + "data_type_label": "event", + "result_table_id": "{bk_biz_id}_bkmonitor_event_{event_data_id}", + "custom_event_name": "character-consistency", + "data_source_label": "custom" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "DBM_MYSQL", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "only_notice", + "by_rule" + ], + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "chart_image_enabled": true, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 72160, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "other_rt", + "bk_biz_id": 100465, + "data_source_type": "自定义事件" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \346\227\245\345\277\227\347\233\221\346\216\247-\350\207\264\345\221\275.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \346\227\245\345\277\227\347\233\221\346\216\247-\350\207\264\345\221\275.json" new file mode 100644 index 0000000000..1bd195a19e --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \346\227\245\345\277\227\347\233\221\346\216\247-\350\207\264\345\221\275.json" @@ -0,0 +1,263 @@ +{ + "bk_biz_id": 0, + "name": "MySQL 日志监控-致命", + "db_type": "mysql", + "details": { + "app": "", + "name": "MySQL 日志监控-致命", + "path": "", + "type": "monitor", + "items": [ + { + "name": "COUNT(mysql-err-critical)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008148 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 78716, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 1 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 77814, + "name": "mysql-err-critical", + "alias": "a", + "functions": [], + "metric_id": "custom.event.{bk_biz_id}_bkmonitor_event_{event_data_id}.mysql-err-critical", + "agg_method": "COUNT", + "agg_interval": 60, + "agg_condition": [], + "agg_dimension": [ + "bk_cloud_id", + "server_ip", + "role", + "machine_type", + "immute_domain", + "app_id", + "port" + ], + "data_type_label": "event", + "result_table_id": "{bk_biz_id}_bkmonitor_event_{event_data_id}", + "custom_event_name": "mysql-err-critical", + "data_source_label": "custom" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "DBM_MYSQL", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "no_data", + "abnormal" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "only_notice", + "by_rule" + ], + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "chart_image_enabled": true, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 72046, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "other_rt", + "bk_biz_id": 100465, + "data_source_type": "自定义事件" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \346\227\245\345\277\227\347\233\221\346\216\247-\351\242\204\350\255\246.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \346\227\245\345\277\227\347\233\221\346\216\247-\351\242\204\350\255\246.json" new file mode 100644 index 0000000000..5df5f700f3 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \346\227\245\345\277\227\347\233\221\346\216\247-\351\242\204\350\255\246.json" @@ -0,0 +1,263 @@ +{ + "bk_biz_id": 0, + "name": "MySQL 日志监控-预警", + "db_type": "mysql", + "details": { + "app": "", + "name": "MySQL 日志监控-预警", + "path": "", + "type": "monitor", + "items": [ + { + "name": "COUNT(mysql-err-notice)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008148 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 78717, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 1 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 77815, + "name": "mysql-err-notice", + "alias": "a", + "functions": [], + "metric_id": "custom.event.{bk_biz_id}_bkmonitor_event_{event_data_id}.mysql-err-notice", + "agg_method": "COUNT", + "agg_interval": 60, + "agg_condition": [], + "agg_dimension": [ + "bk_cloud_id", + "server_ip", + "role", + "machine_type", + "immute_domain", + "app_id", + "port" + ], + "data_type_label": "event", + "result_table_id": "{bk_biz_id}_bkmonitor_event_{event_data_id}", + "custom_event_name": "mysql-err-notice", + "data_source_label": "custom" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "DBM_MYSQL", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "only_notice", + "by_rule" + ], + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "chart_image_enabled": true, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 72047, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "other_rt", + "bk_biz_id": 100465, + "data_source_type": "自定义事件" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \346\263\250\345\205\245.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \346\263\250\345\205\245.json" new file mode 100644 index 0000000000..e5af4febb4 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \346\263\250\345\205\245.json" @@ -0,0 +1,263 @@ +{ + "bk_biz_id": 0, + "name": "MySQL 注入", + "db_type": "mysql", + "details": { + "app": "", + "name": "MySQL 注入", + "path": "", + "type": "monitor", + "items": [ + { + "name": "COUNT(mysql-inject)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008148 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 78715, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 1 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 77813, + "name": "mysql-inject", + "alias": "a", + "functions": [], + "metric_id": "custom.event.{bk_biz_id}_bkmonitor_event_{event_data_id}.mysql-inject", + "agg_method": "COUNT", + "agg_interval": 60, + "agg_condition": [], + "agg_dimension": [ + "bk_cloud_id", + "server_ip", + "role", + "machine_type", + "immute_domain", + "port", + "app_id" + ], + "data_type_label": "event", + "result_table_id": "{bk_biz_id}_bkmonitor_event_{event_data_id}", + "custom_event_name": "mysql-inject", + "data_source_label": "custom" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "DBM_MYSQL", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "only_notice", + "by_rule" + ], + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "chart_image_enabled": true, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 72045, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "other_rt", + "bk_biz_id": 100465, + "data_source_type": "自定义事件" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \350\277\236\346\216\245\345\244\261\350\264\245.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \350\277\236\346\216\245\345\244\261\350\264\245.json" new file mode 100644 index 0000000000..d9ca73ba5e --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \350\277\236\346\216\245\345\244\261\350\264\245.json" @@ -0,0 +1,263 @@ +{ + "bk_biz_id": 0, + "name": "MySQL 连接失败", + "db_type": "mysql", + "details": { + "app": "", + "name": "MySQL 连接失败", + "path": "", + "type": "monitor", + "items": [ + { + "name": "COUNT(db-up)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008148 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 78695, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 1 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 77795, + "name": "db-up", + "alias": "a", + "functions": [], + "metric_id": "custom.event.{bk_biz_id}_bkmonitor_event_{event_data_id}.db-up", + "agg_method": "COUNT", + "agg_interval": 60, + "agg_condition": [], + "agg_dimension": [ + "server_ip", + "port", + "immute_domain", + "bk_cloud_id", + "machine_type", + "role", + "app_id" + ], + "data_type_label": "event", + "result_table_id": "{bk_biz_id}_bkmonitor_event_{event_data_id}", + "custom_event_name": "db-up", + "data_source_label": "custom" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "DBM_MYSQL", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "only_notice", + "by_rule" + ], + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "chart_image_enabled": true, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 72020, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "other_rt", + "bk_biz_id": 100465, + "data_source_type": "自定义事件" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \350\277\236\346\216\245\346\225\260\344\275\277\347\224\250\347\216\207.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \350\277\236\346\216\245\346\225\260\344\275\277\347\224\250\347\216\207.json" new file mode 100644 index 0000000000..17c2292605 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \350\277\236\346\216\245\346\225\260\344\275\277\347\224\250\347\216\207.json" @@ -0,0 +1,321 @@ +{ + "bk_biz_id": 0, + "name": "MySQL 连接数使用率", + "db_type": "mysql", + "details": { + "app": "", + "name": "MySQL 连接数使用率", + "path": "", + "type": "monitor", + "items": [ + { + "name": "100 * AVG(mysql_global_status_threads_connected) / AVG(mysql_global_variables_max_connections)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008148 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6151, + "type": "Threshold", + "level": 3, + "config": [ + [ + { + "method": "gte", + "threshold": 95 + } + ] + ], + "unit_prefix": "" + }, + { + "id": 6152, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 1 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "100 * a / b", + "origin_sql": "", + "query_configs": [ + { + "id": 6148, + "name": "mysql_global_status_threads_connected", + "unit": "", + "alias": "a", + "functions": [], + "metric_id": "bk_monitor.exporter_dbm_mysqld_exporter.gstatus.mysql_global_sta", + "agg_method": "AVG", + "agg_interval": 60, + "metric_field": "mysql_global_status_threads_connected", + "agg_condition": [], + "agg_dimension": [ + "bk_target_service_instance_id", + "app", + "cluster_domain", + "instance_role", + "instance" + ], + "data_type_label": "time_series", + "result_table_id": "exporter_dbm_mysqld_exporter.gstatus", + "data_source_label": "bk_monitor" + }, + { + "id": 6149, + "name": "mysql_global_variables_max_connections", + "unit": "", + "alias": "b", + "functions": [], + "metric_id": "bk_monitor.exporter_dbm_mysqld_exporter.gvars.mysql_global_varia", + "agg_method": "AVG", + "agg_interval": 60, + "metric_field": "mysql_global_variables_max_connections", + "agg_condition": [], + "agg_dimension": [ + "bk_target_service_instance_id", + "app", + "cluster_domain", + "instance_role", + "instance" + ], + "data_type_label": "time_series", + "result_table_id": "exporter_dbm_mysqld_exporter.gvars", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_service_instance_id" + ] + } + } + ], + "labels": [ + "MySQL", + "DBM_MYSQL", + "DBM_MYSQL", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}%\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 8668, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 3, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 2, + "status_setter": "recovery" + } + }, + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 2, + "status_setter": "recovery" + } + } + ], + "scenario": "component", + "bk_biz_id": 2005000194, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \350\277\236\346\216\245\346\227\245\345\277\227\350\277\207\345\244\232.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \350\277\236\346\216\245\346\227\245\345\277\227\350\277\207\345\244\232.json" new file mode 100644 index 0000000000..866bf07169 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \350\277\236\346\216\245\346\227\245\345\277\227\350\277\207\345\244\232.json" @@ -0,0 +1,263 @@ +{ + "bk_biz_id": 0, + "name": "MySQL 连接日志过多", + "db_type": "mysql", + "details": { + "app": "", + "name": "MySQL 连接日志过多", + "path": "", + "type": "monitor", + "items": [ + { + "name": "COUNT(mysql-connlog-size)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008148 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 78713, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 1 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 77811, + "name": "mysql-connlog-size", + "alias": "a", + "functions": [], + "metric_id": "custom.event.{bk_biz_id}_bkmonitor_event_{event_data_id}.mysql-connlog-size", + "agg_method": "COUNT", + "agg_interval": 60, + "agg_condition": [], + "agg_dimension": [ + "bk_cloud_id", + "port", + "app_id", + "server_ip", + "role", + "immute_domain", + "machine_type" + ], + "data_type_label": "event", + "result_table_id": "{bk_biz_id}_bkmonitor_event_{event_data_id}", + "custom_event_name": "mysql-connlog-size", + "data_source_label": "custom" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "DBM_MYSQL", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "no_data", + "abnormal" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "only_notice", + "by_rule" + ], + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "chart_image_enabled": true, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 72043, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "other_rt", + "bk_biz_id": 100465, + "data_source_type": "自定义事件" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \351\205\215\347\275\256\345\274\202\345\270\270.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \351\205\215\347\275\256\345\274\202\345\270\270.json" new file mode 100644 index 0000000000..6c3d03f908 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \351\205\215\347\275\256\345\274\202\345\270\270.json" @@ -0,0 +1,263 @@ +{ + "bk_biz_id": 0, + "name": "MySQL 配置异常", + "db_type": "mysql", + "details": { + "app": "", + "name": "MySQL 配置异常", + "path": "", + "type": "monitor", + "items": [ + { + "name": "COUNT(mysql-config-diff)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008148 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 78712, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 1 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 77810, + "name": "mysql-config-diff", + "alias": "a", + "functions": [], + "metric_id": "custom.event.{bk_biz_id}_bkmonitor_event_{event_data_id}.mysql-config-diff", + "agg_method": "COUNT", + "agg_interval": 60, + "agg_condition": [], + "agg_dimension": [ + "bk_cloud_id", + "port", + "app_id", + "server_ip", + "role", + "immute_domain", + "machine_type" + ], + "data_type_label": "event", + "result_table_id": "{bk_biz_id}_bkmonitor_event_{event_data_id}", + "custom_event_name": "mysql-config-diff", + "data_source_label": "custom" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "DBM_MYSQL", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "no_data", + "abnormal" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "only_notice", + "by_rule" + ], + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "chart_image_enabled": true, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 72042, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "other_rt", + "bk_biz_id": 100465, + "data_source_type": "自定义事件" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \351\224\201\350\241\250.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \351\224\201\350\241\250.json" new file mode 100644 index 0000000000..79a9211309 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \351\224\201\350\241\250.json" @@ -0,0 +1,263 @@ +{ + "bk_biz_id": 0, + "name": "MySQL 锁表", + "db_type": "mysql", + "details": { + "app": "", + "name": "MySQL 锁表", + "path": "", + "type": "monitor", + "items": [ + { + "name": "COUNT(mysql-lock)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008148 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 78714, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 1 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 77812, + "name": "mysql-lock", + "alias": "a", + "functions": [], + "metric_id": "custom.event.{bk_biz_id}_bkmonitor_event_{event_data_id}.mysql-lock", + "agg_method": "COUNT", + "agg_interval": 60, + "agg_condition": [], + "agg_dimension": [ + "bk_cloud_id", + "server_ip", + "role", + "machine_type", + "immute_domain", + "port", + "app_id" + ], + "data_type_label": "event", + "result_table_id": "{bk_biz_id}_bkmonitor_event_{event_data_id}", + "custom_event_name": "mysql-lock", + "data_source_label": "custom" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "DBM_MYSQL", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "no_data", + "abnormal" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "only_notice", + "by_rule" + ], + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "chart_image_enabled": true, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 72044, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "other_rt", + "bk_biz_id": 100465, + "data_source_type": "自定义事件" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \351\225\277\347\251\272\351\227\262\344\272\213\345\212\241\346\234\252\345\205\263\351\227\255.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \351\225\277\347\251\272\351\227\262\344\272\213\345\212\241\346\234\252\345\205\263\351\227\255.json" new file mode 100644 index 0000000000..79bf53671a --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/MySQL \351\225\277\347\251\272\351\227\262\344\272\213\345\212\241\346\234\252\345\205\263\351\227\255.json" @@ -0,0 +1,263 @@ +{ + "bk_biz_id": 0, + "name": "MySQL 长空闲事务未关闭", + "db_type": "mysql", + "details": { + "app": "", + "name": "MySQL 长空闲事务未关闭", + "path": "", + "type": "monitor", + "items": [ + { + "name": "AVG(mysql_engine_innodb_trx_idle_time_max)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008148 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6305, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 3600 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 6302, + "name": "mysql_engine_innodb_trx_idle_time_max", + "unit": "", + "alias": "a", + "functions": [], + "metric_id": "bk_monitor.exporter_dbm_mysqld_exporter.innodbtrx.mysql_engine_i", + "agg_method": "AVG", + "agg_interval": 60, + "metric_field": "mysql_engine_innodb_trx_idle_time_max", + "agg_condition": [], + "agg_dimension": [ + "bk_target_service_instance_id", + "app", + "cluster_domain", + "instance", + "instance_role" + ], + "data_type_label": "time_series", + "result_table_id": "exporter_dbm_mysqld_exporter.innodbtrx", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_service_instance_id" + ] + } + } + ], + "labels": [ + "MySQL", + "DBM_MYSQL", + "DBM_MYSQL", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 8816, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "component", + "bk_biz_id": 2005000194, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/Proxy \345\220\216\347\253\257.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/Proxy \345\220\216\347\253\257.json" new file mode 100644 index 0000000000..6af605f215 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/Proxy \345\220\216\347\253\257.json" @@ -0,0 +1,263 @@ +{ + "bk_biz_id": 0, + "name": "Proxy 后端", + "db_type": "mysql", + "details": { + "app": "", + "name": "Proxy 后端", + "path": "", + "type": "monitor", + "items": [ + { + "name": "COUNT(proxy-backend)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008148 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 79036, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 1 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 78127, + "name": "proxy-backend", + "alias": "a", + "functions": [], + "metric_id": "custom.event.{bk_biz_id}_bkmonitor_event_{event_data_id}.proxy-backend", + "agg_method": "COUNT", + "agg_interval": 60, + "agg_condition": [], + "agg_dimension": [ + "app_id", + "role", + "port", + "machine_type", + "immute_domain", + "bk_cloud_id", + "server_ip" + ], + "data_type_label": "event", + "result_table_id": "{bk_biz_id}_bkmonitor_event_{event_data_id}", + "custom_event_name": "proxy-backend", + "data_source_label": "custom" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "DBM_MYSQL", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "only_notice", + "by_rule" + ], + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "chart_image_enabled": true, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 72372, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "other_rt", + "bk_biz_id": 100465, + "data_source_type": "自定义事件" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/Proxy \347\231\275\345\220\215\345\215\225.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/Proxy \347\231\275\345\220\215\345\215\225.json" new file mode 100644 index 0000000000..d813f080cc --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/Proxy \347\231\275\345\220\215\345\215\225.json" @@ -0,0 +1,263 @@ +{ + "bk_biz_id": 0, + "name": "Proxy 白名单", + "db_type": "mysql", + "details": { + "app": "", + "name": "Proxy 白名单", + "path": "", + "type": "monitor", + "items": [ + { + "name": "COUNT(proxy-user-list)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008148 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008149 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 79037, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 1 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 78128, + "name": "proxy-user-list", + "alias": "a", + "functions": [], + "metric_id": "custom.event.{bk_biz_id}_bkmonitor_event_{event_data_id}.proxy-user-list", + "agg_method": "COUNT", + "agg_interval": 60, + "agg_condition": [], + "agg_dimension": [ + "app_id", + "role", + "port", + "machine_type", + "immute_domain", + "bk_cloud_id", + "server_ip" + ], + "data_type_label": "event", + "result_table_id": "{bk_biz_id}_bkmonitor_event_{event_data_id}", + "custom_event_name": "proxy-user-list", + "data_source_label": "custom" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "DBM_MYSQL", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "only_notice", + "by_rule" + ], + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "chart_image_enabled": true, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 72373, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "other_rt", + "bk_biz_id": 100465, + "data_source_type": "自定义事件" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/[ES]-\344\270\273\346\234\272 CPU \344\275\277\347\224\250\347\216\207.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/[ES]-\344\270\273\346\234\272 CPU \344\275\277\347\224\250\347\216\207.json" new file mode 100644 index 0000000000..1d2733d3ed --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/[ES]-\344\270\273\346\234\272 CPU \344\275\277\347\224\250\347\216\207.json" @@ -0,0 +1,261 @@ +{ + "bk_biz_id": 0, + "name": "[ES]-主机 CPU 使用率", + "db_type": "es", + "details": { + "app": "", + "name": "[ES]-主机 CPU 使用率", + "path": "", + "type": "monitor", + "items": [ + { + "name": "AVG(CPU使用率)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008154 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008154 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008154 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6212, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 90 + } + ] + ], + "unit_prefix": "%" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 6208, + "name": "CPU使用率", + "unit": "percent", + "alias": "a", + "functions": [], + "metric_id": "bk_monitor.system.cpu_summary.usage", + "agg_method": "AVG", + "agg_interval": 60, + "metric_field": "usage", + "agg_condition": [], + "agg_dimension": [ + "bk_target_cloud_id", + "bk_target_ip" + ], + "data_type_label": "time_series", + "result_table_id": "system.cpu_summary", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_ip", + "bk_target_cloud_id" + ] + } + } + ], + "labels": [ + "ES", + "DBM_ES", + "DBM_ES", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 8723, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "os", + "bk_biz_id": 2005000194, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/[ES]-\344\270\273\346\234\272\347\243\201\347\233\230IO\345\210\251\347\224\250\347\216\207.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/[ES]-\344\270\273\346\234\272\347\243\201\347\233\230IO\345\210\251\347\224\250\347\216\207.json" new file mode 100644 index 0000000000..8ef7c153e3 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/[ES]-\344\270\273\346\234\272\347\243\201\347\233\230IO\345\210\251\347\224\250\347\216\207.json" @@ -0,0 +1,262 @@ +{ + "bk_biz_id": 0, + "name": "[ES]-主机磁盘IO利用率", + "db_type": "es", + "details": { + "app": "", + "name": "[ES]-主机磁盘IO利用率", + "path": "", + "type": "monitor", + "items": [ + { + "name": "AVG(I/O使用率)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008154 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008154 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008154 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6211, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 90 + } + ] + ], + "unit_prefix": "%" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 6207, + "name": "I/O使用率", + "unit": "percentunit", + "alias": "a", + "functions": [], + "metric_id": "bk_monitor.system.io.util", + "agg_method": "AVG", + "agg_interval": 60, + "metric_field": "util", + "agg_condition": [], + "agg_dimension": [ + "bk_target_ip", + "bk_target_cloud_id", + "device_name" + ], + "data_type_label": "time_series", + "result_table_id": "system.io", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_ip", + "bk_target_cloud_id" + ] + } + } + ], + "labels": [ + "ES", + "DBM_ES", + "DBM_ES", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 8722, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "os", + "bk_biz_id": 2005000194, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/[ES]-\344\270\273\346\234\272\347\243\201\347\233\230\347\251\272\351\227\264\344\275\277\347\224\250\347\216\207.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/[ES]-\344\270\273\346\234\272\347\243\201\347\233\230\347\251\272\351\227\264\344\275\277\347\224\250\347\216\207.json" new file mode 100644 index 0000000000..0dcc954f78 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/[ES]-\344\270\273\346\234\272\347\243\201\347\233\230\347\251\272\351\227\264\344\275\277\347\224\250\347\216\207.json" @@ -0,0 +1,262 @@ +{ + "bk_biz_id": 0, + "name": "[ES]-主机磁盘空间使用率", + "db_type": "es", + "details": { + "app": "", + "name": "[ES]-主机磁盘空间使用率", + "path": "", + "type": "monitor", + "items": [ + { + "name": "AVG(磁盘空间使用率)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008154 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008154 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008154 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6213, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 85 + } + ] + ], + "unit_prefix": "%" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 6209, + "name": "磁盘空间使用率", + "unit": "percent", + "alias": "a", + "functions": [], + "metric_id": "bk_monitor.system.disk.in_use", + "agg_method": "AVG", + "agg_interval": 60, + "metric_field": "in_use", + "agg_condition": [], + "agg_dimension": [ + "bk_target_ip", + "bk_target_cloud_id", + "mount_point" + ], + "data_type_label": "time_series", + "result_table_id": "system.disk", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_ip", + "bk_target_cloud_id" + ] + } + } + ], + "labels": [ + "ES", + "DBM_ES", + "DBM_ES", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 8724, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "os", + "bk_biz_id": 2005000194, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/[ES]-\344\270\273\346\234\272\347\275\221\347\273\234\345\205\245\346\265\201\351\207\217.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/[ES]-\344\270\273\346\234\272\347\275\221\347\273\234\345\205\245\346\265\201\351\207\217.json" new file mode 100644 index 0000000000..3edd0017a5 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/[ES]-\344\270\273\346\234\272\347\275\221\347\273\234\345\205\245\346\265\201\351\207\217.json" @@ -0,0 +1,262 @@ +{ + "bk_biz_id": 0, + "name": "[ES]-主机网络入流量", + "db_type": "es", + "details": { + "app": "", + "name": "[ES]-主机网络入流量", + "path": "", + "type": "monitor", + "items": [ + { + "name": "AVG(网卡入流量)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008154 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008154 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008154 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6214, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 250 + } + ] + ], + "unit_prefix": "M" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 6210, + "name": "网卡入流量", + "unit": "Bps", + "alias": "a", + "functions": [], + "metric_id": "bk_monitor.system.net.speed_recv", + "agg_method": "AVG", + "agg_interval": 60, + "metric_field": "speed_recv", + "agg_condition": [], + "agg_dimension": [ + "bk_target_ip", + "bk_target_cloud_id", + "device_name" + ], + "data_type_label": "time_series", + "result_table_id": "system.net", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_ip", + "bk_target_cloud_id" + ] + } + } + ], + "labels": [ + "ES", + "DBM_ES", + "DBM_ES", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 8725, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "os", + "bk_biz_id": 2005000194, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/[ES]-\345\206\231\346\213\222\347\273\235.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/[ES]-\345\206\231\346\213\222\347\273\235.json" new file mode 100644 index 0000000000..1047029956 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/[ES]-\345\206\231\346\213\222\347\273\235.json" @@ -0,0 +1,250 @@ +{ + "bk_biz_id": 0, + "name": "[ES]-写拒绝", + "db_type": "es", + "details": { + "app": "", + "name": "[ES]-写拒绝", + "path": "", + "type": "monitor", + "items": [ + { + "name": "[ES]-写拒绝", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008154 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008154 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008154 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6217, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gt", + "threshold": 100 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "max by(bk_target_service_instance_id, app, cluster_name, name) (bkmonitor:exporter_dbm_elasticsearch_exporter:group6:elasticsearch_thread_pool_rejected_coun{type=\"write\"})", + "query_configs": [ + { + "id": 6213, + "name": "", + "alias": "a", + "promql": "max by(bk_target_service_instance_id, app, cluster_name, name) (bkmonitor:exporter_dbm_elasticsearch_exporter:group6:elasticsearch_thread_pool_rejected_coun{type=\"write\"})", + "functions": [], + "metric_id": "max by(bk_target_service_instance_id, app, cluster_name, name) (", + "agg_interval": 60, + "data_type_label": "time_series", + "data_source_label": "prometheus" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "ES", + "DBM_ES", + "DBM_ES", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 8728, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "component", + "bk_biz_id": 2005000194, + "data_source_type": "Prometheus" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/[ES]-\345\206\231\346\216\222\351\230\237.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/[ES]-\345\206\231\346\216\222\351\230\237.json" new file mode 100644 index 0000000000..ad56a5b7ae --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/[ES]-\345\206\231\346\216\222\351\230\237.json" @@ -0,0 +1,250 @@ +{ + "bk_biz_id": 0, + "name": "[ES]-写排队", + "db_type": "es", + "details": { + "app": "", + "name": "[ES]-写排队", + "path": "", + "type": "monitor", + "items": [ + { + "name": "[ES]-写排队", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008154 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008154 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008154 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6216, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gt", + "threshold": 100 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "max by(bk_target_service_instance_id, app, cluster_name, name) (max_over_time(bkmonitor:exporter_dbm_elasticsearch_exporter:group6:elasticsearch_thread_pool_queue_count{type=\"write\"}[1m]))", + "query_configs": [ + { + "id": 6212, + "name": "", + "alias": "a", + "promql": "max by(bk_target_service_instance_id, app, cluster_name, name) (max_over_time(bkmonitor:exporter_dbm_elasticsearch_exporter:group6:elasticsearch_thread_pool_queue_count{type=\"write\"}[1m]))", + "functions": [], + "metric_id": "max by(bk_target_service_instance_id, app, cluster_name, name) (", + "agg_interval": 60, + "data_type_label": "time_series", + "data_source_label": "prometheus" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "ES", + "DBM_ES", + "DBM_ES", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 8727, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "component", + "bk_biz_id": 2005000194, + "data_source_type": "Prometheus" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/[ES]-\346\220\234\347\264\242\346\213\222\347\273\235.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/[ES]-\346\220\234\347\264\242\346\213\222\347\273\235.json" new file mode 100644 index 0000000000..13f728789f --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/[ES]-\346\220\234\347\264\242\346\213\222\347\273\235.json" @@ -0,0 +1,250 @@ +{ + "bk_biz_id": 0, + "name": "[ES]-搜索拒绝", + "db_type": "es", + "details": { + "app": "", + "name": "[ES]-搜索拒绝", + "path": "", + "type": "monitor", + "items": [ + { + "name": "[ES]-搜索拒绝", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008154 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008154 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008154 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6218, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gt", + "threshold": 100 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "max by(bk_target_service_instance_id, app, cluster_name, name) (bkmonitor:exporter_dbm_elasticsearch_exporter:group6:elasticsearch_thread_pool_rejected_count{type=\"search\"})", + "query_configs": [ + { + "id": 6214, + "name": "", + "alias": "a", + "promql": "max by(bk_target_service_instance_id, app, cluster_name, name) (bkmonitor:exporter_dbm_elasticsearch_exporter:group6:elasticsearch_thread_pool_rejected_count{type=\"search\"})", + "functions": [], + "metric_id": "max by(bk_target_service_instance_id, app, cluster_name, name) (", + "agg_interval": 60, + "data_type_label": "time_series", + "data_source_label": "prometheus" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "ES", + "DBM_ES", + "DBM_ES", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 8729, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "component", + "bk_biz_id": 2005000194, + "data_source_type": "Prometheus" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/[ES]-\351\233\206\347\276\244\347\212\266\346\200\201.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/[ES]-\351\233\206\347\276\244\347\212\266\346\200\201.json" new file mode 100644 index 0000000000..f6de7f53b1 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/[ES]-\351\233\206\347\276\244\347\212\266\346\200\201.json" @@ -0,0 +1,275 @@ +{ + "bk_biz_id": 0, + "name": "[ES]-集群状态", + "db_type": "es", + "details": { + "app": "", + "name": "[ES]-集群状态", + "path": "", + "type": "monitor", + "items": [ + { + "name": "MAX(elasticsearch_cluster_health_status)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008154 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008154 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008154 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6300, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "eq", + "threshold": 1 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 6296, + "name": "elasticsearch_cluster_health_status", + "unit": "", + "alias": "a", + "functions": [], + "metric_id": "bk_monitor.exporter_dbm_elasticsearch_exporter.group15.elasticse", + "agg_method": "MAX", + "agg_interval": 60, + "metric_field": "elasticsearch_cluster_health_status", + "agg_condition": [ + { + "key": "color", + "value": [ + "red" + ], + "method": "eq", + "condition": "and", + "dimension_name": "color" + } + ], + "agg_dimension": [ + "bk_target_service_instance_id", + "app", + "cluster_domain" + ], + "data_type_label": "time_series", + "result_table_id": "exporter_dbm_elasticsearch_exporter.group15", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_service_instance_id" + ] + } + } + ], + "labels": [ + "ES", + "DBM_ES", + "DBM_ES", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [ + "app", + "cluster_domain", + "bk_target_service_instance_id" + ], + "is_enabled": false + } + }, + "config_id": 8811, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "component", + "bk_biz_id": 2005000194, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/[HDFS]-CorruptBlocks.json b/dbm-ui/backend/db_monitor/tpls/alarm/[HDFS]-CorruptBlocks.json new file mode 100644 index 0000000000..cd43fff3bb --- /dev/null +++ b/dbm-ui/backend/db_monitor/tpls/alarm/[HDFS]-CorruptBlocks.json @@ -0,0 +1,291 @@ +{ + "bk_biz_id": 0, + "name": "[HDFS]-CorruptBlocks", + "db_type": "hdfs", + "details": { + "app": "", + "name": "[HDFS]-CorruptBlocks", + "path": "", + "type": "monitor", + "items": [ + { + "name": "AVG(hadoop_namenode_corrupt_blocks)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008157 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008157 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6469, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 10 + } + ] + ], + "unit_prefix": "" + }, + { + "id": 6470, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 500 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 6442, + "name": "hadoop_namenode_corrupt_blocks", + "unit": "", + "alias": "a", + "functions": [], + "metric_id": "bk_monitor.exporter_hdfs_exporter.group1.hadoop_namenode_corrupt", + "agg_method": "AVG", + "agg_interval": 60, + "metric_field": "hadoop_namenode_corrupt_blocks", + "agg_condition": [], + "agg_dimension": [ + "bk_target_service_instance_id" + ], + "data_type_label": "time_series", + "result_table_id": "exporter_hdfs_exporter.group1", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_service_instance_id" + ] + } + } + ], + "labels": [ + "HDFS", + "DBM_HDFS", + "DBM_HDFS", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 8949, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + }, + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "component", + "bk_biz_id": 2005000194, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/[HDFS]-EditLogRoll.json b/dbm-ui/backend/db_monitor/tpls/alarm/[HDFS]-EditLogRoll.json new file mode 100644 index 0000000000..a7618b7edf --- /dev/null +++ b/dbm-ui/backend/db_monitor/tpls/alarm/[HDFS]-EditLogRoll.json @@ -0,0 +1,291 @@ +{ + "bk_biz_id": 0, + "name": "[HDFS]-EditLogRoll", + "db_type": "hdfs", + "details": { + "app": "", + "name": "[HDFS]-EditLogRoll", + "path": "", + "type": "monitor", + "items": [ + { + "name": "MAX(hadoop_namenode_transactions_since_last_checkpoint)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008157 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008157 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6467, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 5000000 + } + ] + ], + "unit_prefix": "" + }, + { + "id": 6468, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 6000000 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 6441, + "name": "hadoop_namenode_transactions_since_last_checkpoint", + "unit": "", + "alias": "a", + "functions": [], + "metric_id": "bk_monitor.exporter_hdfs_exporter.group1.hadoop_namenode_transac", + "agg_method": "MAX", + "agg_interval": 60, + "metric_field": "hadoop_namenode_transactions_since_last_checkpoint", + "agg_condition": [], + "agg_dimension": [ + "bk_target_service_instance_id" + ], + "data_type_label": "time_series", + "result_table_id": "exporter_hdfs_exporter.group1", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_service_instance_id" + ] + } + } + ], + "labels": [ + "HDFS", + "DBM_HDFS", + "DBM_HDFS", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 8948, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + }, + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "component", + "bk_biz_id": 2005000194, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/[HDFS]-MissingBlocks.json b/dbm-ui/backend/db_monitor/tpls/alarm/[HDFS]-MissingBlocks.json new file mode 100644 index 0000000000..306a87bfcb --- /dev/null +++ b/dbm-ui/backend/db_monitor/tpls/alarm/[HDFS]-MissingBlocks.json @@ -0,0 +1,291 @@ +{ + "bk_biz_id": 0, + "name": "[HDFS]-MissingBlocks", + "db_type": "hdfs", + "details": { + "app": "", + "name": "[HDFS]-MissingBlocks", + "path": "", + "type": "monitor", + "items": [ + { + "name": "MAX(hadoop_namenode_missing_blocks)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008157 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008157 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6473, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 500 + } + ] + ], + "unit_prefix": "" + }, + { + "id": 6474, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 10 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 6445, + "name": "hadoop_namenode_missing_blocks", + "unit": "", + "alias": "a", + "functions": [], + "metric_id": "bk_monitor.exporter_hdfs_exporter.group1.hadoop_namenode_missing", + "agg_method": "MAX", + "agg_interval": 60, + "metric_field": "hadoop_namenode_missing_blocks", + "agg_condition": [], + "agg_dimension": [ + "bk_target_service_instance_id" + ], + "data_type_label": "time_series", + "result_table_id": "exporter_hdfs_exporter.group1", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_service_instance_id" + ] + } + } + ], + "labels": [ + "HDFS", + "DBM_HDFS", + "DBM_HDFS", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 8952, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + }, + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "component", + "bk_biz_id": 2005000194, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git a/dbm-ui/backend/db_monitor/tpls/alarm/[HDFS]-SafeNode.json b/dbm-ui/backend/db_monitor/tpls/alarm/[HDFS]-SafeNode.json new file mode 100644 index 0000000000..87398c5049 --- /dev/null +++ b/dbm-ui/backend/db_monitor/tpls/alarm/[HDFS]-SafeNode.json @@ -0,0 +1,268 @@ +{ + "bk_biz_id": 0, + "name": "[HDFS]-SafeNode", + "db_type": "hdfs", + "details": { + "app": "", + "name": "[HDFS]-SafeNode", + "path": "", + "type": "monitor", + "items": [ + { + "name": "SUM(hadoop_namenode_f_s_state)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008157 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008157 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6475, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "lt", + "threshold": 1 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 6446, + "name": "hadoop_namenode_f_s_state", + "unit": "", + "alias": "a", + "functions": [], + "metric_id": "bk_monitor.exporter_dbm_hdfs_exporter.group1.hadoop_namenode_f_s", + "agg_method": "SUM", + "agg_interval": 60, + "metric_field": "hadoop_namenode_f_s_state", + "agg_condition": [ + { + "key": "instance_role", + "value": [ + "hdfs_namenode" + ], + "method": "eq", + "condition": "and", + "dimension_name": "instance_role" + } + ], + "agg_dimension": [ + "app", + "cluster_domain" + ], + "data_type_label": "time_series", + "result_table_id": "exporter_dbm_hdfs_exporter.group1", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "HDFS", + "DBM_HDFS", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "no_data", + "abnormal" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "only_notice", + "by_rule" + ], + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [ + "app", + "cluster_domain" + ], + "is_enabled": false + } + }, + "config_id": 8953, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "component", + "bk_biz_id": 2005000194, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/[HDFS]-\345\256\271\351\207\217\344\275\277\347\224\250\347\216\207.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/[HDFS]-\345\256\271\351\207\217\344\275\277\347\224\250\347\216\207.json" new file mode 100644 index 0000000000..00c729f347 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/[HDFS]-\345\256\271\351\207\217\344\275\277\347\224\250\347\216\207.json" @@ -0,0 +1,317 @@ +{ + "bk_biz_id": 0, + "name": "[HDFS]-容量使用率", + "db_type": "hdfs", + "details": { + "app": "", + "name": "[HDFS]-容量使用率", + "path": "", + "type": "monitor", + "items": [ + { + "name": "AVG(hadoop_namenode_capacity_used)/AVG(hadoop_namenode_capacity_total)*100", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008157 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008157 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6463, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 86 + } + ] + ], + "unit_prefix": "" + }, + { + "id": 6464, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 80 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a/b*100", + "origin_sql": "", + "query_configs": [ + { + "id": 6437, + "name": "hadoop_namenode_capacity_used", + "unit": "", + "alias": "a", + "functions": [], + "metric_id": "bk_monitor.exporter_dbm_hdfs_exporter.group1.hadoop_namenode_cap", + "agg_method": "AVG", + "agg_interval": 60, + "metric_field": "hadoop_namenode_capacity_used", + "agg_condition": [], + "agg_dimension": [ + "app", + "cluster_domain", + "bk_target_service_instance_id" + ], + "data_type_label": "time_series", + "result_table_id": "exporter_dbm_hdfs_exporter.group1", + "data_source_label": "bk_monitor" + }, + { + "id": 6438, + "name": "hadoop_namenode_capacity_total", + "unit": "", + "alias": "b", + "functions": [], + "metric_id": "bk_monitor.exporter_dbm_hdfs_exporter.group1.hadoop_namenode_cap", + "agg_method": "AVG", + "agg_interval": 60, + "metric_field": "hadoop_namenode_capacity_total", + "agg_condition": [], + "agg_dimension": [ + "bk_target_service_instance_id", + "cluster_domain", + "app" + ], + "data_type_label": "time_series", + "result_table_id": "exporter_dbm_hdfs_exporter.group1", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_service_instance_id" + ] + } + } + ], + "labels": [ + "HDFS", + "DBM_HDFS", + "DBM_HDFS", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "no_data", + "abnormal" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [ + "app", + "cluster_domain", + "bk_target_service_instance_id" + ], + "is_enabled": false + } + }, + "config_id": 8945, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + }, + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "component", + "bk_biz_id": 2005000194, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/[Kafka]-\344\270\273\346\234\272 CPU \344\275\277\347\224\250\347\216\207.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/[Kafka]-\344\270\273\346\234\272 CPU \344\275\277\347\224\250\347\216\207.json" new file mode 100644 index 0000000000..3cb4789715 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/[Kafka]-\344\270\273\346\234\272 CPU \344\275\277\347\224\250\347\216\207.json" @@ -0,0 +1,246 @@ +{ + "bk_biz_id": 0, + "name": "[Kafka]-主机 CPU 使用率", + "db_type": "kafka", + "details": { + "app": "", + "name": "[Kafka]-主机 CPU 使用率", + "path": "", + "type": "monitor", + "items": [ + { + "name": "[Kafka]-主机 CPU 使用率", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008155 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008156 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6222, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 90 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "avg by(bk_target_cloud_id, bk_target_ip) (avg_over_time(bkmonitor:system:cpu_summary:usage[1m]))", + "query_configs": [ + { + "id": 6218, + "name": "", + "alias": "a", + "promql": "avg by(bk_target_cloud_id, bk_target_ip) (avg_over_time(bkmonitor:system:cpu_summary:usage[1m]))", + "functions": [], + "metric_id": "avg by(bk_target_cloud_id, bk_target_ip) (avg_over_time(bkmonito", + "agg_interval": 60, + "data_type_label": "time_series", + "data_source_label": "prometheus" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "Kafka", + "DBM_KAFKA", + "DBM_KAFKA", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 8733, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "os", + "bk_biz_id": 2005000194, + "data_source_type": "Prometheus" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/[Kafka]-\344\270\273\346\234\272\347\243\201\347\233\230IO\345\210\251\347\224\250\347\216\207.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/[Kafka]-\344\270\273\346\234\272\347\243\201\347\233\230IO\345\210\251\347\224\250\347\216\207.json" new file mode 100644 index 0000000000..044a80dfa7 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/[Kafka]-\344\270\273\346\234\272\347\243\201\347\233\230IO\345\210\251\347\224\250\347\216\207.json" @@ -0,0 +1,258 @@ +{ + "bk_biz_id": 0, + "name": "[Kafka]-主机磁盘IO利用率", + "db_type": "kafka", + "details": { + "app": "", + "name": "[Kafka]-主机磁盘IO利用率", + "path": "", + "type": "monitor", + "items": [ + { + "name": "AVG(I/O使用率)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008155 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008156 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6219, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 90 + } + ] + ], + "unit_prefix": "%" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 6215, + "name": "I/O使用率", + "unit": "percentunit", + "alias": "a", + "functions": [], + "metric_id": "bk_monitor.system.io.util", + "agg_method": "AVG", + "agg_interval": 60, + "metric_field": "util", + "agg_condition": [], + "agg_dimension": [ + "bk_target_ip", + "bk_target_cloud_id", + "device_name" + ], + "data_type_label": "time_series", + "result_table_id": "system.io", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_ip", + "bk_target_cloud_id" + ] + } + } + ], + "labels": [ + "Kafka", + "DBM_KAFKA", + "DBM_KAFKA", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 8730, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "os", + "bk_biz_id": 2005000194, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/[Kafka]-\344\270\273\346\234\272\347\243\201\347\233\230\347\251\272\351\227\264\344\275\277\347\224\250\347\216\207.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/[Kafka]-\344\270\273\346\234\272\347\243\201\347\233\230\347\251\272\351\227\264\344\275\277\347\224\250\347\216\207.json" new file mode 100644 index 0000000000..d8028db0d3 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/[Kafka]-\344\270\273\346\234\272\347\243\201\347\233\230\347\251\272\351\227\264\344\275\277\347\224\250\347\216\207.json" @@ -0,0 +1,258 @@ +{ + "bk_biz_id": 0, + "name": "[Kafka]-主机磁盘空间使用率", + "db_type": "kafka", + "details": { + "app": "", + "name": "[Kafka]-主机磁盘空间使用率", + "path": "", + "type": "monitor", + "items": [ + { + "name": "AVG(磁盘空间使用率)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008155 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008156 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6220, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 85 + } + ] + ], + "unit_prefix": "%" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 6216, + "name": "磁盘空间使用率", + "unit": "percent", + "alias": "a", + "functions": [], + "metric_id": "bk_monitor.system.disk.in_use", + "agg_method": "AVG", + "agg_interval": 60, + "metric_field": "in_use", + "agg_condition": [], + "agg_dimension": [ + "bk_target_ip", + "bk_target_cloud_id", + "mount_point" + ], + "data_type_label": "time_series", + "result_table_id": "system.disk", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_ip", + "bk_target_cloud_id" + ] + } + } + ], + "labels": [ + "Kafka", + "DBM_KAFKA", + "DBM_KAFKA", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 8731, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "os", + "bk_biz_id": 2005000194, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/[Kafka]-\344\270\273\346\234\272\347\275\221\347\273\234\345\205\245\346\265\201\351\207\217.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/[Kafka]-\344\270\273\346\234\272\347\275\221\347\273\234\345\205\245\346\265\201\351\207\217.json" new file mode 100644 index 0000000000..170b732111 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/[Kafka]-\344\270\273\346\234\272\347\275\221\347\273\234\345\205\245\346\265\201\351\207\217.json" @@ -0,0 +1,258 @@ +{ + "bk_biz_id": 0, + "name": "[Kafka]-主机网络入流量", + "db_type": "kafka", + "details": { + "app": "", + "name": "[Kafka]-主机网络入流量", + "path": "", + "type": "monitor", + "items": [ + { + "name": "AVG(网卡入流量)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008155 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008156 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6221, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 250 + } + ] + ], + "unit_prefix": "M" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 6217, + "name": "网卡入流量", + "unit": "Bps", + "alias": "a", + "functions": [], + "metric_id": "bk_monitor.system.net.speed_recv", + "agg_method": "AVG", + "agg_interval": 60, + "metric_field": "speed_recv", + "agg_condition": [], + "agg_dimension": [ + "bk_target_ip", + "bk_target_cloud_id", + "device_name" + ], + "data_type_label": "time_series", + "result_table_id": "system.net", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_ip", + "bk_target_cloud_id" + ] + } + } + ], + "labels": [ + "Kafka", + "DBM_KAFKA", + "DBM_KAFKA", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 8732, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "os", + "bk_biz_id": 2005000194, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/[Kafka]-\346\266\210\350\264\271\347\273\204\345\273\266\350\277\237.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/[Kafka]-\346\266\210\350\264\271\347\273\204\345\273\266\350\277\237.json" new file mode 100644 index 0000000000..774bee280a --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/[Kafka]-\346\266\210\350\264\271\347\273\204\345\273\266\350\277\237.json" @@ -0,0 +1,246 @@ +{ + "bk_biz_id": 0, + "name": "[Kafka]-消费组延迟", + "db_type": "kafka", + "details": { + "app": "", + "name": "[Kafka]-消费组延迟", + "path": "", + "type": "monitor", + "items": [ + { + "name": "[Kafka]-消费组延迟", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008155 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008156 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6228, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 100000 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "avg by(bk_target_service_instance_id, app, cluster_name, consumergroup) (bkmonitor:exporter_dbm_kafka_exporter:group1:kafka_consumergroup_lag_sum)", + "query_configs": [ + { + "id": 6224, + "name": "", + "alias": "a", + "promql": "avg by(bk_target_service_instance_id, app, cluster_name, consumergroup) (bkmonitor:exporter_dbm_kafka_exporter:group1:kafka_consumergroup_lag_sum)", + "functions": [], + "metric_id": "avg by(bk_target_service_instance_id, app, cluster_name, consume", + "agg_interval": 60, + "data_type_label": "time_series", + "data_source_label": "prometheus" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "Kafka", + "DBM_KAFKA", + "DBM_KAFKA", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 8739, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "component", + "bk_biz_id": 2005000194, + "data_source_type": "Prometheus" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/[Pulsar]-\344\270\273\346\234\272 CPU \344\275\277\347\224\250\347\216\207.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/[Pulsar]-\344\270\273\346\234\272 CPU \344\275\277\347\224\250\347\216\207.json" new file mode 100644 index 0000000000..664ec11391 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/[Pulsar]-\344\270\273\346\234\272 CPU \344\275\277\347\224\250\347\216\207.json" @@ -0,0 +1,262 @@ +{ + "bk_biz_id": 0, + "name": "[Pulsar]-主机 CPU 使用率", + "db_type": "pulsar", + "details": { + "app": "", + "name": "[Pulsar]-主机 CPU 使用率", + "path": "", + "type": "monitor", + "items": [ + { + "name": "AVG(CPU使用率)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008231 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008232 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008233 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6226, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 90 + } + ] + ], + "unit_prefix": "%" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 6222, + "name": "CPU使用率", + "unit": "percent", + "alias": "a", + "functions": [], + "metric_id": "bk_monitor.system.cpu_summary.usage", + "agg_method": "AVG", + "agg_interval": 60, + "metric_field": "usage", + "agg_condition": [], + "agg_dimension": [ + "bk_target_cloud_id", + "bk_target_ip" + ], + "data_type_label": "time_series", + "result_table_id": "system.cpu_summary", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_ip", + "bk_target_cloud_id" + ] + } + } + ], + "labels": [ + "Pulsar", + "DBM_PULSAR", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "only_notice", + "by_rule" + ], + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 8737, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "os", + "bk_biz_id": 2005000194, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/[Pulsar]-\344\270\273\346\234\272\347\243\201\347\233\230IO\345\210\251\347\224\250\347\216\207.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/[Pulsar]-\344\270\273\346\234\272\347\243\201\347\233\230IO\345\210\251\347\224\250\347\216\207.json" new file mode 100644 index 0000000000..b385b517c1 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/[Pulsar]-\344\270\273\346\234\272\347\243\201\347\233\230IO\345\210\251\347\224\250\347\216\207.json" @@ -0,0 +1,263 @@ +{ + "bk_biz_id": 0, + "name": "[Pulsar]-主机磁盘IO利用率", + "db_type": "pulsar", + "details": { + "app": "", + "name": "[Pulsar]-主机磁盘IO利用率", + "path": "", + "type": "monitor", + "items": [ + { + "name": "AVG(I/O使用率)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008231 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008232 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008233 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6223, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 90 + } + ] + ], + "unit_prefix": "%" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 6219, + "name": "I/O使用率", + "unit": "percentunit", + "alias": "a", + "functions": [], + "metric_id": "bk_monitor.system.io.util", + "agg_method": "AVG", + "agg_interval": 60, + "metric_field": "util", + "agg_condition": [], + "agg_dimension": [ + "bk_target_ip", + "bk_target_cloud_id", + "device_name" + ], + "data_type_label": "time_series", + "result_table_id": "system.io", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_ip", + "bk_target_cloud_id" + ] + } + } + ], + "labels": [ + "Pulsar", + "DBM_PULSAR", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "only_notice", + "by_rule" + ], + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 8734, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "os", + "bk_biz_id": 2005000194, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/[Pulsar]-\344\270\273\346\234\272\347\243\201\347\233\230\347\251\272\351\227\264\344\275\277\347\224\250\347\216\207.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/[Pulsar]-\344\270\273\346\234\272\347\243\201\347\233\230\347\251\272\351\227\264\344\275\277\347\224\250\347\216\207.json" new file mode 100644 index 0000000000..d018366bc9 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/[Pulsar]-\344\270\273\346\234\272\347\243\201\347\233\230\347\251\272\351\227\264\344\275\277\347\224\250\347\216\207.json" @@ -0,0 +1,263 @@ +{ + "bk_biz_id": 0, + "name": "[Pulsar]-主机磁盘空间使用率", + "db_type": "pulsar", + "details": { + "app": "", + "name": "[Pulsar]-主机磁盘空间使用率", + "path": "", + "type": "monitor", + "items": [ + { + "name": "AVG(磁盘空间使用率)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008231 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008232 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008233 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6224, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 85 + } + ] + ], + "unit_prefix": "%" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 6220, + "name": "磁盘空间使用率", + "unit": "percent", + "alias": "a", + "functions": [], + "metric_id": "bk_monitor.system.disk.in_use", + "agg_method": "AVG", + "agg_interval": 60, + "metric_field": "in_use", + "agg_condition": [], + "agg_dimension": [ + "bk_target_ip", + "bk_target_cloud_id", + "mount_point" + ], + "data_type_label": "time_series", + "result_table_id": "system.disk", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_ip", + "bk_target_cloud_id" + ] + } + } + ], + "labels": [ + "Pulsar", + "DBM_PULSAR", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "only_notice", + "by_rule" + ], + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 8735, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "os", + "bk_biz_id": 2005000194, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/[Pulsar]-\344\270\273\346\234\272\347\275\221\347\273\234\345\205\245\346\265\201\351\207\217.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/[Pulsar]-\344\270\273\346\234\272\347\275\221\347\273\234\345\205\245\346\265\201\351\207\217.json" new file mode 100644 index 0000000000..c0e2603c1e --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/[Pulsar]-\344\270\273\346\234\272\347\275\221\347\273\234\345\205\245\346\265\201\351\207\217.json" @@ -0,0 +1,263 @@ +{ + "bk_biz_id": 0, + "name": "[Pulsar]-主机网络入流量", + "db_type": "pulsar", + "details": { + "app": "", + "name": "[Pulsar]-主机网络入流量", + "path": "", + "type": "monitor", + "items": [ + { + "name": "AVG(网卡入流量)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008231 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008232 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008233 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6225, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 250 + } + ] + ], + "unit_prefix": "M" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 6221, + "name": "网卡入流量", + "unit": "Bps", + "alias": "a", + "functions": [], + "metric_id": "bk_monitor.system.net.speed_recv", + "agg_method": "AVG", + "agg_interval": 60, + "metric_field": "speed_recv", + "agg_condition": [], + "agg_dimension": [ + "bk_target_ip", + "bk_target_cloud_id", + "device_name" + ], + "data_type_label": "time_series", + "result_table_id": "system.net", + "data_source_label": "bk_monitor" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [ + "bk_target_ip", + "bk_target_cloud_id" + ] + } + } + ], + "labels": [ + "Pulsar", + "DBM_PULSAR", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "only_notice", + "by_rule" + ], + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 8736, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "os", + "bk_biz_id": 2005000194, + "data_source_type": "监控采集指标" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/predixy_login\344\272\213\344\273\266\345\221\212\350\255\246.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/predixy_login\344\272\213\344\273\266\345\221\212\350\255\246.json" new file mode 100644 index 0000000000..53679f60c0 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/predixy_login\344\272\213\344\273\266\345\221\212\350\255\246.json" @@ -0,0 +1,272 @@ +{ + "bk_biz_id": 0, + "name": "predixy_login事件告警", + "db_type": "redis", + "details": { + "app": "", + "name": "predixy_login事件告警", + "path": "", + "type": "monitor", + "items": [ + { + "name": "COUNT(predixy_login)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008150 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008151 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008152 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008153 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008230 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 79121, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 1 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 78200, + "name": "predixy_login", + "alias": "a", + "functions": [], + "metric_id": "custom.event.{bk_biz_id}_bkmonitor_event_{event_data_id}.predixy_login", + "agg_method": "COUNT", + "agg_interval": 60, + "agg_condition": [], + "agg_dimension": [ + "bk_cloud_id", + "server_port", + "domain", + "role", + "server_ip", + "target", + "warn_level" + ], + "data_type_label": "event", + "result_table_id": "{bk_biz_id}_bkmonitor_event_{event_data_id}", + "custom_event_name": "predixy_login", + "data_source_label": "custom" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "REDIS", + "DBM_REDIS", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "no_data", + "abnormal" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "only_notice", + "by_rule" + ], + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "chart_image_enabled": true, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 72449, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "other_rt", + "bk_biz_id": 100465, + "data_source_type": "自定义事件" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/redis_cluster_state error\344\272\213\344\273\266\345\221\212\350\255\246.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/redis_cluster_state error\344\272\213\344\273\266\345\221\212\350\255\246.json" new file mode 100644 index 0000000000..f7a6bd7ec3 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/redis_cluster_state error\344\272\213\344\273\266\345\221\212\350\255\246.json" @@ -0,0 +1,282 @@ +{ + "bk_biz_id": 0, + "name": "redis_cluster_state error事件告警", + "db_type": "redis", + "details": { + "app": "", + "name": "redis_cluster_state error事件告警", + "path": "", + "type": "monitor", + "items": [ + { + "name": "COUNT(redis_cluster_state)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008150 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008151 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008152 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008153 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008230 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 79143, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 1 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 78219, + "name": "redis_cluster_state", + "alias": "a", + "functions": [], + "metric_id": "custom.event.{bk_biz_id}_bkmonitor_event_{event_data_id}.redis_cluster_state", + "agg_method": "COUNT", + "agg_interval": 60, + "agg_condition": [ + { + "key": "warn_level", + "value": [ + "error" + ], + "method": "eq", + "condition": "and", + "dimension_name": "warn_level" + } + ], + "agg_dimension": [ + "bk_cloud_id", + "domain", + "role", + "server_ip", + "server_port", + "warn_level", + "target" + ], + "data_type_label": "event", + "result_table_id": "{bk_biz_id}_bkmonitor_event_{event_data_id}", + "custom_event_name": "redis_cluster_state", + "data_source_label": "custom" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "REDIS", + "DBM_REDIS", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "only_notice", + "by_rule" + ], + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "chart_image_enabled": true, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 72468, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "other_rt", + "bk_biz_id": 100465, + "data_source_type": "自定义事件" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/redis_cluster_state warn\344\272\213\344\273\266\345\221\212\350\255\246.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/redis_cluster_state warn\344\272\213\344\273\266\345\221\212\350\255\246.json" new file mode 100644 index 0000000000..16d3d5a1dc --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/redis_cluster_state warn\344\272\213\344\273\266\345\221\212\350\255\246.json" @@ -0,0 +1,282 @@ +{ + "bk_biz_id": 0, + "name": "redis_cluster_state warn事件告警", + "db_type": "redis", + "details": { + "app": "", + "name": "redis_cluster_state warn事件告警", + "path": "", + "type": "monitor", + "items": [ + { + "name": "COUNT(redis_cluster_state)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008150 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008151 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008152 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008153 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008230 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 79144, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 1 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 78220, + "name": "redis_cluster_state", + "alias": "a", + "functions": [], + "metric_id": "custom.event.{bk_biz_id}_bkmonitor_event_{event_data_id}.redis_cluster_state", + "agg_method": "COUNT", + "agg_interval": 60, + "agg_condition": [ + { + "key": "warn_level", + "value": [ + "warning" + ], + "method": "eq", + "condition": "and", + "dimension_name": "warn_level" + } + ], + "agg_dimension": [ + "bk_cloud_id", + "domain", + "role", + "server_ip", + "server_port", + "warn_level", + "target" + ], + "data_type_label": "event", + "result_table_id": "{bk_biz_id}_bkmonitor_event_{event_data_id}", + "custom_event_name": "redis_cluster_state", + "data_source_label": "custom" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "REDIS", + "DBM_REDIS", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "only_notice", + "by_rule" + ], + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "chart_image_enabled": true, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 72469, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "other_rt", + "bk_biz_id": 100465, + "data_source_type": "自定义事件" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/redis_login\344\272\213\344\273\266\345\221\212\350\255\246.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/redis_login\344\272\213\344\273\266\345\221\212\350\255\246.json" new file mode 100644 index 0000000000..26533e4e20 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/redis_login\344\272\213\344\273\266\345\221\212\350\255\246.json" @@ -0,0 +1,272 @@ +{ + "bk_biz_id": 0, + "name": "redis_login事件告警", + "db_type": "redis", + "details": { + "app": "", + "name": "redis_login事件告警", + "path": "", + "type": "monitor", + "items": [ + { + "name": "COUNT(REDIS_LOGIN)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008150 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008151 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008152 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008153 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008230 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 79120, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 1 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 78199, + "name": "REDIS_LOGIN", + "alias": "a", + "functions": [], + "metric_id": "custom.event.{bk_biz_id}_bkmonitor_event_{event_data_id}.REDIS_LOGIN", + "agg_method": "COUNT", + "agg_interval": 60, + "agg_condition": [], + "agg_dimension": [ + "bk_cloud_id", + "app_id", + "domain", + "role", + "server_ip", + "server_port", + "target" + ], + "data_type_label": "event", + "result_table_id": "{bk_biz_id}_bkmonitor_event_{event_data_id}", + "custom_event_name": "REDIS_LOGIN", + "data_source_label": "custom" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "REDIS", + "DBM_REDIS", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "only_notice", + "by_rule" + ], + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "chart_image_enabled": true, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 72448, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "other_rt", + "bk_biz_id": 100465, + "data_source_type": "自定义事件" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/redis_persist error\344\272\213\344\273\266\345\221\212\350\255\246.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/redis_persist error\344\272\213\344\273\266\345\221\212\350\255\246.json" new file mode 100644 index 0000000000..05a2208d60 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/redis_persist error\344\272\213\344\273\266\345\221\212\350\255\246.json" @@ -0,0 +1,282 @@ +{ + "bk_biz_id": 0, + "name": "redis_persist error事件告警", + "db_type": "redis", + "details": { + "app": "", + "name": "redis_persist error事件告警", + "path": "", + "type": "monitor", + "items": [ + { + "name": "COUNT(redis_persist)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008150 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008151 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008152 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008153 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008230 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 79141, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 1 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 78217, + "name": "redis_persist", + "alias": "a", + "functions": [], + "metric_id": "custom.event.{bk_biz_id}_bkmonitor_event_{event_data_id}.redis_persist", + "agg_method": "COUNT", + "agg_interval": 60, + "agg_condition": [ + { + "key": "warn_level", + "value": [ + "error" + ], + "method": "eq", + "condition": "and", + "dimension_name": "warn_level" + } + ], + "agg_dimension": [ + "bk_cloud_id", + "server_port", + "warn_level", + "target", + "server_ip", + "role", + "domain" + ], + "data_type_label": "event", + "result_table_id": "{bk_biz_id}_bkmonitor_event_{event_data_id}", + "custom_event_name": "redis_persist", + "data_source_label": "custom" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "REDIS", + "DBM_REDIS", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "no_data", + "abnormal" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "only_notice", + "by_rule" + ], + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "chart_image_enabled": true, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 72466, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "other_rt", + "bk_biz_id": 100465, + "data_source_type": "自定义事件" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/redis_persist warning\344\272\213\344\273\266\345\221\212\350\255\246.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/redis_persist warning\344\272\213\344\273\266\345\221\212\350\255\246.json" new file mode 100644 index 0000000000..35e5d1bb3a --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/redis_persist warning\344\272\213\344\273\266\345\221\212\350\255\246.json" @@ -0,0 +1,282 @@ +{ + "bk_biz_id": 0, + "name": "redis_persist warning事件告警", + "db_type": "redis", + "details": { + "app": "", + "name": "redis_persist warning事件告警", + "path": "", + "type": "monitor", + "items": [ + { + "name": "COUNT(redis_persist)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008150 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008151 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008152 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008153 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008230 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 79142, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 1 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 78218, + "name": "redis_persist", + "alias": "a", + "functions": [], + "metric_id": "custom.event.{bk_biz_id}_bkmonitor_event_{event_data_id}.redis_persist", + "agg_method": "COUNT", + "agg_interval": 60, + "agg_condition": [ + { + "key": "warn_level", + "value": [ + "warning" + ], + "method": "eq", + "condition": "and", + "dimension_name": "warn_level" + } + ], + "agg_dimension": [ + "bk_cloud_id", + "server_port", + "warn_level", + "target", + "server_ip", + "role", + "domain" + ], + "data_type_label": "event", + "result_table_id": "{bk_biz_id}_bkmonitor_event_{event_data_id}", + "custom_event_name": "redis_persist", + "data_source_label": "custom" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "REDIS", + "DBM_REDIS", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "only_notice", + "by_rule" + ], + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "chart_image_enabled": true, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 72467, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "other_rt", + "bk_biz_id": 100465, + "data_source_type": "自定义事件" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/redis_sync error\344\272\213\344\273\266\345\221\212\350\255\246.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/redis_sync error\344\272\213\344\273\266\345\221\212\350\255\246.json" new file mode 100644 index 0000000000..a72966f3e9 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/redis_sync error\344\272\213\344\273\266\345\221\212\350\255\246.json" @@ -0,0 +1,281 @@ +{ + "bk_biz_id": 0, + "name": "redis_sync error事件告警", + "db_type": "redis", + "details": { + "app": "", + "name": "redis_sync error事件告警", + "path": "", + "type": "monitor", + "items": [ + { + "name": "COUNT(redis_sync)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008150 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008151 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008152 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008153 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008230 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 79136, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 1 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 78215, + "name": "redis_sync", + "alias": "a", + "functions": [], + "metric_id": "custom.event.{bk_biz_id}_bkmonitor_event_{event_data_id}.redis_sync", + "agg_method": "COUNT", + "agg_interval": 60, + "agg_condition": [ + { + "key": "warn_level", + "value": [ + "error" + ], + "method": "eq", + "condition": "and", + "dimension_name": "warn_level" + } + ], + "agg_dimension": [ + "bk_cloud_id", + "role", + "server_ip", + "server_port", + "domain", + "warn_level" + ], + "data_type_label": "event", + "result_table_id": "{bk_biz_id}_bkmonitor_event_{event_data_id}", + "custom_event_name": "redis_sync", + "data_source_label": "custom" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "REDIS", + "DBM_REDIS", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "no_data", + "abnormal" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "only_notice", + "by_rule" + ], + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "chart_image_enabled": true, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 72464, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "other_rt", + "bk_biz_id": 100465, + "data_source_type": "自定义事件" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/redis_sync warning\344\272\213\344\273\266\345\221\212\350\255\246.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/redis_sync warning\344\272\213\344\273\266\345\221\212\350\255\246.json" new file mode 100644 index 0000000000..c4cc5e1fce --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/redis_sync warning\344\272\213\344\273\266\345\221\212\350\255\246.json" @@ -0,0 +1,281 @@ +{ + "bk_biz_id": 0, + "name": "redis_sync warning事件告警", + "db_type": "redis", + "details": { + "app": "", + "name": "redis_sync warning事件告警", + "path": "", + "type": "monitor", + "items": [ + { + "name": "COUNT(redis_sync)", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008150 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008151 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008152 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008153 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008230 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 79135, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 1 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "", + "query_configs": [ + { + "id": 78214, + "name": "redis_sync", + "alias": "a", + "functions": [], + "metric_id": "custom.event.{bk_biz_id}_bkmonitor_event_{event_data_id}.redis_sync", + "agg_method": "COUNT", + "agg_interval": 60, + "agg_condition": [ + { + "key": "warn_level", + "value": [ + "warning" + ], + "method": "eq", + "condition": "and", + "dimension_name": "warn_level" + } + ], + "agg_dimension": [ + "bk_cloud_id", + "role", + "server_ip", + "server_port", + "domain", + "warn_level" + ], + "data_type_label": "event", + "result_table_id": "{bk_biz_id}_bkmonitor_event_{event_data_id}", + "custom_event_name": "redis_sync", + "data_source_label": "custom" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "REDIS", + "DBM_REDIS", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 7200, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [ + "only_notice", + "by_rule" + ], + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "chart_image_enabled": true, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 72463, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 1, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "other_rt", + "bk_biz_id": 100465, + "data_source_type": "自定义事件" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/redis\344\270\273\346\234\272CPU\344\275\277\347\224\250\347\216\207\345\221\212\350\255\246.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/redis\344\270\273\346\234\272CPU\344\275\277\347\224\250\347\216\207\345\221\212\350\255\246.json" new file mode 100644 index 0000000000..e608e7550b --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/redis\344\270\273\346\234\272CPU\344\275\277\347\224\250\347\216\207\345\221\212\350\255\246.json" @@ -0,0 +1,293 @@ +{ + "bk_biz_id": 0, + "name": "redis主机CPU使用率告警", + "db_type": "redis", + "details": { + "app": "", + "name": "redis主机CPU使用率告警", + "path": "", + "type": "monitor", + "items": [ + { + "name": "redis主机CPU使用率告警", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008150 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008151 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008152 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008153 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008230 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6309, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 90 + } + ] + ], + "unit_prefix": "" + }, + { + "id": 6310, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 80 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "min(min_over_time(bkmonitor:dbm_system:cpu_summary:usage{instance_role=\"redis_master\"}[1m])) by (bk_target_cloud_id,app,cluster_domain,instance_role,cluster_type,bk_target_ip)", + "query_configs": [ + { + "id": 6305, + "alias": "a", + "promql": "min(min_over_time(bkmonitor:dbm_system:cpu_summary:usage{instance_role=\"redis_master\"}[1m])) by (bk_target_cloud_id,app,cluster_domain,instance_role,cluster_type,bk_target_ip)", + "functions": [], + "metric_id": "min(min_over_time(bkmonitor:dbm_system:cpu_summary:usage{instanc", + "agg_interval": 60, + "data_type_label": "time_series", + "data_source_label": "prometheus" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "REDIS", + "DBM_REDIS", + "DBM_REDIS", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 1800, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 8819, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 2, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + }, + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 2, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "os", + "bk_biz_id": 2005000194, + "data_source_type": "Prometheus" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/redis\344\270\273\346\234\272\345\206\205\345\255\230\344\275\277\347\224\250\347\216\207.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/redis\344\270\273\346\234\272\345\206\205\345\255\230\344\275\277\347\224\250\347\216\207.json" new file mode 100644 index 0000000000..ea7c87200f --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/redis\344\270\273\346\234\272\345\206\205\345\255\230\344\275\277\347\224\250\347\216\207.json" @@ -0,0 +1,293 @@ +{ + "bk_biz_id": 0, + "name": "redis主机内存使用率", + "db_type": "redis", + "details": { + "app": "", + "name": "redis主机内存使用率", + "path": "", + "type": "monitor", + "items": [ + { + "name": "redis主机内存使用率", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008150 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008151 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008152 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008153 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008230 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6327, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 90 + } + ] + ], + "unit_prefix": "" + }, + { + "id": 6328, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 80 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "min(min_over_time(bkmonitor:dbm_system:mem:pct_used{instance_role=\"redis_master\"}[1m])) \nby (bk_target_cloud_id,app,cluster_domain,instance_role,cluster_type,bk_target_ip)", + "query_configs": [ + { + "id": 6319, + "alias": "a", + "promql": "min(min_over_time(bkmonitor:dbm_system:mem:pct_used{instance_role=\"redis_master\"}[1m])) \nby (bk_target_cloud_id,app,cluster_domain,instance_role,cluster_type,bk_target_ip)", + "functions": [], + "metric_id": "min(min_over_time(bkmonitor:dbm_system:mem:pct_used{instance_rol", + "agg_interval": 60, + "data_type_label": "time_series", + "data_source_label": "prometheus" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "REDIS", + "DBM_REDIS", + "DBM_REDIS", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}\n{{action_instance.assignees}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 1800, + "interval_notify_mode": "standard" + }, + "signal": [ + "no_data", + "abnormal" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 8833, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 2, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + }, + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 2, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "os", + "bk_biz_id": 2005000194, + "data_source_type": "Prometheus" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/redis\344\270\273\346\234\272\345\215\225\346\240\270CPU\344\275\277\347\224\250\347\216\207.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/redis\344\270\273\346\234\272\345\215\225\346\240\270CPU\344\275\277\347\224\250\347\216\207.json" new file mode 100644 index 0000000000..320f7f27ad --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/redis\344\270\273\346\234\272\345\215\225\346\240\270CPU\344\275\277\347\224\250\347\216\207.json" @@ -0,0 +1,293 @@ +{ + "bk_biz_id": 0, + "name": "redis主机单核CPU使用率", + "db_type": "redis", + "details": { + "app": "", + "name": "redis主机单核CPU使用率", + "path": "", + "type": "monitor", + "items": [ + { + "name": "redis主机单核CPU使用率", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008150 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008151 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008152 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008153 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008230 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6333, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 90 + } + ] + ], + "unit_prefix": "" + }, + { + "id": 6334, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 80 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "min(max_over_time(bkmonitor:dbm_system:cpu_detail:usage{instance_role=\"redis_master\"}[1m])) \nby (bk_target_cloud_id,app,cluster_domain,instance_role,cluster_type,bk_target_ip)", + "query_configs": [ + { + "id": 6322, + "alias": "a", + "promql": "min(max_over_time(bkmonitor:dbm_system:cpu_detail:usage{instance_role=\"redis_master\"}[1m])) \nby (bk_target_cloud_id,app,cluster_domain,instance_role,cluster_type,bk_target_ip)", + "functions": [], + "metric_id": "min(max_over_time(bkmonitor:dbm_system:cpu_detail:usage{instance", + "agg_interval": 60, + "data_type_label": "time_series", + "data_source_label": "prometheus" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "REDIS", + "DBM_REDIS", + "DBM_REDIS", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 1800, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 8836, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 2, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + }, + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 2, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "os", + "bk_biz_id": 2005000194, + "data_source_type": "Prometheus" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/redis\344\270\273\346\234\272\347\243\201\347\233\230IO\344\275\277\347\224\250\347\216\207.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/redis\344\270\273\346\234\272\347\243\201\347\233\230IO\344\275\277\347\224\250\347\216\207.json" new file mode 100644 index 0000000000..be1b413e61 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/redis\344\270\273\346\234\272\347\243\201\347\233\230IO\344\275\277\347\224\250\347\216\207.json" @@ -0,0 +1,293 @@ +{ + "bk_biz_id": 0, + "name": "redis主机磁盘IO使用率", + "db_type": "redis", + "details": { + "app": "", + "name": "redis主机磁盘IO使用率", + "path": "", + "type": "monitor", + "items": [ + { + "name": "redis主机磁盘IO使用率", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008150 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008151 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008152 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008153 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008230 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6331, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 90 + } + ] + ], + "unit_prefix": "" + }, + { + "id": 6332, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 80 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "min(min_over_time(bkmonitor:dbm_system:io:util{instance_role=\"redis_master\"}[1m])) \nby (bk_target_cloud_id,app,cluster_domain,instance_role,cluster_type,bk_target_ip)", + "query_configs": [ + { + "id": 6321, + "alias": "a", + "promql": "min(min_over_time(bkmonitor:dbm_system:io:util{instance_role=\"redis_master\"}[1m])) \nby (bk_target_cloud_id,app,cluster_domain,instance_role,cluster_type,bk_target_ip)", + "functions": [], + "metric_id": "min(min_over_time(bkmonitor:dbm_system:io:util{instance_role=\"re", + "agg_interval": 60, + "data_type_label": "time_series", + "data_source_label": "prometheus" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "REDIS", + "DBM_REDIS", + "DBM_REDIS", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 1800, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 8835, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 2, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + }, + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 2, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "os", + "bk_biz_id": 2005000194, + "data_source_type": "Prometheus" + }, + "is_enabled": true +} diff --git "a/dbm-ui/backend/db_monitor/tpls/alarm/redis\344\270\273\346\234\272\347\243\201\347\233\230\345\256\271\351\207\217\344\275\277\347\224\250\347\216\207.json" "b/dbm-ui/backend/db_monitor/tpls/alarm/redis\344\270\273\346\234\272\347\243\201\347\233\230\345\256\271\351\207\217\344\275\277\347\224\250\347\216\207.json" new file mode 100644 index 0000000000..5e6a1ee4d1 --- /dev/null +++ "b/dbm-ui/backend/db_monitor/tpls/alarm/redis\344\270\273\346\234\272\347\243\201\347\233\230\345\256\271\351\207\217\344\275\277\347\224\250\347\216\207.json" @@ -0,0 +1,293 @@ +{ + "bk_biz_id": 0, + "name": "redis主机磁盘容量使用率", + "db_type": "redis", + "details": { + "app": "", + "name": "redis主机磁盘容量使用率", + "path": "", + "type": "monitor", + "items": [ + { + "name": "redis主机磁盘容量使用率", + "target": [ + [ + { + "field": "host_topo_node", + "value": [ + { + "bk_obj_id": "set", + "bk_inst_id": 2000008150 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008151 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008152 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008153 + }, + { + "bk_obj_id": "set", + "bk_inst_id": 2000008230 + } + ], + "method": "eq" + } + ] + ], + "functions": [], + "algorithms": [ + { + "id": 6329, + "type": "Threshold", + "level": 1, + "config": [ + [ + { + "method": "gte", + "threshold": 90 + } + ] + ], + "unit_prefix": "" + }, + { + "id": 6330, + "type": "Threshold", + "level": 2, + "config": [ + [ + { + "method": "gte", + "threshold": 80 + } + ] + ], + "unit_prefix": "" + } + ], + "expression": "a", + "origin_sql": "min(min_over_time(bkmonitor:dbm_system:disk:in_use{instance_role=\"redis_master\"}[1m])) \nby (bk_target_cloud_id,app,cluster_domain,instance_role,cluster_type,bk_target_ip)", + "query_configs": [ + { + "id": 6320, + "alias": "a", + "promql": "min(min_over_time(bkmonitor:dbm_system:disk:in_use{instance_role=\"redis_master\"}[1m])) \nby (bk_target_cloud_id,app,cluster_domain,instance_role,cluster_type,bk_target_ip)", + "functions": [], + "metric_id": "min(min_over_time(bkmonitor:dbm_system:disk:in_use{instance_role", + "agg_interval": 60, + "data_type_label": "time_series", + "data_source_label": "prometheus" + } + ], + "no_data_config": { + "level": 2, + "continuous": 10, + "is_enabled": false, + "agg_dimension": [] + } + } + ], + "labels": [ + "REDIS", + "DBM_REDIS", + "DBM_REDIS", + "DBM" + ], + "notice": { + "config": { + "template": [ + { + "signal": "abnormal", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "recovered", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + }, + { + "signal": "closed", + "title_tmpl": "{{business.bk_biz_name}} - {{alarm.name}}{{alarm.display_type}}", + "message_tmpl": "{{content.level}}\n{{content.begin_time}}\n{{content.time}}\n{{content.duration}}\n{{content.target_type}}\n{{content.data_source}}\n{{content.content}}\n{{content.current_value}}\n{{content.biz}}\n{{content.target}}\n{{content.dimension}}\n{{content.detail}}\n{{content.related_info}}" + } + ], + "need_poll": true, + "notify_interval": 1800, + "interval_notify_mode": "standard" + }, + "signal": [ + "abnormal", + "no_data" + ], + "options": { + "end_time": "23:59:59", + "start_time": "00:00:00", + "assign_mode": [], + "upgrade_config": {}, + "converge_config": { + "count": 1, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "strategy_id" + }, + { + "value": [ + "self" + ], + "dimension": "dimensions" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + }, + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + } + ], + "timedelta": 60, + "is_enabled": true, + "converge_func": "collect", + "need_biz_converge": true, + "sub_converge_config": { + "count": 2, + "condition": [ + { + "value": [ + "self" + ], + "dimension": "bk_biz_id" + }, + { + "value": [ + "self" + ], + "dimension": "notice_receiver" + }, + { + "value": [ + "self" + ], + "dimension": "notice_way" + }, + { + "value": [ + "self" + ], + "dimension": "alert_level" + }, + { + "value": [ + "self" + ], + "dimension": "signal" + } + ], + "timedelta": 60, + "converge_func": "collect_alarm" + } + }, + "exclude_notice_ways": { + "closed": [], + "recovered": [] + }, + "noise_reduce_config": { + "unit": "percent", + "count": 10, + "timedelta": 5, + "dimensions": [], + "is_enabled": false + } + }, + "config_id": 8834, + "relate_type": "NOTICE", + "user_groups": [ + 0 + ] + }, + "source": "bkmonitorv3", + "actions": [], + "detects": [ + { + "level": 1, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 2, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + }, + { + "level": 2, + "connector": "and", + "expression": "", + "trigger_config": { + "count": 2, + "uptime": { + "calendars": [], + "time_ranges": [ + { + "end": "23:59", + "start": "00:00" + } + ] + }, + "check_window": 5 + }, + "recovery_config": { + "check_window": 5, + "status_setter": "recovery" + } + } + ], + "scenario": "os", + "bk_biz_id": 2005000194, + "data_source_type": "Prometheus" + }, + "is_enabled": true +} diff --git a/dbm-ui/backend/db_monitor/tpls/collect/0.es.dbm_elasticsearch_exporter.json b/dbm-ui/backend/db_monitor/tpls/collect/0.es.dbm_elasticsearch_exporter.json new file mode 100644 index 0000000000..770ace9e71 --- /dev/null +++ b/dbm-ui/backend/db_monitor/tpls/collect/0.es.dbm_elasticsearch_exporter.json @@ -0,0 +1,5222 @@ +{ + "bk_biz_id": 0, + "plugin_id": "dbm_elasticsearch_exporter", + "db_type": "es", + "details": { + "name": "dbm_elasticsearch_exporter", + "label": "component", + "params": { + "plugin": { + "--es.all": "", + "--es.uri": "http://{{ target.host.bk_host_innerip }}:{{ target.process[\"java\"].bind_info[0].port }}", + "--es.shards": "", + "--es.indices": "", + "--es.timeout": "30s", + "--web.listen-address": "${host}:${port}", + "服务实例维度注入": { + "app": "app", + "instance": "instance", + "cluster_name": "cluster_name", + "cluster_type": "cluster_type", + "instance_host": "instance_host", + "instance_role": "instance_role", + "cluster_domain": "cluster_domain" + } + }, + "collector": { + "host": "127.0.0.1", + "port": "9527", + "period": 60, + "timeout": 60 + }, + "target_node_type": "TOPO", + "target_object_type": "SERVICE" + }, + "plugin_info": { + "plugin_id": "dbm_elasticsearch_exporter", + "metric_json": [ + { + "fields": [ + { + "name": "elasticsearch_cluster_health_json_parse_failures", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_cluster_health_total_scrapes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_cluster_health_up", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_json_parse_failures", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_total_scrapes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_up", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_node_shards_json_parse_failures", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_node_stats_json_parse_failures", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_node_stats_total_scrapes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_node_stats_up", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_gc_duration_seconds_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_gc_duration_seconds_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_goroutines", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_alloc_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_alloc_bytes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_buck_hash_sys_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_frees_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_gc_sys_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_heap_alloc_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_heap_idle_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_heap_inuse_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_heap_objects", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_heap_released_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_heap_sys_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_last_gc_time_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_lookups_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_mallocs_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_mcache_inuse_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_mcache_sys_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_mspan_inuse_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_mspan_sys_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_next_gc_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_other_sys_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_stack_inuse_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_stack_sys_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_sys_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_threads", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_cpu_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_max_fds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_open_fds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_resident_memory_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_start_time_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_virtual_memory_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_virtual_memory_max_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "promhttp_metric_handler_requests_in_flight", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_elasticsearch_exporter.Group1", + "table_desc": "分组1", + "table_name": "Group1" + }, + { + "fields": [ + { + "name": "__name", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "name", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "elasticsearch_filesystem_data_available_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_data_node", + "mount", + "es_ingest_node", + "es_master_node", + "es_client_node", + "host", + "path", + "name", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_filesystem_data_free_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "name", + "es_master_node", + "path", + "cluster", + "host", + "mount", + "es_client_node", + "es_ingest_node", + "es_data_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_filesystem_data_size_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_ingest_node", + "es_master_node", + "host", + "path", + "cluster", + "es_data_node", + "es_client_node", + "mount", + "name" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_completion_size_in_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_master_node", + "host", + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_docs", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_docs_deleted", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_master_node", + "host", + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_fielddata_evictions", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_master_node", + "host", + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_fielddata_memory_size_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_filter_cache_evictions", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_filter_cache_memory_size_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_flush_time_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_flush_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_master_node", + "host", + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_get_exists_time_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name", + "cluster", + "es_client_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_get_exists_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_get_missing_time_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_master_node", + "host", + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_get_missing_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_get_time_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_get_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_indexing_delete_time_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_master_node", + "host", + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_indexing_delete_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_indexing_index_time_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_indexing_index_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_indexing_is_throttled", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_indexing_throttle_time_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_merges_current", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_merges_current_size_in_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_merges_docs_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_merges_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name", + "cluster", + "es_client_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_merges_total_size_bytes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_merges_total_throttled_time_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_merges_total_time_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_query_cache_cache_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name", + "cluster", + "es_client_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_query_cache_cache_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_query_cache_evictions", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_master_node", + "host", + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_query_cache_memory_size_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_query_cache_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_refresh_time_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name", + "cluster", + "es_client_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_refresh_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_request_cache_evictions", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_request_cache_memory_size_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_ingest_node", + "es_master_node", + "host", + "name", + "cluster", + "es_client_node", + "es_data_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_search_fetch_time_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_search_fetch_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_search_query_time_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_search_query_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_ingest_node", + "es_master_node", + "host", + "name", + "cluster", + "es_client_node", + "es_data_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_search_scroll_time_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_search_scroll_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_search_suggest_time_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_search_suggest_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_segments_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_segments_doc_values_memory_in_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_ingest_node", + "es_master_node", + "host", + "name", + "cluster", + "es_client_node", + "es_data_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_segments_fixed_bit_set_memory_in_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_segments_index_writer_memory_in_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_segments_memory_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_segments_norms_memory_in_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_master_node", + "host", + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_segments_points_memory_in_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_segments_stored_fields_memory_in_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name", + "cluster", + "es_client_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_segments_term_vectors_memory_in_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_master_node", + "host", + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_segments_terms_memory_in_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_segments_version_map_memory_in_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_store_size_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_store_throttle_time_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_translog_operations", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_master_node", + "host", + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_translog_size_in_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_warmer_time_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_warmer_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_os_cpu_percent", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_os_load1", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_os_load15", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_os_load5", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_os_mem_actual_free_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_os_mem_actual_used_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_os_mem_free_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_os_mem_used_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name", + "cluster", + "es_client_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_process_cpu_percent", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name", + "cluster", + "es_client_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_process_cpu_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_process_max_files_descriptors", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_process_mem_resident_size_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name", + "cluster", + "es_client_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_process_mem_share_size_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_ingest_node", + "es_master_node", + "host", + "name", + "cluster", + "es_client_node", + "es_data_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_process_mem_virtual_size_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name", + "cluster", + "es_client_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_process_open_files_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name", + "cluster", + "es_client_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_transport_rx_packets_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_ingest_node", + "es_master_node", + "host", + "name", + "cluster", + "es_client_node", + "es_data_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_transport_rx_size_bytes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_transport_tx_packets_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name", + "cluster", + "es_client_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_transport_tx_size_bytes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "name", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_cluster_health_active_primary_shards", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_cluster_health_active_shards", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_cluster_health_delayed_unassigned_shards", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_cluster_health_initializing_shards", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_cluster_health_number_of_data_nodes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_cluster_health_number_of_in_flight_fetch", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_cluster_health_number_of_nodes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_cluster_health_number_of_pending_tasks", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_cluster_health_relocating_shards", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_cluster_health_task_max_waiting_in_queue_millis", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_cluster_health_unassigned_shards", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_node_shards_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "es_data_node", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "mount", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "es_ingest_node", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "es_master_node", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "es_client_node", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "host", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "path", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "cluster", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "node", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_elasticsearch_exporter.Group2", + "table_desc": "分组2", + "table_name": "Group2" + }, + { + "fields": [ + { + "name": "__name", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "name", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "elasticsearch_breakers_estimated_size_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_master_node", + "name", + "es_data_node", + "host", + "breaker", + "cluster", + "es_client_node", + "es_ingest_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_breakers_limit_size_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "breaker", + "es_ingest_node", + "es_master_node", + "host", + "cluster", + "es_client_node", + "es_data_node", + "name" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_breakers_overhead", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "name", + "es_master_node", + "breaker", + "cluster", + "es_ingest_node", + "es_client_node", + "es_data_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_breakers_tripped", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_master_node", + "host", + "name", + "breaker", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "es_master_node", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "es_data_node", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "host", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "breaker", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "cluster", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "es_client_node", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "es_ingest_node", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_elasticsearch_exporter.Group3", + "table_desc": "分组3", + "table_name": "Group3" + }, + { + "fields": [ + { + "name": "__name", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "name", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "elasticsearch_filesystem_io_stats_device_operations_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_master_node", + "host", + "name", + "device", + "es_data_node", + "es_ingest_node", + "cluster", + "es_client_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_filesystem_io_stats_device_read_operations_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "name", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "cluster", + "device" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_filesystem_io_stats_device_read_size_kilobytes_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_data_node", + "host", + "device", + "es_ingest_node", + "es_master_node", + "name", + "cluster", + "es_client_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_filesystem_io_stats_device_write_operations_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "name", + "es_master_node", + "cluster", + "es_client_node", + "es_data_node", + "es_ingest_node", + "device", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_filesystem_io_stats_device_write_size_kilobytes_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_master_node", + "host", + "name", + "cluster", + "device", + "es_client_node", + "es_data_node", + "es_ingest_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "es_master_node", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "host", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "device", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "es_data_node", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "es_ingest_node", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "cluster", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "es_client_node", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_elasticsearch_exporter.Group4", + "table_desc": "分组4", + "table_name": "Group4" + }, + { + "fields": [ + { + "name": "__name", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "name", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_query_cache_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_client_node", + "es_ingest_node", + "es_master_node", + "name", + "cache", + "cluster", + "host", + "es_data_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_query_miss_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "cluster", + "es_data_node", + "es_ingest_node", + "cache", + "es_client_node", + "es_master_node", + "name" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_request_cache_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "name", + "cache", + "es_ingest_node", + "cluster", + "es_client_node", + "es_data_node", + "es_master_node", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_request_miss_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cache", + "es_client_node", + "es_data_node", + "es_master_node", + "host", + "name", + "cluster", + "es_ingest_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "es_client_node", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "es_ingest_node", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "es_master_node", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "cache", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "cluster", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "host", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "es_data_node", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_elasticsearch_exporter.Group5", + "table_desc": "分组5", + "table_name": "Group5" + }, + { + "fields": [ + { + "name": "__name", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "name", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "elasticsearch_jvm_buffer_pool_used_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "type", + "cluster", + "es_data_node", + "host", + "name", + "es_client_node", + "es_ingest_node", + "es_master_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_jvm_uptime_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_master_node", + "name", + "es_client_node", + "es_ingest_node", + "host", + "type", + "cluster", + "es_data_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_thread_pool_active_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "name", + "type", + "es_client_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_thread_pool_completed_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_data_node", + "es_ingest_node", + "host", + "name", + "es_client_node", + "es_master_node", + "type", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_thread_pool_largest_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "name", + "type", + "es_ingest_node", + "cluster", + "es_client_node", + "es_data_node", + "es_master_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_thread_pool_queue_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "es_client_node", + "es_master_node", + "type", + "es_data_node", + "es_ingest_node", + "host", + "name" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_thread_pool_rejected_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_client_node", + "es_data_node", + "name", + "cluster", + "es_ingest_node", + "es_master_node", + "host", + "type" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_thread_pool_threads_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_master_node", + "cluster", + "name", + "type", + "es_data_node", + "host", + "es_client_node", + "es_ingest_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "type", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "cluster", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "es_data_node", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "host", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "es_client_node", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "es_ingest_node", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "es_master_node", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_elasticsearch_exporter.Group6", + "table_desc": "分组6", + "table_name": "Group6" + }, + { + "fields": [ + { + "name": "__name", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "name", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "elasticsearch_jvm_gc_collection_seconds_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_ingest_node", + "gc", + "host", + "name", + "es_data_node", + "es_master_node", + "cluster", + "es_client_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_jvm_gc_collection_seconds_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "es_data_node", + "es_master_node", + "name", + "cluster", + "es_client_node", + "es_ingest_node", + "gc" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "es_ingest_node", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "gc", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "host", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "es_data_node", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "es_master_node", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "cluster", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "es_client_node", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_elasticsearch_exporter.Group7", + "table_desc": "分组7", + "table_name": "Group7" + }, + { + "fields": [ + { + "name": "__name", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "name", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "elasticsearch_jvm_memory_committed_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "name", + "es_master_node", + "host", + "area", + "cluster", + "es_ingest_node", + "es_client_node", + "es_data_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_jvm_memory_max_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "area", + "cluster", + "es_data_node", + "es_master_node", + "es_client_node", + "es_ingest_node", + "host", + "name" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_jvm_memory_used_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "name", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node", + "host", + "area" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "es_master_node", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "host", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "area", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "cluster", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "es_ingest_node", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "es_client_node", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "es_data_node", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_elasticsearch_exporter.Group8", + "table_desc": "分组8", + "table_name": "Group8" + }, + { + "fields": [ + { + "name": "__name", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "name", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "elasticsearch_jvm_memory_pool_max_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "name", + "cluster", + "pool", + "es_client_node", + "es_data_node", + "es_ingest_node", + "es_master_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_jvm_memory_pool_peak_max_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "es_ingest_node", + "es_data_node", + "es_master_node", + "host", + "name", + "pool", + "cluster", + "es_client_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_jvm_memory_pool_peak_used_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "name", + "es_data_node", + "es_ingest_node", + "pool", + "cluster", + "es_client_node", + "es_master_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_jvm_memory_pool_used_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "name", + "cluster", + "es_data_node", + "pool", + "es_client_node", + "es_ingest_node", + "es_master_node" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "host", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "cluster", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "pool", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "es_client_node", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "es_data_node", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "es_ingest_node", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "es_master_node", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_elasticsearch_exporter.Group9", + "table_desc": "分组9", + "table_name": "Group9" + }, + { + "fields": [ + { + "name": "elasticsearch_clusterinfo_version_info", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "version", + "build_date", + "build_hash", + "cluster", + "cluster_uuid", + "lucene_version" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_version", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "lucene_version", + "version", + "build_date", + "build_hash", + "cluster", + "cluster_uuid" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_info", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "version" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "version", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "build_date", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "build_hash", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "cluster", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "cluster_uuid", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "lucene_version", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_elasticsearch_exporter.Group10", + "table_desc": "分组10", + "table_name": "Group10" + }, + { + "fields": [ + { + "name": "_shard", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "shard", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_shards_docs", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index", + "node", + "primary", + "shard" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_shards_docs_deleted", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "node", + "primary", + "shard", + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_shards_store_size_in_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "node", + "primary", + "shard", + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_fielddata_evictions_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "index", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_fielddata_memory_bytes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "index", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_flush_time_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "index", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_flush_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "index", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_get_time_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_get_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "index", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_index_current", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_indexing_delete_time_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_indexing_delete_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_indexing_index_time_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_indexing_index_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_indexing_noop_update_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_indexing_throttle_time_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_merge_auto_throttle_bytes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_merge_stopped_time_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "index", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_merge_throttle_time_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "index", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_merge_time_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_merge_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_query_cache_caches_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_query_cache_evictions_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "index", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_query_cache_hits_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_query_cache_memory_bytes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "index", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_query_cache_misses_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_query_cache_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_refresh_time_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_refresh_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_request_cache_evictions_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_request_cache_hits_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "index", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_request_cache_memory_bytes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_request_cache_misses_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "index", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_search_fetch_time_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_search_fetch_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "index", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_search_query_time_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "index", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_search_query_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "index", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_search_scroll_current", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_search_scroll_time_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_search_scroll_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_search_suggest_time_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "index", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_search_suggest_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_warmer_time_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_index_stats_warmer_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_completion_bytes_primary", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_completion_bytes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "index", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_deleted_docs_primary", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "index", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_deleted_docs_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_docs_primary", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_docs_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "index", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_segment_count_primary", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "index", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_segment_count_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_segment_doc_values_memory_bytes_primary", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_segment_doc_values_memory_bytes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_segment_fields_memory_bytes_primary", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_segment_fields_memory_bytes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_segment_fixed_bit_set_memory_bytes_primary", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "index", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_segment_fixed_bit_set_memory_bytes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_segment_index_writer_memory_bytes_primary", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "index", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_segment_index_writer_memory_bytes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "index", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_segment_memory_bytes_primary", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_segment_memory_bytes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_segment_norms_memory_bytes_primary", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "index", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_segment_norms_memory_bytes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "index", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_segment_points_memory_bytes_primary", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_segment_points_memory_bytes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_segment_term_vectors_memory_primary_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_segment_term_vectors_memory_total_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_segment_terms_memory_primary", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_segment_terms_memory_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "index", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_segment_version_map_memory_bytes_primary", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_segment_version_map_memory_bytes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_store_size_bytes_primary", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "index", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_indices_store_size_bytes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_search_active_queries", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "index" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "cluster", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "index", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "node", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "primary", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_elasticsearch_exporter.Group11", + "table_desc": "分组11", + "table_name": "Group11" + }, + { + "fields": [ + { + "name": "elasticsearch_exporter_build_info", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "version", + "branch", + "goversion", + "revision" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "version", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "branch", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "goversion", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "revision", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_elasticsearch_exporter.Group12", + "table_desc": "分组12", + "table_name": "Group12" + }, + { + "fields": [ + { + "name": "__name", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "name", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "elasticsearch_nodes_roles", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "name", + "role", + "cluster", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "role", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "cluster", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "host", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_elasticsearch_exporter.Group13", + "table_desc": "分组13", + "table_name": "Group13" + }, + { + "fields": [ + { + "name": "elasticsearch_indices_aliases", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "index", + "alias", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "index", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "alias", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "cluster", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_elasticsearch_exporter.Group14", + "table_desc": "分组14", + "table_name": "Group14" + }, + { + "fields": [ + { + "name": "elasticsearch_cluster_health_status", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "color" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "cluster", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "color", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_elasticsearch_exporter.Group15", + "table_desc": "分组15", + "table_name": "Group15" + }, + { + "fields": [ + { + "name": "elasticsearch_clusterinfo_last_retrieval_success_ts", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "url" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_clusterinfo_up", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "url" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "url", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_elasticsearch_exporter.Group16", + "table_desc": "分组16", + "table_name": "Group16" + }, + { + "fields": [ + { + "name": "elasticsearch_scrape_duration_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "collector" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "elasticsearch_scrape_success", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "collector" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "collector", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_elasticsearch_exporter.Group17", + "table_desc": "分组17", + "table_name": "Group17" + }, + { + "fields": [ + { + "name": "go_gc_duration_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "quantile" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "quantile", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_elasticsearch_exporter.Group18", + "table_desc": "分组18", + "table_name": "Group18" + }, + { + "fields": [ + { + "name": "promhttp_metric_handler_requests_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "code" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "code", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_elasticsearch_exporter.Group19", + "table_desc": "分组19", + "table_name": "Group19" + } + ], + "plugin_type": "Exporter", + "os_type_list": [ + "linux" + ] + }, + "collect_type": "Exporter", + "target_nodes": [], + "target_node_type": "TOPO", + "target_object_type": "SERVICE" + } +} diff --git a/dbm-ui/backend/db_monitor/tpls/collect/0.es.dbm_elasticsearch_exporter.tpl64 b/dbm-ui/backend/db_monitor/tpls/collect/0.es.dbm_elasticsearch_exporter.tpl64 deleted file mode 100644 index c826fa7ce5..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/collect/0.es.dbm_elasticsearch_exporter.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAicGx1Z2luX2lkIjogImRibV9lbGFzdGljc2VhcmNoX2V4cG9ydGVyIiwgImRiX3R5cGUiOiAiZXMiLCAiZGV0YWlscyI6IHsibmFtZSI6ICJkYm1fZWxhc3RpY3NlYXJjaF9leHBvcnRlciIsICJsYWJlbCI6ICJjb21wb25lbnQiLCAicGFyYW1zIjogeyJwbHVnaW4iOiB7Ii0tZXMuYWxsIjogIiIsICItLWVzLnVyaSI6ICJodHRwOi8ve3sgdGFyZ2V0Lmhvc3QuYmtfaG9zdF9pbm5lcmlwIH19Ont7IHRhcmdldC5wcm9jZXNzW1wiamF2YVwiXS5iaW5kX2luZm9bMF0ucG9ydCB9fSIsICItLWVzLnNoYXJkcyI6ICIiLCAiLS1lcy5pbmRpY2VzIjogIiIsICItLWVzLnRpbWVvdXQiOiAiMzBzIiwgIi0td2ViLmxpc3Rlbi1hZGRyZXNzIjogIiR7aG9zdH06JHtwb3J0fSIsICJcdTY3MGRcdTUyYTFcdTViOWVcdTRmOGJcdTdlZjRcdTVlYTZcdTZjZThcdTUxNjUiOiB7ImFwcCI6ICJhcHAiLCAiaW5zdGFuY2UiOiAiaW5zdGFuY2UiLCAiY2x1c3Rlcl9uYW1lIjogImNsdXN0ZXJfbmFtZSIsICJjbHVzdGVyX3R5cGUiOiAiY2x1c3Rlcl90eXBlIiwgImluc3RhbmNlX2hvc3QiOiAiaW5zdGFuY2VfaG9zdCIsICJpbnN0YW5jZV9yb2xlIjogImluc3RhbmNlX3JvbGUiLCAiY2x1c3Rlcl9kb21haW4iOiAiY2x1c3Rlcl9kb21haW4ifX0sICJjb2xsZWN0b3IiOiB7Imhvc3QiOiAiMTI3LjAuMC4xIiwgInBvcnQiOiAiOTUyNyIsICJwZXJpb2QiOiA2MCwgInRpbWVvdXQiOiA2MH0sICJ0YXJnZXRfbm9kZV90eXBlIjogIlRPUE8iLCAidGFyZ2V0X29iamVjdF90eXBlIjogIlNFUlZJQ0UifSwgInBsdWdpbl9pbmZvIjogeyJwbHVnaW5faWQiOiAiZGJtX2VsYXN0aWNzZWFyY2hfZXhwb3J0ZXIiLCAibWV0cmljX2pzb24iOiBbeyJmaWVsZHMiOiBbeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfY2x1c3Rlcl9oZWFsdGhfanNvbl9wYXJzZV9mYWlsdXJlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfY2x1c3Rlcl9oZWFsdGhfdG90YWxfc2NyYXBlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfY2x1c3Rlcl9oZWFsdGhfdXAiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGV4X3N0YXRzX2pzb25fcGFyc2VfZmFpbHVyZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGV4X3N0YXRzX3RvdGFsX3NjcmFwZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGV4X3N0YXRzX3VwIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9ub2RlX3NoYXJkc19qc29uX3BhcnNlX2ZhaWx1cmVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9ub2RlX3N0YXRzX2pzb25fcGFyc2VfZmFpbHVyZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX25vZGVfc3RhdHNfdG90YWxfc2NyYXBlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfbm9kZV9zdGF0c191cCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX2djX2R1cmF0aW9uX3NlY29uZHNfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fZ2NfZHVyYXRpb25fc2Vjb25kc19jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX2dvcm91dGluZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJnb19tZW1zdGF0c19hbGxvY19ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX2FsbG9jX2J5dGVzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fbWVtc3RhdHNfYnVja19oYXNoX3N5c19ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX2ZyZWVzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fbWVtc3RhdHNfZ2Nfc3lzX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fbWVtc3RhdHNfaGVhcF9hbGxvY19ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX2hlYXBfaWRsZV9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX2hlYXBfaW51c2VfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJnb19tZW1zdGF0c19oZWFwX29iamVjdHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJnb19tZW1zdGF0c19oZWFwX3JlbGVhc2VkX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fbWVtc3RhdHNfaGVhcF9zeXNfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJnb19tZW1zdGF0c19sYXN0X2djX3RpbWVfc2Vjb25kcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX2xvb2t1cHNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJnb19tZW1zdGF0c19tYWxsb2NzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fbWVtc3RhdHNfbWNhY2hlX2ludXNlX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fbWVtc3RhdHNfbWNhY2hlX3N5c19ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX21zcGFuX2ludXNlX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fbWVtc3RhdHNfbXNwYW5fc3lzX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fbWVtc3RhdHNfbmV4dF9nY19ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX290aGVyX3N5c19ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX3N0YWNrX2ludXNlX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fbWVtc3RhdHNfc3RhY2tfc3lzX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fbWVtc3RhdHNfc3lzX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fdGhyZWFkcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb2Nlc3NfY3B1X3NlY29uZHNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jZXNzX21heF9mZHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jZXNzX29wZW5fZmRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY2Vzc19yZXNpZGVudF9tZW1vcnlfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jZXNzX3N0YXJ0X3RpbWVfc2Vjb25kcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb2Nlc3NfdmlydHVhbF9tZW1vcnlfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jZXNzX3ZpcnR1YWxfbWVtb3J5X21heF9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb21odHRwX21ldHJpY19oYW5kbGVyX3JlcXVlc3RzX2luX2ZsaWdodCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfV0sICJ0YWJsZV9pZCI6ICJleHBvcnRlcl9kYm1fZWxhc3RpY3NlYXJjaF9leHBvcnRlci5Hcm91cDEiLCAidGFibGVfZGVzYyI6ICJcdTUyMDZcdTdlYzQxIiwgInRhYmxlX25hbWUiOiAiR3JvdXAxIn0sIHsiZmllbGRzIjogW3sibmFtZSI6ICJfX25hbWUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAibmFtZSIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9maWxlc3lzdGVtX2RhdGFfYXZhaWxhYmxlX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZXNfZGF0YV9ub2RlIiwgIm1vdW50IiwgImVzX2luZ2VzdF9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIiwgImVzX2NsaWVudF9ub2RlIiwgImhvc3QiLCAicGF0aCIsICJuYW1lIiwgImNsdXN0ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfZmlsZXN5c3RlbV9kYXRhX2ZyZWVfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJuYW1lIiwgImVzX21hc3Rlcl9ub2RlIiwgInBhdGgiLCAiY2x1c3RlciIsICJob3N0IiwgIm1vdW50IiwgImVzX2NsaWVudF9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIiwgImVzX2RhdGFfbm9kZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9maWxlc3lzdGVtX2RhdGFfc2l6ZV9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImVzX2luZ2VzdF9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIiwgImhvc3QiLCAicGF0aCIsICJjbHVzdGVyIiwgImVzX2RhdGFfbm9kZSIsICJlc19jbGllbnRfbm9kZSIsICJtb3VudCIsICJuYW1lIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGljZXNfY29tcGxldGlvbl9zaXplX2luX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZXNfbWFzdGVyX25vZGUiLCAiaG9zdCIsICJuYW1lIiwgImNsdXN0ZXIiLCAiZXNfY2xpZW50X25vZGUiLCAiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGljZXNfZG9jcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImVzX2NsaWVudF9ub2RlIiwgImVzX2RhdGFfbm9kZSIsICJlc19pbmdlc3Rfbm9kZSIsICJlc19tYXN0ZXJfbm9kZSIsICJob3N0IiwgIm5hbWUiLCAiY2x1c3RlciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRpY2VzX2RvY3NfZGVsZXRlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImVzX21hc3Rlcl9ub2RlIiwgImhvc3QiLCAibmFtZSIsICJjbHVzdGVyIiwgImVzX2NsaWVudF9ub2RlIiwgImVzX2RhdGFfbm9kZSIsICJlc19pbmdlc3Rfbm9kZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRpY2VzX2ZpZWxkZGF0YV9ldmljdGlvbnMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJlc19tYXN0ZXJfbm9kZSIsICJob3N0IiwgIm5hbWUiLCAiY2x1c3RlciIsICJlc19jbGllbnRfbm9kZSIsICJlc19kYXRhX25vZGUiLCAiZXNfaW5nZXN0X25vZGUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19maWVsZGRhdGFfbWVtb3J5X3NpemVfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJuYW1lIiwgImNsdXN0ZXIiLCAiZXNfY2xpZW50X25vZGUiLCAiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIiwgImhvc3QiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19maWx0ZXJfY2FjaGVfZXZpY3Rpb25zIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJlc19jbGllbnRfbm9kZSIsICJlc19kYXRhX25vZGUiLCAiZXNfaW5nZXN0X25vZGUiLCAiZXNfbWFzdGVyX25vZGUiLCAiaG9zdCIsICJuYW1lIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGljZXNfZmlsdGVyX2NhY2hlX21lbW9yeV9zaXplX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJlc19jbGllbnRfbm9kZSIsICJlc19kYXRhX25vZGUiLCAiZXNfaW5nZXN0X25vZGUiLCAiZXNfbWFzdGVyX25vZGUiLCAiaG9zdCIsICJuYW1lIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGljZXNfZmx1c2hfdGltZV9zZWNvbmRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaG9zdCIsICJuYW1lIiwgImNsdXN0ZXIiLCAiZXNfY2xpZW50X25vZGUiLCAiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGljZXNfZmx1c2hfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJlc19tYXN0ZXJfbm9kZSIsICJob3N0IiwgIm5hbWUiLCAiY2x1c3RlciIsICJlc19jbGllbnRfbm9kZSIsICJlc19kYXRhX25vZGUiLCAiZXNfaW5nZXN0X25vZGUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19nZXRfZXhpc3RzX3RpbWVfc2Vjb25kcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImVzX2RhdGFfbm9kZSIsICJlc19pbmdlc3Rfbm9kZSIsICJlc19tYXN0ZXJfbm9kZSIsICJob3N0IiwgIm5hbWUiLCAiY2x1c3RlciIsICJlc19jbGllbnRfbm9kZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRpY2VzX2dldF9leGlzdHNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjbHVzdGVyIiwgImVzX2NsaWVudF9ub2RlIiwgImVzX2RhdGFfbm9kZSIsICJlc19pbmdlc3Rfbm9kZSIsICJlc19tYXN0ZXJfbm9kZSIsICJob3N0IiwgIm5hbWUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19nZXRfbWlzc2luZ190aW1lX3NlY29uZHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJlc19tYXN0ZXJfbm9kZSIsICJob3N0IiwgIm5hbWUiLCAiY2x1c3RlciIsICJlc19jbGllbnRfbm9kZSIsICJlc19kYXRhX25vZGUiLCAiZXNfaW5nZXN0X25vZGUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19nZXRfbWlzc2luZ190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbIm5hbWUiLCAiY2x1c3RlciIsICJlc19jbGllbnRfbm9kZSIsICJlc19kYXRhX25vZGUiLCAiZXNfaW5nZXN0X25vZGUiLCAiZXNfbWFzdGVyX25vZGUiLCAiaG9zdCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRpY2VzX2dldF90aW1lX3NlY29uZHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJuYW1lIiwgImNsdXN0ZXIiLCAiZXNfY2xpZW50X25vZGUiLCAiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIiwgImhvc3QiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19nZXRfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJuYW1lIiwgImNsdXN0ZXIiLCAiZXNfY2xpZW50X25vZGUiLCAiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIiwgImhvc3QiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19pbmRleGluZ19kZWxldGVfdGltZV9zZWNvbmRzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZXNfbWFzdGVyX25vZGUiLCAiaG9zdCIsICJuYW1lIiwgImNsdXN0ZXIiLCAiZXNfY2xpZW50X25vZGUiLCAiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGljZXNfaW5kZXhpbmdfZGVsZXRlX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJlc19jbGllbnRfbm9kZSIsICJlc19kYXRhX25vZGUiLCAiZXNfaW5nZXN0X25vZGUiLCAiZXNfbWFzdGVyX25vZGUiLCAiaG9zdCIsICJuYW1lIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGljZXNfaW5kZXhpbmdfaW5kZXhfdGltZV9zZWNvbmRzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZXNfY2xpZW50X25vZGUiLCAiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIiwgImhvc3QiLCAibmFtZSIsICJjbHVzdGVyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGljZXNfaW5kZXhpbmdfaW5kZXhfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJlc19jbGllbnRfbm9kZSIsICJlc19kYXRhX25vZGUiLCAiZXNfaW5nZXN0X25vZGUiLCAiZXNfbWFzdGVyX25vZGUiLCAiaG9zdCIsICJuYW1lIiwgImNsdXN0ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19pbmRleGluZ19pc190aHJvdHRsZWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJuYW1lIiwgImNsdXN0ZXIiLCAiZXNfY2xpZW50X25vZGUiLCAiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIiwgImhvc3QiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19pbmRleGluZ190aHJvdHRsZV90aW1lX3NlY29uZHNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJlc19jbGllbnRfbm9kZSIsICJlc19kYXRhX25vZGUiLCAiZXNfaW5nZXN0X25vZGUiLCAiZXNfbWFzdGVyX25vZGUiLCAiaG9zdCIsICJuYW1lIiwgImNsdXN0ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19tZXJnZXNfY3VycmVudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImhvc3QiLCAibmFtZSIsICJjbHVzdGVyIiwgImVzX2NsaWVudF9ub2RlIiwgImVzX2RhdGFfbm9kZSIsICJlc19pbmdlc3Rfbm9kZSIsICJlc19tYXN0ZXJfbm9kZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRpY2VzX21lcmdlc19jdXJyZW50X3NpemVfaW5fYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJuYW1lIiwgImNsdXN0ZXIiLCAiZXNfY2xpZW50X25vZGUiLCAiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIiwgImhvc3QiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19tZXJnZXNfZG9jc190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbIm5hbWUiLCAiY2x1c3RlciIsICJlc19jbGllbnRfbm9kZSIsICJlc19kYXRhX25vZGUiLCAiZXNfaW5nZXN0X25vZGUiLCAiZXNfbWFzdGVyX25vZGUiLCAiaG9zdCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRpY2VzX21lcmdlc190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImVzX2RhdGFfbm9kZSIsICJlc19pbmdlc3Rfbm9kZSIsICJlc19tYXN0ZXJfbm9kZSIsICJob3N0IiwgIm5hbWUiLCAiY2x1c3RlciIsICJlc19jbGllbnRfbm9kZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRpY2VzX21lcmdlc190b3RhbF9zaXplX2J5dGVzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZXNfY2xpZW50X25vZGUiLCAiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIiwgImhvc3QiLCAibmFtZSIsICJjbHVzdGVyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGljZXNfbWVyZ2VzX3RvdGFsX3Rocm90dGxlZF90aW1lX3NlY29uZHNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJuYW1lIiwgImNsdXN0ZXIiLCAiZXNfY2xpZW50X25vZGUiLCAiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIiwgImhvc3QiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19tZXJnZXNfdG90YWxfdGltZV9zZWNvbmRzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaG9zdCIsICJuYW1lIiwgImNsdXN0ZXIiLCAiZXNfY2xpZW50X25vZGUiLCAiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGljZXNfcXVlcnlfY2FjaGVfY2FjaGVfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImVzX2RhdGFfbm9kZSIsICJlc19pbmdlc3Rfbm9kZSIsICJlc19tYXN0ZXJfbm9kZSIsICJob3N0IiwgIm5hbWUiLCAiY2x1c3RlciIsICJlc19jbGllbnRfbm9kZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRpY2VzX3F1ZXJ5X2NhY2hlX2NhY2hlX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZXNfY2xpZW50X25vZGUiLCAiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIiwgImhvc3QiLCAibmFtZSIsICJjbHVzdGVyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGljZXNfcXVlcnlfY2FjaGVfZXZpY3Rpb25zIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZXNfbWFzdGVyX25vZGUiLCAiaG9zdCIsICJuYW1lIiwgImNsdXN0ZXIiLCAiZXNfY2xpZW50X25vZGUiLCAiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGljZXNfcXVlcnlfY2FjaGVfbWVtb3J5X3NpemVfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjbHVzdGVyIiwgImVzX2NsaWVudF9ub2RlIiwgImVzX2RhdGFfbm9kZSIsICJlc19pbmdlc3Rfbm9kZSIsICJlc19tYXN0ZXJfbm9kZSIsICJob3N0IiwgIm5hbWUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19xdWVyeV9jYWNoZV90b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImhvc3QiLCAibmFtZSIsICJjbHVzdGVyIiwgImVzX2NsaWVudF9ub2RlIiwgImVzX2RhdGFfbm9kZSIsICJlc19pbmdlc3Rfbm9kZSIsICJlc19tYXN0ZXJfbm9kZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRpY2VzX3JlZnJlc2hfdGltZV9zZWNvbmRzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIiwgImhvc3QiLCAibmFtZSIsICJjbHVzdGVyIiwgImVzX2NsaWVudF9ub2RlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGljZXNfcmVmcmVzaF90b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbIm5hbWUiLCAiY2x1c3RlciIsICJlc19jbGllbnRfbm9kZSIsICJlc19kYXRhX25vZGUiLCAiZXNfaW5nZXN0X25vZGUiLCAiZXNfbWFzdGVyX25vZGUiLCAiaG9zdCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRpY2VzX3JlcXVlc3RfY2FjaGVfZXZpY3Rpb25zIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaG9zdCIsICJuYW1lIiwgImNsdXN0ZXIiLCAiZXNfY2xpZW50X25vZGUiLCAiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGljZXNfcmVxdWVzdF9jYWNoZV9tZW1vcnlfc2l6ZV9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImVzX2luZ2VzdF9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIiwgImhvc3QiLCAibmFtZSIsICJjbHVzdGVyIiwgImVzX2NsaWVudF9ub2RlIiwgImVzX2RhdGFfbm9kZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRpY2VzX3NlYXJjaF9mZXRjaF90aW1lX3NlY29uZHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJlc19jbGllbnRfbm9kZSIsICJlc19kYXRhX25vZGUiLCAiZXNfaW5nZXN0X25vZGUiLCAiZXNfbWFzdGVyX25vZGUiLCAiaG9zdCIsICJuYW1lIiwgImNsdXN0ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19zZWFyY2hfZmV0Y2hfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJuYW1lIiwgImNsdXN0ZXIiLCAiZXNfY2xpZW50X25vZGUiLCAiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIiwgImhvc3QiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19zZWFyY2hfcXVlcnlfdGltZV9zZWNvbmRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZXNfY2xpZW50X25vZGUiLCAiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIiwgImhvc3QiLCAibmFtZSIsICJjbHVzdGVyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGljZXNfc2VhcmNoX3F1ZXJ5X3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZXNfaW5nZXN0X25vZGUiLCAiZXNfbWFzdGVyX25vZGUiLCAiaG9zdCIsICJuYW1lIiwgImNsdXN0ZXIiLCAiZXNfY2xpZW50X25vZGUiLCAiZXNfZGF0YV9ub2RlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGljZXNfc2VhcmNoX3Njcm9sbF90aW1lX3NlY29uZHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJob3N0IiwgIm5hbWUiLCAiY2x1c3RlciIsICJlc19jbGllbnRfbm9kZSIsICJlc19kYXRhX25vZGUiLCAiZXNfaW5nZXN0X25vZGUiLCAiZXNfbWFzdGVyX25vZGUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19zZWFyY2hfc2Nyb2xsX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJlc19jbGllbnRfbm9kZSIsICJlc19kYXRhX25vZGUiLCAiZXNfaW5nZXN0X25vZGUiLCAiZXNfbWFzdGVyX25vZGUiLCAiaG9zdCIsICJuYW1lIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGljZXNfc2VhcmNoX3N1Z2dlc3RfdGltZV9zZWNvbmRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJlc19jbGllbnRfbm9kZSIsICJlc19kYXRhX25vZGUiLCAiZXNfaW5nZXN0X25vZGUiLCAiZXNfbWFzdGVyX25vZGUiLCAiaG9zdCIsICJuYW1lIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGljZXNfc2VhcmNoX3N1Z2dlc3RfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJob3N0IiwgIm5hbWUiLCAiY2x1c3RlciIsICJlc19jbGllbnRfbm9kZSIsICJlc19kYXRhX25vZGUiLCAiZXNfaW5nZXN0X25vZGUiLCAiZXNfbWFzdGVyX25vZGUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19zZWdtZW50c19jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNsdXN0ZXIiLCAiZXNfY2xpZW50X25vZGUiLCAiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIiwgImhvc3QiLCAibmFtZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRpY2VzX3NlZ21lbnRzX2RvY192YWx1ZXNfbWVtb3J5X2luX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZXNfaW5nZXN0X25vZGUiLCAiZXNfbWFzdGVyX25vZGUiLCAiaG9zdCIsICJuYW1lIiwgImNsdXN0ZXIiLCAiZXNfY2xpZW50X25vZGUiLCAiZXNfZGF0YV9ub2RlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGljZXNfc2VnbWVudHNfZml4ZWRfYml0X3NldF9tZW1vcnlfaW5fYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJuYW1lIiwgImNsdXN0ZXIiLCAiZXNfY2xpZW50X25vZGUiLCAiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIiwgImhvc3QiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19zZWdtZW50c19pbmRleF93cml0ZXJfbWVtb3J5X2luX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsibmFtZSIsICJjbHVzdGVyIiwgImVzX2NsaWVudF9ub2RlIiwgImVzX2RhdGFfbm9kZSIsICJlc19pbmdlc3Rfbm9kZSIsICJlc19tYXN0ZXJfbm9kZSIsICJob3N0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGljZXNfc2VnbWVudHNfbWVtb3J5X2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJlc19jbGllbnRfbm9kZSIsICJlc19kYXRhX25vZGUiLCAiZXNfaW5nZXN0X25vZGUiLCAiZXNfbWFzdGVyX25vZGUiLCAiaG9zdCIsICJuYW1lIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGljZXNfc2VnbWVudHNfbm9ybXNfbWVtb3J5X2luX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZXNfbWFzdGVyX25vZGUiLCAiaG9zdCIsICJuYW1lIiwgImNsdXN0ZXIiLCAiZXNfY2xpZW50X25vZGUiLCAiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGljZXNfc2VnbWVudHNfcG9pbnRzX21lbW9yeV9pbl9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImVzX2NsaWVudF9ub2RlIiwgImVzX2RhdGFfbm9kZSIsICJlc19pbmdlc3Rfbm9kZSIsICJlc19tYXN0ZXJfbm9kZSIsICJob3N0IiwgIm5hbWUiLCAiY2x1c3RlciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRpY2VzX3NlZ21lbnRzX3N0b3JlZF9maWVsZHNfbWVtb3J5X2luX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIiwgImhvc3QiLCAibmFtZSIsICJjbHVzdGVyIiwgImVzX2NsaWVudF9ub2RlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGljZXNfc2VnbWVudHNfdGVybV92ZWN0b3JzX21lbW9yeV9pbl9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImVzX21hc3Rlcl9ub2RlIiwgImhvc3QiLCAibmFtZSIsICJjbHVzdGVyIiwgImVzX2NsaWVudF9ub2RlIiwgImVzX2RhdGFfbm9kZSIsICJlc19pbmdlc3Rfbm9kZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRpY2VzX3NlZ21lbnRzX3Rlcm1zX21lbW9yeV9pbl9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNsdXN0ZXIiLCAiZXNfY2xpZW50X25vZGUiLCAiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIiwgImhvc3QiLCAibmFtZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRpY2VzX3NlZ21lbnRzX3ZlcnNpb25fbWFwX21lbW9yeV9pbl9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImVzX2NsaWVudF9ub2RlIiwgImVzX2RhdGFfbm9kZSIsICJlc19pbmdlc3Rfbm9kZSIsICJlc19tYXN0ZXJfbm9kZSIsICJob3N0IiwgIm5hbWUiLCAiY2x1c3RlciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRpY2VzX3N0b3JlX3NpemVfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjbHVzdGVyIiwgImVzX2NsaWVudF9ub2RlIiwgImVzX2RhdGFfbm9kZSIsICJlc19pbmdlc3Rfbm9kZSIsICJlc19tYXN0ZXJfbm9kZSIsICJob3N0IiwgIm5hbWUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19zdG9yZV90aHJvdHRsZV90aW1lX3NlY29uZHNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJuYW1lIiwgImNsdXN0ZXIiLCAiZXNfY2xpZW50X25vZGUiLCAiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIiwgImhvc3QiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc190cmFuc2xvZ19vcGVyYXRpb25zIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZXNfbWFzdGVyX25vZGUiLCAiaG9zdCIsICJuYW1lIiwgImNsdXN0ZXIiLCAiZXNfY2xpZW50X25vZGUiLCAiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGljZXNfdHJhbnNsb2dfc2l6ZV9pbl9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbIm5hbWUiLCAiY2x1c3RlciIsICJlc19jbGllbnRfbm9kZSIsICJlc19kYXRhX25vZGUiLCAiZXNfaW5nZXN0X25vZGUiLCAiZXNfbWFzdGVyX25vZGUiLCAiaG9zdCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRpY2VzX3dhcm1lcl90aW1lX3NlY29uZHNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJob3N0IiwgIm5hbWUiLCAiY2x1c3RlciIsICJlc19jbGllbnRfbm9kZSIsICJlc19kYXRhX25vZGUiLCAiZXNfaW5nZXN0X25vZGUiLCAiZXNfbWFzdGVyX25vZGUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc193YXJtZXJfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJlc19jbGllbnRfbm9kZSIsICJlc19kYXRhX25vZGUiLCAiZXNfaW5nZXN0X25vZGUiLCAiZXNfbWFzdGVyX25vZGUiLCAiaG9zdCIsICJuYW1lIiwgImNsdXN0ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfb3NfY3B1X3BlcmNlbnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJob3N0IiwgIm5hbWUiLCAiY2x1c3RlciIsICJlc19jbGllbnRfbm9kZSIsICJlc19kYXRhX25vZGUiLCAiZXNfaW5nZXN0X25vZGUiLCAiZXNfbWFzdGVyX25vZGUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfb3NfbG9hZDEiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJlc19jbGllbnRfbm9kZSIsICJlc19kYXRhX25vZGUiLCAiZXNfaW5nZXN0X25vZGUiLCAiZXNfbWFzdGVyX25vZGUiLCAiaG9zdCIsICJuYW1lIiwgImNsdXN0ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfb3NfbG9hZDE1IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJlc19jbGllbnRfbm9kZSIsICJlc19kYXRhX25vZGUiLCAiZXNfaW5nZXN0X25vZGUiLCAiZXNfbWFzdGVyX25vZGUiLCAiaG9zdCIsICJuYW1lIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX29zX2xvYWQ1IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaG9zdCIsICJuYW1lIiwgImNsdXN0ZXIiLCAiZXNfY2xpZW50X25vZGUiLCAiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX29zX21lbV9hY3R1YWxfZnJlZV9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbIm5hbWUiLCAiY2x1c3RlciIsICJlc19jbGllbnRfbm9kZSIsICJlc19kYXRhX25vZGUiLCAiZXNfaW5nZXN0X25vZGUiLCAiZXNfbWFzdGVyX25vZGUiLCAiaG9zdCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9vc19tZW1fYWN0dWFsX3VzZWRfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjbHVzdGVyIiwgImVzX2NsaWVudF9ub2RlIiwgImVzX2RhdGFfbm9kZSIsICJlc19pbmdlc3Rfbm9kZSIsICJlc19tYXN0ZXJfbm9kZSIsICJob3N0IiwgIm5hbWUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfb3NfbWVtX2ZyZWVfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJob3N0IiwgIm5hbWUiLCAiY2x1c3RlciIsICJlc19jbGllbnRfbm9kZSIsICJlc19kYXRhX25vZGUiLCAiZXNfaW5nZXN0X25vZGUiLCAiZXNfbWFzdGVyX25vZGUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfb3NfbWVtX3VzZWRfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJlc19kYXRhX25vZGUiLCAiZXNfaW5nZXN0X25vZGUiLCAiZXNfbWFzdGVyX25vZGUiLCAiaG9zdCIsICJuYW1lIiwgImNsdXN0ZXIiLCAiZXNfY2xpZW50X25vZGUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfcHJvY2Vzc19jcHVfcGVyY2VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImVzX2RhdGFfbm9kZSIsICJlc19pbmdlc3Rfbm9kZSIsICJlc19tYXN0ZXJfbm9kZSIsICJob3N0IiwgIm5hbWUiLCAiY2x1c3RlciIsICJlc19jbGllbnRfbm9kZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9wcm9jZXNzX2NwdV9zZWNvbmRzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsibmFtZSIsICJjbHVzdGVyIiwgImVzX2NsaWVudF9ub2RlIiwgImVzX2RhdGFfbm9kZSIsICJlc19pbmdlc3Rfbm9kZSIsICJlc19tYXN0ZXJfbm9kZSIsICJob3N0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX3Byb2Nlc3NfbWF4X2ZpbGVzX2Rlc2NyaXB0b3JzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJlc19jbGllbnRfbm9kZSIsICJlc19kYXRhX25vZGUiLCAiZXNfaW5nZXN0X25vZGUiLCAiZXNfbWFzdGVyX25vZGUiLCAiaG9zdCIsICJuYW1lIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX3Byb2Nlc3NfbWVtX3Jlc2lkZW50X3NpemVfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJlc19kYXRhX25vZGUiLCAiZXNfaW5nZXN0X25vZGUiLCAiZXNfbWFzdGVyX25vZGUiLCAiaG9zdCIsICJuYW1lIiwgImNsdXN0ZXIiLCAiZXNfY2xpZW50X25vZGUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfcHJvY2Vzc19tZW1fc2hhcmVfc2l6ZV9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImVzX2luZ2VzdF9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIiwgImhvc3QiLCAibmFtZSIsICJjbHVzdGVyIiwgImVzX2NsaWVudF9ub2RlIiwgImVzX2RhdGFfbm9kZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9wcm9jZXNzX21lbV92aXJ0dWFsX3NpemVfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJlc19kYXRhX25vZGUiLCAiZXNfaW5nZXN0X25vZGUiLCAiZXNfbWFzdGVyX25vZGUiLCAiaG9zdCIsICJuYW1lIiwgImNsdXN0ZXIiLCAiZXNfY2xpZW50X25vZGUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfcHJvY2Vzc19vcGVuX2ZpbGVzX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIiwgImhvc3QiLCAibmFtZSIsICJjbHVzdGVyIiwgImVzX2NsaWVudF9ub2RlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX3RyYW5zcG9ydF9yeF9wYWNrZXRzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZXNfaW5nZXN0X25vZGUiLCAiZXNfbWFzdGVyX25vZGUiLCAiaG9zdCIsICJuYW1lIiwgImNsdXN0ZXIiLCAiZXNfY2xpZW50X25vZGUiLCAiZXNfZGF0YV9ub2RlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX3RyYW5zcG9ydF9yeF9zaXplX2J5dGVzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaG9zdCIsICJuYW1lIiwgImNsdXN0ZXIiLCAiZXNfY2xpZW50X25vZGUiLCAiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX3RyYW5zcG9ydF90eF9wYWNrZXRzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIiwgImhvc3QiLCAibmFtZSIsICJjbHVzdGVyIiwgImVzX2NsaWVudF9ub2RlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX3RyYW5zcG9ydF90eF9zaXplX2J5dGVzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaG9zdCIsICJuYW1lIiwgImNsdXN0ZXIiLCAiZXNfY2xpZW50X25vZGUiLCAiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2NsdXN0ZXJfaGVhbHRoX2FjdGl2ZV9wcmltYXJ5X3NoYXJkcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNsdXN0ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfY2x1c3Rlcl9oZWFsdGhfYWN0aXZlX3NoYXJkcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNsdXN0ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfY2x1c3Rlcl9oZWFsdGhfZGVsYXllZF91bmFzc2lnbmVkX3NoYXJkcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNsdXN0ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfY2x1c3Rlcl9oZWFsdGhfaW5pdGlhbGl6aW5nX3NoYXJkcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNsdXN0ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfY2x1c3Rlcl9oZWFsdGhfbnVtYmVyX29mX2RhdGFfbm9kZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjbHVzdGVyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2NsdXN0ZXJfaGVhbHRoX251bWJlcl9vZl9pbl9mbGlnaHRfZmV0Y2giLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjbHVzdGVyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2NsdXN0ZXJfaGVhbHRoX251bWJlcl9vZl9ub2RlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNsdXN0ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfY2x1c3Rlcl9oZWFsdGhfbnVtYmVyX29mX3BlbmRpbmdfdGFza3MiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjbHVzdGVyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2NsdXN0ZXJfaGVhbHRoX3JlbG9jYXRpbmdfc2hhcmRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9jbHVzdGVyX2hlYWx0aF90YXNrX21heF93YWl0aW5nX2luX3F1ZXVlX21pbGxpcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNsdXN0ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfY2x1c3Rlcl9oZWFsdGhfdW5hc3NpZ25lZF9zaGFyZHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjbHVzdGVyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX25vZGVfc2hhcmRzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsibm9kZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZXNfZGF0YV9ub2RlIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibW91bnQiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlc19pbmdlc3Rfbm9kZSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVzX21hc3Rlcl9ub2RlIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZXNfY2xpZW50X25vZGUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJob3N0IiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicGF0aCIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImNsdXN0ZXIiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJub2RlIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9XSwgInRhYmxlX2lkIjogImV4cG9ydGVyX2RibV9lbGFzdGljc2VhcmNoX2V4cG9ydGVyLkdyb3VwMiIsICJ0YWJsZV9kZXNjIjogIlx1NTIwNlx1N2VjNDIiLCAidGFibGVfbmFtZSI6ICJHcm91cDIifSwgeyJmaWVsZHMiOiBbeyJuYW1lIjogIl9fbmFtZSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICJuYW1lIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2JyZWFrZXJzX2VzdGltYXRlZF9zaXplX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZXNfbWFzdGVyX25vZGUiLCAibmFtZSIsICJlc19kYXRhX25vZGUiLCAiaG9zdCIsICJicmVha2VyIiwgImNsdXN0ZXIiLCAiZXNfY2xpZW50X25vZGUiLCAiZXNfaW5nZXN0X25vZGUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfYnJlYWtlcnNfbGltaXRfc2l6ZV9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImJyZWFrZXIiLCAiZXNfaW5nZXN0X25vZGUiLCAiZXNfbWFzdGVyX25vZGUiLCAiaG9zdCIsICJjbHVzdGVyIiwgImVzX2NsaWVudF9ub2RlIiwgImVzX2RhdGFfbm9kZSIsICJuYW1lIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2JyZWFrZXJzX292ZXJoZWFkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaG9zdCIsICJuYW1lIiwgImVzX21hc3Rlcl9ub2RlIiwgImJyZWFrZXIiLCAiY2x1c3RlciIsICJlc19pbmdlc3Rfbm9kZSIsICJlc19jbGllbnRfbm9kZSIsICJlc19kYXRhX25vZGUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfYnJlYWtlcnNfdHJpcHBlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImVzX21hc3Rlcl9ub2RlIiwgImhvc3QiLCAibmFtZSIsICJicmVha2VyIiwgImNsdXN0ZXIiLCAiZXNfY2xpZW50X25vZGUiLCAiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlc19tYXN0ZXJfbm9kZSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVzX2RhdGFfbm9kZSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhvc3QiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJicmVha2VyIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiY2x1c3RlciIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVzX2NsaWVudF9ub2RlIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZXNfaW5nZXN0X25vZGUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX1dLCAidGFibGVfaWQiOiAiZXhwb3J0ZXJfZGJtX2VsYXN0aWNzZWFyY2hfZXhwb3J0ZXIuR3JvdXAzIiwgInRhYmxlX2Rlc2MiOiAiXHU1MjA2XHU3ZWM0MyIsICJ0YWJsZV9uYW1lIjogIkdyb3VwMyJ9LCB7ImZpZWxkcyI6IFt7Im5hbWUiOiAiX19uYW1lIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIm5hbWUiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfZmlsZXN5c3RlbV9pb19zdGF0c19kZXZpY2Vfb3BlcmF0aW9uc19jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImVzX21hc3Rlcl9ub2RlIiwgImhvc3QiLCAibmFtZSIsICJkZXZpY2UiLCAiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIiwgImNsdXN0ZXIiLCAiZXNfY2xpZW50X25vZGUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfZmlsZXN5c3RlbV9pb19zdGF0c19kZXZpY2VfcmVhZF9vcGVyYXRpb25zX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsibmFtZSIsICJlc19jbGllbnRfbm9kZSIsICJlc19kYXRhX25vZGUiLCAiZXNfaW5nZXN0X25vZGUiLCAiZXNfbWFzdGVyX25vZGUiLCAiaG9zdCIsICJjbHVzdGVyIiwgImRldmljZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9maWxlc3lzdGVtX2lvX3N0YXRzX2RldmljZV9yZWFkX3NpemVfa2lsb2J5dGVzX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImVzX2RhdGFfbm9kZSIsICJob3N0IiwgImRldmljZSIsICJlc19pbmdlc3Rfbm9kZSIsICJlc19tYXN0ZXJfbm9kZSIsICJuYW1lIiwgImNsdXN0ZXIiLCAiZXNfY2xpZW50X25vZGUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfZmlsZXN5c3RlbV9pb19zdGF0c19kZXZpY2Vfd3JpdGVfb3BlcmF0aW9uc19jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbIm5hbWUiLCAiZXNfbWFzdGVyX25vZGUiLCAiY2x1c3RlciIsICJlc19jbGllbnRfbm9kZSIsICJlc19kYXRhX25vZGUiLCAiZXNfaW5nZXN0X25vZGUiLCAiZGV2aWNlIiwgImhvc3QiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfZmlsZXN5c3RlbV9pb19zdGF0c19kZXZpY2Vfd3JpdGVfc2l6ZV9raWxvYnl0ZXNfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZXNfbWFzdGVyX25vZGUiLCAiaG9zdCIsICJuYW1lIiwgImNsdXN0ZXIiLCAiZGV2aWNlIiwgImVzX2NsaWVudF9ub2RlIiwgImVzX2RhdGFfbm9kZSIsICJlc19pbmdlc3Rfbm9kZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZXNfbWFzdGVyX25vZGUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJob3N0IiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZGV2aWNlIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZXNfZGF0YV9ub2RlIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZXNfaW5nZXN0X25vZGUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJjbHVzdGVyIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZXNfY2xpZW50X25vZGUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX1dLCAidGFibGVfaWQiOiAiZXhwb3J0ZXJfZGJtX2VsYXN0aWNzZWFyY2hfZXhwb3J0ZXIuR3JvdXA0IiwgInRhYmxlX2Rlc2MiOiAiXHU1MjA2XHU3ZWM0NCIsICJ0YWJsZV9uYW1lIjogIkdyb3VwNCJ9LCB7ImZpZWxkcyI6IFt7Im5hbWUiOiAiX19uYW1lIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIm5hbWUiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19xdWVyeV9jYWNoZV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImVzX2NsaWVudF9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIiwgIm5hbWUiLCAiY2FjaGUiLCAiY2x1c3RlciIsICJob3N0IiwgImVzX2RhdGFfbm9kZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRpY2VzX3F1ZXJ5X21pc3NfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJob3N0IiwgImNsdXN0ZXIiLCAiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIiwgImNhY2hlIiwgImVzX2NsaWVudF9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIiwgIm5hbWUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19yZXF1ZXN0X2NhY2hlX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsibmFtZSIsICJjYWNoZSIsICJlc19pbmdlc3Rfbm9kZSIsICJjbHVzdGVyIiwgImVzX2NsaWVudF9ub2RlIiwgImVzX2RhdGFfbm9kZSIsICJlc19tYXN0ZXJfbm9kZSIsICJob3N0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGljZXNfcmVxdWVzdF9taXNzX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2FjaGUiLCAiZXNfY2xpZW50X25vZGUiLCAiZXNfZGF0YV9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIiwgImhvc3QiLCAibmFtZSIsICJjbHVzdGVyIiwgImVzX2luZ2VzdF9ub2RlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlc19jbGllbnRfbm9kZSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVzX2luZ2VzdF9ub2RlIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZXNfbWFzdGVyX25vZGUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJjYWNoZSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImNsdXN0ZXIiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJob3N0IiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZXNfZGF0YV9ub2RlIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9XSwgInRhYmxlX2lkIjogImV4cG9ydGVyX2RibV9lbGFzdGljc2VhcmNoX2V4cG9ydGVyLkdyb3VwNSIsICJ0YWJsZV9kZXNjIjogIlx1NTIwNlx1N2VjNDUiLCAidGFibGVfbmFtZSI6ICJHcm91cDUifSwgeyJmaWVsZHMiOiBbeyJuYW1lIjogIl9fbmFtZSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICJuYW1lIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2p2bV9idWZmZXJfcG9vbF91c2VkX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsidHlwZSIsICJjbHVzdGVyIiwgImVzX2RhdGFfbm9kZSIsICJob3N0IiwgIm5hbWUiLCAiZXNfY2xpZW50X25vZGUiLCAiZXNfaW5nZXN0X25vZGUiLCAiZXNfbWFzdGVyX25vZGUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfanZtX3VwdGltZV9zZWNvbmRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZXNfbWFzdGVyX25vZGUiLCAibmFtZSIsICJlc19jbGllbnRfbm9kZSIsICJlc19pbmdlc3Rfbm9kZSIsICJob3N0IiwgInR5cGUiLCAiY2x1c3RlciIsICJlc19kYXRhX25vZGUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfdGhyZWFkX3Bvb2xfYWN0aXZlX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJlc19kYXRhX25vZGUiLCAiZXNfaW5nZXN0X25vZGUiLCAiZXNfbWFzdGVyX25vZGUiLCAiaG9zdCIsICJuYW1lIiwgInR5cGUiLCAiZXNfY2xpZW50X25vZGUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfdGhyZWFkX3Bvb2xfY29tcGxldGVkX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIiwgImhvc3QiLCAibmFtZSIsICJlc19jbGllbnRfbm9kZSIsICJlc19tYXN0ZXJfbm9kZSIsICJ0eXBlIiwgImNsdXN0ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfdGhyZWFkX3Bvb2xfbGFyZ2VzdF9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImhvc3QiLCAibmFtZSIsICJ0eXBlIiwgImVzX2luZ2VzdF9ub2RlIiwgImNsdXN0ZXIiLCAiZXNfY2xpZW50X25vZGUiLCAiZXNfZGF0YV9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX3RocmVhZF9wb29sX3F1ZXVlX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJlc19jbGllbnRfbm9kZSIsICJlc19tYXN0ZXJfbm9kZSIsICJ0eXBlIiwgImVzX2RhdGFfbm9kZSIsICJlc19pbmdlc3Rfbm9kZSIsICJob3N0IiwgIm5hbWUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfdGhyZWFkX3Bvb2xfcmVqZWN0ZWRfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJlc19jbGllbnRfbm9kZSIsICJlc19kYXRhX25vZGUiLCAibmFtZSIsICJjbHVzdGVyIiwgImVzX2luZ2VzdF9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIiwgImhvc3QiLCAidHlwZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF90aHJlYWRfcG9vbF90aHJlYWRzX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZXNfbWFzdGVyX25vZGUiLCAiY2x1c3RlciIsICJuYW1lIiwgInR5cGUiLCAiZXNfZGF0YV9ub2RlIiwgImhvc3QiLCAiZXNfY2xpZW50X25vZGUiLCAiZXNfaW5nZXN0X25vZGUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInR5cGUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJjbHVzdGVyIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZXNfZGF0YV9ub2RlIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaG9zdCIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVzX2NsaWVudF9ub2RlIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZXNfaW5nZXN0X25vZGUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlc19tYXN0ZXJfbm9kZSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfV0sICJ0YWJsZV9pZCI6ICJleHBvcnRlcl9kYm1fZWxhc3RpY3NlYXJjaF9leHBvcnRlci5Hcm91cDYiLCAidGFibGVfZGVzYyI6ICJcdTUyMDZcdTdlYzQ2IiwgInRhYmxlX25hbWUiOiAiR3JvdXA2In0sIHsiZmllbGRzIjogW3sibmFtZSI6ICJfX25hbWUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAibmFtZSIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9qdm1fZ2NfY29sbGVjdGlvbl9zZWNvbmRzX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZXNfaW5nZXN0X25vZGUiLCAiZ2MiLCAiaG9zdCIsICJuYW1lIiwgImVzX2RhdGFfbm9kZSIsICJlc19tYXN0ZXJfbm9kZSIsICJjbHVzdGVyIiwgImVzX2NsaWVudF9ub2RlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2p2bV9nY19jb2xsZWN0aW9uX3NlY29uZHNfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaG9zdCIsICJlc19kYXRhX25vZGUiLCAiZXNfbWFzdGVyX25vZGUiLCAibmFtZSIsICJjbHVzdGVyIiwgImVzX2NsaWVudF9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIiwgImdjIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlc19pbmdlc3Rfbm9kZSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdjIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaG9zdCIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVzX2RhdGFfbm9kZSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVzX21hc3Rlcl9ub2RlIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiY2x1c3RlciIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVzX2NsaWVudF9ub2RlIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9XSwgInRhYmxlX2lkIjogImV4cG9ydGVyX2RibV9lbGFzdGljc2VhcmNoX2V4cG9ydGVyLkdyb3VwNyIsICJ0YWJsZV9kZXNjIjogIlx1NTIwNlx1N2VjNDciLCAidGFibGVfbmFtZSI6ICJHcm91cDcifSwgeyJmaWVsZHMiOiBbeyJuYW1lIjogIl9fbmFtZSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICJuYW1lIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2p2bV9tZW1vcnlfY29tbWl0dGVkX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsibmFtZSIsICJlc19tYXN0ZXJfbm9kZSIsICJob3N0IiwgImFyZWEiLCAiY2x1c3RlciIsICJlc19pbmdlc3Rfbm9kZSIsICJlc19jbGllbnRfbm9kZSIsICJlc19kYXRhX25vZGUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfanZtX21lbW9yeV9tYXhfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJhcmVhIiwgImNsdXN0ZXIiLCAiZXNfZGF0YV9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIiwgImVzX2NsaWVudF9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIiwgImhvc3QiLCAibmFtZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9qdm1fbWVtb3J5X3VzZWRfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjbHVzdGVyIiwgIm5hbWUiLCAiZXNfY2xpZW50X25vZGUiLCAiZXNfZGF0YV9ub2RlIiwgImVzX2luZ2VzdF9ub2RlIiwgImVzX21hc3Rlcl9ub2RlIiwgImhvc3QiLCAiYXJlYSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZXNfbWFzdGVyX25vZGUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJob3N0IiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYXJlYSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImNsdXN0ZXIiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlc19pbmdlc3Rfbm9kZSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVzX2NsaWVudF9ub2RlIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZXNfZGF0YV9ub2RlIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9XSwgInRhYmxlX2lkIjogImV4cG9ydGVyX2RibV9lbGFzdGljc2VhcmNoX2V4cG9ydGVyLkdyb3VwOCIsICJ0YWJsZV9kZXNjIjogIlx1NTIwNlx1N2VjNDgiLCAidGFibGVfbmFtZSI6ICJHcm91cDgifSwgeyJmaWVsZHMiOiBbeyJuYW1lIjogIl9fbmFtZSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICJuYW1lIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2p2bV9tZW1vcnlfcG9vbF9tYXhfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJob3N0IiwgIm5hbWUiLCAiY2x1c3RlciIsICJwb29sIiwgImVzX2NsaWVudF9ub2RlIiwgImVzX2RhdGFfbm9kZSIsICJlc19pbmdlc3Rfbm9kZSIsICJlc19tYXN0ZXJfbm9kZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9qdm1fbWVtb3J5X3Bvb2xfcGVha19tYXhfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJlc19pbmdlc3Rfbm9kZSIsICJlc19kYXRhX25vZGUiLCAiZXNfbWFzdGVyX25vZGUiLCAiaG9zdCIsICJuYW1lIiwgInBvb2wiLCAiY2x1c3RlciIsICJlc19jbGllbnRfbm9kZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9qdm1fbWVtb3J5X3Bvb2xfcGVha191c2VkX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaG9zdCIsICJuYW1lIiwgImVzX2RhdGFfbm9kZSIsICJlc19pbmdlc3Rfbm9kZSIsICJwb29sIiwgImNsdXN0ZXIiLCAiZXNfY2xpZW50X25vZGUiLCAiZXNfbWFzdGVyX25vZGUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfanZtX21lbW9yeV9wb29sX3VzZWRfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJob3N0IiwgIm5hbWUiLCAiY2x1c3RlciIsICJlc19kYXRhX25vZGUiLCAicG9vbCIsICJlc19jbGllbnRfbm9kZSIsICJlc19pbmdlc3Rfbm9kZSIsICJlc19tYXN0ZXJfbm9kZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaG9zdCIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImNsdXN0ZXIiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwb29sIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZXNfY2xpZW50X25vZGUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlc19kYXRhX25vZGUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlc19pbmdlc3Rfbm9kZSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVzX21hc3Rlcl9ub2RlIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9XSwgInRhYmxlX2lkIjogImV4cG9ydGVyX2RibV9lbGFzdGljc2VhcmNoX2V4cG9ydGVyLkdyb3VwOSIsICJ0YWJsZV9kZXNjIjogIlx1NTIwNlx1N2VjNDkiLCAidGFibGVfbmFtZSI6ICJHcm91cDkifSwgeyJmaWVsZHMiOiBbeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfY2x1c3RlcmluZm9fdmVyc2lvbl9pbmZvIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsidmVyc2lvbiIsICJidWlsZF9kYXRlIiwgImJ1aWxkX2hhc2giLCAiY2x1c3RlciIsICJjbHVzdGVyX3V1aWQiLCAibHVjZW5lX3ZlcnNpb24iXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfdmVyc2lvbiIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImx1Y2VuZV92ZXJzaW9uIiwgInZlcnNpb24iLCAiYnVpbGRfZGF0ZSIsICJidWlsZF9oYXNoIiwgImNsdXN0ZXIiLCAiY2x1c3Rlcl91dWlkIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJnb19pbmZvIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsidmVyc2lvbiJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAidmVyc2lvbiIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJ1aWxkX2RhdGUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJidWlsZF9oYXNoIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiY2x1c3RlciIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImNsdXN0ZXJfdXVpZCIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImx1Y2VuZV92ZXJzaW9uIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9XSwgInRhYmxlX2lkIjogImV4cG9ydGVyX2RibV9lbGFzdGljc2VhcmNoX2V4cG9ydGVyLkdyb3VwMTAiLCAidGFibGVfZGVzYyI6ICJcdTUyMDZcdTdlYzQxMCIsICJ0YWJsZV9uYW1lIjogIkdyb3VwMTAifSwgeyJmaWVsZHMiOiBbeyJuYW1lIjogIl9zaGFyZCIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICJzaGFyZCIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRpY2VzX3NoYXJkc19kb2NzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJpbmRleCIsICJub2RlIiwgInByaW1hcnkiLCAic2hhcmQiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19zaGFyZHNfZG9jc19kZWxldGVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsibm9kZSIsICJwcmltYXJ5IiwgInNoYXJkIiwgImNsdXN0ZXIiLCAiaW5kZXgiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19zaGFyZHNfc3RvcmVfc2l6ZV9pbl9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbIm5vZGUiLCAicHJpbWFyeSIsICJzaGFyZCIsICJjbHVzdGVyIiwgImluZGV4Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGV4X3N0YXRzX2ZpZWxkZGF0YV9ldmljdGlvbnNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJpbmRleCIsICJjbHVzdGVyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGV4X3N0YXRzX2ZpZWxkZGF0YV9tZW1vcnlfYnl0ZXNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJpbmRleCIsICJjbHVzdGVyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGV4X3N0YXRzX2ZsdXNoX3RpbWVfc2Vjb25kc190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImluZGV4IiwgImNsdXN0ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kZXhfc3RhdHNfZmx1c2hfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJpbmRleCIsICJjbHVzdGVyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGV4X3N0YXRzX2dldF90aW1lX3NlY29uZHNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjbHVzdGVyIiwgImluZGV4Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGV4X3N0YXRzX2dldF90b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImluZGV4IiwgImNsdXN0ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kZXhfc3RhdHNfaW5kZXhfY3VycmVudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNsdXN0ZXIiLCAiaW5kZXgiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kZXhfc3RhdHNfaW5kZXhpbmdfZGVsZXRlX3RpbWVfc2Vjb25kc190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNsdXN0ZXIiLCAiaW5kZXgiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kZXhfc3RhdHNfaW5kZXhpbmdfZGVsZXRlX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJpbmRleCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRleF9zdGF0c19pbmRleGluZ19pbmRleF90aW1lX3NlY29uZHNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjbHVzdGVyIiwgImluZGV4Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGV4X3N0YXRzX2luZGV4aW5nX2luZGV4X3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJpbmRleCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRleF9zdGF0c19pbmRleGluZ19ub29wX3VwZGF0ZV90b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNsdXN0ZXIiLCAiaW5kZXgiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kZXhfc3RhdHNfaW5kZXhpbmdfdGhyb3R0bGVfdGltZV9zZWNvbmRzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJpbmRleCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRleF9zdGF0c19tZXJnZV9hdXRvX3Rocm90dGxlX2J5dGVzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJpbmRleCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRleF9zdGF0c19tZXJnZV9zdG9wcGVkX3RpbWVfc2Vjb25kc190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImluZGV4IiwgImNsdXN0ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kZXhfc3RhdHNfbWVyZ2VfdGhyb3R0bGVfdGltZV9zZWNvbmRzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaW5kZXgiLCAiY2x1c3RlciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRleF9zdGF0c19tZXJnZV90aW1lX3NlY29uZHNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjbHVzdGVyIiwgImluZGV4Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGV4X3N0YXRzX21lcmdlX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJpbmRleCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRleF9zdGF0c19xdWVyeV9jYWNoZV9jYWNoZXNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjbHVzdGVyIiwgImluZGV4Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGV4X3N0YXRzX3F1ZXJ5X2NhY2hlX2V2aWN0aW9uc190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImluZGV4IiwgImNsdXN0ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kZXhfc3RhdHNfcXVlcnlfY2FjaGVfaGl0c190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNsdXN0ZXIiLCAiaW5kZXgiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kZXhfc3RhdHNfcXVlcnlfY2FjaGVfbWVtb3J5X2J5dGVzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaW5kZXgiLCAiY2x1c3RlciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRleF9zdGF0c19xdWVyeV9jYWNoZV9taXNzZXNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjbHVzdGVyIiwgImluZGV4Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGV4X3N0YXRzX3F1ZXJ5X2NhY2hlX3NpemUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjbHVzdGVyIiwgImluZGV4Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGV4X3N0YXRzX3JlZnJlc2hfdGltZV9zZWNvbmRzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJpbmRleCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRleF9zdGF0c19yZWZyZXNoX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJpbmRleCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRleF9zdGF0c19yZXF1ZXN0X2NhY2hlX2V2aWN0aW9uc190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNsdXN0ZXIiLCAiaW5kZXgiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kZXhfc3RhdHNfcmVxdWVzdF9jYWNoZV9oaXRzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaW5kZXgiLCAiY2x1c3RlciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRleF9zdGF0c19yZXF1ZXN0X2NhY2hlX21lbW9yeV9ieXRlc190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNsdXN0ZXIiLCAiaW5kZXgiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kZXhfc3RhdHNfcmVxdWVzdF9jYWNoZV9taXNzZXNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJpbmRleCIsICJjbHVzdGVyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGV4X3N0YXRzX3NlYXJjaF9mZXRjaF90aW1lX3NlY29uZHNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjbHVzdGVyIiwgImluZGV4Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGV4X3N0YXRzX3NlYXJjaF9mZXRjaF90b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImluZGV4IiwgImNsdXN0ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kZXhfc3RhdHNfc2VhcmNoX3F1ZXJ5X3RpbWVfc2Vjb25kc190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImluZGV4IiwgImNsdXN0ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kZXhfc3RhdHNfc2VhcmNoX3F1ZXJ5X3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaW5kZXgiLCAiY2x1c3RlciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRleF9zdGF0c19zZWFyY2hfc2Nyb2xsX2N1cnJlbnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjbHVzdGVyIiwgImluZGV4Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGV4X3N0YXRzX3NlYXJjaF9zY3JvbGxfdGltZV9zZWNvbmRzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJpbmRleCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRleF9zdGF0c19zZWFyY2hfc2Nyb2xsX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJpbmRleCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRleF9zdGF0c19zZWFyY2hfc3VnZ2VzdF90aW1lX3NlY29uZHNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJpbmRleCIsICJjbHVzdGVyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGV4X3N0YXRzX3NlYXJjaF9zdWdnZXN0X3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJpbmRleCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRleF9zdGF0c193YXJtZXJfdGltZV9zZWNvbmRzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJpbmRleCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRleF9zdGF0c193YXJtZXJfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjbHVzdGVyIiwgImluZGV4Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGljZXNfY29tcGxldGlvbl9ieXRlc19wcmltYXJ5IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJpbmRleCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRpY2VzX2NvbXBsZXRpb25fYnl0ZXNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJpbmRleCIsICJjbHVzdGVyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGljZXNfZGVsZXRlZF9kb2NzX3ByaW1hcnkiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJpbmRleCIsICJjbHVzdGVyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGljZXNfZGVsZXRlZF9kb2NzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJpbmRleCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRpY2VzX2RvY3NfcHJpbWFyeSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNsdXN0ZXIiLCAiaW5kZXgiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19kb2NzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaW5kZXgiLCAiY2x1c3RlciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRpY2VzX3NlZ21lbnRfY291bnRfcHJpbWFyeSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImluZGV4IiwgImNsdXN0ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19zZWdtZW50X2NvdW50X3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJpbmRleCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRpY2VzX3NlZ21lbnRfZG9jX3ZhbHVlc19tZW1vcnlfYnl0ZXNfcHJpbWFyeSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNsdXN0ZXIiLCAiaW5kZXgiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19zZWdtZW50X2RvY192YWx1ZXNfbWVtb3J5X2J5dGVzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJpbmRleCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRpY2VzX3NlZ21lbnRfZmllbGRzX21lbW9yeV9ieXRlc19wcmltYXJ5IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJpbmRleCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRpY2VzX3NlZ21lbnRfZmllbGRzX21lbW9yeV9ieXRlc190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNsdXN0ZXIiLCAiaW5kZXgiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19zZWdtZW50X2ZpeGVkX2JpdF9zZXRfbWVtb3J5X2J5dGVzX3ByaW1hcnkiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJpbmRleCIsICJjbHVzdGVyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGljZXNfc2VnbWVudF9maXhlZF9iaXRfc2V0X21lbW9yeV9ieXRlc190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNsdXN0ZXIiLCAiaW5kZXgiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19zZWdtZW50X2luZGV4X3dyaXRlcl9tZW1vcnlfYnl0ZXNfcHJpbWFyeSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImluZGV4IiwgImNsdXN0ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19zZWdtZW50X2luZGV4X3dyaXRlcl9tZW1vcnlfYnl0ZXNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJpbmRleCIsICJjbHVzdGVyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGljZXNfc2VnbWVudF9tZW1vcnlfYnl0ZXNfcHJpbWFyeSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNsdXN0ZXIiLCAiaW5kZXgiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19zZWdtZW50X21lbW9yeV9ieXRlc190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNsdXN0ZXIiLCAiaW5kZXgiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19zZWdtZW50X25vcm1zX21lbW9yeV9ieXRlc19wcmltYXJ5IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaW5kZXgiLCAiY2x1c3RlciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRpY2VzX3NlZ21lbnRfbm9ybXNfbWVtb3J5X2J5dGVzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaW5kZXgiLCAiY2x1c3RlciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRpY2VzX3NlZ21lbnRfcG9pbnRzX21lbW9yeV9ieXRlc19wcmltYXJ5IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJpbmRleCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRpY2VzX3NlZ21lbnRfcG9pbnRzX21lbW9yeV9ieXRlc190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNsdXN0ZXIiLCAiaW5kZXgiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19zZWdtZW50X3Rlcm1fdmVjdG9yc19tZW1vcnlfcHJpbWFyeV9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNsdXN0ZXIiLCAiaW5kZXgiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19zZWdtZW50X3Rlcm1fdmVjdG9yc19tZW1vcnlfdG90YWxfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjbHVzdGVyIiwgImluZGV4Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGljZXNfc2VnbWVudF90ZXJtc19tZW1vcnlfcHJpbWFyeSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNsdXN0ZXIiLCAiaW5kZXgiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19zZWdtZW50X3Rlcm1zX21lbW9yeV90b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImluZGV4IiwgImNsdXN0ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19zZWdtZW50X3ZlcnNpb25fbWFwX21lbW9yeV9ieXRlc19wcmltYXJ5IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJpbmRleCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRpY2VzX3NlZ21lbnRfdmVyc2lvbl9tYXBfbWVtb3J5X2J5dGVzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJpbmRleCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9pbmRpY2VzX3N0b3JlX3NpemVfYnl0ZXNfcHJpbWFyeSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImluZGV4IiwgImNsdXN0ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfaW5kaWNlc19zdG9yZV9zaXplX2J5dGVzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJpbmRleCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9zZWFyY2hfYWN0aXZlX3F1ZXJpZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjbHVzdGVyIiwgImluZGV4Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJjbHVzdGVyIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5kZXgiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJub2RlIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJpbWFyeSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfV0sICJ0YWJsZV9pZCI6ICJleHBvcnRlcl9kYm1fZWxhc3RpY3NlYXJjaF9leHBvcnRlci5Hcm91cDExIiwgInRhYmxlX2Rlc2MiOiAiXHU1MjA2XHU3ZWM0MTEiLCAidGFibGVfbmFtZSI6ICJHcm91cDExIn0sIHsiZmllbGRzIjogW3sibmFtZSI6ICJlbGFzdGljc2VhcmNoX2V4cG9ydGVyX2J1aWxkX2luZm8iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJ2ZXJzaW9uIiwgImJyYW5jaCIsICJnb3ZlcnNpb24iLCAicmV2aXNpb24iXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInZlcnNpb24iLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJicmFuY2giLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJnb3ZlcnNpb24iLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXZpc2lvbiIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfV0sICJ0YWJsZV9pZCI6ICJleHBvcnRlcl9kYm1fZWxhc3RpY3NlYXJjaF9leHBvcnRlci5Hcm91cDEyIiwgInRhYmxlX2Rlc2MiOiAiXHU1MjA2XHU3ZWM0MTIiLCAidGFibGVfbmFtZSI6ICJHcm91cDEyIn0sIHsiZmllbGRzIjogW3sibmFtZSI6ICJfX25hbWUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAibmFtZSIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9ub2Rlc19yb2xlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbIm5hbWUiLCAicm9sZSIsICJjbHVzdGVyIiwgImhvc3QiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJvbGUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJjbHVzdGVyIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaG9zdCIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfV0sICJ0YWJsZV9pZCI6ICJleHBvcnRlcl9kYm1fZWxhc3RpY3NlYXJjaF9leHBvcnRlci5Hcm91cDEzIiwgInRhYmxlX2Rlc2MiOiAiXHU1MjA2XHU3ZWM0MTMiLCAidGFibGVfbmFtZSI6ICJHcm91cDEzIn0sIHsiZmllbGRzIjogW3sibmFtZSI6ICJlbGFzdGljc2VhcmNoX2luZGljZXNfYWxpYXNlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImluZGV4IiwgImFsaWFzIiwgImNsdXN0ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImluZGV4IiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYWxpYXMiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJjbHVzdGVyIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9XSwgInRhYmxlX2lkIjogImV4cG9ydGVyX2RibV9lbGFzdGljc2VhcmNoX2V4cG9ydGVyLkdyb3VwMTQiLCAidGFibGVfZGVzYyI6ICJcdTUyMDZcdTdlYzQxNCIsICJ0YWJsZV9uYW1lIjogIkdyb3VwMTQifSwgeyJmaWVsZHMiOiBbeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfY2x1c3Rlcl9oZWFsdGhfc3RhdHVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJjb2xvciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiY2x1c3RlciIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImNvbG9yIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9XSwgInRhYmxlX2lkIjogImV4cG9ydGVyX2RibV9lbGFzdGljc2VhcmNoX2V4cG9ydGVyLkdyb3VwMTUiLCAidGFibGVfZGVzYyI6ICJcdTUyMDZcdTdlYzQxNSIsICJ0YWJsZV9uYW1lIjogIkdyb3VwMTUifSwgeyJmaWVsZHMiOiBbeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfY2x1c3RlcmluZm9fbGFzdF9yZXRyaWV2YWxfc3VjY2Vzc190cyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbInVybCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxhc3RpY3NlYXJjaF9jbHVzdGVyaW5mb191cCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbInVybCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAidXJsIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9XSwgInRhYmxlX2lkIjogImV4cG9ydGVyX2RibV9lbGFzdGljc2VhcmNoX2V4cG9ydGVyLkdyb3VwMTYiLCAidGFibGVfZGVzYyI6ICJcdTUyMDZcdTdlYzQxNiIsICJ0YWJsZV9uYW1lIjogIkdyb3VwMTYifSwgeyJmaWVsZHMiOiBbeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfc2NyYXBlX2R1cmF0aW9uX3NlY29uZHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjb2xsZWN0b3IiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVsYXN0aWNzZWFyY2hfc2NyYXBlX3N1Y2Nlc3MiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjb2xsZWN0b3IiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImNvbGxlY3RvciIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfV0sICJ0YWJsZV9pZCI6ICJleHBvcnRlcl9kYm1fZWxhc3RpY3NlYXJjaF9leHBvcnRlci5Hcm91cDE3IiwgInRhYmxlX2Rlc2MiOiAiXHU1MjA2XHU3ZWM0MTciLCAidGFibGVfbmFtZSI6ICJHcm91cDE3In0sIHsiZmllbGRzIjogW3sibmFtZSI6ICJnb19nY19kdXJhdGlvbl9zZWNvbmRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsicXVhbnRpbGUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInF1YW50aWxlIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9XSwgInRhYmxlX2lkIjogImV4cG9ydGVyX2RibV9lbGFzdGljc2VhcmNoX2V4cG9ydGVyLkdyb3VwMTgiLCAidGFibGVfZGVzYyI6ICJcdTUyMDZcdTdlYzQxOCIsICJ0YWJsZV9uYW1lIjogIkdyb3VwMTgifSwgeyJmaWVsZHMiOiBbeyJuYW1lIjogInByb21odHRwX21ldHJpY19oYW5kbGVyX3JlcXVlc3RzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY29kZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiY29kZSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfV0sICJ0YWJsZV9pZCI6ICJleHBvcnRlcl9kYm1fZWxhc3RpY3NlYXJjaF9leHBvcnRlci5Hcm91cDE5IiwgInRhYmxlX2Rlc2MiOiAiXHU1MjA2XHU3ZWM0MTkiLCAidGFibGVfbmFtZSI6ICJHcm91cDE5In1dLCAicGx1Z2luX3R5cGUiOiAiRXhwb3J0ZXIiLCAib3NfdHlwZV9saXN0IjogWyJsaW51eCJdfSwgImNvbGxlY3RfdHlwZSI6ICJFeHBvcnRlciIsICJ0YXJnZXRfbm9kZXMiOiBbXSwgInRhcmdldF9ub2RlX3R5cGUiOiAiVE9QTyIsICJ0YXJnZXRfb2JqZWN0X3R5cGUiOiAiU0VSVklDRSJ9fQ== \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/collect/0.hdfs.dbm_hdfs_exporter.json b/dbm-ui/backend/db_monitor/tpls/collect/0.hdfs.dbm_hdfs_exporter.json new file mode 100644 index 0000000000..56c87b76d3 --- /dev/null +++ b/dbm-ui/backend/db_monitor/tpls/collect/0.hdfs.dbm_hdfs_exporter.json @@ -0,0 +1,2063 @@ +{ + "bk_biz_id": 0, + "plugin_id": "dbm_hdfs_exporter", + "db_type": "hdfs", + "details": { + "name": "dbm_hdfs_exporter", + "label": "component", + "params": { + "plugin": { + "-component": "{{ target.service.labels[\"instance_name\"] }}", + "-jmx.http.host": "{{ target.host.bk_host_innerip }}", + "-jmx.http.port": "{{ target.service.labels[\"jmx_http_port\"] }}", + "-component.rpc.port": "{{ target.service.labels[\"rpc_port\"] }}", + "-web.listen.address": "${host}:${port}", + "服务实例维度注入": { + "app": "app", + "instance": "instance", + "cluster_name": "cluster_name", + "cluster_type": "cluster_type", + "instance_host": "instance_host", + "instance_role": "instance_role", + "cluster_domain": "cluster_domain" + }, + "-component.service.rpc.port": "{{ target.service.labels[\"service_rpc_port\"] }}" + }, + "collector": { + "host": "127.0.0.1", + "port": "9070", + "period": 60, + "timeout": 60 + }, + "target_node_type": "TOPO", + "target_object_type": "SERVICE" + }, + "plugin_info": { + "plugin_id": "dbm_hdfs_exporter", + "metric_json": [ + { + "fields": [ + { + "name": "hadoop_datanode_block_reports_num_ops", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_blocks_cached", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_blocks_read", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_blocks_removed", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_blocks_replicated", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_blocks_uncached", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_blocks_written", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_bytes_read", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_bytes_written", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_datanode_network_errors", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_flush_nanos_avg_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_flush_nanos_num_ops", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_fsync_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_fsync_nanos_avg_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_fsync_nanos_num_ops", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_heartbeats_num_ops", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_ram_disk_blocks_evicted", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_ram_disk_blocks_read_hits", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_read_block_op_avg_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_volume_failures", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_write_block_op_avg_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_add_block_avg_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_add_block_num_ops", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_call_queue_length", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_delete_avg_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_delete_num_ops", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_gc_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_gc_time_millis", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_get_file_info_avg_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_get_file_info_num_ops", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_get_fs_stats_avg_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_get_fs_stats_num_ops", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_log_error", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_log_fatal", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_log_info", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_log_warn", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_mem_heap_committed_m", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_mem_heap_max_m", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_mem_heap_used_m", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_mem_max_m", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_mem_non_heap_committed_m", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_mem_non_heap_max_m", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_mem_non_heap_used_m", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_received_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_rpc_authorization_failures", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_rpc_processing_time_avg_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_rpc_processing_time_num_ops", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_rpc_queue_time_avg_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_rpc_queue_time_num_ops", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_sent_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_set_safe_mode_avg_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_set_safe_mode_num_ops", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_threads_blocked", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_threads_new", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_threads_runnable", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_threads_timed_waiting", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_threads_waiting", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_datanode_xceiver_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_State", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_block_capacity", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_blocks_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_capacity_remaining", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_capacity_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_capacity_used", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_capacity_used_non_d_f_s", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_corrupt_blocks", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_estimated_capacity_lost_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_excess_blocks", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_expired_heartbeats", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_f_s_state", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_files_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_last_checkpoint_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_last_written_transaction_id", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_missing_blocks", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_missing_repl_one_blocks", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_num_dead_data_nodes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_num_decom_dead_data_nodes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_num_live_data_nodes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_num_stale_data_nodes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_num_stale_storages", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_pending_data_node_message_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_pending_deletion_blocks", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_pending_replication_blocks", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_postponed_mis_replicated_blocks", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_scheduled_replication_blocks", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_stale_data_nodes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_total_files", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_total_load", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_transactions_since_last_checkpoint", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_transactions_since_last_log_roll", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_under_replicated_blocks", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_volume_failures_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_gc_duration_seconds_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_gc_duration_seconds_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_goroutines", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_alloc_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_alloc_bytes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_buck_hash_sys_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_frees_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_gc_sys_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_heap_alloc_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_heap_idle_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_heap_inuse_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_heap_objects", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_heap_released_bytes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_heap_sys_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_last_gc_time_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_lookups_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_mallocs_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_mcache_inuse_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_mcache_sys_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_mspan_inuse_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_mspan_sys_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_next_gc_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_other_sys_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_stack_inuse_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_stack_sys_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_sys_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_add_block_avg_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_add_block_num_ops", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_add_block_ops", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_block_received_and_deleted_avg_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_block_received_and_deleted_num_ops", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_block_received_and_deleted_ops", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_block_report_avg_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_block_report_num_ops", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_call_queue_length", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_commit_block_synchronization_avg_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_commit_block_synchronization_num_ops", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_create_file_ops", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_delete_avg_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_delete_file_ops", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_delete_num_ops", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_file_info_ops", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_files_appended", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_files_deleted", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_files_in_get_listing_ops", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_gc_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_gc_time_millis", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_get_file_info_avg_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_get_file_info_num_ops", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_get_fs_stats_avg_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_get_fs_stats_num_ops", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_log_error", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_log_fatal", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_log_info", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_log_warn", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_mem_heap_committed_m", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_mem_heap_max_m", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_mem_heap_used_m", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_mem_max_m", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_mem_non_heap_committed_m", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_mem_non_heap_max_m", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_mem_non_heap_used_m", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_received_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_roll_edit_log_num_ops", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_rpc_authorization_failures", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_rpc_processing_time_avg_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_rpc_processing_time_num_ops", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_rpc_queue_time_avg_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_rpc_queue_time_num_ops", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_sent_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_set_safe_mode_avg_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_set_safe_mode_num_ops", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_syncs_avg_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_syncs_num_ops", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_threads_blocked", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_threads_new", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_threads_runnable", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_threads_timed_waiting", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "hadoop_namenode_threads_waiting", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_cpu_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_max_fds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_open_fds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_resident_memory_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_start_time_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_virtual_memory_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_hdfs_exporter.Group1", + "table_desc": "分组1", + "table_name": "Group1" + }, + { + "fields": [ + { + "name": "go_gc_duration_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "quantile" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "quantile", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_hdfs_exporter.Group2", + "table_desc": "分组2", + "table_name": "Group2" + }, + { + "fields": [ + { + "name": "hadoop_namenode_live_nodes_last_contact", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "datanode" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "datanode", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_hdfs_exporter.Group3", + "table_desc": "分组3", + "table_name": "Group3" + } + ], + "plugin_type": "Exporter", + "os_type_list": [ + "linux" + ] + }, + "collect_type": "Exporter", + "target_nodes": [], + "target_node_type": "TOPO", + "target_object_type": "SERVICE" + } +} diff --git a/dbm-ui/backend/db_monitor/tpls/collect/0.hdfs.dbm_hdfs_exporter.tpl64 b/dbm-ui/backend/db_monitor/tpls/collect/0.hdfs.dbm_hdfs_exporter.tpl64 deleted file mode 100644 index 7195e9701a..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/collect/0.hdfs.dbm_hdfs_exporter.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAicGx1Z2luX2lkIjogImRibV9oZGZzX2V4cG9ydGVyIiwgImRiX3R5cGUiOiAiaGRmcyIsICJkZXRhaWxzIjogeyJuYW1lIjogImRibV9oZGZzX2V4cG9ydGVyIiwgImxhYmVsIjogImNvbXBvbmVudCIsICJwYXJhbXMiOiB7InBsdWdpbiI6IHsiLWNvbXBvbmVudCI6ICJ7eyB0YXJnZXQuc2VydmljZS5sYWJlbHNbXCJpbnN0YW5jZV9uYW1lXCJdIH19IiwgIi1qbXguaHR0cC5ob3N0IjogInt7IHRhcmdldC5ob3N0LmJrX2hvc3RfaW5uZXJpcCB9fSIsICItam14Lmh0dHAucG9ydCI6ICJ7eyB0YXJnZXQuc2VydmljZS5sYWJlbHNbXCJqbXhfaHR0cF9wb3J0XCJdIH19IiwgIi1jb21wb25lbnQucnBjLnBvcnQiOiAie3sgdGFyZ2V0LnNlcnZpY2UubGFiZWxzW1wicnBjX3BvcnRcIl0gfX0iLCAiLXdlYi5saXN0ZW4uYWRkcmVzcyI6ICIke2hvc3R9OiR7cG9ydH0iLCAiXHU2NzBkXHU1MmExXHU1YjllXHU0ZjhiXHU3ZWY0XHU1ZWE2XHU2Y2U4XHU1MTY1IjogeyJhcHAiOiAiYXBwIiwgImluc3RhbmNlIjogImluc3RhbmNlIiwgImNsdXN0ZXJfbmFtZSI6ICJjbHVzdGVyX25hbWUiLCAiY2x1c3Rlcl90eXBlIjogImNsdXN0ZXJfdHlwZSIsICJpbnN0YW5jZV9ob3N0IjogImluc3RhbmNlX2hvc3QiLCAiaW5zdGFuY2Vfcm9sZSI6ICJpbnN0YW5jZV9yb2xlIiwgImNsdXN0ZXJfZG9tYWluIjogImNsdXN0ZXJfZG9tYWluIn0sICItY29tcG9uZW50LnNlcnZpY2UucnBjLnBvcnQiOiAie3sgdGFyZ2V0LnNlcnZpY2UubGFiZWxzW1wic2VydmljZV9ycGNfcG9ydFwiXSB9fSJ9LCAiY29sbGVjdG9yIjogeyJob3N0IjogIjEyNy4wLjAuMSIsICJwb3J0IjogIjkwNzAiLCAicGVyaW9kIjogNjAsICJ0aW1lb3V0IjogNjB9LCAidGFyZ2V0X25vZGVfdHlwZSI6ICJUT1BPIiwgInRhcmdldF9vYmplY3RfdHlwZSI6ICJTRVJWSUNFIn0sICJwbHVnaW5faW5mbyI6IHsicGx1Z2luX2lkIjogImRibV9oZGZzX2V4cG9ydGVyIiwgIm1ldHJpY19qc29uIjogW3siZmllbGRzIjogW3sibmFtZSI6ICJoYWRvb3BfZGF0YW5vZGVfYmxvY2tfcmVwb3J0c19udW1fb3BzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX2RhdGFub2RlX2Jsb2Nrc19jYWNoZWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfZGF0YW5vZGVfYmxvY2tzX3JlYWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfZGF0YW5vZGVfYmxvY2tzX3JlbW92ZWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfZGF0YW5vZGVfYmxvY2tzX3JlcGxpY2F0ZWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfZGF0YW5vZGVfYmxvY2tzX3VuY2FjaGVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX2RhdGFub2RlX2Jsb2Nrc193cml0dGVuIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX2RhdGFub2RlX2J5dGVzX3JlYWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfZGF0YW5vZGVfYnl0ZXNfd3JpdHRlbiIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9kYXRhbm9kZV9kYXRhbm9kZV9uZXR3b3JrX2Vycm9ycyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9kYXRhbm9kZV9mbHVzaF9uYW5vc19hdmdfdGltZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9kYXRhbm9kZV9mbHVzaF9uYW5vc19udW1fb3BzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX2RhdGFub2RlX2ZzeW5jX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX2RhdGFub2RlX2ZzeW5jX25hbm9zX2F2Z190aW1lIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX2RhdGFub2RlX2ZzeW5jX25hbm9zX251bV9vcHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfZGF0YW5vZGVfaGVhcnRiZWF0c19udW1fb3BzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX2RhdGFub2RlX3JhbV9kaXNrX2Jsb2Nrc19ldmljdGVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX2RhdGFub2RlX3JhbV9kaXNrX2Jsb2Nrc19yZWFkX2hpdHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfZGF0YW5vZGVfcmVhZF9ibG9ja19vcF9hdmdfdGltZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9kYXRhbm9kZV92b2x1bWVfZmFpbHVyZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfZGF0YW5vZGVfd3JpdGVfYmxvY2tfb3BfYXZnX3RpbWUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfZGF0YW5vZGVfYWRkX2Jsb2NrX2F2Z190aW1lIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX2RhdGFub2RlX2FkZF9ibG9ja19udW1fb3BzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX2RhdGFub2RlX2NhbGxfcXVldWVfbGVuZ3RoIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX2RhdGFub2RlX2RlbGV0ZV9hdmdfdGltZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9kYXRhbm9kZV9kZWxldGVfbnVtX29wcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9kYXRhbm9kZV9nY19jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9kYXRhbm9kZV9nY190aW1lX21pbGxpcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9kYXRhbm9kZV9nZXRfZmlsZV9pbmZvX2F2Z190aW1lIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX2RhdGFub2RlX2dldF9maWxlX2luZm9fbnVtX29wcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9kYXRhbm9kZV9nZXRfZnNfc3RhdHNfYXZnX3RpbWUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfZGF0YW5vZGVfZ2V0X2ZzX3N0YXRzX251bV9vcHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfZGF0YW5vZGVfbG9nX2Vycm9yIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX2RhdGFub2RlX2xvZ19mYXRhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9kYXRhbm9kZV9sb2dfaW5mbyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9kYXRhbm9kZV9sb2dfd2FybiIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9kYXRhbm9kZV9tZW1faGVhcF9jb21taXR0ZWRfbSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9kYXRhbm9kZV9tZW1faGVhcF9tYXhfbSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9kYXRhbm9kZV9tZW1faGVhcF91c2VkX20iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfZGF0YW5vZGVfbWVtX21heF9tIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX2RhdGFub2RlX21lbV9ub25faGVhcF9jb21taXR0ZWRfbSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9kYXRhbm9kZV9tZW1fbm9uX2hlYXBfbWF4X20iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfZGF0YW5vZGVfbWVtX25vbl9oZWFwX3VzZWRfbSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9kYXRhbm9kZV9yZWNlaXZlZF9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9kYXRhbm9kZV9ycGNfYXV0aG9yaXphdGlvbl9mYWlsdXJlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9kYXRhbm9kZV9ycGNfcHJvY2Vzc2luZ190aW1lX2F2Z190aW1lIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX2RhdGFub2RlX3JwY19wcm9jZXNzaW5nX3RpbWVfbnVtX29wcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9kYXRhbm9kZV9ycGNfcXVldWVfdGltZV9hdmdfdGltZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9kYXRhbm9kZV9ycGNfcXVldWVfdGltZV9udW1fb3BzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX2RhdGFub2RlX3NlbnRfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfZGF0YW5vZGVfc2V0X3NhZmVfbW9kZV9hdmdfdGltZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9kYXRhbm9kZV9zZXRfc2FmZV9tb2RlX251bV9vcHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfZGF0YW5vZGVfdGhyZWFkc19ibG9ja2VkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX2RhdGFub2RlX3RocmVhZHNfbmV3IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX2RhdGFub2RlX3RocmVhZHNfcnVubmFibGUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfZGF0YW5vZGVfdGhyZWFkc190aW1lZF93YWl0aW5nIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX2RhdGFub2RlX3RocmVhZHNfd2FpdGluZyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9kYXRhbm9kZV94Y2VpdmVyX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX25hbWVub2RlX1N0YXRlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX25hbWVub2RlX2Jsb2NrX2NhcGFjaXR5IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX25hbWVub2RlX2Jsb2Nrc190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV9jYXBhY2l0eV9yZW1haW5pbmciLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfbmFtZW5vZGVfY2FwYWNpdHlfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfbmFtZW5vZGVfY2FwYWNpdHlfdXNlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV9jYXBhY2l0eV91c2VkX25vbl9kX2ZfcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV9jb3JydXB0X2Jsb2NrcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV9lc3RpbWF0ZWRfY2FwYWNpdHlfbG9zdF90b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV9leGNlc3NfYmxvY2tzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX25hbWVub2RlX2V4cGlyZWRfaGVhcnRiZWF0cyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV9mX3Nfc3RhdGUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfbmFtZW5vZGVfZmlsZXNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfbmFtZW5vZGVfbGFzdF9jaGVja3BvaW50X3RpbWUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfbmFtZW5vZGVfbGFzdF93cml0dGVuX3RyYW5zYWN0aW9uX2lkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX25hbWVub2RlX21pc3NpbmdfYmxvY2tzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX25hbWVub2RlX21pc3NpbmdfcmVwbF9vbmVfYmxvY2tzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX25hbWVub2RlX251bV9kZWFkX2RhdGFfbm9kZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfbmFtZW5vZGVfbnVtX2RlY29tX2RlYWRfZGF0YV9ub2RlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV9udW1fbGl2ZV9kYXRhX25vZGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX25hbWVub2RlX251bV9zdGFsZV9kYXRhX25vZGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX25hbWVub2RlX251bV9zdGFsZV9zdG9yYWdlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV9wZW5kaW5nX2RhdGFfbm9kZV9tZXNzYWdlX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX25hbWVub2RlX3BlbmRpbmdfZGVsZXRpb25fYmxvY2tzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX25hbWVub2RlX3BlbmRpbmdfcmVwbGljYXRpb25fYmxvY2tzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX25hbWVub2RlX3Bvc3Rwb25lZF9taXNfcmVwbGljYXRlZF9ibG9ja3MiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfbmFtZW5vZGVfc2NoZWR1bGVkX3JlcGxpY2F0aW9uX2Jsb2NrcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV9zdGFsZV9kYXRhX25vZGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX25hbWVub2RlX3RvdGFsX2ZpbGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX25hbWVub2RlX3RvdGFsX2xvYWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfbmFtZW5vZGVfdHJhbnNhY3Rpb25zX3NpbmNlX2xhc3RfY2hlY2twb2ludCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV90cmFuc2FjdGlvbnNfc2luY2VfbGFzdF9sb2dfcm9sbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV91bmRlcl9yZXBsaWNhdGVkX2Jsb2NrcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV92b2x1bWVfZmFpbHVyZXNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJnb19nY19kdXJhdGlvbl9zZWNvbmRzX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX2djX2R1cmF0aW9uX3NlY29uZHNfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJnb19nb3JvdXRpbmVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fbWVtc3RhdHNfYWxsb2NfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJnb19tZW1zdGF0c19hbGxvY19ieXRlc190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX2J1Y2tfaGFzaF9zeXNfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJnb19tZW1zdGF0c19mcmVlc190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX2djX3N5c19ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX2hlYXBfYWxsb2NfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJnb19tZW1zdGF0c19oZWFwX2lkbGVfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJnb19tZW1zdGF0c19oZWFwX2ludXNlX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fbWVtc3RhdHNfaGVhcF9vYmplY3RzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fbWVtc3RhdHNfaGVhcF9yZWxlYXNlZF9ieXRlc190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX2hlYXBfc3lzX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fbWVtc3RhdHNfbGFzdF9nY190aW1lX3NlY29uZHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJnb19tZW1zdGF0c19sb29rdXBzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fbWVtc3RhdHNfbWFsbG9jc190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX21jYWNoZV9pbnVzZV9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX21jYWNoZV9zeXNfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJnb19tZW1zdGF0c19tc3Bhbl9pbnVzZV9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX21zcGFuX3N5c19ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX25leHRfZ2NfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJnb19tZW1zdGF0c19vdGhlcl9zeXNfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJnb19tZW1zdGF0c19zdGFja19pbnVzZV9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX3N0YWNrX3N5c19ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX3N5c19ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV9hZGRfYmxvY2tfYXZnX3RpbWUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfbmFtZW5vZGVfYWRkX2Jsb2NrX251bV9vcHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfbmFtZW5vZGVfYWRkX2Jsb2NrX29wcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV9ibG9ja19yZWNlaXZlZF9hbmRfZGVsZXRlZF9hdmdfdGltZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV9ibG9ja19yZWNlaXZlZF9hbmRfZGVsZXRlZF9udW1fb3BzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX25hbWVub2RlX2Jsb2NrX3JlY2VpdmVkX2FuZF9kZWxldGVkX29wcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV9ibG9ja19yZXBvcnRfYXZnX3RpbWUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfbmFtZW5vZGVfYmxvY2tfcmVwb3J0X251bV9vcHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfbmFtZW5vZGVfY2FsbF9xdWV1ZV9sZW5ndGgiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfbmFtZW5vZGVfY29tbWl0X2Jsb2NrX3N5bmNocm9uaXphdGlvbl9hdmdfdGltZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV9jb21taXRfYmxvY2tfc3luY2hyb25pemF0aW9uX251bV9vcHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfbmFtZW5vZGVfY3JlYXRlX2ZpbGVfb3BzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX25hbWVub2RlX2RlbGV0ZV9hdmdfdGltZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV9kZWxldGVfZmlsZV9vcHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfbmFtZW5vZGVfZGVsZXRlX251bV9vcHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfbmFtZW5vZGVfZmlsZV9pbmZvX29wcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV9maWxlc19hcHBlbmRlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV9maWxlc19kZWxldGVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX25hbWVub2RlX2ZpbGVzX2luX2dldF9saXN0aW5nX29wcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV9nY19jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV9nY190aW1lX21pbGxpcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV9nZXRfZmlsZV9pbmZvX2F2Z190aW1lIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX25hbWVub2RlX2dldF9maWxlX2luZm9fbnVtX29wcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV9nZXRfZnNfc3RhdHNfYXZnX3RpbWUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfbmFtZW5vZGVfZ2V0X2ZzX3N0YXRzX251bV9vcHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfbmFtZW5vZGVfbG9nX2Vycm9yIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX25hbWVub2RlX2xvZ19mYXRhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV9sb2dfaW5mbyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV9sb2dfd2FybiIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV9tZW1faGVhcF9jb21taXR0ZWRfbSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV9tZW1faGVhcF9tYXhfbSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV9tZW1faGVhcF91c2VkX20iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfbmFtZW5vZGVfbWVtX21heF9tIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX25hbWVub2RlX21lbV9ub25faGVhcF9jb21taXR0ZWRfbSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV9tZW1fbm9uX2hlYXBfbWF4X20iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfbmFtZW5vZGVfbWVtX25vbl9oZWFwX3VzZWRfbSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV9yZWNlaXZlZF9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV9yb2xsX2VkaXRfbG9nX251bV9vcHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfbmFtZW5vZGVfcnBjX2F1dGhvcml6YXRpb25fZmFpbHVyZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfbmFtZW5vZGVfcnBjX3Byb2Nlc3NpbmdfdGltZV9hdmdfdGltZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV9ycGNfcHJvY2Vzc2luZ190aW1lX251bV9vcHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfbmFtZW5vZGVfcnBjX3F1ZXVlX3RpbWVfYXZnX3RpbWUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfbmFtZW5vZGVfcnBjX3F1ZXVlX3RpbWVfbnVtX29wcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhZG9vcF9uYW1lbm9kZV9zZW50X2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX25hbWVub2RlX3NldF9zYWZlX21vZGVfYXZnX3RpbWUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfbmFtZW5vZGVfc2V0X3NhZmVfbW9kZV9udW1fb3BzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX25hbWVub2RlX3N5bmNzX2F2Z190aW1lIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX25hbWVub2RlX3N5bmNzX251bV9vcHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfbmFtZW5vZGVfdGhyZWFkc19ibG9ja2VkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX25hbWVub2RlX3RocmVhZHNfbmV3IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX25hbWVub2RlX3RocmVhZHNfcnVubmFibGUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJoYWRvb3BfbmFtZW5vZGVfdGhyZWFkc190aW1lZF93YWl0aW5nIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaGFkb29wX25hbWVub2RlX3RocmVhZHNfd2FpdGluZyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb2Nlc3NfY3B1X3NlY29uZHNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jZXNzX21heF9mZHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jZXNzX29wZW5fZmRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY2Vzc19yZXNpZGVudF9tZW1vcnlfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jZXNzX3N0YXJ0X3RpbWVfc2Vjb25kcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb2Nlc3NfdmlydHVhbF9tZW1vcnlfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX1dLCAidGFibGVfaWQiOiAiZXhwb3J0ZXJfZGJtX2hkZnNfZXhwb3J0ZXIuR3JvdXAxIiwgInRhYmxlX2Rlc2MiOiAiXHU1MjA2XHU3ZWM0MSIsICJ0YWJsZV9uYW1lIjogIkdyb3VwMSJ9LCB7ImZpZWxkcyI6IFt7Im5hbWUiOiAiZ29fZ2NfZHVyYXRpb25fc2Vjb25kcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbInF1YW50aWxlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJxdWFudGlsZSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfV0sICJ0YWJsZV9pZCI6ICJleHBvcnRlcl9kYm1faGRmc19leHBvcnRlci5Hcm91cDIiLCAidGFibGVfZGVzYyI6ICJcdTUyMDZcdTdlYzQyIiwgInRhYmxlX25hbWUiOiAiR3JvdXAyIn0sIHsiZmllbGRzIjogW3sibmFtZSI6ICJoYWRvb3BfbmFtZW5vZGVfbGl2ZV9ub2Rlc19sYXN0X2NvbnRhY3QiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJkYXRhbm9kZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZGF0YW5vZGUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX1dLCAidGFibGVfaWQiOiAiZXhwb3J0ZXJfZGJtX2hkZnNfZXhwb3J0ZXIuR3JvdXAzIiwgInRhYmxlX2Rlc2MiOiAiXHU1MjA2XHU3ZWM0MyIsICJ0YWJsZV9uYW1lIjogIkdyb3VwMyJ9XSwgInBsdWdpbl90eXBlIjogIkV4cG9ydGVyIiwgIm9zX3R5cGVfbGlzdCI6IFsibGludXgiXX0sICJjb2xsZWN0X3R5cGUiOiAiRXhwb3J0ZXIiLCAidGFyZ2V0X25vZGVzIjogW10sICJ0YXJnZXRfbm9kZV90eXBlIjogIlRPUE8iLCAidGFyZ2V0X29iamVjdF90eXBlIjogIlNFUlZJQ0UifX0= \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/collect/0.influxdb.dbm_influxdb_bkpull.json b/dbm-ui/backend/db_monitor/tpls/collect/0.influxdb.dbm_influxdb_bkpull.json new file mode 100644 index 0000000000..0b7f6a3f55 --- /dev/null +++ b/dbm-ui/backend/db_monitor/tpls/collect/0.influxdb.dbm_influxdb_bkpull.json @@ -0,0 +1,5797 @@ +{ + "bk_biz_id": 0, + "plugin_id": "dbm_influxdb_bkpull", + "db_type": "influxdb", + "details": { + "name": "dbm_influxdb_bkpull", + "label": "component", + "params": { + "plugin": { + "服务实例维度注入": { + "app": "app", + "db_group": "db_group", + "instance": "instance", + "bk_app_code": "app_id", + "cluster_name": "cluster_name", + "instance_host": "instance_host", + "instance_port": "instance_port" + } + }, + "collector": { + "period": 60, + "timeout": 60, + "password": "", + "username": "", + "metrics_url": "http://127.0.0.1:9274/metrics" + }, + "target_node_type": "TOPO", + "target_object_type": "SERVICE" + }, + "plugin_info": { + "plugin_id": "dbm_influxdb_bkpull", + "metric_json": [ + { + "fields": [ + { + "name": "_database", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "database", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "influxdb_shard_diskBytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "url", + "engine", + "path", + "walPath", + "id", + "cluster", + "dbgroup", + "influx_host", + "dbgroup_id", + "database", + "host", + "retentionPolicy", + "influx_port", + "indexType", + "dbrole" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_shard_fieldsCreate", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_port", + "database", + "dbrole", + "cluster", + "id", + "path", + "influx_host", + "engine", + "walPath", + "dbgroup_id", + "retentionPolicy", + "host", + "url", + "dbgroup", + "indexType" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_shard_seriesCreate", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "engine", + "path", + "id", + "dbgroup", + "influx_port", + "retentionPolicy", + "host", + "database", + "url", + "influx_host", + "dbrole", + "indexType", + "cluster", + "walPath", + "dbgroup_id" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_shard_writeBytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_port", + "walPath", + "engine", + "path", + "cluster", + "host", + "url", + "influx_host", + "dbgroup_id", + "dbgroup", + "dbrole", + "id", + "indexType", + "database", + "retentionPolicy" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_shard_writePointsDropped", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup", + "id", + "influx_port", + "influx_host", + "url", + "database", + "dbgroup_id", + "indexType", + "path", + "dbrole", + "host", + "retentionPolicy", + "engine", + "cluster", + "walPath" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_shard_writePointsErr", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "url", + "walPath", + "path", + "host", + "cluster", + "engine", + "dbgroup_id", + "dbrole", + "dbgroup", + "indexType", + "id", + "influx_host", + "retentionPolicy", + "influx_port", + "database" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_shard_writePointsOk", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "path", + "walPath", + "url", + "id", + "cluster", + "dbrole", + "dbgroup", + "retentionPolicy", + "dbgroup_id", + "engine", + "host", + "indexType", + "influx_host", + "database", + "influx_port" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_shard_writeReq", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbrole", + "cluster", + "dbgroup_id", + "walPath", + "engine", + "id", + "influx_host", + "path", + "host", + "indexType", + "influx_port", + "url", + "retentionPolicy", + "dbgroup", + "database" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_shard_writeReqErr", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "dbrole", + "database", + "url", + "retentionPolicy", + "influx_host", + "engine", + "dbgroup", + "path", + "indexType", + "id", + "dbgroup_id", + "host", + "walPath", + "influx_port" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_shard_writeReqOk", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "engine", + "host", + "dbgroup_id", + "walPath", + "cluster", + "influx_host", + "influx_port", + "url", + "retentionPolicy", + "path", + "id", + "indexType", + "dbgroup", + "dbrole", + "database" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_shard_writeValuesOk", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "database", + "influx_host", + "cluster", + "indexType", + "dbgroup", + "host", + "walPath", + "dbgroup_id", + "id", + "engine", + "dbrole", + "influx_port", + "retentionPolicy", + "url", + "path" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_cache_WALCompactionTimeMs", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "indexType", + "database", + "engine", + "path", + "dbgroup", + "influx_port", + "retentionPolicy", + "cluster", + "influx_host", + "dbrole", + "id", + "host", + "url", + "walPath", + "dbgroup_id" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_cache_cacheAgeMs", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup_id", + "url", + "walPath", + "engine", + "indexType", + "database", + "retentionPolicy", + "dbgroup", + "influx_host", + "cluster", + "host", + "id", + "path", + "dbrole", + "influx_port" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_cache_cachedBytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "id", + "database", + "engine", + "influx_port", + "dbgroup", + "url", + "path", + "walPath", + "influx_host", + "cluster", + "dbrole", + "host", + "dbgroup_id", + "retentionPolicy", + "indexType" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_cache_diskBytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "walPath", + "database", + "dbgroup", + "influx_port", + "path", + "id", + "dbrole", + "host", + "url", + "cluster", + "indexType", + "retentionPolicy", + "engine", + "dbgroup_id", + "influx_host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_cache_memBytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "retentionPolicy", + "dbgroup_id", + "dbrole", + "host", + "indexType", + "walPath", + "path", + "id", + "cluster", + "influx_port", + "influx_host", + "database", + "dbgroup", + "engine", + "url" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_cache_snapshotCount", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "retentionPolicy", + "indexType", + "cluster", + "dbrole", + "dbgroup", + "engine", + "influx_port", + "path", + "database", + "host", + "influx_host", + "dbgroup_id", + "url", + "id", + "walPath" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_cache_writeDropped", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "retentionPolicy", + "host", + "cluster", + "database", + "dbgroup_id", + "url", + "walPath", + "id", + "influx_host", + "dbrole", + "dbgroup", + "influx_port", + "path", + "engine", + "indexType" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_cache_writeErr", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbrole", + "walPath", + "id", + "influx_host", + "host", + "dbgroup_id", + "dbgroup", + "database", + "path", + "url", + "influx_port", + "indexType", + "engine", + "cluster", + "retentionPolicy" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_cache_writeOk", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup", + "path", + "indexType", + "walPath", + "engine", + "influx_port", + "database", + "dbrole", + "retentionPolicy", + "dbgroup_id", + "url", + "influx_host", + "id", + "host", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_engine_cacheCompactionDuration", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "url", + "influx_port", + "walPath", + "path", + "indexType", + "database", + "dbgroup_id", + "engine", + "retentionPolicy", + "dbgroup", + "cluster", + "id", + "influx_host", + "host", + "dbrole" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_engine_cacheCompactionErr", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "indexType", + "path", + "influx_host", + "dbrole", + "dbgroup_id", + "url", + "retentionPolicy", + "engine", + "cluster", + "walPath", + "dbgroup", + "id", + "host", + "database", + "influx_port" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_engine_cacheCompactions", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "url", + "cluster", + "walPath", + "indexType", + "engine", + "dbgroup", + "database", + "host", + "id", + "influx_port", + "dbgroup_id", + "influx_host", + "retentionPolicy", + "dbrole", + "path" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_engine_cacheCompactionsActive", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "url", + "cluster", + "dbgroup", + "database", + "dbrole", + "walPath", + "influx_port", + "id", + "indexType", + "influx_host", + "host", + "engine", + "path", + "retentionPolicy", + "dbgroup_id" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_engine_tsmFullCompactionDuration", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "engine", + "url", + "database", + "dbrole", + "influx_host", + "retentionPolicy", + "host", + "path", + "indexType", + "id", + "dbgroup", + "influx_port", + "walPath", + "dbgroup_id" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_engine_tsmFullCompactionErr", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "engine", + "host", + "indexType", + "dbrole", + "cluster", + "database", + "id", + "path", + "influx_port", + "dbgroup", + "retentionPolicy", + "dbgroup_id", + "url", + "walPath", + "influx_host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_engine_tsmFullCompactionQueue", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "retentionPolicy", + "cluster", + "id", + "url", + "path", + "influx_host", + "engine", + "influx_port", + "dbrole", + "host", + "dbgroup_id", + "indexType", + "dbgroup", + "database", + "walPath" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_engine_tsmFullCompactions", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "indexType", + "dbrole", + "retentionPolicy", + "url", + "id", + "influx_host", + "dbgroup_id", + "dbgroup", + "cluster", + "engine", + "walPath", + "database", + "path", + "influx_port", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_engine_tsmFullCompactionsActive", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "engine", + "dbgroup", + "cluster", + "dbgroup_id", + "host", + "path", + "url", + "indexType", + "dbrole", + "influx_host", + "influx_port", + "retentionPolicy", + "walPath", + "database", + "id" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_engine_tsmLevel1CompactionDuration", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "retentionPolicy", + "url", + "indexType", + "id", + "database", + "cluster", + "walPath", + "dbgroup", + "dbgroup_id", + "host", + "influx_host", + "influx_port", + "path", + "engine", + "dbrole" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_engine_tsmLevel1CompactionErr", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup", + "host", + "walPath", + "indexType", + "id", + "influx_port", + "cluster", + "retentionPolicy", + "path", + "influx_host", + "engine", + "dbrole", + "database", + "dbgroup_id", + "url" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_engine_tsmLevel1CompactionQueue", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "url", + "dbgroup", + "dbrole", + "id", + "indexType", + "host", + "path", + "engine", + "influx_host", + "retentionPolicy", + "dbgroup_id", + "influx_port", + "walPath", + "database", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_engine_tsmLevel1Compactions", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "indexType", + "url", + "influx_port", + "host", + "dbgroup", + "path", + "retentionPolicy", + "dbrole", + "walPath", + "dbgroup_id", + "influx_host", + "engine", + "cluster", + "id", + "database" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_engine_tsmLevel1CompactionsActive", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbrole", + "engine", + "url", + "influx_port", + "retentionPolicy", + "database", + "dbgroup", + "dbgroup_id", + "indexType", + "id", + "cluster", + "influx_host", + "path", + "host", + "walPath" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_engine_tsmLevel2CompactionDuration", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "walPath", + "dbgroup_id", + "indexType", + "retentionPolicy", + "dbrole", + "database", + "url", + "id", + "influx_port", + "path", + "influx_host", + "cluster", + "engine", + "dbgroup", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_engine_tsmLevel2CompactionErr", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "engine", + "influx_port", + "dbrole", + "database", + "host", + "walPath", + "id", + "influx_host", + "dbgroup_id", + "path", + "retentionPolicy", + "indexType", + "cluster", + "url", + "dbgroup" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_engine_tsmLevel2CompactionQueue", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "indexType", + "influx_host", + "dbgroup", + "influx_port", + "path", + "engine", + "dbrole", + "walPath", + "dbgroup_id", + "host", + "retentionPolicy", + "url", + "database", + "cluster", + "id" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_engine_tsmLevel2Compactions", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "url", + "id", + "path", + "dbrole", + "dbgroup_id", + "database", + "influx_host", + "retentionPolicy", + "walPath", + "dbgroup", + "cluster", + "engine", + "influx_port", + "indexType" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_engine_tsmLevel2CompactionsActive", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "url", + "influx_port", + "walPath", + "dbgroup_id", + "influx_host", + "id", + "engine", + "database", + "path", + "dbrole", + "host", + "cluster", + "indexType", + "dbgroup", + "retentionPolicy" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_engine_tsmLevel3CompactionDuration", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbrole", + "id", + "retentionPolicy", + "influx_host", + "walPath", + "engine", + "url", + "cluster", + "database", + "dbgroup_id", + "indexType", + "dbgroup", + "host", + "influx_port", + "path" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_engine_tsmLevel3CompactionErr", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "id", + "dbrole", + "path", + "dbgroup_id", + "cluster", + "retentionPolicy", + "url", + "database", + "dbgroup", + "engine", + "walPath", + "host", + "indexType", + "influx_port", + "influx_host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_engine_tsmLevel3CompactionQueue", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "influx_host", + "dbgroup", + "url", + "database", + "dbrole", + "id", + "influx_port", + "host", + "indexType", + "path", + "engine", + "retentionPolicy", + "walPath", + "dbgroup_id" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_engine_tsmLevel3Compactions", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "id", + "walPath", + "dbgroup_id", + "engine", + "database", + "dbrole", + "retentionPolicy", + "influx_host", + "dbgroup", + "path", + "influx_port", + "indexType", + "host", + "url" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_engine_tsmLevel3CompactionsActive", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup_id", + "walPath", + "path", + "database", + "engine", + "cluster", + "id", + "retentionPolicy", + "url", + "indexType", + "influx_host", + "host", + "influx_port", + "dbgroup", + "dbrole" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_engine_tsmOptimizeCompactionDuration", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_port", + "influx_host", + "dbgroup", + "host", + "cluster", + "retentionPolicy", + "path", + "dbrole", + "indexType", + "url", + "id", + "database", + "engine", + "walPath", + "dbgroup_id" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_engine_tsmOptimizeCompactionErr", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup", + "path", + "dbrole", + "walPath", + "influx_host", + "cluster", + "dbgroup_id", + "indexType", + "url", + "id", + "influx_port", + "host", + "database", + "engine", + "retentionPolicy" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_engine_tsmOptimizeCompactionQueue", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup_id", + "dbrole", + "retentionPolicy", + "cluster", + "dbgroup", + "indexType", + "influx_host", + "engine", + "path", + "database", + "host", + "url", + "id", + "walPath", + "influx_port" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_engine_tsmOptimizeCompactions", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_port", + "indexType", + "dbgroup", + "dbgroup_id", + "engine", + "id", + "retentionPolicy", + "walPath", + "path", + "database", + "dbrole", + "influx_host", + "url", + "cluster", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_engine_tsmOptimizeCompactionsActive", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "engine", + "dbrole", + "influx_port", + "id", + "walPath", + "indexType", + "dbgroup", + "dbgroup_id", + "cluster", + "influx_host", + "host", + "url", + "retentionPolicy", + "path", + "database" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_filestore_diskBytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "indexType", + "influx_port", + "dbgroup_id", + "engine", + "host", + "walPath", + "dbgroup", + "database", + "dbrole", + "id", + "path", + "influx_host", + "cluster", + "retentionPolicy", + "url" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_filestore_numFiles", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "database", + "walPath", + "id", + "dbrole", + "path", + "url", + "influx_port", + "cluster", + "indexType", + "engine", + "dbgroup_id", + "host", + "dbgroup", + "influx_host", + "retentionPolicy" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_wal_currentSegmentDiskBytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_port", + "database", + "dbrole", + "influx_host", + "dbgroup_id", + "cluster", + "url", + "host", + "retentionPolicy", + "dbgroup", + "walPath", + "path", + "engine", + "indexType", + "id" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_wal_oldSegmentsDiskBytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup", + "retentionPolicy", + "influx_host", + "dbgroup_id", + "cluster", + "dbrole", + "path", + "walPath", + "influx_port", + "id", + "database", + "host", + "indexType", + "engine", + "url" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_wal_writeErr", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup", + "cluster", + "id", + "walPath", + "path", + "host", + "retentionPolicy", + "engine", + "indexType", + "database", + "dbrole", + "influx_port", + "url", + "dbgroup_id", + "influx_host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_tsm1_wal_writeOk", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbrole", + "walPath", + "indexType", + "dbgroup_id", + "host", + "dbgroup", + "retentionPolicy", + "engine", + "influx_port", + "database", + "influx_host", + "id", + "path", + "url", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_database_numMeasurements", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup_id", + "host", + "database", + "dbrole", + "cluster", + "dbgroup", + "url", + "influx_port", + "influx_host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_database_numSeries", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "url", + "host", + "dbrole", + "influx_host", + "dbgroup", + "influx_port", + "cluster", + "database", + "dbgroup_id" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_cq_queryFail", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup_id", + "host", + "dbrole", + "influx_host", + "influx_port", + "url", + "cluster", + "dbgroup" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_cq_queryOk", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup", + "dbgroup_id", + "influx_host", + "cluster", + "dbrole", + "host", + "influx_port", + "url" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_queryExecutor_queriesActive", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_host", + "influx_port", + "cluster", + "dbgroup_id", + "dbrole", + "dbgroup", + "url", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_queryExecutor_queriesExecuted", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "influx_host", + "dbgroup_id", + "dbrole", + "influx_port", + "url", + "cluster", + "dbgroup" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_queryExecutor_queriesFinished", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbrole", + "cluster", + "dbgroup", + "dbgroup_id", + "host", + "influx_host", + "influx_port", + "url" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_queryExecutor_queryDurationNs", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "url", + "dbgroup_id", + "host", + "influx_host", + "dbgroup", + "cluster", + "dbrole", + "influx_port" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_queryExecutor_recoveredPanics", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "dbgroup", + "dbgroup_id", + "dbrole", + "host", + "influx_host", + "influx_port", + "url" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_subscriber_createFailures", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "dbgroup_id", + "dbrole", + "influx_host", + "url", + "host", + "influx_port", + "dbgroup" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_subscriber_pointsWritten", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup_id", + "influx_host", + "influx_port", + "url", + "cluster", + "dbgroup", + "dbrole", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_subscriber_writeFailures", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_host", + "url", + "dbgroup", + "influx_port", + "cluster", + "dbgroup_id", + "dbrole", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_write_pointReq", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup_id", + "dbrole", + "cluster", + "url", + "host", + "influx_host", + "influx_port", + "dbgroup" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_write_pointReqLocal", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbrole", + "host", + "influx_port", + "cluster", + "dbgroup", + "dbgroup_id", + "influx_host", + "url" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_write_req", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup_id", + "dbrole", + "influx_port", + "url", + "dbgroup", + "host", + "influx_host", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_write_subWriteDrop", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "url", + "cluster", + "dbgroup", + "dbgroup_id", + "dbrole", + "host", + "influx_host", + "influx_port" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_write_subWriteOk", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_port", + "url", + "cluster", + "dbgroup_id", + "host", + "influx_host", + "dbgroup", + "dbrole" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_write_writeDrop", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_port", + "dbgroup", + "dbgroup_id", + "dbrole", + "influx_host", + "cluster", + "host", + "url" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_write_writeError", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "dbgroup", + "influx_port", + "url", + "dbgroup_id", + "dbrole", + "host", + "influx_host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_write_writeOk", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup", + "host", + "influx_host", + "influx_port", + "url", + "dbgroup_id", + "dbrole", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_write_writeTimeout", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "url", + "cluster", + "influx_host", + "dbrole", + "host", + "dbgroup", + "dbgroup_id", + "influx_port" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_n_shards", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_port", + "cluster", + "dbgroup", + "dbgroup_id", + "dbrole", + "host", + "influx_host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mem_active", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbrole", + "host", + "influx_host", + "influx_port", + "cluster", + "dbgroup", + "dbgroup_id" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mem_available", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "influx_host", + "influx_port", + "cluster", + "dbgroup", + "dbgroup_id", + "dbrole" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mem_available_percent", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbrole", + "host", + "influx_host", + "influx_port", + "cluster", + "dbgroup", + "dbgroup_id" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mem_buffered", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_host", + "influx_port", + "cluster", + "dbgroup", + "dbgroup_id", + "dbrole", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mem_cached", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup", + "dbgroup_id", + "dbrole", + "host", + "influx_host", + "influx_port", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mem_commit_limit", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup_id", + "dbrole", + "host", + "influx_host", + "influx_port", + "cluster", + "dbgroup" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mem_committed_as", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbrole", + "host", + "influx_host", + "influx_port", + "cluster", + "dbgroup", + "dbgroup_id" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mem_dirty", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_port", + "cluster", + "dbgroup", + "dbgroup_id", + "dbrole", + "host", + "influx_host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mem_free", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup", + "dbgroup_id", + "dbrole", + "host", + "influx_host", + "influx_port", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mem_high_free", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "dbgroup", + "dbgroup_id", + "dbrole", + "host", + "influx_host", + "influx_port" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mem_high_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_host", + "influx_port", + "cluster", + "dbgroup", + "dbgroup_id", + "dbrole", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mem_huge_page_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_host", + "influx_port", + "cluster", + "dbgroup", + "dbgroup_id", + "dbrole", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mem_huge_pages_free", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbrole", + "host", + "influx_host", + "influx_port", + "cluster", + "dbgroup", + "dbgroup_id" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mem_huge_pages_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "dbgroup", + "dbgroup_id", + "dbrole", + "host", + "influx_host", + "influx_port" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mem_inactive", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_host", + "influx_port", + "cluster", + "dbgroup", + "dbgroup_id", + "dbrole", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mem_low_free", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup", + "dbgroup_id", + "dbrole", + "host", + "influx_host", + "influx_port", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mem_low_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup", + "dbgroup_id", + "dbrole", + "host", + "influx_host", + "influx_port", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mem_mapped", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "influx_host", + "influx_port", + "cluster", + "dbgroup", + "dbgroup_id", + "dbrole" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mem_page_tables", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup_id", + "dbrole", + "host", + "influx_host", + "influx_port", + "cluster", + "dbgroup" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mem_shared", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup_id", + "dbrole", + "host", + "influx_host", + "influx_port", + "cluster", + "dbgroup" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mem_slab", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_host", + "influx_port", + "cluster", + "dbgroup", + "dbgroup_id", + "dbrole", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mem_swap_cached", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup_id", + "dbrole", + "host", + "influx_host", + "influx_port", + "cluster", + "dbgroup" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mem_swap_free", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_host", + "influx_port", + "cluster", + "dbgroup", + "dbgroup_id", + "dbrole", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mem_swap_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbrole", + "host", + "influx_host", + "influx_port", + "cluster", + "dbgroup", + "dbgroup_id" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mem_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "influx_host", + "influx_port", + "cluster", + "dbgroup", + "dbgroup_id", + "dbrole" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mem_used", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "influx_host", + "influx_port", + "cluster", + "dbgroup", + "dbgroup_id", + "dbrole" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mem_used_percent", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "dbgroup", + "dbgroup_id", + "dbrole", + "host", + "influx_host", + "influx_port" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mem_vmalloc_chunk", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "influx_host", + "influx_port", + "cluster", + "dbgroup", + "dbgroup_id", + "dbrole" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mem_vmalloc_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_host", + "influx_port", + "cluster", + "dbgroup", + "dbgroup_id", + "dbrole", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mem_vmalloc_used", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "dbgroup", + "dbgroup_id", + "dbrole", + "host", + "influx_host", + "influx_port" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mem_wired", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup", + "dbgroup_id", + "dbrole", + "host", + "influx_host", + "influx_port", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mem_write_back", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup", + "dbgroup_id", + "dbrole", + "host", + "influx_host", + "influx_port", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mem_write_back_tmp", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_host", + "influx_port", + "cluster", + "dbgroup", + "dbgroup_id", + "dbrole", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "url", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "engine", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "path", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "walPath", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "id", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "cluster", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "dbgroup", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "influx_host", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "dbgroup_id", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "host", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "retentionPolicy", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "influx_port", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "indexType", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "dbrole", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "pushgateway_dbm_influxdb_bkpull.Group1", + "table_desc": "分组1", + "table_name": "Group1" + }, + { + "fields": [ + { + "name": "_server", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "server", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "http_response_http_response_code", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_host", + "dbrole", + "cluster", + "host", + "server", + "result", + "dbgroup", + "method", + "influx_port", + "status_code", + "dbgroup_id", + "result_type" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "http_response_response_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "dbgroup", + "result_type", + "dbgroup_id", + "dbrole", + "method", + "result", + "influx_port", + "status_code", + "influx_host", + "server", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "http_response_result_code", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "dbgroup", + "dbrole", + "influx_port", + "dbgroup_id", + "host", + "result", + "status_code", + "method", + "influx_host", + "server", + "result_type" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influx_host", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "dbrole", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "cluster", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "host", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "result", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "dbgroup", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "method", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "influx_port", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "status_code", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "dbgroup_id", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "result_type", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "pushgateway_dbm_influxdb_bkpull.Group2", + "table_desc": "分组2", + "table_name": "Group2" + }, + { + "fields": [ + { + "name": "disk_free", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbrole", + "fstype", + "dbgroup", + "influx_port", + "influx_host", + "device", + "mode", + "dbgroup_id", + "host", + "path", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "disk_inodes_free", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "influx_host", + "dbgroup", + "fstype", + "influx_port", + "dbrole", + "device", + "path", + "mode", + "dbgroup_id", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "disk_inodes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_port", + "device", + "dbgroup", + "influx_host", + "fstype", + "path", + "dbgroup_id", + "mode", + "dbrole", + "host", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "disk_inodes_used", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup_id", + "dbgroup", + "mode", + "dbrole", + "device", + "cluster", + "fstype", + "path", + "influx_host", + "influx_port", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "disk_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "device", + "host", + "path", + "dbgroup", + "influx_host", + "influx_port", + "dbrole", + "mode", + "dbgroup_id", + "fstype", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "disk_used", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_host", + "host", + "dbgroup", + "device", + "cluster", + "dbrole", + "fstype", + "influx_port", + "dbgroup_id", + "path", + "mode" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "disk_used_percent", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup", + "cluster", + "path", + "device", + "fstype", + "host", + "dbrole", + "influx_host", + "mode", + "dbgroup_id", + "influx_port" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "dbrole", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "fstype", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "dbgroup", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "influx_port", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "influx_host", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "device", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "mode", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "dbgroup_id", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "host", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "path", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "cluster", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "pushgateway_dbm_influxdb_bkpull.Group3", + "table_desc": "分组3", + "table_name": "Group3" + }, + { + "fields": [ + { + "name": "procstat_cpu_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "exe", + "cluster", + "dbgroup", + "influx_host", + "dbrole", + "dbgroup_id", + "process_name", + "user", + "influx_port", + "pid" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_cpu_time_guest", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "influx_host", + "user", + "dbrole", + "process_name", + "pid", + "cluster", + "exe", + "influx_port", + "dbgroup", + "dbgroup_id" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_cpu_time_guest_nice", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_host", + "pid", + "dbgroup_id", + "host", + "influx_port", + "user", + "exe", + "process_name", + "dbgroup", + "dbrole", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_cpu_time_idle", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "user", + "dbgroup", + "influx_host", + "influx_port", + "dbrole", + "host", + "cluster", + "process_name", + "dbgroup_id", + "exe", + "pid" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_cpu_time_iowait", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_host", + "influx_port", + "dbgroup_id", + "pid", + "exe", + "user", + "dbgroup", + "host", + "dbrole", + "process_name", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_cpu_time_irq", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup", + "process_name", + "dbgroup_id", + "pid", + "dbrole", + "influx_host", + "user", + "exe", + "host", + "influx_port", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_cpu_time_nice", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "exe", + "cluster", + "influx_host", + "influx_port", + "dbrole", + "process_name", + "user", + "dbgroup", + "dbgroup_id", + "host", + "pid" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_cpu_time_soft_irq", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup", + "host", + "dbrole", + "influx_host", + "pid", + "user", + "exe", + "dbgroup_id", + "process_name", + "cluster", + "influx_port" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_cpu_time_steal", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup_id", + "exe", + "influx_port", + "influx_host", + "process_name", + "user", + "cluster", + "host", + "pid", + "dbrole", + "dbgroup" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_cpu_time_stolen", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "pid", + "dbrole", + "host", + "influx_port", + "process_name", + "exe", + "influx_host", + "user", + "dbgroup", + "dbgroup_id", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_cpu_time_system", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_host", + "process_name", + "dbgroup_id", + "user", + "cluster", + "dbgroup", + "dbrole", + "exe", + "host", + "pid", + "influx_port" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_cpu_time_user", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "process_name", + "cluster", + "dbrole", + "exe", + "dbgroup_id", + "user", + "influx_port", + "host", + "influx_host", + "pid", + "dbgroup" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_cpu_usage", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup", + "influx_port", + "cluster", + "dbgroup_id", + "dbrole", + "host", + "pid", + "user", + "influx_host", + "exe", + "process_name" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_involuntary_context_switches", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbrole", + "pid", + "influx_port", + "exe", + "host", + "dbgroup_id", + "influx_host", + "cluster", + "user", + "dbgroup", + "process_name" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_memory_data", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_host", + "dbgroup_id", + "process_name", + "user", + "dbgroup", + "dbrole", + "host", + "pid", + "cluster", + "exe", + "influx_port" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_memory_locked", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "process_name", + "user", + "exe", + "pid", + "cluster", + "dbgroup", + "dbrole", + "host", + "influx_host", + "influx_port", + "dbgroup_id" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_memory_rss", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_port", + "process_name", + "cluster", + "dbgroup_id", + "dbrole", + "pid", + "user", + "exe", + "host", + "dbgroup", + "influx_host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_memory_stack", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "user", + "pid", + "process_name", + "host", + "exe", + "cluster", + "dbgroup", + "influx_port", + "dbrole", + "dbgroup_id", + "influx_host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_memory_swap", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_port", + "user", + "exe", + "pid", + "cluster", + "process_name", + "dbgroup_id", + "influx_host", + "dbgroup", + "dbrole", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_memory_vms", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_host", + "influx_port", + "dbgroup", + "user", + "exe", + "pid", + "dbgroup_id", + "process_name", + "cluster", + "host", + "dbrole" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_nice_priority", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "exe", + "influx_port", + "dbgroup", + "pid", + "process_name", + "dbgroup_id", + "host", + "influx_host", + "user", + "dbrole" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_num_fds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup_id", + "dbgroup", + "influx_port", + "user", + "cluster", + "dbrole", + "host", + "influx_host", + "exe", + "pid", + "process_name" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_num_threads", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_port", + "dbgroup_id", + "host", + "pid", + "cluster", + "dbrole", + "dbgroup", + "exe", + "process_name", + "influx_host", + "user" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_read_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbrole", + "host", + "exe", + "dbgroup_id", + "influx_host", + "cluster", + "dbgroup", + "influx_port", + "pid", + "process_name", + "user" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_read_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "pid", + "dbrole", + "exe", + "process_name", + "cluster", + "dbgroup_id", + "host", + "influx_port", + "user", + "dbgroup", + "influx_host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_realtime_priority", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_host", + "cluster", + "user", + "host", + "dbrole", + "exe", + "dbgroup", + "influx_port", + "pid", + "process_name", + "dbgroup_id" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_rlimit_cpu_time_hard", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "process_name", + "dbgroup_id", + "exe", + "influx_port", + "user", + "influx_host", + "host", + "pid", + "cluster", + "dbrole", + "dbgroup" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_rlimit_cpu_time_soft", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "exe", + "user", + "influx_port", + "process_name", + "dbgroup", + "host", + "cluster", + "dbgroup_id", + "dbrole", + "influx_host", + "pid" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_rlimit_file_locks_hard", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "user", + "dbgroup", + "dbgroup_id", + "influx_port", + "cluster", + "dbrole", + "exe", + "process_name", + "influx_host", + "pid", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_rlimit_file_locks_soft", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "exe", + "dbgroup", + "pid", + "dbrole", + "influx_host", + "dbgroup_id", + "host", + "user", + "cluster", + "influx_port", + "process_name" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_rlimit_memory_data_hard", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbrole", + "influx_host", + "influx_port", + "process_name", + "cluster", + "dbgroup", + "dbgroup_id", + "pid", + "exe", + "host", + "user" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_rlimit_memory_data_soft", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup_id", + "cluster", + "process_name", + "host", + "influx_host", + "influx_port", + "pid", + "user", + "dbrole", + "exe", + "dbgroup" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_rlimit_memory_locked_hard", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "influx_host", + "user", + "dbgroup", + "influx_port", + "exe", + "pid", + "dbgroup_id", + "dbrole", + "process_name", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_rlimit_memory_locked_soft", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup", + "user", + "process_name", + "host", + "cluster", + "dbrole", + "influx_host", + "dbgroup_id", + "exe", + "influx_port", + "pid" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_rlimit_memory_rss_hard", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "dbgroup", + "dbrole", + "exe", + "pid", + "user", + "influx_port", + "dbgroup_id", + "process_name", + "host", + "influx_host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_rlimit_memory_rss_soft", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "user", + "dbgroup_id", + "influx_host", + "pid", + "exe", + "host", + "influx_port", + "dbrole", + "dbgroup", + "process_name", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_rlimit_memory_stack_hard", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "process_name", + "user", + "influx_host", + "pid", + "dbrole", + "dbgroup_id", + "host", + "cluster", + "exe", + "influx_port", + "dbgroup" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_rlimit_memory_stack_soft", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "exe", + "host", + "influx_port", + "cluster", + "dbgroup", + "influx_host", + "process_name", + "dbrole", + "pid", + "dbgroup_id", + "user" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_rlimit_memory_vms_hard", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "process_name", + "influx_host", + "user", + "cluster", + "dbgroup", + "exe", + "pid", + "dbrole", + "influx_port", + "dbgroup_id" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_rlimit_memory_vms_soft", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "cluster", + "dbgroup_id", + "influx_port", + "user", + "pid", + "dbrole", + "exe", + "dbgroup", + "process_name", + "influx_host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_rlimit_nice_priority_hard", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_port", + "process_name", + "dbrole", + "host", + "influx_host", + "dbgroup_id", + "user", + "dbgroup", + "cluster", + "exe", + "pid" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_rlimit_nice_priority_soft", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_port", + "user", + "exe", + "cluster", + "dbgroup_id", + "dbgroup", + "process_name", + "dbrole", + "host", + "influx_host", + "pid" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_rlimit_num_fds_hard", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbrole", + "exe", + "pid", + "influx_port", + "host", + "influx_host", + "dbgroup", + "user", + "cluster", + "dbgroup_id", + "process_name" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_rlimit_num_fds_soft", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "process_name", + "dbgroup", + "exe", + "influx_port", + "dbrole", + "host", + "influx_host", + "dbgroup_id", + "user", + "cluster", + "pid" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_rlimit_realtime_priority_hard", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_host", + "influx_port", + "exe", + "pid", + "dbgroup_id", + "host", + "process_name", + "user", + "dbgroup", + "cluster", + "dbrole" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_rlimit_realtime_priority_soft", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "process_name", + "pid", + "exe", + "dbgroup_id", + "dbrole", + "influx_host", + "cluster", + "dbgroup", + "host", + "influx_port", + "user" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_rlimit_signals_pending_hard", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_port", + "pid", + "process_name", + "cluster", + "host", + "dbgroup_id", + "exe", + "influx_host", + "dbgroup", + "dbrole", + "user" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_rlimit_signals_pending_soft", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "user", + "process_name", + "dbgroup", + "exe", + "dbrole", + "cluster", + "influx_host", + "influx_port", + "host", + "pid", + "dbgroup_id" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_signals_pending", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup_id", + "user", + "influx_port", + "host", + "influx_host", + "dbgroup", + "dbrole", + "exe", + "pid", + "process_name", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_voluntary_context_switches", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "process_name", + "user", + "dbgroup", + "influx_port", + "influx_host", + "pid", + "exe", + "dbgroup_id", + "dbrole", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_write_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "user", + "influx_host", + "exe", + "host", + "dbgroup_id", + "dbgroup", + "pid", + "cluster", + "influx_port", + "process_name", + "dbrole" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_write_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "user", + "dbrole", + "influx_host", + "host", + "exe", + "pid", + "cluster", + "process_name", + "influx_port", + "dbgroup", + "dbgroup_id" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "__name", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "name", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "diskio_io_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "name", + "cluster", + "influx_port", + "host", + "influx_host", + "dbgroup", + "dbgroup_id", + "dbrole" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "diskio_iops_in_progress", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "dbgroup_id", + "dbrole", + "influx_port", + "dbgroup", + "influx_host", + "host", + "name" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "diskio_read_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup_id", + "host", + "influx_host", + "influx_port", + "name", + "dbgroup", + "dbrole", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "diskio_read_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_port", + "name", + "dbgroup", + "dbgroup_id", + "cluster", + "dbrole", + "host", + "influx_host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "diskio_reads", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "dbgroup_id", + "dbrole", + "influx_host", + "name", + "dbgroup", + "host", + "influx_port" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "diskio_weighted_io_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "dbgroup", + "host", + "influx_host", + "name", + "dbgroup_id", + "dbrole", + "influx_port" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "diskio_write_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "name", + "dbgroup", + "dbgroup_id", + "cluster", + "dbrole", + "host", + "influx_host", + "influx_port" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "diskio_write_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "influx_host", + "influx_port", + "name", + "dbgroup_id", + "dbrole", + "cluster", + "dbgroup" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "diskio_writes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "dbgroup", + "host", + "dbgroup_id", + "dbrole", + "influx_host", + "influx_port", + "name" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "host", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "exe", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "cluster", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "dbgroup", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "influx_host", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "dbrole", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "dbgroup_id", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "process_name", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "user", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "influx_port", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "pid", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "pushgateway_dbm_influxdb_bkpull.Group4", + "table_desc": "分组4", + "table_name": "Group4" + }, + { + "fields": [ + { + "name": "procstat_lookup_pid_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup_id", + "influx_host", + "pid_finder", + "exe", + "host", + "result", + "cluster", + "dbrole", + "dbgroup", + "influx_port" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_lookup_result_code", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "pid_finder", + "result", + "host", + "cluster", + "dbrole", + "dbgroup", + "dbgroup_id", + "influx_host", + "influx_port", + "exe" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "procstat_lookup_running", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_port", + "result", + "dbgroup_id", + "dbrole", + "exe", + "host", + "influx_host", + "cluster", + "pid_finder", + "dbgroup" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "dbgroup_id", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "influx_host", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "pid_finder", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "exe", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "host", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "result", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "cluster", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "dbrole", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "dbgroup", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "influx_port", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "pushgateway_dbm_influxdb_bkpull.Group5", + "table_desc": "分组5", + "table_name": "Group5" + }, + { + "fields": [ + { + "name": "influxdb_httpd_authFail", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "bind", + "dbgroup_id", + "influx_host", + "influx_port", + "url", + "dbrole", + "cluster", + "dbgroup", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_httpd_clientError", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup", + "influx_host", + "host", + "bind", + "url", + "influx_port", + "cluster", + "dbrole", + "dbgroup_id" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_httpd_fluxQueryReq", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "url", + "cluster", + "influx_host", + "dbgroup_id", + "influx_port", + "bind", + "dbgroup", + "dbrole", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_httpd_fluxQueryReqDurationNs", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup_id", + "influx_host", + "cluster", + "url", + "bind", + "dbgroup", + "influx_port", + "dbrole", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_httpd_pingReq", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup_id", + "dbgroup", + "dbrole", + "host", + "influx_host", + "influx_port", + "cluster", + "url", + "bind" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_httpd_pointsWrittenDropped", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup_id", + "host", + "influx_port", + "bind", + "url", + "influx_host", + "dbgroup", + "cluster", + "dbrole" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_httpd_pointsWrittenFail", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_host", + "cluster", + "dbgroup_id", + "url", + "influx_port", + "dbgroup", + "bind", + "dbrole", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_httpd_pointsWrittenOK", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "bind", + "cluster", + "url", + "host", + "dbgroup", + "influx_port", + "dbrole", + "dbgroup_id", + "influx_host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_httpd_promReadReq", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "host", + "bind", + "url", + "dbgroup_id", + "dbgroup", + "influx_host", + "influx_port", + "dbrole" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_httpd_promWriteReq", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_port", + "bind", + "dbgroup_id", + "host", + "dbgroup", + "url", + "influx_host", + "cluster", + "dbrole" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_httpd_queryReq", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_port", + "url", + "bind", + "dbgroup", + "dbgroup_id", + "host", + "cluster", + "dbrole", + "influx_host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_httpd_queryReqDurationNs", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "cluster", + "bind", + "dbgroup", + "dbrole", + "url", + "influx_port", + "dbgroup_id", + "influx_host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_httpd_queryRespBytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "influx_host", + "dbgroup_id", + "url", + "bind", + "cluster", + "dbgroup", + "dbrole", + "influx_port" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_httpd_recoveredPanics", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_host", + "influx_port", + "cluster", + "dbgroup", + "dbgroup_id", + "dbrole", + "url", + "bind", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_httpd_req", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup", + "dbgroup_id", + "url", + "influx_host", + "cluster", + "dbrole", + "influx_port", + "host", + "bind" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_httpd_reqActive", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "bind", + "dbgroup_id", + "dbgroup", + "dbrole", + "influx_port", + "cluster", + "url", + "host", + "influx_host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_httpd_reqDurationNs", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "influx_port", + "bind", + "dbgroup", + "dbrole", + "dbgroup_id", + "url", + "influx_host", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_httpd_serverError", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup", + "bind", + "influx_host", + "cluster", + "dbrole", + "influx_port", + "dbgroup_id", + "host", + "url" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_httpd_statusReq", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "host", + "cluster", + "dbgroup", + "influx_port", + "influx_host", + "bind", + "dbgroup_id", + "url", + "dbrole" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_httpd_valuesWrittenOK", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "url", + "influx_port", + "dbgroup_id", + "host", + "dbrole", + "cluster", + "dbgroup", + "influx_host", + "bind" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_httpd_writeReq", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "bind", + "cluster", + "dbgroup", + "influx_host", + "dbgroup_id", + "dbrole", + "influx_port", + "url", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_httpd_writeReqActive", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup_id", + "influx_port", + "influx_host", + "cluster", + "bind", + "dbrole", + "dbgroup", + "host", + "url" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_httpd_writeReqBytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "dbgroup", + "dbgroup_id", + "bind", + "host", + "influx_host", + "dbrole", + "url", + "influx_port" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "influxdb_httpd_writeReqDurationNs", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbrole", + "dbgroup_id", + "influx_port", + "cluster", + "influx_host", + "bind", + "dbgroup", + "url", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bind", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "dbgroup_id", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "influx_host", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "influx_port", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "url", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "dbrole", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "cluster", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "dbgroup", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "host", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "pushgateway_dbm_influxdb_bkpull.Group6", + "table_desc": "分组6", + "table_name": "Group6" + }, + { + "fields": [ + { + "name": "cpu_usage_guest", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbrole", + "host", + "dbgroup", + "dbgroup_id", + "influx_host", + "influx_port", + "cluster", + "cpu" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "cpu_usage_guest_nice", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "dbrole", + "influx_port", + "cpu", + "dbgroup", + "dbgroup_id", + "host", + "influx_host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "cpu_usage_idle", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_port", + "cluster", + "cpu", + "dbgroup", + "host", + "dbgroup_id", + "dbrole", + "influx_host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "cpu_usage_iowait", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_host", + "dbgroup", + "dbgroup_id", + "dbrole", + "influx_port", + "cluster", + "cpu", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "cpu_usage_irq", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup_id", + "influx_host", + "influx_port", + "cluster", + "cpu", + "dbgroup", + "dbrole", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "cpu_usage_nice", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cpu", + "dbgroup_id", + "dbrole", + "influx_host", + "influx_port", + "cluster", + "dbgroup", + "host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "cpu_usage_softirq", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbrole", + "host", + "influx_host", + "dbgroup", + "dbgroup_id", + "influx_port", + "cluster", + "cpu" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "cpu_usage_steal", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cpu", + "dbgroup", + "influx_host", + "influx_port", + "dbgroup_id", + "dbrole", + "host", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "cpu_usage_system", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "influx_port", + "cluster", + "cpu", + "dbgroup", + "dbgroup_id", + "dbrole", + "host", + "influx_host" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "cpu_usage_user", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "dbgroup_id", + "host", + "influx_host", + "influx_port", + "cpu", + "dbrole", + "cluster", + "dbgroup" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "dbrole", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "host", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "dbgroup", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "dbgroup_id", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "influx_host", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "influx_port", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "cluster", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "cpu", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "pushgateway_dbm_influxdb_bkpull.Group7", + "table_desc": "分组7", + "table_name": "Group7" + } + ], + "plugin_type": "Pushgateway", + "os_type_list": [ + "linux", + "windows", + "linux_aarch64" + ] + }, + "collect_type": "Pushgateway", + "target_nodes": [], + "target_node_type": "TOPO", + "target_object_type": "SERVICE" + } +} diff --git a/dbm-ui/backend/db_monitor/tpls/collect/0.influxdb.dbm_influxdb_bkpull.tpl64 b/dbm-ui/backend/db_monitor/tpls/collect/0.influxdb.dbm_influxdb_bkpull.tpl64 deleted file mode 100644 index 656a441228..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/collect/0.influxdb.dbm_influxdb_bkpull.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAicGx1Z2luX2lkIjogImRibV9pbmZsdXhkYl9ia3B1bGwiLCAiZGJfdHlwZSI6ICJpbmZsdXhkYiIsICJkZXRhaWxzIjogeyJuYW1lIjogImRibV9pbmZsdXhkYl9ia3B1bGwiLCAibGFiZWwiOiAiY29tcG9uZW50IiwgInBhcmFtcyI6IHsicGx1Z2luIjogeyJcdTY3MGRcdTUyYTFcdTViOWVcdTRmOGJcdTdlZjRcdTVlYTZcdTZjZThcdTUxNjUiOiB7ImFwcCI6ICJhcHAiLCAiZGJfZ3JvdXAiOiAiZGJfZ3JvdXAiLCAiaW5zdGFuY2UiOiAiaW5zdGFuY2UiLCAiYmtfYXBwX2NvZGUiOiAiYXBwX2lkIiwgImNsdXN0ZXJfbmFtZSI6ICJjbHVzdGVyX25hbWUiLCAiaW5zdGFuY2VfaG9zdCI6ICJpbnN0YW5jZV9ob3N0IiwgImluc3RhbmNlX3BvcnQiOiAiaW5zdGFuY2VfcG9ydCJ9fSwgImNvbGxlY3RvciI6IHsicGVyaW9kIjogNjAsICJ0aW1lb3V0IjogNjAsICJwYXNzd29yZCI6ICIiLCAidXNlcm5hbWUiOiAiIiwgIm1ldHJpY3NfdXJsIjogImh0dHA6Ly8xMjcuMC4wLjE6OTI3NC9tZXRyaWNzIn0sICJ0YXJnZXRfbm9kZV90eXBlIjogIlRPUE8iLCAidGFyZ2V0X29iamVjdF90eXBlIjogIlNFUlZJQ0UifSwgInBsdWdpbl9pbmZvIjogeyJwbHVnaW5faWQiOiAiZGJtX2luZmx1eGRiX2JrcHVsbCIsICJtZXRyaWNfanNvbiI6IFt7ImZpZWxkcyI6IFt7Im5hbWUiOiAiX2RhdGFiYXNlIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogImRhdGFiYXNlIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl9zaGFyZF9kaXNrQnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJ1cmwiLCAiZW5naW5lIiwgInBhdGgiLCAid2FsUGF0aCIsICJpZCIsICJjbHVzdGVyIiwgImRiZ3JvdXAiLCAiaW5mbHV4X2hvc3QiLCAiZGJncm91cF9pZCIsICJkYXRhYmFzZSIsICJob3N0IiwgInJldGVudGlvblBvbGljeSIsICJpbmZsdXhfcG9ydCIsICJpbmRleFR5cGUiLCAiZGJyb2xlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl9zaGFyZF9maWVsZHNDcmVhdGUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJpbmZsdXhfcG9ydCIsICJkYXRhYmFzZSIsICJkYnJvbGUiLCAiY2x1c3RlciIsICJpZCIsICJwYXRoIiwgImluZmx1eF9ob3N0IiwgImVuZ2luZSIsICJ3YWxQYXRoIiwgImRiZ3JvdXBfaWQiLCAicmV0ZW50aW9uUG9saWN5IiwgImhvc3QiLCAidXJsIiwgImRiZ3JvdXAiLCAiaW5kZXhUeXBlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl9zaGFyZF9zZXJpZXNDcmVhdGUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJlbmdpbmUiLCAicGF0aCIsICJpZCIsICJkYmdyb3VwIiwgImluZmx1eF9wb3J0IiwgInJldGVudGlvblBvbGljeSIsICJob3N0IiwgImRhdGFiYXNlIiwgInVybCIsICJpbmZsdXhfaG9zdCIsICJkYnJvbGUiLCAiaW5kZXhUeXBlIiwgImNsdXN0ZXIiLCAid2FsUGF0aCIsICJkYmdyb3VwX2lkIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl9zaGFyZF93cml0ZUJ5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaW5mbHV4X3BvcnQiLCAid2FsUGF0aCIsICJlbmdpbmUiLCAicGF0aCIsICJjbHVzdGVyIiwgImhvc3QiLCAidXJsIiwgImluZmx1eF9ob3N0IiwgImRiZ3JvdXBfaWQiLCAiZGJncm91cCIsICJkYnJvbGUiLCAiaWQiLCAiaW5kZXhUeXBlIiwgImRhdGFiYXNlIiwgInJldGVudGlvblBvbGljeSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfc2hhcmRfd3JpdGVQb2ludHNEcm9wcGVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZGJncm91cCIsICJpZCIsICJpbmZsdXhfcG9ydCIsICJpbmZsdXhfaG9zdCIsICJ1cmwiLCAiZGF0YWJhc2UiLCAiZGJncm91cF9pZCIsICJpbmRleFR5cGUiLCAicGF0aCIsICJkYnJvbGUiLCAiaG9zdCIsICJyZXRlbnRpb25Qb2xpY3kiLCAiZW5naW5lIiwgImNsdXN0ZXIiLCAid2FsUGF0aCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfc2hhcmRfd3JpdGVQb2ludHNFcnIiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJ1cmwiLCAid2FsUGF0aCIsICJwYXRoIiwgImhvc3QiLCAiY2x1c3RlciIsICJlbmdpbmUiLCAiZGJncm91cF9pZCIsICJkYnJvbGUiLCAiZGJncm91cCIsICJpbmRleFR5cGUiLCAiaWQiLCAiaW5mbHV4X2hvc3QiLCAicmV0ZW50aW9uUG9saWN5IiwgImluZmx1eF9wb3J0IiwgImRhdGFiYXNlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl9zaGFyZF93cml0ZVBvaW50c09rIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsicGF0aCIsICJ3YWxQYXRoIiwgInVybCIsICJpZCIsICJjbHVzdGVyIiwgImRicm9sZSIsICJkYmdyb3VwIiwgInJldGVudGlvblBvbGljeSIsICJkYmdyb3VwX2lkIiwgImVuZ2luZSIsICJob3N0IiwgImluZGV4VHlwZSIsICJpbmZsdXhfaG9zdCIsICJkYXRhYmFzZSIsICJpbmZsdXhfcG9ydCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfc2hhcmRfd3JpdGVSZXEiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJkYnJvbGUiLCAiY2x1c3RlciIsICJkYmdyb3VwX2lkIiwgIndhbFBhdGgiLCAiZW5naW5lIiwgImlkIiwgImluZmx1eF9ob3N0IiwgInBhdGgiLCAiaG9zdCIsICJpbmRleFR5cGUiLCAiaW5mbHV4X3BvcnQiLCAidXJsIiwgInJldGVudGlvblBvbGljeSIsICJkYmdyb3VwIiwgImRhdGFiYXNlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl9zaGFyZF93cml0ZVJlcUVyciIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNsdXN0ZXIiLCAiZGJyb2xlIiwgImRhdGFiYXNlIiwgInVybCIsICJyZXRlbnRpb25Qb2xpY3kiLCAiaW5mbHV4X2hvc3QiLCAiZW5naW5lIiwgImRiZ3JvdXAiLCAicGF0aCIsICJpbmRleFR5cGUiLCAiaWQiLCAiZGJncm91cF9pZCIsICJob3N0IiwgIndhbFBhdGgiLCAiaW5mbHV4X3BvcnQiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImluZmx1eGRiX3NoYXJkX3dyaXRlUmVxT2siLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJlbmdpbmUiLCAiaG9zdCIsICJkYmdyb3VwX2lkIiwgIndhbFBhdGgiLCAiY2x1c3RlciIsICJpbmZsdXhfaG9zdCIsICJpbmZsdXhfcG9ydCIsICJ1cmwiLCAicmV0ZW50aW9uUG9saWN5IiwgInBhdGgiLCAiaWQiLCAiaW5kZXhUeXBlIiwgImRiZ3JvdXAiLCAiZGJyb2xlIiwgImRhdGFiYXNlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl9zaGFyZF93cml0ZVZhbHVlc09rIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZGF0YWJhc2UiLCAiaW5mbHV4X2hvc3QiLCAiY2x1c3RlciIsICJpbmRleFR5cGUiLCAiZGJncm91cCIsICJob3N0IiwgIndhbFBhdGgiLCAiZGJncm91cF9pZCIsICJpZCIsICJlbmdpbmUiLCAiZGJyb2xlIiwgImluZmx1eF9wb3J0IiwgInJldGVudGlvblBvbGljeSIsICJ1cmwiLCAicGF0aCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfdHNtMV9jYWNoZV9XQUxDb21wYWN0aW9uVGltZU1zIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaW5kZXhUeXBlIiwgImRhdGFiYXNlIiwgImVuZ2luZSIsICJwYXRoIiwgImRiZ3JvdXAiLCAiaW5mbHV4X3BvcnQiLCAicmV0ZW50aW9uUG9saWN5IiwgImNsdXN0ZXIiLCAiaW5mbHV4X2hvc3QiLCAiZGJyb2xlIiwgImlkIiwgImhvc3QiLCAidXJsIiwgIndhbFBhdGgiLCAiZGJncm91cF9pZCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfdHNtMV9jYWNoZV9jYWNoZUFnZU1zIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZGJncm91cF9pZCIsICJ1cmwiLCAid2FsUGF0aCIsICJlbmdpbmUiLCAiaW5kZXhUeXBlIiwgImRhdGFiYXNlIiwgInJldGVudGlvblBvbGljeSIsICJkYmdyb3VwIiwgImluZmx1eF9ob3N0IiwgImNsdXN0ZXIiLCAiaG9zdCIsICJpZCIsICJwYXRoIiwgImRicm9sZSIsICJpbmZsdXhfcG9ydCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfdHNtMV9jYWNoZV9jYWNoZWRCeXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImlkIiwgImRhdGFiYXNlIiwgImVuZ2luZSIsICJpbmZsdXhfcG9ydCIsICJkYmdyb3VwIiwgInVybCIsICJwYXRoIiwgIndhbFBhdGgiLCAiaW5mbHV4X2hvc3QiLCAiY2x1c3RlciIsICJkYnJvbGUiLCAiaG9zdCIsICJkYmdyb3VwX2lkIiwgInJldGVudGlvblBvbGljeSIsICJpbmRleFR5cGUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImluZmx1eGRiX3RzbTFfY2FjaGVfZGlza0J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsid2FsUGF0aCIsICJkYXRhYmFzZSIsICJkYmdyb3VwIiwgImluZmx1eF9wb3J0IiwgInBhdGgiLCAiaWQiLCAiZGJyb2xlIiwgImhvc3QiLCAidXJsIiwgImNsdXN0ZXIiLCAiaW5kZXhUeXBlIiwgInJldGVudGlvblBvbGljeSIsICJlbmdpbmUiLCAiZGJncm91cF9pZCIsICJpbmZsdXhfaG9zdCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfdHNtMV9jYWNoZV9tZW1CeXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbInJldGVudGlvblBvbGljeSIsICJkYmdyb3VwX2lkIiwgImRicm9sZSIsICJob3N0IiwgImluZGV4VHlwZSIsICJ3YWxQYXRoIiwgInBhdGgiLCAiaWQiLCAiY2x1c3RlciIsICJpbmZsdXhfcG9ydCIsICJpbmZsdXhfaG9zdCIsICJkYXRhYmFzZSIsICJkYmdyb3VwIiwgImVuZ2luZSIsICJ1cmwiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImluZmx1eGRiX3RzbTFfY2FjaGVfc25hcHNob3RDb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbInJldGVudGlvblBvbGljeSIsICJpbmRleFR5cGUiLCAiY2x1c3RlciIsICJkYnJvbGUiLCAiZGJncm91cCIsICJlbmdpbmUiLCAiaW5mbHV4X3BvcnQiLCAicGF0aCIsICJkYXRhYmFzZSIsICJob3N0IiwgImluZmx1eF9ob3N0IiwgImRiZ3JvdXBfaWQiLCAidXJsIiwgImlkIiwgIndhbFBhdGgiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImluZmx1eGRiX3RzbTFfY2FjaGVfd3JpdGVEcm9wcGVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsicmV0ZW50aW9uUG9saWN5IiwgImhvc3QiLCAiY2x1c3RlciIsICJkYXRhYmFzZSIsICJkYmdyb3VwX2lkIiwgInVybCIsICJ3YWxQYXRoIiwgImlkIiwgImluZmx1eF9ob3N0IiwgImRicm9sZSIsICJkYmdyb3VwIiwgImluZmx1eF9wb3J0IiwgInBhdGgiLCAiZW5naW5lIiwgImluZGV4VHlwZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfdHNtMV9jYWNoZV93cml0ZUVyciIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImRicm9sZSIsICJ3YWxQYXRoIiwgImlkIiwgImluZmx1eF9ob3N0IiwgImhvc3QiLCAiZGJncm91cF9pZCIsICJkYmdyb3VwIiwgImRhdGFiYXNlIiwgInBhdGgiLCAidXJsIiwgImluZmx1eF9wb3J0IiwgImluZGV4VHlwZSIsICJlbmdpbmUiLCAiY2x1c3RlciIsICJyZXRlbnRpb25Qb2xpY3kiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImluZmx1eGRiX3RzbTFfY2FjaGVfd3JpdGVPayIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImRiZ3JvdXAiLCAicGF0aCIsICJpbmRleFR5cGUiLCAid2FsUGF0aCIsICJlbmdpbmUiLCAiaW5mbHV4X3BvcnQiLCAiZGF0YWJhc2UiLCAiZGJyb2xlIiwgInJldGVudGlvblBvbGljeSIsICJkYmdyb3VwX2lkIiwgInVybCIsICJpbmZsdXhfaG9zdCIsICJpZCIsICJob3N0IiwgImNsdXN0ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImluZmx1eGRiX3RzbTFfZW5naW5lX2NhY2hlQ29tcGFjdGlvbkR1cmF0aW9uIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsidXJsIiwgImluZmx1eF9wb3J0IiwgIndhbFBhdGgiLCAicGF0aCIsICJpbmRleFR5cGUiLCAiZGF0YWJhc2UiLCAiZGJncm91cF9pZCIsICJlbmdpbmUiLCAicmV0ZW50aW9uUG9saWN5IiwgImRiZ3JvdXAiLCAiY2x1c3RlciIsICJpZCIsICJpbmZsdXhfaG9zdCIsICJob3N0IiwgImRicm9sZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfdHNtMV9lbmdpbmVfY2FjaGVDb21wYWN0aW9uRXJyIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaW5kZXhUeXBlIiwgInBhdGgiLCAiaW5mbHV4X2hvc3QiLCAiZGJyb2xlIiwgImRiZ3JvdXBfaWQiLCAidXJsIiwgInJldGVudGlvblBvbGljeSIsICJlbmdpbmUiLCAiY2x1c3RlciIsICJ3YWxQYXRoIiwgImRiZ3JvdXAiLCAiaWQiLCAiaG9zdCIsICJkYXRhYmFzZSIsICJpbmZsdXhfcG9ydCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfdHNtMV9lbmdpbmVfY2FjaGVDb21wYWN0aW9ucyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbInVybCIsICJjbHVzdGVyIiwgIndhbFBhdGgiLCAiaW5kZXhUeXBlIiwgImVuZ2luZSIsICJkYmdyb3VwIiwgImRhdGFiYXNlIiwgImhvc3QiLCAiaWQiLCAiaW5mbHV4X3BvcnQiLCAiZGJncm91cF9pZCIsICJpbmZsdXhfaG9zdCIsICJyZXRlbnRpb25Qb2xpY3kiLCAiZGJyb2xlIiwgInBhdGgiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImluZmx1eGRiX3RzbTFfZW5naW5lX2NhY2hlQ29tcGFjdGlvbnNBY3RpdmUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJ1cmwiLCAiY2x1c3RlciIsICJkYmdyb3VwIiwgImRhdGFiYXNlIiwgImRicm9sZSIsICJ3YWxQYXRoIiwgImluZmx1eF9wb3J0IiwgImlkIiwgImluZGV4VHlwZSIsICJpbmZsdXhfaG9zdCIsICJob3N0IiwgImVuZ2luZSIsICJwYXRoIiwgInJldGVudGlvblBvbGljeSIsICJkYmdyb3VwX2lkIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl90c20xX2VuZ2luZV90c21GdWxsQ29tcGFjdGlvbkR1cmF0aW9uIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJlbmdpbmUiLCAidXJsIiwgImRhdGFiYXNlIiwgImRicm9sZSIsICJpbmZsdXhfaG9zdCIsICJyZXRlbnRpb25Qb2xpY3kiLCAiaG9zdCIsICJwYXRoIiwgImluZGV4VHlwZSIsICJpZCIsICJkYmdyb3VwIiwgImluZmx1eF9wb3J0IiwgIndhbFBhdGgiLCAiZGJncm91cF9pZCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfdHNtMV9lbmdpbmVfdHNtRnVsbENvbXBhY3Rpb25FcnIiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJlbmdpbmUiLCAiaG9zdCIsICJpbmRleFR5cGUiLCAiZGJyb2xlIiwgImNsdXN0ZXIiLCAiZGF0YWJhc2UiLCAiaWQiLCAicGF0aCIsICJpbmZsdXhfcG9ydCIsICJkYmdyb3VwIiwgInJldGVudGlvblBvbGljeSIsICJkYmdyb3VwX2lkIiwgInVybCIsICJ3YWxQYXRoIiwgImluZmx1eF9ob3N0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl90c20xX2VuZ2luZV90c21GdWxsQ29tcGFjdGlvblF1ZXVlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsicmV0ZW50aW9uUG9saWN5IiwgImNsdXN0ZXIiLCAiaWQiLCAidXJsIiwgInBhdGgiLCAiaW5mbHV4X2hvc3QiLCAiZW5naW5lIiwgImluZmx1eF9wb3J0IiwgImRicm9sZSIsICJob3N0IiwgImRiZ3JvdXBfaWQiLCAiaW5kZXhUeXBlIiwgImRiZ3JvdXAiLCAiZGF0YWJhc2UiLCAid2FsUGF0aCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfdHNtMV9lbmdpbmVfdHNtRnVsbENvbXBhY3Rpb25zIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaW5kZXhUeXBlIiwgImRicm9sZSIsICJyZXRlbnRpb25Qb2xpY3kiLCAidXJsIiwgImlkIiwgImluZmx1eF9ob3N0IiwgImRiZ3JvdXBfaWQiLCAiZGJncm91cCIsICJjbHVzdGVyIiwgImVuZ2luZSIsICJ3YWxQYXRoIiwgImRhdGFiYXNlIiwgInBhdGgiLCAiaW5mbHV4X3BvcnQiLCAiaG9zdCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfdHNtMV9lbmdpbmVfdHNtRnVsbENvbXBhY3Rpb25zQWN0aXZlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZW5naW5lIiwgImRiZ3JvdXAiLCAiY2x1c3RlciIsICJkYmdyb3VwX2lkIiwgImhvc3QiLCAicGF0aCIsICJ1cmwiLCAiaW5kZXhUeXBlIiwgImRicm9sZSIsICJpbmZsdXhfaG9zdCIsICJpbmZsdXhfcG9ydCIsICJyZXRlbnRpb25Qb2xpY3kiLCAid2FsUGF0aCIsICJkYXRhYmFzZSIsICJpZCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfdHNtMV9lbmdpbmVfdHNtTGV2ZWwxQ29tcGFjdGlvbkR1cmF0aW9uIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsicmV0ZW50aW9uUG9saWN5IiwgInVybCIsICJpbmRleFR5cGUiLCAiaWQiLCAiZGF0YWJhc2UiLCAiY2x1c3RlciIsICJ3YWxQYXRoIiwgImRiZ3JvdXAiLCAiZGJncm91cF9pZCIsICJob3N0IiwgImluZmx1eF9ob3N0IiwgImluZmx1eF9wb3J0IiwgInBhdGgiLCAiZW5naW5lIiwgImRicm9sZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfdHNtMV9lbmdpbmVfdHNtTGV2ZWwxQ29tcGFjdGlvbkVyciIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImRiZ3JvdXAiLCAiaG9zdCIsICJ3YWxQYXRoIiwgImluZGV4VHlwZSIsICJpZCIsICJpbmZsdXhfcG9ydCIsICJjbHVzdGVyIiwgInJldGVudGlvblBvbGljeSIsICJwYXRoIiwgImluZmx1eF9ob3N0IiwgImVuZ2luZSIsICJkYnJvbGUiLCAiZGF0YWJhc2UiLCAiZGJncm91cF9pZCIsICJ1cmwiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImluZmx1eGRiX3RzbTFfZW5naW5lX3RzbUxldmVsMUNvbXBhY3Rpb25RdWV1ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbInVybCIsICJkYmdyb3VwIiwgImRicm9sZSIsICJpZCIsICJpbmRleFR5cGUiLCAiaG9zdCIsICJwYXRoIiwgImVuZ2luZSIsICJpbmZsdXhfaG9zdCIsICJyZXRlbnRpb25Qb2xpY3kiLCAiZGJncm91cF9pZCIsICJpbmZsdXhfcG9ydCIsICJ3YWxQYXRoIiwgImRhdGFiYXNlIiwgImNsdXN0ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImluZmx1eGRiX3RzbTFfZW5naW5lX3RzbUxldmVsMUNvbXBhY3Rpb25zIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaW5kZXhUeXBlIiwgInVybCIsICJpbmZsdXhfcG9ydCIsICJob3N0IiwgImRiZ3JvdXAiLCAicGF0aCIsICJyZXRlbnRpb25Qb2xpY3kiLCAiZGJyb2xlIiwgIndhbFBhdGgiLCAiZGJncm91cF9pZCIsICJpbmZsdXhfaG9zdCIsICJlbmdpbmUiLCAiY2x1c3RlciIsICJpZCIsICJkYXRhYmFzZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfdHNtMV9lbmdpbmVfdHNtTGV2ZWwxQ29tcGFjdGlvbnNBY3RpdmUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJkYnJvbGUiLCAiZW5naW5lIiwgInVybCIsICJpbmZsdXhfcG9ydCIsICJyZXRlbnRpb25Qb2xpY3kiLCAiZGF0YWJhc2UiLCAiZGJncm91cCIsICJkYmdyb3VwX2lkIiwgImluZGV4VHlwZSIsICJpZCIsICJjbHVzdGVyIiwgImluZmx1eF9ob3N0IiwgInBhdGgiLCAiaG9zdCIsICJ3YWxQYXRoIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl90c20xX2VuZ2luZV90c21MZXZlbDJDb21wYWN0aW9uRHVyYXRpb24iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJ3YWxQYXRoIiwgImRiZ3JvdXBfaWQiLCAiaW5kZXhUeXBlIiwgInJldGVudGlvblBvbGljeSIsICJkYnJvbGUiLCAiZGF0YWJhc2UiLCAidXJsIiwgImlkIiwgImluZmx1eF9wb3J0IiwgInBhdGgiLCAiaW5mbHV4X2hvc3QiLCAiY2x1c3RlciIsICJlbmdpbmUiLCAiZGJncm91cCIsICJob3N0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl90c20xX2VuZ2luZV90c21MZXZlbDJDb21wYWN0aW9uRXJyIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZW5naW5lIiwgImluZmx1eF9wb3J0IiwgImRicm9sZSIsICJkYXRhYmFzZSIsICJob3N0IiwgIndhbFBhdGgiLCAiaWQiLCAiaW5mbHV4X2hvc3QiLCAiZGJncm91cF9pZCIsICJwYXRoIiwgInJldGVudGlvblBvbGljeSIsICJpbmRleFR5cGUiLCAiY2x1c3RlciIsICJ1cmwiLCAiZGJncm91cCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfdHNtMV9lbmdpbmVfdHNtTGV2ZWwyQ29tcGFjdGlvblF1ZXVlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaW5kZXhUeXBlIiwgImluZmx1eF9ob3N0IiwgImRiZ3JvdXAiLCAiaW5mbHV4X3BvcnQiLCAicGF0aCIsICJlbmdpbmUiLCAiZGJyb2xlIiwgIndhbFBhdGgiLCAiZGJncm91cF9pZCIsICJob3N0IiwgInJldGVudGlvblBvbGljeSIsICJ1cmwiLCAiZGF0YWJhc2UiLCAiY2x1c3RlciIsICJpZCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfdHNtMV9lbmdpbmVfdHNtTGV2ZWwyQ29tcGFjdGlvbnMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJob3N0IiwgInVybCIsICJpZCIsICJwYXRoIiwgImRicm9sZSIsICJkYmdyb3VwX2lkIiwgImRhdGFiYXNlIiwgImluZmx1eF9ob3N0IiwgInJldGVudGlvblBvbGljeSIsICJ3YWxQYXRoIiwgImRiZ3JvdXAiLCAiY2x1c3RlciIsICJlbmdpbmUiLCAiaW5mbHV4X3BvcnQiLCAiaW5kZXhUeXBlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl90c20xX2VuZ2luZV90c21MZXZlbDJDb21wYWN0aW9uc0FjdGl2ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbInVybCIsICJpbmZsdXhfcG9ydCIsICJ3YWxQYXRoIiwgImRiZ3JvdXBfaWQiLCAiaW5mbHV4X2hvc3QiLCAiaWQiLCAiZW5naW5lIiwgImRhdGFiYXNlIiwgInBhdGgiLCAiZGJyb2xlIiwgImhvc3QiLCAiY2x1c3RlciIsICJpbmRleFR5cGUiLCAiZGJncm91cCIsICJyZXRlbnRpb25Qb2xpY3kiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImluZmx1eGRiX3RzbTFfZW5naW5lX3RzbUxldmVsM0NvbXBhY3Rpb25EdXJhdGlvbiIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImRicm9sZSIsICJpZCIsICJyZXRlbnRpb25Qb2xpY3kiLCAiaW5mbHV4X2hvc3QiLCAid2FsUGF0aCIsICJlbmdpbmUiLCAidXJsIiwgImNsdXN0ZXIiLCAiZGF0YWJhc2UiLCAiZGJncm91cF9pZCIsICJpbmRleFR5cGUiLCAiZGJncm91cCIsICJob3N0IiwgImluZmx1eF9wb3J0IiwgInBhdGgiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImluZmx1eGRiX3RzbTFfZW5naW5lX3RzbUxldmVsM0NvbXBhY3Rpb25FcnIiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJpZCIsICJkYnJvbGUiLCAicGF0aCIsICJkYmdyb3VwX2lkIiwgImNsdXN0ZXIiLCAicmV0ZW50aW9uUG9saWN5IiwgInVybCIsICJkYXRhYmFzZSIsICJkYmdyb3VwIiwgImVuZ2luZSIsICJ3YWxQYXRoIiwgImhvc3QiLCAiaW5kZXhUeXBlIiwgImluZmx1eF9wb3J0IiwgImluZmx1eF9ob3N0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl90c20xX2VuZ2luZV90c21MZXZlbDNDb21wYWN0aW9uUXVldWUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjbHVzdGVyIiwgImluZmx1eF9ob3N0IiwgImRiZ3JvdXAiLCAidXJsIiwgImRhdGFiYXNlIiwgImRicm9sZSIsICJpZCIsICJpbmZsdXhfcG9ydCIsICJob3N0IiwgImluZGV4VHlwZSIsICJwYXRoIiwgImVuZ2luZSIsICJyZXRlbnRpb25Qb2xpY3kiLCAid2FsUGF0aCIsICJkYmdyb3VwX2lkIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl90c20xX2VuZ2luZV90c21MZXZlbDNDb21wYWN0aW9ucyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNsdXN0ZXIiLCAiaWQiLCAid2FsUGF0aCIsICJkYmdyb3VwX2lkIiwgImVuZ2luZSIsICJkYXRhYmFzZSIsICJkYnJvbGUiLCAicmV0ZW50aW9uUG9saWN5IiwgImluZmx1eF9ob3N0IiwgImRiZ3JvdXAiLCAicGF0aCIsICJpbmZsdXhfcG9ydCIsICJpbmRleFR5cGUiLCAiaG9zdCIsICJ1cmwiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImluZmx1eGRiX3RzbTFfZW5naW5lX3RzbUxldmVsM0NvbXBhY3Rpb25zQWN0aXZlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZGJncm91cF9pZCIsICJ3YWxQYXRoIiwgInBhdGgiLCAiZGF0YWJhc2UiLCAiZW5naW5lIiwgImNsdXN0ZXIiLCAiaWQiLCAicmV0ZW50aW9uUG9saWN5IiwgInVybCIsICJpbmRleFR5cGUiLCAiaW5mbHV4X2hvc3QiLCAiaG9zdCIsICJpbmZsdXhfcG9ydCIsICJkYmdyb3VwIiwgImRicm9sZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfdHNtMV9lbmdpbmVfdHNtT3B0aW1pemVDb21wYWN0aW9uRHVyYXRpb24iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJpbmZsdXhfcG9ydCIsICJpbmZsdXhfaG9zdCIsICJkYmdyb3VwIiwgImhvc3QiLCAiY2x1c3RlciIsICJyZXRlbnRpb25Qb2xpY3kiLCAicGF0aCIsICJkYnJvbGUiLCAiaW5kZXhUeXBlIiwgInVybCIsICJpZCIsICJkYXRhYmFzZSIsICJlbmdpbmUiLCAid2FsUGF0aCIsICJkYmdyb3VwX2lkIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl90c20xX2VuZ2luZV90c21PcHRpbWl6ZUNvbXBhY3Rpb25FcnIiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJkYmdyb3VwIiwgInBhdGgiLCAiZGJyb2xlIiwgIndhbFBhdGgiLCAiaW5mbHV4X2hvc3QiLCAiY2x1c3RlciIsICJkYmdyb3VwX2lkIiwgImluZGV4VHlwZSIsICJ1cmwiLCAiaWQiLCAiaW5mbHV4X3BvcnQiLCAiaG9zdCIsICJkYXRhYmFzZSIsICJlbmdpbmUiLCAicmV0ZW50aW9uUG9saWN5Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl90c20xX2VuZ2luZV90c21PcHRpbWl6ZUNvbXBhY3Rpb25RdWV1ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImRiZ3JvdXBfaWQiLCAiZGJyb2xlIiwgInJldGVudGlvblBvbGljeSIsICJjbHVzdGVyIiwgImRiZ3JvdXAiLCAiaW5kZXhUeXBlIiwgImluZmx1eF9ob3N0IiwgImVuZ2luZSIsICJwYXRoIiwgImRhdGFiYXNlIiwgImhvc3QiLCAidXJsIiwgImlkIiwgIndhbFBhdGgiLCAiaW5mbHV4X3BvcnQiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImluZmx1eGRiX3RzbTFfZW5naW5lX3RzbU9wdGltaXplQ29tcGFjdGlvbnMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJpbmZsdXhfcG9ydCIsICJpbmRleFR5cGUiLCAiZGJncm91cCIsICJkYmdyb3VwX2lkIiwgImVuZ2luZSIsICJpZCIsICJyZXRlbnRpb25Qb2xpY3kiLCAid2FsUGF0aCIsICJwYXRoIiwgImRhdGFiYXNlIiwgImRicm9sZSIsICJpbmZsdXhfaG9zdCIsICJ1cmwiLCAiY2x1c3RlciIsICJob3N0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl90c20xX2VuZ2luZV90c21PcHRpbWl6ZUNvbXBhY3Rpb25zQWN0aXZlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZW5naW5lIiwgImRicm9sZSIsICJpbmZsdXhfcG9ydCIsICJpZCIsICJ3YWxQYXRoIiwgImluZGV4VHlwZSIsICJkYmdyb3VwIiwgImRiZ3JvdXBfaWQiLCAiY2x1c3RlciIsICJpbmZsdXhfaG9zdCIsICJob3N0IiwgInVybCIsICJyZXRlbnRpb25Qb2xpY3kiLCAicGF0aCIsICJkYXRhYmFzZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfdHNtMV9maWxlc3RvcmVfZGlza0J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaW5kZXhUeXBlIiwgImluZmx1eF9wb3J0IiwgImRiZ3JvdXBfaWQiLCAiZW5naW5lIiwgImhvc3QiLCAid2FsUGF0aCIsICJkYmdyb3VwIiwgImRhdGFiYXNlIiwgImRicm9sZSIsICJpZCIsICJwYXRoIiwgImluZmx1eF9ob3N0IiwgImNsdXN0ZXIiLCAicmV0ZW50aW9uUG9saWN5IiwgInVybCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfdHNtMV9maWxlc3RvcmVfbnVtRmlsZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJkYXRhYmFzZSIsICJ3YWxQYXRoIiwgImlkIiwgImRicm9sZSIsICJwYXRoIiwgInVybCIsICJpbmZsdXhfcG9ydCIsICJjbHVzdGVyIiwgImluZGV4VHlwZSIsICJlbmdpbmUiLCAiZGJncm91cF9pZCIsICJob3N0IiwgImRiZ3JvdXAiLCAiaW5mbHV4X2hvc3QiLCAicmV0ZW50aW9uUG9saWN5Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl90c20xX3dhbF9jdXJyZW50U2VnbWVudERpc2tCeXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImluZmx1eF9wb3J0IiwgImRhdGFiYXNlIiwgImRicm9sZSIsICJpbmZsdXhfaG9zdCIsICJkYmdyb3VwX2lkIiwgImNsdXN0ZXIiLCAidXJsIiwgImhvc3QiLCAicmV0ZW50aW9uUG9saWN5IiwgImRiZ3JvdXAiLCAid2FsUGF0aCIsICJwYXRoIiwgImVuZ2luZSIsICJpbmRleFR5cGUiLCAiaWQiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImluZmx1eGRiX3RzbTFfd2FsX29sZFNlZ21lbnRzRGlza0J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZGJncm91cCIsICJyZXRlbnRpb25Qb2xpY3kiLCAiaW5mbHV4X2hvc3QiLCAiZGJncm91cF9pZCIsICJjbHVzdGVyIiwgImRicm9sZSIsICJwYXRoIiwgIndhbFBhdGgiLCAiaW5mbHV4X3BvcnQiLCAiaWQiLCAiZGF0YWJhc2UiLCAiaG9zdCIsICJpbmRleFR5cGUiLCAiZW5naW5lIiwgInVybCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfdHNtMV93YWxfd3JpdGVFcnIiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJkYmdyb3VwIiwgImNsdXN0ZXIiLCAiaWQiLCAid2FsUGF0aCIsICJwYXRoIiwgImhvc3QiLCAicmV0ZW50aW9uUG9saWN5IiwgImVuZ2luZSIsICJpbmRleFR5cGUiLCAiZGF0YWJhc2UiLCAiZGJyb2xlIiwgImluZmx1eF9wb3J0IiwgInVybCIsICJkYmdyb3VwX2lkIiwgImluZmx1eF9ob3N0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl90c20xX3dhbF93cml0ZU9rIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZGJyb2xlIiwgIndhbFBhdGgiLCAiaW5kZXhUeXBlIiwgImRiZ3JvdXBfaWQiLCAiaG9zdCIsICJkYmdyb3VwIiwgInJldGVudGlvblBvbGljeSIsICJlbmdpbmUiLCAiaW5mbHV4X3BvcnQiLCAiZGF0YWJhc2UiLCAiaW5mbHV4X2hvc3QiLCAiaWQiLCAicGF0aCIsICJ1cmwiLCAiY2x1c3RlciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfZGF0YWJhc2VfbnVtTWVhc3VyZW1lbnRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZGJncm91cF9pZCIsICJob3N0IiwgImRhdGFiYXNlIiwgImRicm9sZSIsICJjbHVzdGVyIiwgImRiZ3JvdXAiLCAidXJsIiwgImluZmx1eF9wb3J0IiwgImluZmx1eF9ob3N0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl9kYXRhYmFzZV9udW1TZXJpZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJ1cmwiLCAiaG9zdCIsICJkYnJvbGUiLCAiaW5mbHV4X2hvc3QiLCAiZGJncm91cCIsICJpbmZsdXhfcG9ydCIsICJjbHVzdGVyIiwgImRhdGFiYXNlIiwgImRiZ3JvdXBfaWQiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImluZmx1eGRiX2NxX3F1ZXJ5RmFpbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImRiZ3JvdXBfaWQiLCAiaG9zdCIsICJkYnJvbGUiLCAiaW5mbHV4X2hvc3QiLCAiaW5mbHV4X3BvcnQiLCAidXJsIiwgImNsdXN0ZXIiLCAiZGJncm91cCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfY3FfcXVlcnlPayIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImRiZ3JvdXAiLCAiZGJncm91cF9pZCIsICJpbmZsdXhfaG9zdCIsICJjbHVzdGVyIiwgImRicm9sZSIsICJob3N0IiwgImluZmx1eF9wb3J0IiwgInVybCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfcXVlcnlFeGVjdXRvcl9xdWVyaWVzQWN0aXZlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaW5mbHV4X2hvc3QiLCAiaW5mbHV4X3BvcnQiLCAiY2x1c3RlciIsICJkYmdyb3VwX2lkIiwgImRicm9sZSIsICJkYmdyb3VwIiwgInVybCIsICJob3N0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl9xdWVyeUV4ZWN1dG9yX3F1ZXJpZXNFeGVjdXRlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImhvc3QiLCAiaW5mbHV4X2hvc3QiLCAiZGJncm91cF9pZCIsICJkYnJvbGUiLCAiaW5mbHV4X3BvcnQiLCAidXJsIiwgImNsdXN0ZXIiLCAiZGJncm91cCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfcXVlcnlFeGVjdXRvcl9xdWVyaWVzRmluaXNoZWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJkYnJvbGUiLCAiY2x1c3RlciIsICJkYmdyb3VwIiwgImRiZ3JvdXBfaWQiLCAiaG9zdCIsICJpbmZsdXhfaG9zdCIsICJpbmZsdXhfcG9ydCIsICJ1cmwiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImluZmx1eGRiX3F1ZXJ5RXhlY3V0b3JfcXVlcnlEdXJhdGlvbk5zIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsidXJsIiwgImRiZ3JvdXBfaWQiLCAiaG9zdCIsICJpbmZsdXhfaG9zdCIsICJkYmdyb3VwIiwgImNsdXN0ZXIiLCAiZGJyb2xlIiwgImluZmx1eF9wb3J0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl9xdWVyeUV4ZWN1dG9yX3JlY292ZXJlZFBhbmljcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNsdXN0ZXIiLCAiZGJncm91cCIsICJkYmdyb3VwX2lkIiwgImRicm9sZSIsICJob3N0IiwgImluZmx1eF9ob3N0IiwgImluZmx1eF9wb3J0IiwgInVybCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfc3Vic2NyaWJlcl9jcmVhdGVGYWlsdXJlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNsdXN0ZXIiLCAiZGJncm91cF9pZCIsICJkYnJvbGUiLCAiaW5mbHV4X2hvc3QiLCAidXJsIiwgImhvc3QiLCAiaW5mbHV4X3BvcnQiLCAiZGJncm91cCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfc3Vic2NyaWJlcl9wb2ludHNXcml0dGVuIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZGJncm91cF9pZCIsICJpbmZsdXhfaG9zdCIsICJpbmZsdXhfcG9ydCIsICJ1cmwiLCAiY2x1c3RlciIsICJkYmdyb3VwIiwgImRicm9sZSIsICJob3N0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl9zdWJzY3JpYmVyX3dyaXRlRmFpbHVyZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJpbmZsdXhfaG9zdCIsICJ1cmwiLCAiZGJncm91cCIsICJpbmZsdXhfcG9ydCIsICJjbHVzdGVyIiwgImRiZ3JvdXBfaWQiLCAiZGJyb2xlIiwgImhvc3QiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImluZmx1eGRiX3dyaXRlX3BvaW50UmVxIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZGJncm91cF9pZCIsICJkYnJvbGUiLCAiY2x1c3RlciIsICJ1cmwiLCAiaG9zdCIsICJpbmZsdXhfaG9zdCIsICJpbmZsdXhfcG9ydCIsICJkYmdyb3VwIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl93cml0ZV9wb2ludFJlcUxvY2FsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZGJyb2xlIiwgImhvc3QiLCAiaW5mbHV4X3BvcnQiLCAiY2x1c3RlciIsICJkYmdyb3VwIiwgImRiZ3JvdXBfaWQiLCAiaW5mbHV4X2hvc3QiLCAidXJsIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl93cml0ZV9yZXEiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJkYmdyb3VwX2lkIiwgImRicm9sZSIsICJpbmZsdXhfcG9ydCIsICJ1cmwiLCAiZGJncm91cCIsICJob3N0IiwgImluZmx1eF9ob3N0IiwgImNsdXN0ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImluZmx1eGRiX3dyaXRlX3N1YldyaXRlRHJvcCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbInVybCIsICJjbHVzdGVyIiwgImRiZ3JvdXAiLCAiZGJncm91cF9pZCIsICJkYnJvbGUiLCAiaG9zdCIsICJpbmZsdXhfaG9zdCIsICJpbmZsdXhfcG9ydCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfd3JpdGVfc3ViV3JpdGVPayIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImluZmx1eF9wb3J0IiwgInVybCIsICJjbHVzdGVyIiwgImRiZ3JvdXBfaWQiLCAiaG9zdCIsICJpbmZsdXhfaG9zdCIsICJkYmdyb3VwIiwgImRicm9sZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfd3JpdGVfd3JpdGVEcm9wIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaW5mbHV4X3BvcnQiLCAiZGJncm91cCIsICJkYmdyb3VwX2lkIiwgImRicm9sZSIsICJpbmZsdXhfaG9zdCIsICJjbHVzdGVyIiwgImhvc3QiLCAidXJsIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl93cml0ZV93cml0ZUVycm9yIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJkYmdyb3VwIiwgImluZmx1eF9wb3J0IiwgInVybCIsICJkYmdyb3VwX2lkIiwgImRicm9sZSIsICJob3N0IiwgImluZmx1eF9ob3N0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl93cml0ZV93cml0ZU9rIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZGJncm91cCIsICJob3N0IiwgImluZmx1eF9ob3N0IiwgImluZmx1eF9wb3J0IiwgInVybCIsICJkYmdyb3VwX2lkIiwgImRicm9sZSIsICJjbHVzdGVyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl93cml0ZV93cml0ZVRpbWVvdXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJ1cmwiLCAiY2x1c3RlciIsICJpbmZsdXhfaG9zdCIsICJkYnJvbGUiLCAiaG9zdCIsICJkYmdyb3VwIiwgImRiZ3JvdXBfaWQiLCAiaW5mbHV4X3BvcnQiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImluZmx1eGRiX25fc2hhcmRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaW5mbHV4X3BvcnQiLCAiY2x1c3RlciIsICJkYmdyb3VwIiwgImRiZ3JvdXBfaWQiLCAiZGJyb2xlIiwgImhvc3QiLCAiaW5mbHV4X2hvc3QiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm1lbV9hY3RpdmUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJkYnJvbGUiLCAiaG9zdCIsICJpbmZsdXhfaG9zdCIsICJpbmZsdXhfcG9ydCIsICJjbHVzdGVyIiwgImRiZ3JvdXAiLCAiZGJncm91cF9pZCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibWVtX2F2YWlsYWJsZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImhvc3QiLCAiaW5mbHV4X2hvc3QiLCAiaW5mbHV4X3BvcnQiLCAiY2x1c3RlciIsICJkYmdyb3VwIiwgImRiZ3JvdXBfaWQiLCAiZGJyb2xlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJtZW1fYXZhaWxhYmxlX3BlcmNlbnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJkYnJvbGUiLCAiaG9zdCIsICJpbmZsdXhfaG9zdCIsICJpbmZsdXhfcG9ydCIsICJjbHVzdGVyIiwgImRiZ3JvdXAiLCAiZGJncm91cF9pZCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibWVtX2J1ZmZlcmVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaW5mbHV4X2hvc3QiLCAiaW5mbHV4X3BvcnQiLCAiY2x1c3RlciIsICJkYmdyb3VwIiwgImRiZ3JvdXBfaWQiLCAiZGJyb2xlIiwgImhvc3QiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm1lbV9jYWNoZWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJkYmdyb3VwIiwgImRiZ3JvdXBfaWQiLCAiZGJyb2xlIiwgImhvc3QiLCAiaW5mbHV4X2hvc3QiLCAiaW5mbHV4X3BvcnQiLCAiY2x1c3RlciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibWVtX2NvbW1pdF9saW1pdCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImRiZ3JvdXBfaWQiLCAiZGJyb2xlIiwgImhvc3QiLCAiaW5mbHV4X2hvc3QiLCAiaW5mbHV4X3BvcnQiLCAiY2x1c3RlciIsICJkYmdyb3VwIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJtZW1fY29tbWl0dGVkX2FzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZGJyb2xlIiwgImhvc3QiLCAiaW5mbHV4X2hvc3QiLCAiaW5mbHV4X3BvcnQiLCAiY2x1c3RlciIsICJkYmdyb3VwIiwgImRiZ3JvdXBfaWQiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm1lbV9kaXJ0eSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImluZmx1eF9wb3J0IiwgImNsdXN0ZXIiLCAiZGJncm91cCIsICJkYmdyb3VwX2lkIiwgImRicm9sZSIsICJob3N0IiwgImluZmx1eF9ob3N0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJtZW1fZnJlZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImRiZ3JvdXAiLCAiZGJncm91cF9pZCIsICJkYnJvbGUiLCAiaG9zdCIsICJpbmZsdXhfaG9zdCIsICJpbmZsdXhfcG9ydCIsICJjbHVzdGVyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJtZW1faGlnaF9mcmVlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJkYmdyb3VwIiwgImRiZ3JvdXBfaWQiLCAiZGJyb2xlIiwgImhvc3QiLCAiaW5mbHV4X2hvc3QiLCAiaW5mbHV4X3BvcnQiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm1lbV9oaWdoX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaW5mbHV4X2hvc3QiLCAiaW5mbHV4X3BvcnQiLCAiY2x1c3RlciIsICJkYmdyb3VwIiwgImRiZ3JvdXBfaWQiLCAiZGJyb2xlIiwgImhvc3QiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm1lbV9odWdlX3BhZ2Vfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImluZmx1eF9ob3N0IiwgImluZmx1eF9wb3J0IiwgImNsdXN0ZXIiLCAiZGJncm91cCIsICJkYmdyb3VwX2lkIiwgImRicm9sZSIsICJob3N0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJtZW1faHVnZV9wYWdlc19mcmVlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZGJyb2xlIiwgImhvc3QiLCAiaW5mbHV4X2hvc3QiLCAiaW5mbHV4X3BvcnQiLCAiY2x1c3RlciIsICJkYmdyb3VwIiwgImRiZ3JvdXBfaWQiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm1lbV9odWdlX3BhZ2VzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJkYmdyb3VwIiwgImRiZ3JvdXBfaWQiLCAiZGJyb2xlIiwgImhvc3QiLCAiaW5mbHV4X2hvc3QiLCAiaW5mbHV4X3BvcnQiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm1lbV9pbmFjdGl2ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImluZmx1eF9ob3N0IiwgImluZmx1eF9wb3J0IiwgImNsdXN0ZXIiLCAiZGJncm91cCIsICJkYmdyb3VwX2lkIiwgImRicm9sZSIsICJob3N0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJtZW1fbG93X2ZyZWUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJkYmdyb3VwIiwgImRiZ3JvdXBfaWQiLCAiZGJyb2xlIiwgImhvc3QiLCAiaW5mbHV4X2hvc3QiLCAiaW5mbHV4X3BvcnQiLCAiY2x1c3RlciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibWVtX2xvd190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImRiZ3JvdXAiLCAiZGJncm91cF9pZCIsICJkYnJvbGUiLCAiaG9zdCIsICJpbmZsdXhfaG9zdCIsICJpbmZsdXhfcG9ydCIsICJjbHVzdGVyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJtZW1fbWFwcGVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaG9zdCIsICJpbmZsdXhfaG9zdCIsICJpbmZsdXhfcG9ydCIsICJjbHVzdGVyIiwgImRiZ3JvdXAiLCAiZGJncm91cF9pZCIsICJkYnJvbGUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm1lbV9wYWdlX3RhYmxlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImRiZ3JvdXBfaWQiLCAiZGJyb2xlIiwgImhvc3QiLCAiaW5mbHV4X2hvc3QiLCAiaW5mbHV4X3BvcnQiLCAiY2x1c3RlciIsICJkYmdyb3VwIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJtZW1fc2hhcmVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZGJncm91cF9pZCIsICJkYnJvbGUiLCAiaG9zdCIsICJpbmZsdXhfaG9zdCIsICJpbmZsdXhfcG9ydCIsICJjbHVzdGVyIiwgImRiZ3JvdXAiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm1lbV9zbGFiIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaW5mbHV4X2hvc3QiLCAiaW5mbHV4X3BvcnQiLCAiY2x1c3RlciIsICJkYmdyb3VwIiwgImRiZ3JvdXBfaWQiLCAiZGJyb2xlIiwgImhvc3QiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm1lbV9zd2FwX2NhY2hlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImRiZ3JvdXBfaWQiLCAiZGJyb2xlIiwgImhvc3QiLCAiaW5mbHV4X2hvc3QiLCAiaW5mbHV4X3BvcnQiLCAiY2x1c3RlciIsICJkYmdyb3VwIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJtZW1fc3dhcF9mcmVlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaW5mbHV4X2hvc3QiLCAiaW5mbHV4X3BvcnQiLCAiY2x1c3RlciIsICJkYmdyb3VwIiwgImRiZ3JvdXBfaWQiLCAiZGJyb2xlIiwgImhvc3QiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm1lbV9zd2FwX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZGJyb2xlIiwgImhvc3QiLCAiaW5mbHV4X2hvc3QiLCAiaW5mbHV4X3BvcnQiLCAiY2x1c3RlciIsICJkYmdyb3VwIiwgImRiZ3JvdXBfaWQiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm1lbV90b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImhvc3QiLCAiaW5mbHV4X2hvc3QiLCAiaW5mbHV4X3BvcnQiLCAiY2x1c3RlciIsICJkYmdyb3VwIiwgImRiZ3JvdXBfaWQiLCAiZGJyb2xlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJtZW1fdXNlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImhvc3QiLCAiaW5mbHV4X2hvc3QiLCAiaW5mbHV4X3BvcnQiLCAiY2x1c3RlciIsICJkYmdyb3VwIiwgImRiZ3JvdXBfaWQiLCAiZGJyb2xlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJtZW1fdXNlZF9wZXJjZW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJkYmdyb3VwIiwgImRiZ3JvdXBfaWQiLCAiZGJyb2xlIiwgImhvc3QiLCAiaW5mbHV4X2hvc3QiLCAiaW5mbHV4X3BvcnQiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm1lbV92bWFsbG9jX2NodW5rIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaG9zdCIsICJpbmZsdXhfaG9zdCIsICJpbmZsdXhfcG9ydCIsICJjbHVzdGVyIiwgImRiZ3JvdXAiLCAiZGJncm91cF9pZCIsICJkYnJvbGUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm1lbV92bWFsbG9jX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaW5mbHV4X2hvc3QiLCAiaW5mbHV4X3BvcnQiLCAiY2x1c3RlciIsICJkYmdyb3VwIiwgImRiZ3JvdXBfaWQiLCAiZGJyb2xlIiwgImhvc3QiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm1lbV92bWFsbG9jX3VzZWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjbHVzdGVyIiwgImRiZ3JvdXAiLCAiZGJncm91cF9pZCIsICJkYnJvbGUiLCAiaG9zdCIsICJpbmZsdXhfaG9zdCIsICJpbmZsdXhfcG9ydCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibWVtX3dpcmVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZGJncm91cCIsICJkYmdyb3VwX2lkIiwgImRicm9sZSIsICJob3N0IiwgImluZmx1eF9ob3N0IiwgImluZmx1eF9wb3J0IiwgImNsdXN0ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm1lbV93cml0ZV9iYWNrIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZGJncm91cCIsICJkYmdyb3VwX2lkIiwgImRicm9sZSIsICJob3N0IiwgImluZmx1eF9ob3N0IiwgImluZmx1eF9wb3J0IiwgImNsdXN0ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm1lbV93cml0ZV9iYWNrX3RtcCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImluZmx1eF9ob3N0IiwgImluZmx1eF9wb3J0IiwgImNsdXN0ZXIiLCAiZGJncm91cCIsICJkYmdyb3VwX2lkIiwgImRicm9sZSIsICJob3N0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJ1cmwiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbmdpbmUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwYXRoIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAid2FsUGF0aCIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImlkIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiY2x1c3RlciIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImRiZ3JvdXAiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhfaG9zdCIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImRiZ3JvdXBfaWQiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJob3N0IiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmV0ZW50aW9uUG9saWN5IiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4X3BvcnQiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmRleFR5cGUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJkYnJvbGUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX1dLCAidGFibGVfaWQiOiAicHVzaGdhdGV3YXlfZGJtX2luZmx1eGRiX2JrcHVsbC5Hcm91cDEiLCAidGFibGVfZGVzYyI6ICJcdTUyMDZcdTdlYzQxIiwgInRhYmxlX25hbWUiOiAiR3JvdXAxIn0sIHsiZmllbGRzIjogW3sibmFtZSI6ICJfc2VydmVyIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogInNlcnZlciIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaHR0cF9yZXNwb25zZV9odHRwX3Jlc3BvbnNlX2NvZGUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJpbmZsdXhfaG9zdCIsICJkYnJvbGUiLCAiY2x1c3RlciIsICJob3N0IiwgInNlcnZlciIsICJyZXN1bHQiLCAiZGJncm91cCIsICJtZXRob2QiLCAiaW5mbHV4X3BvcnQiLCAic3RhdHVzX2NvZGUiLCAiZGJncm91cF9pZCIsICJyZXN1bHRfdHlwZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaHR0cF9yZXNwb25zZV9yZXNwb25zZV90aW1lIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJkYmdyb3VwIiwgInJlc3VsdF90eXBlIiwgImRiZ3JvdXBfaWQiLCAiZGJyb2xlIiwgIm1ldGhvZCIsICJyZXN1bHQiLCAiaW5mbHV4X3BvcnQiLCAic3RhdHVzX2NvZGUiLCAiaW5mbHV4X2hvc3QiLCAic2VydmVyIiwgImhvc3QiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImh0dHBfcmVzcG9uc2VfcmVzdWx0X2NvZGUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjbHVzdGVyIiwgImRiZ3JvdXAiLCAiZGJyb2xlIiwgImluZmx1eF9wb3J0IiwgImRiZ3JvdXBfaWQiLCAiaG9zdCIsICJyZXN1bHQiLCAic3RhdHVzX2NvZGUiLCAibWV0aG9kIiwgImluZmx1eF9ob3N0IiwgInNlcnZlciIsICJyZXN1bHRfdHlwZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4X2hvc3QiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJkYnJvbGUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJjbHVzdGVyIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaG9zdCIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlc3VsdCIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImRiZ3JvdXAiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJtZXRob2QiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhfcG9ydCIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInN0YXR1c19jb2RlIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZGJncm91cF9pZCIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlc3VsdF90eXBlIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9XSwgInRhYmxlX2lkIjogInB1c2hnYXRld2F5X2RibV9pbmZsdXhkYl9ia3B1bGwuR3JvdXAyIiwgInRhYmxlX2Rlc2MiOiAiXHU1MjA2XHU3ZWM0MiIsICJ0YWJsZV9uYW1lIjogIkdyb3VwMiJ9LCB7ImZpZWxkcyI6IFt7Im5hbWUiOiAiZGlza19mcmVlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZGJyb2xlIiwgImZzdHlwZSIsICJkYmdyb3VwIiwgImluZmx1eF9wb3J0IiwgImluZmx1eF9ob3N0IiwgImRldmljZSIsICJtb2RlIiwgImRiZ3JvdXBfaWQiLCAiaG9zdCIsICJwYXRoIiwgImNsdXN0ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImRpc2tfaW5vZGVzX2ZyZWUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJob3N0IiwgImluZmx1eF9ob3N0IiwgImRiZ3JvdXAiLCAiZnN0eXBlIiwgImluZmx1eF9wb3J0IiwgImRicm9sZSIsICJkZXZpY2UiLCAicGF0aCIsICJtb2RlIiwgImRiZ3JvdXBfaWQiLCAiY2x1c3RlciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZGlza19pbm9kZXNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJpbmZsdXhfcG9ydCIsICJkZXZpY2UiLCAiZGJncm91cCIsICJpbmZsdXhfaG9zdCIsICJmc3R5cGUiLCAicGF0aCIsICJkYmdyb3VwX2lkIiwgIm1vZGUiLCAiZGJyb2xlIiwgImhvc3QiLCAiY2x1c3RlciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZGlza19pbm9kZXNfdXNlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImRiZ3JvdXBfaWQiLCAiZGJncm91cCIsICJtb2RlIiwgImRicm9sZSIsICJkZXZpY2UiLCAiY2x1c3RlciIsICJmc3R5cGUiLCAicGF0aCIsICJpbmZsdXhfaG9zdCIsICJpbmZsdXhfcG9ydCIsICJob3N0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJkaXNrX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZGV2aWNlIiwgImhvc3QiLCAicGF0aCIsICJkYmdyb3VwIiwgImluZmx1eF9ob3N0IiwgImluZmx1eF9wb3J0IiwgImRicm9sZSIsICJtb2RlIiwgImRiZ3JvdXBfaWQiLCAiZnN0eXBlIiwgImNsdXN0ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImRpc2tfdXNlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImluZmx1eF9ob3N0IiwgImhvc3QiLCAiZGJncm91cCIsICJkZXZpY2UiLCAiY2x1c3RlciIsICJkYnJvbGUiLCAiZnN0eXBlIiwgImluZmx1eF9wb3J0IiwgImRiZ3JvdXBfaWQiLCAicGF0aCIsICJtb2RlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJkaXNrX3VzZWRfcGVyY2VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImRiZ3JvdXAiLCAiY2x1c3RlciIsICJwYXRoIiwgImRldmljZSIsICJmc3R5cGUiLCAiaG9zdCIsICJkYnJvbGUiLCAiaW5mbHV4X2hvc3QiLCAibW9kZSIsICJkYmdyb3VwX2lkIiwgImluZmx1eF9wb3J0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJkYnJvbGUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJmc3R5cGUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJkYmdyb3VwIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4X3BvcnQiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhfaG9zdCIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImRldmljZSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm1vZGUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJkYmdyb3VwX2lkIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaG9zdCIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInBhdGgiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJjbHVzdGVyIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9XSwgInRhYmxlX2lkIjogInB1c2hnYXRld2F5X2RibV9pbmZsdXhkYl9ia3B1bGwuR3JvdXAzIiwgInRhYmxlX2Rlc2MiOiAiXHU1MjA2XHU3ZWM0MyIsICJ0YWJsZV9uYW1lIjogIkdyb3VwMyJ9LCB7ImZpZWxkcyI6IFt7Im5hbWUiOiAicHJvY3N0YXRfY3B1X3RpbWUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJob3N0IiwgImV4ZSIsICJjbHVzdGVyIiwgImRiZ3JvdXAiLCAiaW5mbHV4X2hvc3QiLCAiZGJyb2xlIiwgImRiZ3JvdXBfaWQiLCAicHJvY2Vzc19uYW1lIiwgInVzZXIiLCAiaW5mbHV4X3BvcnQiLCAicGlkIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jc3RhdF9jcHVfdGltZV9ndWVzdCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImhvc3QiLCAiaW5mbHV4X2hvc3QiLCAidXNlciIsICJkYnJvbGUiLCAicHJvY2Vzc19uYW1lIiwgInBpZCIsICJjbHVzdGVyIiwgImV4ZSIsICJpbmZsdXhfcG9ydCIsICJkYmdyb3VwIiwgImRiZ3JvdXBfaWQiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb2NzdGF0X2NwdV90aW1lX2d1ZXN0X25pY2UiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJpbmZsdXhfaG9zdCIsICJwaWQiLCAiZGJncm91cF9pZCIsICJob3N0IiwgImluZmx1eF9wb3J0IiwgInVzZXIiLCAiZXhlIiwgInByb2Nlc3NfbmFtZSIsICJkYmdyb3VwIiwgImRicm9sZSIsICJjbHVzdGVyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jc3RhdF9jcHVfdGltZV9pZGxlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsidXNlciIsICJkYmdyb3VwIiwgImluZmx1eF9ob3N0IiwgImluZmx1eF9wb3J0IiwgImRicm9sZSIsICJob3N0IiwgImNsdXN0ZXIiLCAicHJvY2Vzc19uYW1lIiwgImRiZ3JvdXBfaWQiLCAiZXhlIiwgInBpZCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY3N0YXRfY3B1X3RpbWVfaW93YWl0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaW5mbHV4X2hvc3QiLCAiaW5mbHV4X3BvcnQiLCAiZGJncm91cF9pZCIsICJwaWQiLCAiZXhlIiwgInVzZXIiLCAiZGJncm91cCIsICJob3N0IiwgImRicm9sZSIsICJwcm9jZXNzX25hbWUiLCAiY2x1c3RlciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY3N0YXRfY3B1X3RpbWVfaXJxIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZGJncm91cCIsICJwcm9jZXNzX25hbWUiLCAiZGJncm91cF9pZCIsICJwaWQiLCAiZGJyb2xlIiwgImluZmx1eF9ob3N0IiwgInVzZXIiLCAiZXhlIiwgImhvc3QiLCAiaW5mbHV4X3BvcnQiLCAiY2x1c3RlciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY3N0YXRfY3B1X3RpbWVfbmljZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImV4ZSIsICJjbHVzdGVyIiwgImluZmx1eF9ob3N0IiwgImluZmx1eF9wb3J0IiwgImRicm9sZSIsICJwcm9jZXNzX25hbWUiLCAidXNlciIsICJkYmdyb3VwIiwgImRiZ3JvdXBfaWQiLCAiaG9zdCIsICJwaWQiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb2NzdGF0X2NwdV90aW1lX3NvZnRfaXJxIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZGJncm91cCIsICJob3N0IiwgImRicm9sZSIsICJpbmZsdXhfaG9zdCIsICJwaWQiLCAidXNlciIsICJleGUiLCAiZGJncm91cF9pZCIsICJwcm9jZXNzX25hbWUiLCAiY2x1c3RlciIsICJpbmZsdXhfcG9ydCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY3N0YXRfY3B1X3RpbWVfc3RlYWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJkYmdyb3VwX2lkIiwgImV4ZSIsICJpbmZsdXhfcG9ydCIsICJpbmZsdXhfaG9zdCIsICJwcm9jZXNzX25hbWUiLCAidXNlciIsICJjbHVzdGVyIiwgImhvc3QiLCAicGlkIiwgImRicm9sZSIsICJkYmdyb3VwIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jc3RhdF9jcHVfdGltZV9zdG9sZW4iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJwaWQiLCAiZGJyb2xlIiwgImhvc3QiLCAiaW5mbHV4X3BvcnQiLCAicHJvY2Vzc19uYW1lIiwgImV4ZSIsICJpbmZsdXhfaG9zdCIsICJ1c2VyIiwgImRiZ3JvdXAiLCAiZGJncm91cF9pZCIsICJjbHVzdGVyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jc3RhdF9jcHVfdGltZV9zeXN0ZW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJpbmZsdXhfaG9zdCIsICJwcm9jZXNzX25hbWUiLCAiZGJncm91cF9pZCIsICJ1c2VyIiwgImNsdXN0ZXIiLCAiZGJncm91cCIsICJkYnJvbGUiLCAiZXhlIiwgImhvc3QiLCAicGlkIiwgImluZmx1eF9wb3J0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jc3RhdF9jcHVfdGltZV91c2VyIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsicHJvY2Vzc19uYW1lIiwgImNsdXN0ZXIiLCAiZGJyb2xlIiwgImV4ZSIsICJkYmdyb3VwX2lkIiwgInVzZXIiLCAiaW5mbHV4X3BvcnQiLCAiaG9zdCIsICJpbmZsdXhfaG9zdCIsICJwaWQiLCAiZGJncm91cCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY3N0YXRfY3B1X3VzYWdlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZGJncm91cCIsICJpbmZsdXhfcG9ydCIsICJjbHVzdGVyIiwgImRiZ3JvdXBfaWQiLCAiZGJyb2xlIiwgImhvc3QiLCAicGlkIiwgInVzZXIiLCAiaW5mbHV4X2hvc3QiLCAiZXhlIiwgInByb2Nlc3NfbmFtZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY3N0YXRfaW52b2x1bnRhcnlfY29udGV4dF9zd2l0Y2hlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImRicm9sZSIsICJwaWQiLCAiaW5mbHV4X3BvcnQiLCAiZXhlIiwgImhvc3QiLCAiZGJncm91cF9pZCIsICJpbmZsdXhfaG9zdCIsICJjbHVzdGVyIiwgInVzZXIiLCAiZGJncm91cCIsICJwcm9jZXNzX25hbWUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb2NzdGF0X21lbW9yeV9kYXRhIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaW5mbHV4X2hvc3QiLCAiZGJncm91cF9pZCIsICJwcm9jZXNzX25hbWUiLCAidXNlciIsICJkYmdyb3VwIiwgImRicm9sZSIsICJob3N0IiwgInBpZCIsICJjbHVzdGVyIiwgImV4ZSIsICJpbmZsdXhfcG9ydCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY3N0YXRfbWVtb3J5X2xvY2tlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbInByb2Nlc3NfbmFtZSIsICJ1c2VyIiwgImV4ZSIsICJwaWQiLCAiY2x1c3RlciIsICJkYmdyb3VwIiwgImRicm9sZSIsICJob3N0IiwgImluZmx1eF9ob3N0IiwgImluZmx1eF9wb3J0IiwgImRiZ3JvdXBfaWQiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb2NzdGF0X21lbW9yeV9yc3MiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJpbmZsdXhfcG9ydCIsICJwcm9jZXNzX25hbWUiLCAiY2x1c3RlciIsICJkYmdyb3VwX2lkIiwgImRicm9sZSIsICJwaWQiLCAidXNlciIsICJleGUiLCAiaG9zdCIsICJkYmdyb3VwIiwgImluZmx1eF9ob3N0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jc3RhdF9tZW1vcnlfc3RhY2siLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJ1c2VyIiwgInBpZCIsICJwcm9jZXNzX25hbWUiLCAiaG9zdCIsICJleGUiLCAiY2x1c3RlciIsICJkYmdyb3VwIiwgImluZmx1eF9wb3J0IiwgImRicm9sZSIsICJkYmdyb3VwX2lkIiwgImluZmx1eF9ob3N0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jc3RhdF9tZW1vcnlfc3dhcCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImluZmx1eF9wb3J0IiwgInVzZXIiLCAiZXhlIiwgInBpZCIsICJjbHVzdGVyIiwgInByb2Nlc3NfbmFtZSIsICJkYmdyb3VwX2lkIiwgImluZmx1eF9ob3N0IiwgImRiZ3JvdXAiLCAiZGJyb2xlIiwgImhvc3QiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb2NzdGF0X21lbW9yeV92bXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJpbmZsdXhfaG9zdCIsICJpbmZsdXhfcG9ydCIsICJkYmdyb3VwIiwgInVzZXIiLCAiZXhlIiwgInBpZCIsICJkYmdyb3VwX2lkIiwgInByb2Nlc3NfbmFtZSIsICJjbHVzdGVyIiwgImhvc3QiLCAiZGJyb2xlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jc3RhdF9uaWNlX3ByaW9yaXR5IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJleGUiLCAiaW5mbHV4X3BvcnQiLCAiZGJncm91cCIsICJwaWQiLCAicHJvY2Vzc19uYW1lIiwgImRiZ3JvdXBfaWQiLCAiaG9zdCIsICJpbmZsdXhfaG9zdCIsICJ1c2VyIiwgImRicm9sZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY3N0YXRfbnVtX2ZkcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImRiZ3JvdXBfaWQiLCAiZGJncm91cCIsICJpbmZsdXhfcG9ydCIsICJ1c2VyIiwgImNsdXN0ZXIiLCAiZGJyb2xlIiwgImhvc3QiLCAiaW5mbHV4X2hvc3QiLCAiZXhlIiwgInBpZCIsICJwcm9jZXNzX25hbWUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb2NzdGF0X251bV90aHJlYWRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaW5mbHV4X3BvcnQiLCAiZGJncm91cF9pZCIsICJob3N0IiwgInBpZCIsICJjbHVzdGVyIiwgImRicm9sZSIsICJkYmdyb3VwIiwgImV4ZSIsICJwcm9jZXNzX25hbWUiLCAiaW5mbHV4X2hvc3QiLCAidXNlciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY3N0YXRfcmVhZF9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImRicm9sZSIsICJob3N0IiwgImV4ZSIsICJkYmdyb3VwX2lkIiwgImluZmx1eF9ob3N0IiwgImNsdXN0ZXIiLCAiZGJncm91cCIsICJpbmZsdXhfcG9ydCIsICJwaWQiLCAicHJvY2Vzc19uYW1lIiwgInVzZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb2NzdGF0X3JlYWRfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJwaWQiLCAiZGJyb2xlIiwgImV4ZSIsICJwcm9jZXNzX25hbWUiLCAiY2x1c3RlciIsICJkYmdyb3VwX2lkIiwgImhvc3QiLCAiaW5mbHV4X3BvcnQiLCAidXNlciIsICJkYmdyb3VwIiwgImluZmx1eF9ob3N0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jc3RhdF9yZWFsdGltZV9wcmlvcml0eSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImluZmx1eF9ob3N0IiwgImNsdXN0ZXIiLCAidXNlciIsICJob3N0IiwgImRicm9sZSIsICJleGUiLCAiZGJncm91cCIsICJpbmZsdXhfcG9ydCIsICJwaWQiLCAicHJvY2Vzc19uYW1lIiwgImRiZ3JvdXBfaWQiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb2NzdGF0X3JsaW1pdF9jcHVfdGltZV9oYXJkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsicHJvY2Vzc19uYW1lIiwgImRiZ3JvdXBfaWQiLCAiZXhlIiwgImluZmx1eF9wb3J0IiwgInVzZXIiLCAiaW5mbHV4X2hvc3QiLCAiaG9zdCIsICJwaWQiLCAiY2x1c3RlciIsICJkYnJvbGUiLCAiZGJncm91cCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY3N0YXRfcmxpbWl0X2NwdV90aW1lX3NvZnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJleGUiLCAidXNlciIsICJpbmZsdXhfcG9ydCIsICJwcm9jZXNzX25hbWUiLCAiZGJncm91cCIsICJob3N0IiwgImNsdXN0ZXIiLCAiZGJncm91cF9pZCIsICJkYnJvbGUiLCAiaW5mbHV4X2hvc3QiLCAicGlkIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jc3RhdF9ybGltaXRfZmlsZV9sb2Nrc19oYXJkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsidXNlciIsICJkYmdyb3VwIiwgImRiZ3JvdXBfaWQiLCAiaW5mbHV4X3BvcnQiLCAiY2x1c3RlciIsICJkYnJvbGUiLCAiZXhlIiwgInByb2Nlc3NfbmFtZSIsICJpbmZsdXhfaG9zdCIsICJwaWQiLCAiaG9zdCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY3N0YXRfcmxpbWl0X2ZpbGVfbG9ja3Nfc29mdCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImV4ZSIsICJkYmdyb3VwIiwgInBpZCIsICJkYnJvbGUiLCAiaW5mbHV4X2hvc3QiLCAiZGJncm91cF9pZCIsICJob3N0IiwgInVzZXIiLCAiY2x1c3RlciIsICJpbmZsdXhfcG9ydCIsICJwcm9jZXNzX25hbWUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb2NzdGF0X3JsaW1pdF9tZW1vcnlfZGF0YV9oYXJkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZGJyb2xlIiwgImluZmx1eF9ob3N0IiwgImluZmx1eF9wb3J0IiwgInByb2Nlc3NfbmFtZSIsICJjbHVzdGVyIiwgImRiZ3JvdXAiLCAiZGJncm91cF9pZCIsICJwaWQiLCAiZXhlIiwgImhvc3QiLCAidXNlciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY3N0YXRfcmxpbWl0X21lbW9yeV9kYXRhX3NvZnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJkYmdyb3VwX2lkIiwgImNsdXN0ZXIiLCAicHJvY2Vzc19uYW1lIiwgImhvc3QiLCAiaW5mbHV4X2hvc3QiLCAiaW5mbHV4X3BvcnQiLCAicGlkIiwgInVzZXIiLCAiZGJyb2xlIiwgImV4ZSIsICJkYmdyb3VwIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jc3RhdF9ybGltaXRfbWVtb3J5X2xvY2tlZF9oYXJkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaG9zdCIsICJpbmZsdXhfaG9zdCIsICJ1c2VyIiwgImRiZ3JvdXAiLCAiaW5mbHV4X3BvcnQiLCAiZXhlIiwgInBpZCIsICJkYmdyb3VwX2lkIiwgImRicm9sZSIsICJwcm9jZXNzX25hbWUiLCAiY2x1c3RlciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY3N0YXRfcmxpbWl0X21lbW9yeV9sb2NrZWRfc29mdCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImRiZ3JvdXAiLCAidXNlciIsICJwcm9jZXNzX25hbWUiLCAiaG9zdCIsICJjbHVzdGVyIiwgImRicm9sZSIsICJpbmZsdXhfaG9zdCIsICJkYmdyb3VwX2lkIiwgImV4ZSIsICJpbmZsdXhfcG9ydCIsICJwaWQiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb2NzdGF0X3JsaW1pdF9tZW1vcnlfcnNzX2hhcmQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjbHVzdGVyIiwgImRiZ3JvdXAiLCAiZGJyb2xlIiwgImV4ZSIsICJwaWQiLCAidXNlciIsICJpbmZsdXhfcG9ydCIsICJkYmdyb3VwX2lkIiwgInByb2Nlc3NfbmFtZSIsICJob3N0IiwgImluZmx1eF9ob3N0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jc3RhdF9ybGltaXRfbWVtb3J5X3Jzc19zb2Z0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsidXNlciIsICJkYmdyb3VwX2lkIiwgImluZmx1eF9ob3N0IiwgInBpZCIsICJleGUiLCAiaG9zdCIsICJpbmZsdXhfcG9ydCIsICJkYnJvbGUiLCAiZGJncm91cCIsICJwcm9jZXNzX25hbWUiLCAiY2x1c3RlciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY3N0YXRfcmxpbWl0X21lbW9yeV9zdGFja19oYXJkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsicHJvY2Vzc19uYW1lIiwgInVzZXIiLCAiaW5mbHV4X2hvc3QiLCAicGlkIiwgImRicm9sZSIsICJkYmdyb3VwX2lkIiwgImhvc3QiLCAiY2x1c3RlciIsICJleGUiLCAiaW5mbHV4X3BvcnQiLCAiZGJncm91cCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY3N0YXRfcmxpbWl0X21lbW9yeV9zdGFja19zb2Z0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZXhlIiwgImhvc3QiLCAiaW5mbHV4X3BvcnQiLCAiY2x1c3RlciIsICJkYmdyb3VwIiwgImluZmx1eF9ob3N0IiwgInByb2Nlc3NfbmFtZSIsICJkYnJvbGUiLCAicGlkIiwgImRiZ3JvdXBfaWQiLCAidXNlciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY3N0YXRfcmxpbWl0X21lbW9yeV92bXNfaGFyZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImhvc3QiLCAicHJvY2Vzc19uYW1lIiwgImluZmx1eF9ob3N0IiwgInVzZXIiLCAiY2x1c3RlciIsICJkYmdyb3VwIiwgImV4ZSIsICJwaWQiLCAiZGJyb2xlIiwgImluZmx1eF9wb3J0IiwgImRiZ3JvdXBfaWQiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb2NzdGF0X3JsaW1pdF9tZW1vcnlfdm1zX3NvZnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJob3N0IiwgImNsdXN0ZXIiLCAiZGJncm91cF9pZCIsICJpbmZsdXhfcG9ydCIsICJ1c2VyIiwgInBpZCIsICJkYnJvbGUiLCAiZXhlIiwgImRiZ3JvdXAiLCAicHJvY2Vzc19uYW1lIiwgImluZmx1eF9ob3N0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jc3RhdF9ybGltaXRfbmljZV9wcmlvcml0eV9oYXJkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaW5mbHV4X3BvcnQiLCAicHJvY2Vzc19uYW1lIiwgImRicm9sZSIsICJob3N0IiwgImluZmx1eF9ob3N0IiwgImRiZ3JvdXBfaWQiLCAidXNlciIsICJkYmdyb3VwIiwgImNsdXN0ZXIiLCAiZXhlIiwgInBpZCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY3N0YXRfcmxpbWl0X25pY2VfcHJpb3JpdHlfc29mdCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImluZmx1eF9wb3J0IiwgInVzZXIiLCAiZXhlIiwgImNsdXN0ZXIiLCAiZGJncm91cF9pZCIsICJkYmdyb3VwIiwgInByb2Nlc3NfbmFtZSIsICJkYnJvbGUiLCAiaG9zdCIsICJpbmZsdXhfaG9zdCIsICJwaWQiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb2NzdGF0X3JsaW1pdF9udW1fZmRzX2hhcmQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJkYnJvbGUiLCAiZXhlIiwgInBpZCIsICJpbmZsdXhfcG9ydCIsICJob3N0IiwgImluZmx1eF9ob3N0IiwgImRiZ3JvdXAiLCAidXNlciIsICJjbHVzdGVyIiwgImRiZ3JvdXBfaWQiLCAicHJvY2Vzc19uYW1lIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jc3RhdF9ybGltaXRfbnVtX2Zkc19zb2Z0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsicHJvY2Vzc19uYW1lIiwgImRiZ3JvdXAiLCAiZXhlIiwgImluZmx1eF9wb3J0IiwgImRicm9sZSIsICJob3N0IiwgImluZmx1eF9ob3N0IiwgImRiZ3JvdXBfaWQiLCAidXNlciIsICJjbHVzdGVyIiwgInBpZCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY3N0YXRfcmxpbWl0X3JlYWx0aW1lX3ByaW9yaXR5X2hhcmQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJpbmZsdXhfaG9zdCIsICJpbmZsdXhfcG9ydCIsICJleGUiLCAicGlkIiwgImRiZ3JvdXBfaWQiLCAiaG9zdCIsICJwcm9jZXNzX25hbWUiLCAidXNlciIsICJkYmdyb3VwIiwgImNsdXN0ZXIiLCAiZGJyb2xlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jc3RhdF9ybGltaXRfcmVhbHRpbWVfcHJpb3JpdHlfc29mdCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbInByb2Nlc3NfbmFtZSIsICJwaWQiLCAiZXhlIiwgImRiZ3JvdXBfaWQiLCAiZGJyb2xlIiwgImluZmx1eF9ob3N0IiwgImNsdXN0ZXIiLCAiZGJncm91cCIsICJob3N0IiwgImluZmx1eF9wb3J0IiwgInVzZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb2NzdGF0X3JsaW1pdF9zaWduYWxzX3BlbmRpbmdfaGFyZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImluZmx1eF9wb3J0IiwgInBpZCIsICJwcm9jZXNzX25hbWUiLCAiY2x1c3RlciIsICJob3N0IiwgImRiZ3JvdXBfaWQiLCAiZXhlIiwgImluZmx1eF9ob3N0IiwgImRiZ3JvdXAiLCAiZGJyb2xlIiwgInVzZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb2NzdGF0X3JsaW1pdF9zaWduYWxzX3BlbmRpbmdfc29mdCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbInVzZXIiLCAicHJvY2Vzc19uYW1lIiwgImRiZ3JvdXAiLCAiZXhlIiwgImRicm9sZSIsICJjbHVzdGVyIiwgImluZmx1eF9ob3N0IiwgImluZmx1eF9wb3J0IiwgImhvc3QiLCAicGlkIiwgImRiZ3JvdXBfaWQiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb2NzdGF0X3NpZ25hbHNfcGVuZGluZyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImRiZ3JvdXBfaWQiLCAidXNlciIsICJpbmZsdXhfcG9ydCIsICJob3N0IiwgImluZmx1eF9ob3N0IiwgImRiZ3JvdXAiLCAiZGJyb2xlIiwgImV4ZSIsICJwaWQiLCAicHJvY2Vzc19uYW1lIiwgImNsdXN0ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb2NzdGF0X3ZvbHVudGFyeV9jb250ZXh0X3N3aXRjaGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJwcm9jZXNzX25hbWUiLCAidXNlciIsICJkYmdyb3VwIiwgImluZmx1eF9wb3J0IiwgImluZmx1eF9ob3N0IiwgInBpZCIsICJleGUiLCAiZGJncm91cF9pZCIsICJkYnJvbGUiLCAiaG9zdCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY3N0YXRfd3JpdGVfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJ1c2VyIiwgImluZmx1eF9ob3N0IiwgImV4ZSIsICJob3N0IiwgImRiZ3JvdXBfaWQiLCAiZGJncm91cCIsICJwaWQiLCAiY2x1c3RlciIsICJpbmZsdXhfcG9ydCIsICJwcm9jZXNzX25hbWUiLCAiZGJyb2xlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jc3RhdF93cml0ZV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbInVzZXIiLCAiZGJyb2xlIiwgImluZmx1eF9ob3N0IiwgImhvc3QiLCAiZXhlIiwgInBpZCIsICJjbHVzdGVyIiwgInByb2Nlc3NfbmFtZSIsICJpbmZsdXhfcG9ydCIsICJkYmdyb3VwIiwgImRiZ3JvdXBfaWQiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIl9fbmFtZSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICJuYW1lIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJkaXNraW9faW9fdGltZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbIm5hbWUiLCAiY2x1c3RlciIsICJpbmZsdXhfcG9ydCIsICJob3N0IiwgImluZmx1eF9ob3N0IiwgImRiZ3JvdXAiLCAiZGJncm91cF9pZCIsICJkYnJvbGUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImRpc2tpb19pb3BzX2luX3Byb2dyZXNzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJkYmdyb3VwX2lkIiwgImRicm9sZSIsICJpbmZsdXhfcG9ydCIsICJkYmdyb3VwIiwgImluZmx1eF9ob3N0IiwgImhvc3QiLCAibmFtZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZGlza2lvX3JlYWRfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJkYmdyb3VwX2lkIiwgImhvc3QiLCAiaW5mbHV4X2hvc3QiLCAiaW5mbHV4X3BvcnQiLCAibmFtZSIsICJkYmdyb3VwIiwgImRicm9sZSIsICJjbHVzdGVyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJkaXNraW9fcmVhZF90aW1lIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaW5mbHV4X3BvcnQiLCAibmFtZSIsICJkYmdyb3VwIiwgImRiZ3JvdXBfaWQiLCAiY2x1c3RlciIsICJkYnJvbGUiLCAiaG9zdCIsICJpbmZsdXhfaG9zdCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZGlza2lvX3JlYWRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJkYmdyb3VwX2lkIiwgImRicm9sZSIsICJpbmZsdXhfaG9zdCIsICJuYW1lIiwgImRiZ3JvdXAiLCAiaG9zdCIsICJpbmZsdXhfcG9ydCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZGlza2lvX3dlaWdodGVkX2lvX3RpbWUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjbHVzdGVyIiwgImRiZ3JvdXAiLCAiaG9zdCIsICJpbmZsdXhfaG9zdCIsICJuYW1lIiwgImRiZ3JvdXBfaWQiLCAiZGJyb2xlIiwgImluZmx1eF9wb3J0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJkaXNraW9fd3JpdGVfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJuYW1lIiwgImRiZ3JvdXAiLCAiZGJncm91cF9pZCIsICJjbHVzdGVyIiwgImRicm9sZSIsICJob3N0IiwgImluZmx1eF9ob3N0IiwgImluZmx1eF9wb3J0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJkaXNraW9fd3JpdGVfdGltZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImhvc3QiLCAiaW5mbHV4X2hvc3QiLCAiaW5mbHV4X3BvcnQiLCAibmFtZSIsICJkYmdyb3VwX2lkIiwgImRicm9sZSIsICJjbHVzdGVyIiwgImRiZ3JvdXAiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImRpc2tpb193cml0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjbHVzdGVyIiwgImRiZ3JvdXAiLCAiaG9zdCIsICJkYmdyb3VwX2lkIiwgImRicm9sZSIsICJpbmZsdXhfaG9zdCIsICJpbmZsdXhfcG9ydCIsICJuYW1lIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJob3N0IiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZXhlIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiY2x1c3RlciIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImRiZ3JvdXAiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhfaG9zdCIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImRicm9sZSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImRiZ3JvdXBfaWQiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jZXNzX25hbWUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJ1c2VyIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4X3BvcnQiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwaWQiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX1dLCAidGFibGVfaWQiOiAicHVzaGdhdGV3YXlfZGJtX2luZmx1eGRiX2JrcHVsbC5Hcm91cDQiLCAidGFibGVfZGVzYyI6ICJcdTUyMDZcdTdlYzQ0IiwgInRhYmxlX25hbWUiOiAiR3JvdXA0In0sIHsiZmllbGRzIjogW3sibmFtZSI6ICJwcm9jc3RhdF9sb29rdXBfcGlkX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZGJncm91cF9pZCIsICJpbmZsdXhfaG9zdCIsICJwaWRfZmluZGVyIiwgImV4ZSIsICJob3N0IiwgInJlc3VsdCIsICJjbHVzdGVyIiwgImRicm9sZSIsICJkYmdyb3VwIiwgImluZmx1eF9wb3J0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jc3RhdF9sb29rdXBfcmVzdWx0X2NvZGUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJwaWRfZmluZGVyIiwgInJlc3VsdCIsICJob3N0IiwgImNsdXN0ZXIiLCAiZGJyb2xlIiwgImRiZ3JvdXAiLCAiZGJncm91cF9pZCIsICJpbmZsdXhfaG9zdCIsICJpbmZsdXhfcG9ydCIsICJleGUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb2NzdGF0X2xvb2t1cF9ydW5uaW5nIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaW5mbHV4X3BvcnQiLCAicmVzdWx0IiwgImRiZ3JvdXBfaWQiLCAiZGJyb2xlIiwgImV4ZSIsICJob3N0IiwgImluZmx1eF9ob3N0IiwgImNsdXN0ZXIiLCAicGlkX2ZpbmRlciIsICJkYmdyb3VwIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJkYmdyb3VwX2lkIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4X2hvc3QiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwaWRfZmluZGVyIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZXhlIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaG9zdCIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlc3VsdCIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImNsdXN0ZXIiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJkYnJvbGUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJkYmdyb3VwIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4X3BvcnQiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX1dLCAidGFibGVfaWQiOiAicHVzaGdhdGV3YXlfZGJtX2luZmx1eGRiX2JrcHVsbC5Hcm91cDUiLCAidGFibGVfZGVzYyI6ICJcdTUyMDZcdTdlYzQ1IiwgInRhYmxlX25hbWUiOiAiR3JvdXA1In0sIHsiZmllbGRzIjogW3sibmFtZSI6ICJpbmZsdXhkYl9odHRwZF9hdXRoRmFpbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImJpbmQiLCAiZGJncm91cF9pZCIsICJpbmZsdXhfaG9zdCIsICJpbmZsdXhfcG9ydCIsICJ1cmwiLCAiZGJyb2xlIiwgImNsdXN0ZXIiLCAiZGJncm91cCIsICJob3N0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl9odHRwZF9jbGllbnRFcnJvciIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImRiZ3JvdXAiLCAiaW5mbHV4X2hvc3QiLCAiaG9zdCIsICJiaW5kIiwgInVybCIsICJpbmZsdXhfcG9ydCIsICJjbHVzdGVyIiwgImRicm9sZSIsICJkYmdyb3VwX2lkIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl9odHRwZF9mbHV4UXVlcnlSZXEiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJ1cmwiLCAiY2x1c3RlciIsICJpbmZsdXhfaG9zdCIsICJkYmdyb3VwX2lkIiwgImluZmx1eF9wb3J0IiwgImJpbmQiLCAiZGJncm91cCIsICJkYnJvbGUiLCAiaG9zdCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfaHR0cGRfZmx1eFF1ZXJ5UmVxRHVyYXRpb25OcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImRiZ3JvdXBfaWQiLCAiaW5mbHV4X2hvc3QiLCAiY2x1c3RlciIsICJ1cmwiLCAiYmluZCIsICJkYmdyb3VwIiwgImluZmx1eF9wb3J0IiwgImRicm9sZSIsICJob3N0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl9odHRwZF9waW5nUmVxIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZGJncm91cF9pZCIsICJkYmdyb3VwIiwgImRicm9sZSIsICJob3N0IiwgImluZmx1eF9ob3N0IiwgImluZmx1eF9wb3J0IiwgImNsdXN0ZXIiLCAidXJsIiwgImJpbmQiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImluZmx1eGRiX2h0dHBkX3BvaW50c1dyaXR0ZW5Ecm9wcGVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZGJncm91cF9pZCIsICJob3N0IiwgImluZmx1eF9wb3J0IiwgImJpbmQiLCAidXJsIiwgImluZmx1eF9ob3N0IiwgImRiZ3JvdXAiLCAiY2x1c3RlciIsICJkYnJvbGUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImluZmx1eGRiX2h0dHBkX3BvaW50c1dyaXR0ZW5GYWlsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaW5mbHV4X2hvc3QiLCAiY2x1c3RlciIsICJkYmdyb3VwX2lkIiwgInVybCIsICJpbmZsdXhfcG9ydCIsICJkYmdyb3VwIiwgImJpbmQiLCAiZGJyb2xlIiwgImhvc3QiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImluZmx1eGRiX2h0dHBkX3BvaW50c1dyaXR0ZW5PSyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImJpbmQiLCAiY2x1c3RlciIsICJ1cmwiLCAiaG9zdCIsICJkYmdyb3VwIiwgImluZmx1eF9wb3J0IiwgImRicm9sZSIsICJkYmdyb3VwX2lkIiwgImluZmx1eF9ob3N0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl9odHRwZF9wcm9tUmVhZFJlcSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNsdXN0ZXIiLCAiaG9zdCIsICJiaW5kIiwgInVybCIsICJkYmdyb3VwX2lkIiwgImRiZ3JvdXAiLCAiaW5mbHV4X2hvc3QiLCAiaW5mbHV4X3BvcnQiLCAiZGJyb2xlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl9odHRwZF9wcm9tV3JpdGVSZXEiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJpbmZsdXhfcG9ydCIsICJiaW5kIiwgImRiZ3JvdXBfaWQiLCAiaG9zdCIsICJkYmdyb3VwIiwgInVybCIsICJpbmZsdXhfaG9zdCIsICJjbHVzdGVyIiwgImRicm9sZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfaHR0cGRfcXVlcnlSZXEiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJpbmZsdXhfcG9ydCIsICJ1cmwiLCAiYmluZCIsICJkYmdyb3VwIiwgImRiZ3JvdXBfaWQiLCAiaG9zdCIsICJjbHVzdGVyIiwgImRicm9sZSIsICJpbmZsdXhfaG9zdCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfaHR0cGRfcXVlcnlSZXFEdXJhdGlvbk5zIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaG9zdCIsICJjbHVzdGVyIiwgImJpbmQiLCAiZGJncm91cCIsICJkYnJvbGUiLCAidXJsIiwgImluZmx1eF9wb3J0IiwgImRiZ3JvdXBfaWQiLCAiaW5mbHV4X2hvc3QiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImluZmx1eGRiX2h0dHBkX3F1ZXJ5UmVzcEJ5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaG9zdCIsICJpbmZsdXhfaG9zdCIsICJkYmdyb3VwX2lkIiwgInVybCIsICJiaW5kIiwgImNsdXN0ZXIiLCAiZGJncm91cCIsICJkYnJvbGUiLCAiaW5mbHV4X3BvcnQiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImluZmx1eGRiX2h0dHBkX3JlY292ZXJlZFBhbmljcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImluZmx1eF9ob3N0IiwgImluZmx1eF9wb3J0IiwgImNsdXN0ZXIiLCAiZGJncm91cCIsICJkYmdyb3VwX2lkIiwgImRicm9sZSIsICJ1cmwiLCAiYmluZCIsICJob3N0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl9odHRwZF9yZXEiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJkYmdyb3VwIiwgImRiZ3JvdXBfaWQiLCAidXJsIiwgImluZmx1eF9ob3N0IiwgImNsdXN0ZXIiLCAiZGJyb2xlIiwgImluZmx1eF9wb3J0IiwgImhvc3QiLCAiYmluZCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfaHR0cGRfcmVxQWN0aXZlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiYmluZCIsICJkYmdyb3VwX2lkIiwgImRiZ3JvdXAiLCAiZGJyb2xlIiwgImluZmx1eF9wb3J0IiwgImNsdXN0ZXIiLCAidXJsIiwgImhvc3QiLCAiaW5mbHV4X2hvc3QiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImluZmx1eGRiX2h0dHBkX3JlcUR1cmF0aW9uTnMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJob3N0IiwgImluZmx1eF9wb3J0IiwgImJpbmQiLCAiZGJncm91cCIsICJkYnJvbGUiLCAiZGJncm91cF9pZCIsICJ1cmwiLCAiaW5mbHV4X2hvc3QiLCAiY2x1c3RlciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfaHR0cGRfc2VydmVyRXJyb3IiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJkYmdyb3VwIiwgImJpbmQiLCAiaW5mbHV4X2hvc3QiLCAiY2x1c3RlciIsICJkYnJvbGUiLCAiaW5mbHV4X3BvcnQiLCAiZGJncm91cF9pZCIsICJob3N0IiwgInVybCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4ZGJfaHR0cGRfc3RhdHVzUmVxIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiaG9zdCIsICJjbHVzdGVyIiwgImRiZ3JvdXAiLCAiaW5mbHV4X3BvcnQiLCAiaW5mbHV4X2hvc3QiLCAiYmluZCIsICJkYmdyb3VwX2lkIiwgInVybCIsICJkYnJvbGUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImluZmx1eGRiX2h0dHBkX3ZhbHVlc1dyaXR0ZW5PSyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbInVybCIsICJpbmZsdXhfcG9ydCIsICJkYmdyb3VwX2lkIiwgImhvc3QiLCAiZGJyb2xlIiwgImNsdXN0ZXIiLCAiZGJncm91cCIsICJpbmZsdXhfaG9zdCIsICJiaW5kIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl9odHRwZF93cml0ZVJlcSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImJpbmQiLCAiY2x1c3RlciIsICJkYmdyb3VwIiwgImluZmx1eF9ob3N0IiwgImRiZ3JvdXBfaWQiLCAiZGJyb2xlIiwgImluZmx1eF9wb3J0IiwgInVybCIsICJob3N0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl9odHRwZF93cml0ZVJlcUFjdGl2ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImRiZ3JvdXBfaWQiLCAiaW5mbHV4X3BvcnQiLCAiaW5mbHV4X2hvc3QiLCAiY2x1c3RlciIsICJiaW5kIiwgImRicm9sZSIsICJkYmdyb3VwIiwgImhvc3QiLCAidXJsIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhkYl9odHRwZF93cml0ZVJlcUJ5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJkYmdyb3VwIiwgImRiZ3JvdXBfaWQiLCAiYmluZCIsICJob3N0IiwgImluZmx1eF9ob3N0IiwgImRicm9sZSIsICJ1cmwiLCAiaW5mbHV4X3BvcnQiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImluZmx1eGRiX2h0dHBkX3dyaXRlUmVxRHVyYXRpb25OcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImRicm9sZSIsICJkYmdyb3VwX2lkIiwgImluZmx1eF9wb3J0IiwgImNsdXN0ZXIiLCAiaW5mbHV4X2hvc3QiLCAiYmluZCIsICJkYmdyb3VwIiwgInVybCIsICJob3N0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJiaW5kIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZGJncm91cF9pZCIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImluZmx1eF9ob3N0IiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbHV4X3BvcnQiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJ1cmwiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJkYnJvbGUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJjbHVzdGVyIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZGJncm91cCIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhvc3QiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX1dLCAidGFibGVfaWQiOiAicHVzaGdhdGV3YXlfZGJtX2luZmx1eGRiX2JrcHVsbC5Hcm91cDYiLCAidGFibGVfZGVzYyI6ICJcdTUyMDZcdTdlYzQ2IiwgInRhYmxlX25hbWUiOiAiR3JvdXA2In0sIHsiZmllbGRzIjogW3sibmFtZSI6ICJjcHVfdXNhZ2VfZ3Vlc3QiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJkYnJvbGUiLCAiaG9zdCIsICJkYmdyb3VwIiwgImRiZ3JvdXBfaWQiLCAiaW5mbHV4X2hvc3QiLCAiaW5mbHV4X3BvcnQiLCAiY2x1c3RlciIsICJjcHUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImNwdV91c2FnZV9ndWVzdF9uaWNlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJkYnJvbGUiLCAiaW5mbHV4X3BvcnQiLCAiY3B1IiwgImRiZ3JvdXAiLCAiZGJncm91cF9pZCIsICJob3N0IiwgImluZmx1eF9ob3N0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJjcHVfdXNhZ2VfaWRsZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImluZmx1eF9wb3J0IiwgImNsdXN0ZXIiLCAiY3B1IiwgImRiZ3JvdXAiLCAiaG9zdCIsICJkYmdyb3VwX2lkIiwgImRicm9sZSIsICJpbmZsdXhfaG9zdCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiY3B1X3VzYWdlX2lvd2FpdCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImluZmx1eF9ob3N0IiwgImRiZ3JvdXAiLCAiZGJncm91cF9pZCIsICJkYnJvbGUiLCAiaW5mbHV4X3BvcnQiLCAiY2x1c3RlciIsICJjcHUiLCAiaG9zdCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiY3B1X3VzYWdlX2lycSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImRiZ3JvdXBfaWQiLCAiaW5mbHV4X2hvc3QiLCAiaW5mbHV4X3BvcnQiLCAiY2x1c3RlciIsICJjcHUiLCAiZGJncm91cCIsICJkYnJvbGUiLCAiaG9zdCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiY3B1X3VzYWdlX25pY2UiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjcHUiLCAiZGJncm91cF9pZCIsICJkYnJvbGUiLCAiaW5mbHV4X2hvc3QiLCAiaW5mbHV4X3BvcnQiLCAiY2x1c3RlciIsICJkYmdyb3VwIiwgImhvc3QiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImNwdV91c2FnZV9zb2Z0aXJxIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZGJyb2xlIiwgImhvc3QiLCAiaW5mbHV4X2hvc3QiLCAiZGJncm91cCIsICJkYmdyb3VwX2lkIiwgImluZmx1eF9wb3J0IiwgImNsdXN0ZXIiLCAiY3B1Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJjcHVfdXNhZ2Vfc3RlYWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjcHUiLCAiZGJncm91cCIsICJpbmZsdXhfaG9zdCIsICJpbmZsdXhfcG9ydCIsICJkYmdyb3VwX2lkIiwgImRicm9sZSIsICJob3N0IiwgImNsdXN0ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImNwdV91c2FnZV9zeXN0ZW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJpbmZsdXhfcG9ydCIsICJjbHVzdGVyIiwgImNwdSIsICJkYmdyb3VwIiwgImRiZ3JvdXBfaWQiLCAiZGJyb2xlIiwgImhvc3QiLCAiaW5mbHV4X2hvc3QiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImNwdV91c2FnZV91c2VyIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZGJncm91cF9pZCIsICJob3N0IiwgImluZmx1eF9ob3N0IiwgImluZmx1eF9wb3J0IiwgImNwdSIsICJkYnJvbGUiLCAiY2x1c3RlciIsICJkYmdyb3VwIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJkYnJvbGUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJob3N0IiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZGJncm91cCIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImRiZ3JvdXBfaWQiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsdXhfaG9zdCIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImluZmx1eF9wb3J0IiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiY2x1c3RlciIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImNwdSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfV0sICJ0YWJsZV9pZCI6ICJwdXNoZ2F0ZXdheV9kYm1faW5mbHV4ZGJfYmtwdWxsLkdyb3VwNyIsICJ0YWJsZV9kZXNjIjogIlx1NTIwNlx1N2VjNDciLCAidGFibGVfbmFtZSI6ICJHcm91cDcifV0sICJwbHVnaW5fdHlwZSI6ICJQdXNoZ2F0ZXdheSIsICJvc190eXBlX2xpc3QiOiBbImxpbnV4IiwgIndpbmRvd3MiLCAibGludXhfYWFyY2g2NCJdfSwgImNvbGxlY3RfdHlwZSI6ICJQdXNoZ2F0ZXdheSIsICJ0YXJnZXRfbm9kZXMiOiBbXSwgInRhcmdldF9ub2RlX3R5cGUiOiAiVE9QTyIsICJ0YXJnZXRfb2JqZWN0X3R5cGUiOiAiU0VSVklDRSJ9fQ== \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/collect/0.kafka.dbm_kafka_bkpull.json b/dbm-ui/backend/db_monitor/tpls/collect/0.kafka.dbm_kafka_bkpull.json new file mode 100644 index 0000000000..815f39fb6b --- /dev/null +++ b/dbm-ui/backend/db_monitor/tpls/collect/0.kafka.dbm_kafka_bkpull.json @@ -0,0 +1,46 @@ +{ + "bk_biz_id": 0, + "plugin_id": "dbm_kafka_bkpull", + "db_type": "kafka", + "details": { + "name": "dbm_kafka_bkpull", + "label": "component", + "params": { + "plugin": { + "服务实例维度注入": { + "app": "app", + "instance": "instance", + "cluster_name": "cluster_name", + "cluster_type": "cluster_type", + "instance_host": "instance_host", + "instance_port": "instance_port", + "instance_role": "instance_role", + "cluster_domain": "cluster_domain" + } + }, + "collector": { + "period": 60, + "timeout": 60, + "password": "", + "username": "", + "metrics_url": "http://localhost:7071" + }, + "target_node_type": "TOPO", + "target_object_type": "SERVICE" + }, + "plugin_info": { + "plugin_id": "dbm_kafka_bkpull", + "metric_json": [], + "plugin_type": "Pushgateway", + "os_type_list": [ + "linux", + "windows", + "linux_aarch64" + ] + }, + "collect_type": "Pushgateway", + "target_nodes": [], + "target_node_type": "TOPO", + "target_object_type": "SERVICE" + } +} diff --git a/dbm-ui/backend/db_monitor/tpls/collect/0.kafka.dbm_kafka_bkpull.tpl64 b/dbm-ui/backend/db_monitor/tpls/collect/0.kafka.dbm_kafka_bkpull.tpl64 deleted file mode 100644 index a898d92bc8..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/collect/0.kafka.dbm_kafka_bkpull.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAicGx1Z2luX2lkIjogImRibV9rYWZrYV9ia3B1bGwiLCAiZGJfdHlwZSI6ICJrYWZrYSIsICJkZXRhaWxzIjogeyJuYW1lIjogImRibV9rYWZrYV9ia3B1bGwiLCAibGFiZWwiOiAiY29tcG9uZW50IiwgInBhcmFtcyI6IHsicGx1Z2luIjogeyJcdTY3MGRcdTUyYTFcdTViOWVcdTRmOGJcdTdlZjRcdTVlYTZcdTZjZThcdTUxNjUiOiB7ImFwcCI6ICJhcHAiLCAiaW5zdGFuY2UiOiAiaW5zdGFuY2UiLCAiY2x1c3Rlcl9uYW1lIjogImNsdXN0ZXJfbmFtZSIsICJjbHVzdGVyX3R5cGUiOiAiY2x1c3Rlcl90eXBlIiwgImluc3RhbmNlX2hvc3QiOiAiaW5zdGFuY2VfaG9zdCIsICJpbnN0YW5jZV9wb3J0IjogImluc3RhbmNlX3BvcnQiLCAiaW5zdGFuY2Vfcm9sZSI6ICJpbnN0YW5jZV9yb2xlIiwgImNsdXN0ZXJfZG9tYWluIjogImNsdXN0ZXJfZG9tYWluIn19LCAiY29sbGVjdG9yIjogeyJwZXJpb2QiOiA2MCwgInRpbWVvdXQiOiA2MCwgInBhc3N3b3JkIjogIiIsICJ1c2VybmFtZSI6ICIiLCAibWV0cmljc191cmwiOiAiaHR0cDovL2xvY2FsaG9zdDo3MDcxIn0sICJ0YXJnZXRfbm9kZV90eXBlIjogIlRPUE8iLCAidGFyZ2V0X29iamVjdF90eXBlIjogIlNFUlZJQ0UifSwgInBsdWdpbl9pbmZvIjogeyJwbHVnaW5faWQiOiAiZGJtX2thZmthX2JrcHVsbCIsICJtZXRyaWNfanNvbiI6IFtdLCAicGx1Z2luX3R5cGUiOiAiUHVzaGdhdGV3YXkiLCAib3NfdHlwZV9saXN0IjogWyJsaW51eCIsICJ3aW5kb3dzIiwgImxpbnV4X2FhcmNoNjQiXX0sICJjb2xsZWN0X3R5cGUiOiAiUHVzaGdhdGV3YXkiLCAidGFyZ2V0X25vZGVzIjogW10sICJ0YXJnZXRfbm9kZV90eXBlIjogIlRPUE8iLCAidGFyZ2V0X29iamVjdF90eXBlIjogIlNFUlZJQ0UifX0= \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/collect/0.kafka.dbm_kafka_exporter.json b/dbm-ui/backend/db_monitor/tpls/collect/0.kafka.dbm_kafka_exporter.json new file mode 100644 index 0000000000..9364b360fa --- /dev/null +++ b/dbm-ui/backend/db_monitor/tpls/collect/0.kafka.dbm_kafka_exporter.json @@ -0,0 +1,47 @@ +{ + "bk_biz_id": 0, + "plugin_id": "dbm_kafka_exporter", + "db_type": "kafka", + "details": { + "name": "dbm_kafka_exporter", + "label": "component", + "params": { + "plugin": { + "--kafka.server": "{{ target.host.bk_host_innerip }}:{{ target.process[\"java\"].bind_info[0].port }}", + "--sasl.enabled": "", + "--sasl.mechanism": "scram-sha512", + "--web.listen-address": "${host}:${port}", + "服务实例维度注入": { + "app": "app", + "instance": "instance", + "cluster_name": "cluster_name", + "cluster_type": "cluster_type", + "instance_host": "instance_host", + "instance_port": "instance_port", + "instance_role": "instance_role", + "cluster_domain": "cluster_domain" + } + }, + "collector": { + "host": "127.0.0.1", + "port": "9528", + "period": 60, + "timeout": 60 + }, + "target_node_type": "TOPO", + "target_object_type": "SERVICE" + }, + "plugin_info": { + "plugin_id": "dbm_kafka_exporter", + "metric_json": [], + "plugin_type": "Exporter", + "os_type_list": [ + "linux" + ] + }, + "collect_type": "Exporter", + "target_nodes": [], + "target_node_type": "TOPO", + "target_object_type": "SERVICE" + } +} diff --git a/dbm-ui/backend/db_monitor/tpls/collect/0.kafka.dbm_kafka_exporter.tpl64 b/dbm-ui/backend/db_monitor/tpls/collect/0.kafka.dbm_kafka_exporter.tpl64 deleted file mode 100644 index 33a368beb5..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/collect/0.kafka.dbm_kafka_exporter.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAicGx1Z2luX2lkIjogImRibV9rYWZrYV9leHBvcnRlciIsICJkYl90eXBlIjogImthZmthIiwgImRldGFpbHMiOiB7Im5hbWUiOiAiZGJtX2thZmthX2V4cG9ydGVyIiwgImxhYmVsIjogImNvbXBvbmVudCIsICJwYXJhbXMiOiB7InBsdWdpbiI6IHsiLS1rYWZrYS5zZXJ2ZXIiOiAie3sgdGFyZ2V0Lmhvc3QuYmtfaG9zdF9pbm5lcmlwIH19Ont7IHRhcmdldC5wcm9jZXNzW1wiamF2YVwiXS5iaW5kX2luZm9bMF0ucG9ydCB9fSIsICItLXNhc2wuZW5hYmxlZCI6ICIiLCAiLS1zYXNsLm1lY2hhbmlzbSI6ICJzY3JhbS1zaGE1MTIiLCAiLS13ZWIubGlzdGVuLWFkZHJlc3MiOiAiJHtob3N0fToke3BvcnR9IiwgIlx1NjcwZFx1NTJhMVx1NWI5ZVx1NGY4Ylx1N2VmNFx1NWVhNlx1NmNlOFx1NTE2NSI6IHsiYXBwIjogImFwcCIsICJpbnN0YW5jZSI6ICJpbnN0YW5jZSIsICJjbHVzdGVyX25hbWUiOiAiY2x1c3Rlcl9uYW1lIiwgImNsdXN0ZXJfdHlwZSI6ICJjbHVzdGVyX3R5cGUiLCAiaW5zdGFuY2VfaG9zdCI6ICJpbnN0YW5jZV9ob3N0IiwgImluc3RhbmNlX3BvcnQiOiAiaW5zdGFuY2VfcG9ydCIsICJpbnN0YW5jZV9yb2xlIjogImluc3RhbmNlX3JvbGUiLCAiY2x1c3Rlcl9kb21haW4iOiAiY2x1c3Rlcl9kb21haW4ifX0sICJjb2xsZWN0b3IiOiB7Imhvc3QiOiAiMTI3LjAuMC4xIiwgInBvcnQiOiAiOTUyOCIsICJwZXJpb2QiOiA2MCwgInRpbWVvdXQiOiA2MH0sICJ0YXJnZXRfbm9kZV90eXBlIjogIlRPUE8iLCAidGFyZ2V0X29iamVjdF90eXBlIjogIlNFUlZJQ0UifSwgInBsdWdpbl9pbmZvIjogeyJwbHVnaW5faWQiOiAiZGJtX2thZmthX2V4cG9ydGVyIiwgIm1ldHJpY19qc29uIjogW10sICJwbHVnaW5fdHlwZSI6ICJFeHBvcnRlciIsICJvc190eXBlX2xpc3QiOiBbImxpbnV4Il19LCAiY29sbGVjdF90eXBlIjogIkV4cG9ydGVyIiwgInRhcmdldF9ub2RlcyI6IFtdLCAidGFyZ2V0X25vZGVfdHlwZSI6ICJUT1BPIiwgInRhcmdldF9vYmplY3RfdHlwZSI6ICJTRVJWSUNFIn19 \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/collect/0.mysql.dbm_mysqld_exporter.json b/dbm-ui/backend/db_monitor/tpls/collect/0.mysql.dbm_mysqld_exporter.json new file mode 100644 index 0000000000..28fa4db5a4 --- /dev/null +++ b/dbm-ui/backend/db_monitor/tpls/collect/0.mysql.dbm_mysqld_exporter.json @@ -0,0 +1,8469 @@ +{ + "bk_biz_id": 0, + "plugin_id": "dbm_mysqld_exporter", + "db_type": "mysql", + "details": { + "name": "dbm_mysqld_exporter", + "label": "component", + "params": { + "plugin": { + "--config.my-cnf": "/etc/{{ target.service.labels[\"exporter_conf_path\"] }}", + "--web.listen-address": "${host}:${port}", + "--collect.datadir_size": "", + "--collect.slave_status": "", + "--collect.global_status": "", + "服务实例维度注入": { + "app": "app", + "instance": "instance", + "bk_app_code": "app_id", + "cluster_name": "cluster_name", + "cluster_type": "cluster_type", + "instance_host": "instance_host", + "instance_role": "instance_role", + "cluster_domain": "cluster_domain" + }, + "--collect.global_variables": "", + "--collect.engine_innodb_status": "", + "--collect.auto_increment.columns": "", + "--collect.info_schema.innodb_trx": "", + "--collect.info_schema.innodb_metrics": "", + "--collect.info_schema.query_response_time": "", + "--collect.info_schema.processlist_ext --collect.info_schema.processlist_ext.by_user": "", + "--collect.info_schema.tables_ext --collect.info_schema.tables_ext.interval=1h --collect.info_schema.tables_ext.databases=*": "" + }, + "collector": { + "host": "127.0.0.1", + "port": "7000", + "period": 60 + } + }, + "plugin_info": { + "plugin_id": "dbm_mysqld_exporter", + "metric_json": [ + { + "fields": [ + { + "name": "mysql_up", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_mysqld_exporter.mysqlup", + "table_desc": "mysql_up", + "table_name": "mysqlup" + }, + { + "fields": [ + { + "name": "mysql_global_status_aborted_clients", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_aborted_connects", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_binlog_cache_disk_use", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_binlog_cache_use", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_binlog_snapshot_position", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_binlog_stmt_cache_disk_use", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_binlog_stmt_cache_use", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_bytes_received", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_bytes_sent", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_compression", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_connections", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_created_tmp_disk_tables", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_created_tmp_files", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_created_tmp_tables", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_delayed_errors", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_delayed_insert_threads", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_delayed_writes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_flush_commands", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_available_undo_logs", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_background_log_sync", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_buffer_pool_bytes_data", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_buffer_pool_bytes_dirty", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_buffer_pool_read_ahead", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_buffer_pool_read_ahead_evicted", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_buffer_pool_read_ahead_rnd", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_buffer_pool_read_requests", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_buffer_pool_reads", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_buffer_pool_wait_free", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_buffer_pool_write_requests", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_buffered_aio_submitted", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_checkpoint_age", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_checkpoint_max_age", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_data_fsyncs", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_data_pending_fsyncs", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_data_pending_reads", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_data_pending_writes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_data_read", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_data_reads", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_data_writes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_data_written", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_dblwr_pages_written", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_dblwr_writes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_ibuf_free_list", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_ibuf_segment_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_log_waits", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_log_write_requests", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_log_writes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_lsn_current", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_lsn_flushed", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_lsn_last_checkpoint", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_master_thread_active_loops", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_master_thread_idle_loops", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_max_trx_id", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_mem_adaptive_hash", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_mem_dictionary", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_num_open_files", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_oldest_view_low_limit_trx_id", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_os_log_fsyncs", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_os_log_pending_fsyncs", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_os_log_pending_writes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_os_log_written", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_page_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_pages_created", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_pages_read", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_pages_written", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_purge_trx_id", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_purge_undo_no", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_row_lock_current_waits", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_row_lock_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_row_lock_time_avg", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_row_lock_time_max", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_row_lock_waits", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_scan_data_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_scan_deleted_recs_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_scan_pages_contiguous", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_scan_pages_disjointed", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_scan_pages_total_seek_distance", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_secondary_index_triggered_cluster_reads", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_secondary_index_triggered_cluster_reads_avoided", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_truncated_status_writes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_key_blocks_not_flushed", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_key_blocks_unused", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_key_blocks_used", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_key_read_requests", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_key_reads", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_key_write_requests", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_key_writes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_last_query_cost", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_last_query_partial_plans", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_locked_connects", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_max_execution_time_exceeded", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_max_execution_time_set", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_max_execution_time_set_failed", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_max_used_connections", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_max_used_connections_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_not_flushed_delayed_rows", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_ongoing_anonymous_transaction_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_open_files", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_open_streams", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_open_table_definitions", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_open_tables", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_opened_files", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_opened_table_definitions", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_opened_tables", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_prepared_stmt_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_qcache_free_blocks", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_qcache_free_memory", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_qcache_hits", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_qcache_inserts", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_qcache_lowmem_prunes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_qcache_not_cached", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_qcache_queries_in_cache", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_qcache_total_blocks", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_queries", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_questions", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_select_full_join", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_select_full_range_join", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_select_range", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_select_range_check", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_select_scan", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_slave_heartbeat_period", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_slave_open_temp_tables", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_slave_received_heartbeats", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_slave_retried_transactions", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_slave_running", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_slow_launch_threads", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_slow_queries", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_sort_merge_passes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_sort_range", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_sort_rows", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_sort_scan", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_ssl_accept_renegotiates", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_ssl_accepts", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_ssl_callback_cache_hits", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_ssl_client_connects", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_ssl_connect_renegotiates", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_ssl_ctx_verify_depth", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_ssl_ctx_verify_mode", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_ssl_default_timeout", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_ssl_finished_accepts", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_ssl_finished_connects", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_ssl_session_cache_hits", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_ssl_session_cache_misses", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_ssl_session_cache_overflows", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_ssl_session_cache_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_ssl_session_cache_timeouts", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_ssl_sessions_reused", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_ssl_used_session_cache_entries", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_ssl_verify_depth", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_ssl_verify_mode", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_table_locks_immediate", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_table_locks_waited", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_table_open_cache_hits", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_table_open_cache_misses", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_table_open_cache_overflows", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_tc_log_max_pages_used", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_tc_log_page_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_tc_log_page_waits", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_threadpool_idle_threads", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_threadpool_threads", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_threads_cached", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_threads_connected", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_threads_created", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_threads_running", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_uptime", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_uptime_since_flush_status", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_buffer_pool_pages", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "state" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_commands_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "command" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_buffer_pool_page_changes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "operation" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_innodb_row_ops_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "operation" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_handlers_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "handler" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_status_connection_errors_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "error" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "state", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "command", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "operation", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "handler", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "error", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_mysqld_exporter.gstatus", + "table_desc": "mysql_global_status", + "table_name": "gstatus" + }, + { + "fields": [ + { + "name": "mysql_global_variables_auto_generate_certs", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_auto_increment_increment", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_auto_increment_offset", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_autocommit", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_automatic_sp_privileges", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_avoid_temporal_upgrade", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_back_log", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_big_tables", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_binlog_cache_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_binlog_direct_non_transactional_updates", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_binlog_group_commit_sync_delay", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_binlog_group_commit_sync_no_delay_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_binlog_gtid_simple_recovery", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_binlog_max_flush_queue_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_binlog_order_commits", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_binlog_rows_query_log_events", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_binlog_stmt_cache_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_blob_compressed", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_bulk_insert_buffer_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_check_proxy_users", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_connect_timeout", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_core_file", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_datetime_precision_use_v1", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_default_password_lifetime", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_default_week_format", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_delay_key_write", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_delayed_insert_limit", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_delayed_insert_timeout", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_delayed_queue_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_disconnect_on_expired_password", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_div_precision_increment", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_end_markers_in_json", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_enforce_gtid_consistency", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_eq_range_index_dive_limit", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_event_scheduler", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_expand_fast_index_creation", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_expire_logs_days", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_explicit_defaults_for_timestamp", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_extra_max_connections", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_extra_port", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_flush", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_flush_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_foreign_key_checks", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_ft_max_word_len", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_ft_min_word_len", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_ft_query_expansion_limit", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_general_log", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_group_concat_max_len", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_gtid_executed_compression_period", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_gtid_mode", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_have_backup_locks", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_have_backup_safe_binlog_info", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_have_compress", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_have_crypt", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_have_dynamic_loading", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_have_geometry", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_have_openssl", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_have_profiling", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_have_query_cache", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_have_rtree_keys", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_have_snapshot_cloning", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_have_ssl", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_have_statement_timeout", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_have_symlink", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_host_cache_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_ignore_builtin_innodb", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_adaptive_flushing", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_adaptive_flushing_lwm", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_adaptive_hash_index", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_adaptive_hash_index_parts", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_adaptive_max_sleep_delay", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_api_bk_commit_interval", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_api_disable_rowlock", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_api_enable_binlog", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_api_enable_mdl", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_api_trx_level", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_autoextend_increment", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_autoinc_lock_mode", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_buffer_pool_chunk_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_buffer_pool_dump_at_shutdown", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_buffer_pool_dump_now", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_buffer_pool_dump_pct", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_buffer_pool_in_core_file", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_buffer_pool_instances", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_buffer_pool_load_abort", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_buffer_pool_load_at_startup", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_buffer_pool_load_now", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_buffer_pool_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_change_buffer_max_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_checksums", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_cmp_per_index_enabled", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_commit_concurrency", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_compressed_columns_threshold", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_compressed_columns_zip_level", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_compression_failure_threshold_pct", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_compression_level", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_compression_pad_pct_max", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_concurrency_tickets", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_deadlock_detect", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_disable_sort_file_cache", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_doublewrite", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_fast_shutdown", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_file_format_check", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_file_per_table", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_fill_factor", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_flush_log_at_timeout", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_flush_log_at_trx_commit", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_flush_neighbors", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_flush_sync", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_flushing_avg_loops", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_force_load_corrupted", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_force_recovery", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_ft_cache_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_ft_enable_diag_print", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_ft_enable_stopword", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_ft_ignore_stopwords", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_ft_max_token_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_ft_min_token_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_ft_num_word_optimize", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_ft_result_cache_limit", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_ft_sort_pll_degree", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_ft_total_cache_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_io_capacity", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_io_capacity_max", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_kill_idle_transaction", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_large_prefix", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_lock_wait_timeout", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_locks_unsafe_for_binlog", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_log_buffer_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_log_checksums", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_log_compressed_pages", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_log_file_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_log_files_in_group", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_log_write_ahead_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_lru_scan_depth", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_max_bitmap_file_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_max_changed_pages", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_max_dirty_pages_pct", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_max_dirty_pages_pct_lwm", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_max_purge_lag", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_max_purge_lag_delay", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_max_undo_log_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_min_blob_compress_length", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_old_blocks_pct", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_old_blocks_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_online_alter_log_max_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_open_files", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_optimize_fulltext_only", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_page_cleaners", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_page_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_print_all_deadlocks", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_print_lock_wait_timeout_info", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_purge_batch_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_purge_rseg_truncate_frequency", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_purge_threads", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_random_read_ahead", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_read_ahead_threshold", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_read_io_threads", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_read_only", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_replication_delay", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_rollback_on_timeout", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_rollback_segments", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_show_locks_held", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_show_verbose_locks", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_sort_buffer_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_spin_wait_delay", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_stats_auto_recalc", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_stats_include_delete_marked", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_stats_on_metadata", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_stats_persistent", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_stats_persistent_sample_pages", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_stats_sample_pages", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_stats_transient_sample_pages", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_status_output", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_status_output_locks", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_strict_mode", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_support_xa", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_sync_array_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_sync_spin_loops", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_table_locks", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_thread_concurrency", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_thread_sleep_delay", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_track_changed_pages", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_undo_log_truncate", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_undo_logs", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_undo_tablespaces", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_use_global_flush_log_at_trx_commit", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_use_native_aio", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_innodb_write_io_threads", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_interactive_timeout", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_join_buffer_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_keep_files_on_create", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_key_buffer_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_key_cache_age_threshold", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_key_cache_block_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_key_cache_division_limit", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_kill_idle_transaction", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_large_files_support", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_large_page_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_large_pages", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_local_infile", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_lock_wait_timeout", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_locked_in_memory", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_log_bin", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_log_bin_compress", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_log_bin_compress_min_len", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_log_bin_trust_function_creators", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_log_bin_use_v1_row_events", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_log_builtin_as_identified_by_password", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_log_error_verbosity", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_log_queries_not_using_indexes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_log_slave_updates", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_log_slow_admin_statements", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_log_slow_rate_limit", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_log_slow_slave_statements", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_log_slow_sp_statements", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_log_statements_unsafe_for_binlog", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_log_syslog", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_log_syslog_include_pid", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_log_throttle_queries_not_using_indexes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_log_warnings", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_long_query_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_low_priority_updates", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_lower_case_file_system", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_lower_case_table_names", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_master_verify_checksum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_max_allowed_packet", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_max_binlog_cache_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_max_binlog_files", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_max_binlog_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_max_binlog_stmt_cache_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_max_connect_errors", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_max_connections", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_max_delayed_threads", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_max_digest_length", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_max_error_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_max_execution_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_max_heap_table_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_max_insert_delayed_threads", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_max_join_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_max_length_for_sort_data", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_max_points_in_geometry", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_max_prepared_stmt_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_max_relay_log_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_max_seeks_for_key", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_max_slowlog_files", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_max_slowlog_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_max_sort_length", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_max_sp_recursion_depth", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_max_tmp_tables", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_max_user_connections", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_max_write_lock_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_max_xa_commit_logs", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_metadata_locks_cache_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_metadata_locks_hash_instances", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_min_examined_row_limit", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_multi_range_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_myisam_data_pointer_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_myisam_max_sort_file_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_myisam_mmap_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_myisam_recover_options", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_myisam_repair_threads", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_myisam_sort_buffer_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_myisam_use_mmap", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_mysql_native_password_proxy_users", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_net_buffer_length", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_net_read_timeout", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_net_retry_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_net_write_timeout", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_new", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_ngram_token_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_offline_mode", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_old", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_old_alter_table", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_old_passwords", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_open_files_limit", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_optimizer_prune_level", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_optimizer_search_depth", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_optimizer_trace_limit", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_optimizer_trace_max_mem_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_optimizer_trace_offset", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_parser_max_mem_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_accounts_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_digests_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_events_stages_history_long_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_events_stages_history_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_events_statements_history_long_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_events_statements_history_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_events_transactions_history_long_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_events_transactions_history_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_events_waits_history_long_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_events_waits_history_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_hosts_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_max_cond_classes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_max_cond_instances", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_max_digest_length", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_max_file_classes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_max_file_handles", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_max_file_instances", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_max_index_stat", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_max_memory_classes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_max_metadata_locks", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_max_mutex_classes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_max_mutex_instances", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_max_prepared_statements_instances", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_max_program_instances", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_max_rwlock_classes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_max_rwlock_instances", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_max_socket_classes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_max_socket_instances", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_max_sql_text_length", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_max_stage_classes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_max_statement_classes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_max_statement_stack", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_max_table_handles", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_max_table_instances", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_max_table_lock_stat", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_max_thread_classes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_max_thread_instances", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_session_connect_attrs_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_setup_actors_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_setup_objects_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_performance_schema_users_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_port", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_preload_buffer_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_profiling", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_profiling_history_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_protocol_version", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_query_alloc_block_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_query_cache_limit", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_query_cache_min_res_unit", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_query_cache_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_query_cache_strip_comments", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_query_cache_type", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_query_cache_wlock_invalidate", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_query_prealloc_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_query_response_time_flush", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_query_response_time_range_base", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_query_response_time_stats", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_range_alloc_block_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_range_optimizer_max_mem_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_read_binlog_speed_limit", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_read_buffer_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_read_only", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_read_rnd_buffer_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_relay_log_purge", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_relay_log_recovery", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_relay_log_space_limit", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_relay_log_uncompress", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_report_port", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_require_secure_transport", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_rpl_stop_slave_timeout", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_secure_auth", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_server_id", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_server_id_bits", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_session_track_gtids", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_session_track_schema", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_session_track_state_change", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_session_track_transaction_info", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_sha256_password_auto_generate_rsa_keys", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_sha256_password_proxy_users", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_show_compatibility_56", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_show_old_temporals", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_skip_external_locking", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_skip_name_resolve", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_skip_networking", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_skip_show_database", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_slave_allow_batching", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_slave_checkpoint_group", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_slave_checkpoint_period", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_slave_compressed_protocol", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_slave_max_allowed_packet", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_slave_net_timeout", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_slave_parallel_workers", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_slave_pending_jobs_size_max", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_slave_preserve_commit_order", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_slave_skip_errors", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_slave_sql_verify_checksum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_slave_transaction_retries", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_slow_launch_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_slow_query_log", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_slow_query_log_always_write_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_sort_buffer_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_sort_when_partition_prefix_order", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_sql_auto_is_null", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_sql_big_selects", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_sql_buffer_result", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_sql_log_bin", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_sql_log_off", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_sql_notes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_sql_quote_show_create", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_sql_safe_updates", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_sql_select_limit", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_sql_slave_skip_counter", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_sql_warnings", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_stored_program_cache", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_super_read_only", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_sync_binlog", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_sync_frm", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_sync_master_info", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_sync_relay_log", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_sync_relay_log_info", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_table_definition_cache", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_table_open_cache", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_table_open_cache_instances", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_thread_cache_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_thread_pool_high_prio_tickets", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_thread_pool_idle_timeout", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_thread_pool_max_threads", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_thread_pool_oversubscribe", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_thread_pool_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_thread_pool_stall_limit", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_thread_stack", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_thread_statistics", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_tmp_table_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_transaction_alloc_block_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_transaction_prealloc_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_transaction_read_only", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_transaction_write_set_extraction", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_tx_read_only", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_unique_checks", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_updatable_views_with_limit", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_userstat", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_global_variables_wait_timeout", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_mysqld_exporter.gvars", + "table_desc": "mysql_global_variables", + "table_name": "gvars" + }, + { + "fields": [ + { + "name": "mysql_datadir_size_kb", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_mysqld_exporter.datasize", + "table_desc": "mysql_datadir_size", + "table_name": "datasize" + }, + { + "fields": [ + { + "name": "mysql_info_schema_innodb_metrics_adaptive_hash_index_adaptive_hash_searches_btree_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_adaptive_hash_index_adaptive_hash_searches_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_buffer_buffer_data_reads_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_buffer_buffer_data_written_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_buffer_buffer_pages_created_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_buffer_buffer_pages_read_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_buffer_buffer_pages_written_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_buffer_buffer_pool_bytes_data", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_buffer_buffer_pool_bytes_dirty", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_buffer_buffer_pool_read_ahead_evicted_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_buffer_buffer_pool_read_ahead_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_buffer_buffer_pool_read_requests_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_buffer_buffer_pool_reads_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_buffer_buffer_pool_wait_free_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_buffer_buffer_pool_write_requests_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_buffer_pool_dirty_pages", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_change_buffer_ibuf_merges_delete_mark_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_change_buffer_ibuf_merges_delete_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_change_buffer_ibuf_merges_discard_delete_mark_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_change_buffer_ibuf_merges_discard_delete_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_change_buffer_ibuf_merges_discard_insert_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_change_buffer_ibuf_merges_insert_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_change_buffer_ibuf_merges_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_change_buffer_ibuf_size_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_dml_dml_deletes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_dml_dml_inserts_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_dml_dml_updates_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_file_system_file_num_open_files", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_lock_lock_deadlocks_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_lock_lock_row_lock_current_waits_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_lock_lock_row_lock_time_avg", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_lock_lock_row_lock_time_max", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_lock_lock_row_lock_time_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_lock_lock_row_lock_waits_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_lock_lock_timeouts_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_os_os_data_fsyncs_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_os_os_data_reads_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_os_os_data_writes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_os_os_log_bytes_written_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_os_os_log_fsyncs_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_os_os_log_pending_fsyncs_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_os_os_log_pending_writes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_recovery_log_padded_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_recovery_log_waits_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_recovery_log_write_requests_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_recovery_log_writes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_server_buffer_pool_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_server_innodb_activity_count_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_server_innodb_dblwr_pages_written_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_server_innodb_dblwr_writes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_server_innodb_page_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_server_innodb_rwlock_s_os_waits_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_server_innodb_rwlock_s_spin_rounds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_server_innodb_rwlock_s_spin_waits_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_server_innodb_rwlock_sx_os_waits_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_server_innodb_rwlock_sx_spin_rounds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_server_innodb_rwlock_sx_spin_waits_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_server_innodb_rwlock_x_os_waits_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_server_innodb_rwlock_x_spin_rounds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_server_innodb_rwlock_x_spin_waits_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_innodb_metrics_transaction_trx_rseg_history_len", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_engine_innodb_queries_in_queue", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_engine_innodb_queries_inside_innodb", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_engine_innodb_read_views_open_inside_innodb", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_mysqld_exporter.innodbmtr", + "table_desc": "mysql_info_schema_innodb_metrics", + "table_name": "innodbmtr" + }, + { + "fields": [ + { + "name": "mysql_engine_innodb_lock_waits_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_engine_innodb_locks_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_engine_innodb_trx_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_engine_innodb_trx_long_query_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_engine_innodb_trx_long_time_max", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_engine_innodb_trx_idle_time_max", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "trx_state", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "command", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "state", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "user", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_mysqld_exporter.innodbtrx", + "table_desc": "mysql_engine_innodb_metrics_locks", + "table_name": "innodbtrx" + }, + { + "fields": [ + { + "name": "mysql_info_schema_query_response_time_seconds_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_query_response_time_seconds_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_read_query_response_time_seconds_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_read_query_response_time_seconds_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_write_query_response_time_seconds_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_write_query_response_time_seconds_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_query_response_time_seconds_bucket", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "le" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_read_query_response_time_seconds_bucket", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "le" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_write_query_response_time_seconds_bucket", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "le" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "le", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_mysqld_exporter.resptime", + "table_desc": "mysql_info_schema_query_response_time", + "table_name": "resptime" + }, + { + "fields": [ + { + "name": "mysql_info_schema_auto_increment_column", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "column", + "schema", + "table" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_auto_increment_column_max", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "column", + "schema", + "table" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "column", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "schema", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "table", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_mysqld_exporter.tableincr", + "table_desc": "mysql_info_schema_auto_increment", + "table_name": "tableincr" + }, + { + "fields": [ + { + "name": "mysql_info_schema_table_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "component", + "schema", + "table" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_total_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "schema" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_table_rows", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "table", + "schema" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "component", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "schema", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "table", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_mysqld_exporter.tablesize", + "table_desc": "mysql_info_schema_table_size schema", + "table_name": "tablesize" + }, + { + "fields": [ + { + "name": "mysql_info_schema_table_version", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "schema", + "table", + "type", + "create_options", + "engine", + "row_format" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "schema", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "table", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "type", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "create_options", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "engine", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "row_format", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "le", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_mysqld_exporter.tabledef", + "table_desc": "mysql_info_schema_table_version", + "table_name": "tabledef" + }, + { + "fields": [ + { + "name": "mysql_version_info", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "version", + "version_comment", + "innodb_version" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "version", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "version_comment", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "innodb_version", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_mysqld_exporter.version", + "table_desc": "mysql_version_info", + "table_name": "version" + }, + { + "fields": [ + { + "name": "mysql_info_schema_processlist_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "command", + "state" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_processlist_threads", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "command", + "state" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_info_schema_processlist_processes_by_user", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "mysql_user" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_user", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "command", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "state", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_mysqld_exporter.plist", + "table_desc": "mysql_info_schema_processlist", + "table_name": "plist" + }, + { + "fields": [ + { + "name": "mysql_slave_status_auto_position", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "channel_name", + "connection_name", + "master_host", + "master_uuid" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_slave_status_connect_retry", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "channel_name", + "connection_name", + "master_host", + "master_uuid" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_slave_status_exec_master_log_pos", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "channel_name", + "connection_name", + "master_host", + "master_uuid" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_slave_status_last_errno", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "channel_name", + "connection_name", + "master_host", + "master_uuid" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_slave_status_last_io_errno", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "channel_name", + "connection_name", + "master_host", + "master_uuid" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_slave_status_last_sql_errno", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "channel_name", + "connection_name", + "master_host", + "master_uuid" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_slave_status_master_port", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "channel_name", + "connection_name", + "master_host", + "master_uuid" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_slave_status_master_retry_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "channel_name", + "connection_name", + "master_host", + "master_uuid" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_slave_status_master_server_id", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "channel_name", + "connection_name", + "master_host", + "master_uuid" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_slave_status_master_ssl_allowed", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "channel_name", + "connection_name", + "master_host", + "master_uuid" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_slave_status_master_ssl_verify_server_cert", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "channel_name", + "connection_name", + "master_host", + "master_uuid" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_slave_status_read_master_log_pos", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "channel_name", + "connection_name", + "master_host", + "master_uuid" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_slave_status_relay_log_pos", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "channel_name", + "connection_name", + "master_host", + "master_uuid" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_slave_status_relay_log_space", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "channel_name", + "connection_name", + "master_host", + "master_uuid" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_slave_status_seconds_behind_master", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "channel_name", + "connection_name", + "master_host", + "master_uuid" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_slave_status_skip_counter", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "channel_name", + "connection_name", + "master_host", + "master_uuid" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_slave_status_slave_io_running", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "channel_name", + "connection_name", + "master_host", + "master_uuid" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_slave_status_slave_sql_running", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "channel_name", + "connection_name", + "master_host", + "master_uuid" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_slave_status_sql_delay", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "channel_name", + "connection_name", + "master_host", + "master_uuid" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_slave_status_until_log_pos", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "channel_name", + "connection_name", + "master_host", + "master_uuid" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "channel_name", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "connection_name", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "master_host", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "master_uuid", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_mysqld_exporter.slavestat", + "table_desc": "mysql_slave_status", + "table_name": "slavestat" + }, + { + "fields": [ + { + "name": "mysql_exporter_collector_duration_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "collector" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_exporter_last_scrape_error", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysql_exporter_scrapes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "collector", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_mysqld_exporter.internal", + "table_desc": "exporter collector", + "table_name": "internal" + } + ], + "plugin_type": "Exporter", + "os_type_list": [ + "linux" + ] + }, + "collect_type": "Exporter", + "target_nodes": [], + "target_node_type": "TOPO", + "target_object_type": "SERVICE" + } +} diff --git a/dbm-ui/backend/db_monitor/tpls/collect/0.mysql.dbm_mysqld_exporter.tpl64 b/dbm-ui/backend/db_monitor/tpls/collect/0.mysql.dbm_mysqld_exporter.tpl64 deleted file mode 100644 index d7d08e9bd4..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/collect/0.mysql.dbm_mysqld_exporter.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAicGx1Z2luX2lkIjogImRibV9teXNxbGRfZXhwb3J0ZXIiLCAiZGJfdHlwZSI6ICJteXNxbCIsICJkZXRhaWxzIjogeyJuYW1lIjogImRibV9teXNxbGRfZXhwb3J0ZXIiLCAibGFiZWwiOiAiY29tcG9uZW50IiwgInBhcmFtcyI6IHsicGx1Z2luIjogeyItLWNvbmZpZy5teS1jbmYiOiAiL2V0Yy97eyB0YXJnZXQuc2VydmljZS5sYWJlbHNbXCJleHBvcnRlcl9jb25mX3BhdGhcIl0gfX0iLCAiLS13ZWIubGlzdGVuLWFkZHJlc3MiOiAiJHtob3N0fToke3BvcnR9IiwgIi0tY29sbGVjdC5kYXRhZGlyX3NpemUiOiAiIiwgIi0tY29sbGVjdC5zbGF2ZV9zdGF0dXMiOiAiIiwgIi0tY29sbGVjdC5nbG9iYWxfc3RhdHVzIjogIiIsICJcdTY3MGRcdTUyYTFcdTViOWVcdTRmOGJcdTdlZjRcdTVlYTZcdTZjZThcdTUxNjUiOiB7ImFwcCI6ICJhcHAiLCAiaW5zdGFuY2UiOiAiaW5zdGFuY2UiLCAiYmtfYXBwX2NvZGUiOiAiYXBwX2lkIiwgImNsdXN0ZXJfbmFtZSI6ICJjbHVzdGVyX25hbWUiLCAiY2x1c3Rlcl90eXBlIjogImNsdXN0ZXJfdHlwZSIsICJpbnN0YW5jZV9ob3N0IjogImluc3RhbmNlX2hvc3QiLCAiaW5zdGFuY2Vfcm9sZSI6ICJpbnN0YW5jZV9yb2xlIiwgImNsdXN0ZXJfZG9tYWluIjogImNsdXN0ZXJfZG9tYWluIn0sICItLWNvbGxlY3QuZ2xvYmFsX3ZhcmlhYmxlcyI6ICIiLCAiLS1jb2xsZWN0LmVuZ2luZV9pbm5vZGJfc3RhdHVzIjogIiIsICItLWNvbGxlY3QuYXV0b19pbmNyZW1lbnQuY29sdW1ucyI6ICIiLCAiLS1jb2xsZWN0LmluZm9fc2NoZW1hLmlubm9kYl90cngiOiAiIiwgIi0tY29sbGVjdC5pbmZvX3NjaGVtYS5pbm5vZGJfbWV0cmljcyI6ICIiLCAiLS1jb2xsZWN0LmluZm9fc2NoZW1hLnF1ZXJ5X3Jlc3BvbnNlX3RpbWUiOiAiIiwgIi0tY29sbGVjdC5pbmZvX3NjaGVtYS5wcm9jZXNzbGlzdF9leHQgLS1jb2xsZWN0LmluZm9fc2NoZW1hLnByb2Nlc3NsaXN0X2V4dC5ieV91c2VyIjogIiIsICItLWNvbGxlY3QuaW5mb19zY2hlbWEudGFibGVzX2V4dCAtLWNvbGxlY3QuaW5mb19zY2hlbWEudGFibGVzX2V4dC5pbnRlcnZhbD0xaCAtLWNvbGxlY3QuaW5mb19zY2hlbWEudGFibGVzX2V4dC5kYXRhYmFzZXM9KiI6ICIifSwgImNvbGxlY3RvciI6IHsiaG9zdCI6ICIxMjcuMC4wLjEiLCAicG9ydCI6ICI3MDAwIiwgInBlcmlvZCI6IDYwfX0sICJwbHVnaW5faW5mbyI6IHsicGx1Z2luX2lkIjogImRibV9teXNxbGRfZXhwb3J0ZXIiLCAibWV0cmljX2pzb24iOiBbeyJmaWVsZHMiOiBbeyJuYW1lIjogIm15c3FsX3VwIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9XSwgInRhYmxlX2lkIjogImV4cG9ydGVyX2RibV9teXNxbGRfZXhwb3J0ZXIubXlzcWx1cCIsICJ0YWJsZV9kZXNjIjogIm15c3FsX3VwIiwgInRhYmxlX25hbWUiOiAibXlzcWx1cCJ9LCB7ImZpZWxkcyI6IFt7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19hYm9ydGVkX2NsaWVudHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX2Fib3J0ZWRfY29ubmVjdHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX2JpbmxvZ19jYWNoZV9kaXNrX3VzZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfYmlubG9nX2NhY2hlX3VzZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfYmlubG9nX3NuYXBzaG90X3Bvc2l0aW9uIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19iaW5sb2dfc3RtdF9jYWNoZV9kaXNrX3VzZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfYmlubG9nX3N0bXRfY2FjaGVfdXNlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19ieXRlc19yZWNlaXZlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfYnl0ZXNfc2VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfY29tcHJlc3Npb24iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX2Nvbm5lY3Rpb25zIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19jcmVhdGVkX3RtcF9kaXNrX3RhYmxlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfY3JlYXRlZF90bXBfZmlsZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX2NyZWF0ZWRfdG1wX3RhYmxlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfZGVsYXllZF9lcnJvcnMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX2RlbGF5ZWRfaW5zZXJ0X3RocmVhZHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX2RlbGF5ZWRfd3JpdGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19mbHVzaF9jb21tYW5kcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfaW5ub2RiX2F2YWlsYWJsZV91bmRvX2xvZ3MiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX2lubm9kYl9iYWNrZ3JvdW5kX2xvZ19zeW5jIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19pbm5vZGJfYnVmZmVyX3Bvb2xfYnl0ZXNfZGF0YSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfaW5ub2RiX2J1ZmZlcl9wb29sX2J5dGVzX2RpcnR5IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19pbm5vZGJfYnVmZmVyX3Bvb2xfcmVhZF9haGVhZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfaW5ub2RiX2J1ZmZlcl9wb29sX3JlYWRfYWhlYWRfZXZpY3RlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfaW5ub2RiX2J1ZmZlcl9wb29sX3JlYWRfYWhlYWRfcm5kIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19pbm5vZGJfYnVmZmVyX3Bvb2xfcmVhZF9yZXF1ZXN0cyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfaW5ub2RiX2J1ZmZlcl9wb29sX3JlYWRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19pbm5vZGJfYnVmZmVyX3Bvb2xfd2FpdF9mcmVlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19pbm5vZGJfYnVmZmVyX3Bvb2xfd3JpdGVfcmVxdWVzdHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX2lubm9kYl9idWZmZXJlZF9haW9fc3VibWl0dGVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19pbm5vZGJfY2hlY2twb2ludF9hZ2UiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX2lubm9kYl9jaGVja3BvaW50X21heF9hZ2UiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX2lubm9kYl9kYXRhX2ZzeW5jcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfaW5ub2RiX2RhdGFfcGVuZGluZ19mc3luY3MiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX2lubm9kYl9kYXRhX3BlbmRpbmdfcmVhZHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX2lubm9kYl9kYXRhX3BlbmRpbmdfd3JpdGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19pbm5vZGJfZGF0YV9yZWFkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19pbm5vZGJfZGF0YV9yZWFkcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfaW5ub2RiX2RhdGFfd3JpdGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19pbm5vZGJfZGF0YV93cml0dGVuIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19pbm5vZGJfZGJsd3JfcGFnZXNfd3JpdHRlbiIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfaW5ub2RiX2RibHdyX3dyaXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfaW5ub2RiX2lidWZfZnJlZV9saXN0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19pbm5vZGJfaWJ1Zl9zZWdtZW50X3NpemUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX2lubm9kYl9sb2dfd2FpdHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX2lubm9kYl9sb2dfd3JpdGVfcmVxdWVzdHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX2lubm9kYl9sb2dfd3JpdGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19pbm5vZGJfbHNuX2N1cnJlbnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX2lubm9kYl9sc25fZmx1c2hlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfaW5ub2RiX2xzbl9sYXN0X2NoZWNrcG9pbnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX2lubm9kYl9tYXN0ZXJfdGhyZWFkX2FjdGl2ZV9sb29wcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfaW5ub2RiX21hc3Rlcl90aHJlYWRfaWRsZV9sb29wcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfaW5ub2RiX21heF90cnhfaWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX2lubm9kYl9tZW1fYWRhcHRpdmVfaGFzaCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfaW5ub2RiX21lbV9kaWN0aW9uYXJ5IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19pbm5vZGJfbnVtX29wZW5fZmlsZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX2lubm9kYl9vbGRlc3Rfdmlld19sb3dfbGltaXRfdHJ4X2lkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19pbm5vZGJfb3NfbG9nX2ZzeW5jcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfaW5ub2RiX29zX2xvZ19wZW5kaW5nX2ZzeW5jcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfaW5ub2RiX29zX2xvZ19wZW5kaW5nX3dyaXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfaW5ub2RiX29zX2xvZ193cml0dGVuIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19pbm5vZGJfcGFnZV9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19pbm5vZGJfcGFnZXNfY3JlYXRlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfaW5ub2RiX3BhZ2VzX3JlYWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX2lubm9kYl9wYWdlc193cml0dGVuIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19pbm5vZGJfcHVyZ2VfdHJ4X2lkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19pbm5vZGJfcHVyZ2VfdW5kb19ubyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfaW5ub2RiX3Jvd19sb2NrX2N1cnJlbnRfd2FpdHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX2lubm9kYl9yb3dfbG9ja190aW1lIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19pbm5vZGJfcm93X2xvY2tfdGltZV9hdmciLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX2lubm9kYl9yb3dfbG9ja190aW1lX21heCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfaW5ub2RiX3Jvd19sb2NrX3dhaXRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19pbm5vZGJfc2Nhbl9kYXRhX3NpemUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX2lubm9kYl9zY2FuX2RlbGV0ZWRfcmVjc19zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19pbm5vZGJfc2Nhbl9wYWdlc19jb250aWd1b3VzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19pbm5vZGJfc2Nhbl9wYWdlc19kaXNqb2ludGVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19pbm5vZGJfc2Nhbl9wYWdlc190b3RhbF9zZWVrX2Rpc3RhbmNlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19pbm5vZGJfc2Vjb25kYXJ5X2luZGV4X3RyaWdnZXJlZF9jbHVzdGVyX3JlYWRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19pbm5vZGJfc2Vjb25kYXJ5X2luZGV4X3RyaWdnZXJlZF9jbHVzdGVyX3JlYWRzX2F2b2lkZWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX2lubm9kYl90cnVuY2F0ZWRfc3RhdHVzX3dyaXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfa2V5X2Jsb2Nrc19ub3RfZmx1c2hlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfa2V5X2Jsb2Nrc191bnVzZWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX2tleV9ibG9ja3NfdXNlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfa2V5X3JlYWRfcmVxdWVzdHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX2tleV9yZWFkcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfa2V5X3dyaXRlX3JlcXVlc3RzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19rZXlfd3JpdGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19sYXN0X3F1ZXJ5X2Nvc3QiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX2xhc3RfcXVlcnlfcGFydGlhbF9wbGFucyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfbG9ja2VkX2Nvbm5lY3RzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19tYXhfZXhlY3V0aW9uX3RpbWVfZXhjZWVkZWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX21heF9leGVjdXRpb25fdGltZV9zZXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX21heF9leGVjdXRpb25fdGltZV9zZXRfZmFpbGVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19tYXhfdXNlZF9jb25uZWN0aW9ucyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfbWF4X3VzZWRfY29ubmVjdGlvbnNfdGltZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfbm90X2ZsdXNoZWRfZGVsYXllZF9yb3dzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19vbmdvaW5nX2Fub255bW91c190cmFuc2FjdGlvbl9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfb3Blbl9maWxlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfb3Blbl9zdHJlYW1zIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19vcGVuX3RhYmxlX2RlZmluaXRpb25zIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19vcGVuX3RhYmxlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfb3BlbmVkX2ZpbGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19vcGVuZWRfdGFibGVfZGVmaW5pdGlvbnMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX29wZW5lZF90YWJsZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX3ByZXBhcmVkX3N0bXRfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX3FjYWNoZV9mcmVlX2Jsb2NrcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfcWNhY2hlX2ZyZWVfbWVtb3J5IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19xY2FjaGVfaGl0cyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfcWNhY2hlX2luc2VydHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX3FjYWNoZV9sb3dtZW1fcHJ1bmVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19xY2FjaGVfbm90X2NhY2hlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfcWNhY2hlX3F1ZXJpZXNfaW5fY2FjaGUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX3FjYWNoZV90b3RhbF9ibG9ja3MiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX3F1ZXJpZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX3F1ZXN0aW9ucyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfc2VsZWN0X2Z1bGxfam9pbiIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfc2VsZWN0X2Z1bGxfcmFuZ2Vfam9pbiIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfc2VsZWN0X3JhbmdlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19zZWxlY3RfcmFuZ2VfY2hlY2siLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX3NlbGVjdF9zY2FuIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19zbGF2ZV9oZWFydGJlYXRfcGVyaW9kIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19zbGF2ZV9vcGVuX3RlbXBfdGFibGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19zbGF2ZV9yZWNlaXZlZF9oZWFydGJlYXRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19zbGF2ZV9yZXRyaWVkX3RyYW5zYWN0aW9ucyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfc2xhdmVfcnVubmluZyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfc2xvd19sYXVuY2hfdGhyZWFkcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfc2xvd19xdWVyaWVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19zb3J0X21lcmdlX3Bhc3NlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfc29ydF9yYW5nZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfc29ydF9yb3dzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19zb3J0X3NjYW4iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX3NzbF9hY2NlcHRfcmVuZWdvdGlhdGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19zc2xfYWNjZXB0cyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfc3NsX2NhbGxiYWNrX2NhY2hlX2hpdHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX3NzbF9jbGllbnRfY29ubmVjdHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX3NzbF9jb25uZWN0X3JlbmVnb3RpYXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfc3NsX2N0eF92ZXJpZnlfZGVwdGgiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX3NzbF9jdHhfdmVyaWZ5X21vZGUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX3NzbF9kZWZhdWx0X3RpbWVvdXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX3NzbF9maW5pc2hlZF9hY2NlcHRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19zc2xfZmluaXNoZWRfY29ubmVjdHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX3NzbF9zZXNzaW9uX2NhY2hlX2hpdHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX3NzbF9zZXNzaW9uX2NhY2hlX21pc3NlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfc3NsX3Nlc3Npb25fY2FjaGVfb3ZlcmZsb3dzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19zc2xfc2Vzc2lvbl9jYWNoZV9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19zc2xfc2Vzc2lvbl9jYWNoZV90aW1lb3V0cyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfc3NsX3Nlc3Npb25zX3JldXNlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfc3NsX3VzZWRfc2Vzc2lvbl9jYWNoZV9lbnRyaWVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19zc2xfdmVyaWZ5X2RlcHRoIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19zc2xfdmVyaWZ5X21vZGUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX3RhYmxlX2xvY2tzX2ltbWVkaWF0ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfdGFibGVfbG9ja3Nfd2FpdGVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c190YWJsZV9vcGVuX2NhY2hlX2hpdHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX3RhYmxlX29wZW5fY2FjaGVfbWlzc2VzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c190YWJsZV9vcGVuX2NhY2hlX292ZXJmbG93cyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfdGNfbG9nX21heF9wYWdlc191c2VkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c190Y19sb2dfcGFnZV9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c190Y19sb2dfcGFnZV93YWl0cyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfdGhyZWFkcG9vbF9pZGxlX3RocmVhZHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX3RocmVhZHBvb2xfdGhyZWFkcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfdGhyZWFkc19jYWNoZWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX3RocmVhZHNfY29ubmVjdGVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c190aHJlYWRzX2NyZWF0ZWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX3RocmVhZHNfcnVubmluZyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfdXB0aW1lIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c191cHRpbWVfc2luY2VfZmx1c2hfc3RhdHVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19idWZmZXJfcG9vbF9wYWdlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbInN0YXRlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX2NvbW1hbmRzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY29tbWFuZCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3N0YXR1c19idWZmZXJfcG9vbF9wYWdlX2NoYW5nZXNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJvcGVyYXRpb24iXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfaW5ub2RiX3Jvd19vcHNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJvcGVyYXRpb24iXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF9zdGF0dXNfaGFuZGxlcnNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJoYW5kbGVyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfc3RhdHVzX2Nvbm5lY3Rpb25fZXJyb3JzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZXJyb3IiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInN0YXRlIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiY29tbWFuZCIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm9wZXJhdGlvbiIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImhhbmRsZXIiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlcnJvciIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfV0sICJ0YWJsZV9pZCI6ICJleHBvcnRlcl9kYm1fbXlzcWxkX2V4cG9ydGVyLmdzdGF0dXMiLCAidGFibGVfZGVzYyI6ICJteXNxbF9nbG9iYWxfc3RhdHVzIiwgInRhYmxlX25hbWUiOiAiZ3N0YXR1cyJ9LCB7ImZpZWxkcyI6IFt7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19hdXRvX2dlbmVyYXRlX2NlcnRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19hdXRvX2luY3JlbWVudF9pbmNyZW1lbnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2F1dG9faW5jcmVtZW50X29mZnNldCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfYXV0b2NvbW1pdCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfYXV0b21hdGljX3NwX3ByaXZpbGVnZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2F2b2lkX3RlbXBvcmFsX3VwZ3JhZGUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2JhY2tfbG9nIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19iaWdfdGFibGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19iaW5sb2dfY2FjaGVfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfYmlubG9nX2RpcmVjdF9ub25fdHJhbnNhY3Rpb25hbF91cGRhdGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19iaW5sb2dfZ3JvdXBfY29tbWl0X3N5bmNfZGVsYXkiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2JpbmxvZ19ncm91cF9jb21taXRfc3luY19ub19kZWxheV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfYmlubG9nX2d0aWRfc2ltcGxlX3JlY292ZXJ5IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19iaW5sb2dfbWF4X2ZsdXNoX3F1ZXVlX3RpbWUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2JpbmxvZ19vcmRlcl9jb21taXRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19iaW5sb2dfcm93c19xdWVyeV9sb2dfZXZlbnRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19iaW5sb2dfc3RtdF9jYWNoZV9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19ibG9iX2NvbXByZXNzZWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2J1bGtfaW5zZXJ0X2J1ZmZlcl9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19jaGVja19wcm94eV91c2VycyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfY29ubmVjdF90aW1lb3V0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19jb3JlX2ZpbGUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2RhdGV0aW1lX3ByZWNpc2lvbl91c2VfdjEiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2RlZmF1bHRfcGFzc3dvcmRfbGlmZXRpbWUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2RlZmF1bHRfd2Vla19mb3JtYXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2RlbGF5X2tleV93cml0ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfZGVsYXllZF9pbnNlcnRfbGltaXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2RlbGF5ZWRfaW5zZXJ0X3RpbWVvdXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2RlbGF5ZWRfcXVldWVfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfZGlzY29ubmVjdF9vbl9leHBpcmVkX3Bhc3N3b3JkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19kaXZfcHJlY2lzaW9uX2luY3JlbWVudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfZW5kX21hcmtlcnNfaW5fanNvbiIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfZW5mb3JjZV9ndGlkX2NvbnNpc3RlbmN5IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19lcV9yYW5nZV9pbmRleF9kaXZlX2xpbWl0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19ldmVudF9zY2hlZHVsZXIiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2V4cGFuZF9mYXN0X2luZGV4X2NyZWF0aW9uIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19leHBpcmVfbG9nc19kYXlzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19leHBsaWNpdF9kZWZhdWx0c19mb3JfdGltZXN0YW1wIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19leHRyYV9tYXhfY29ubmVjdGlvbnMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2V4dHJhX3BvcnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2ZsdXNoIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19mbHVzaF90aW1lIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19mb3JlaWduX2tleV9jaGVja3MiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2Z0X21heF93b3JkX2xlbiIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfZnRfbWluX3dvcmRfbGVuIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19mdF9xdWVyeV9leHBhbnNpb25fbGltaXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2dlbmVyYWxfbG9nIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19ncm91cF9jb25jYXRfbWF4X2xlbiIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfZ3RpZF9leGVjdXRlZF9jb21wcmVzc2lvbl9wZXJpb2QiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2d0aWRfbW9kZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaGF2ZV9iYWNrdXBfbG9ja3MiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2hhdmVfYmFja3VwX3NhZmVfYmlubG9nX2luZm8iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2hhdmVfY29tcHJlc3MiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2hhdmVfY3J5cHQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2hhdmVfZHluYW1pY19sb2FkaW5nIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19oYXZlX2dlb21ldHJ5IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19oYXZlX29wZW5zc2wiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2hhdmVfcHJvZmlsaW5nIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19oYXZlX3F1ZXJ5X2NhY2hlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19oYXZlX3J0cmVlX2tleXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2hhdmVfc25hcHNob3RfY2xvbmluZyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaGF2ZV9zc2wiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2hhdmVfc3RhdGVtZW50X3RpbWVvdXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2hhdmVfc3ltbGluayIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaG9zdF9jYWNoZV9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pZ25vcmVfYnVpbHRpbl9pbm5vZGIiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9hZGFwdGl2ZV9mbHVzaGluZyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX2FkYXB0aXZlX2ZsdXNoaW5nX2x3bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX2FkYXB0aXZlX2hhc2hfaW5kZXgiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9hZGFwdGl2ZV9oYXNoX2luZGV4X3BhcnRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfYWRhcHRpdmVfbWF4X3NsZWVwX2RlbGF5IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfYXBpX2JrX2NvbW1pdF9pbnRlcnZhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX2FwaV9kaXNhYmxlX3Jvd2xvY2siLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9hcGlfZW5hYmxlX2JpbmxvZyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX2FwaV9lbmFibGVfbWRsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfYXBpX3RyeF9sZXZlbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX2F1dG9leHRlbmRfaW5jcmVtZW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfYXV0b2luY19sb2NrX21vZGUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9idWZmZXJfcG9vbF9jaHVua19zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfYnVmZmVyX3Bvb2xfZHVtcF9hdF9zaHV0ZG93biIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX2J1ZmZlcl9wb29sX2R1bXBfbm93IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfYnVmZmVyX3Bvb2xfZHVtcF9wY3QiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9idWZmZXJfcG9vbF9pbl9jb3JlX2ZpbGUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9idWZmZXJfcG9vbF9pbnN0YW5jZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9idWZmZXJfcG9vbF9sb2FkX2Fib3J0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfYnVmZmVyX3Bvb2xfbG9hZF9hdF9zdGFydHVwIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfYnVmZmVyX3Bvb2xfbG9hZF9ub3ciLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9idWZmZXJfcG9vbF9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfY2hhbmdlX2J1ZmZlcl9tYXhfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX2NoZWNrc3VtcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX2NtcF9wZXJfaW5kZXhfZW5hYmxlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX2NvbW1pdF9jb25jdXJyZW5jeSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX2NvbXByZXNzZWRfY29sdW1uc190aHJlc2hvbGQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9jb21wcmVzc2VkX2NvbHVtbnNfemlwX2xldmVsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfY29tcHJlc3Npb25fZmFpbHVyZV90aHJlc2hvbGRfcGN0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfY29tcHJlc3Npb25fbGV2ZWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9jb21wcmVzc2lvbl9wYWRfcGN0X21heCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX2NvbmN1cnJlbmN5X3RpY2tldHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9kZWFkbG9ja19kZXRlY3QiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9kaXNhYmxlX3NvcnRfZmlsZV9jYWNoZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX2RvdWJsZXdyaXRlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfZmFzdF9zaHV0ZG93biIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX2ZpbGVfZm9ybWF0X2NoZWNrIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfZmlsZV9wZXJfdGFibGUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9maWxsX2ZhY3RvciIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX2ZsdXNoX2xvZ19hdF90aW1lb3V0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfZmx1c2hfbG9nX2F0X3RyeF9jb21taXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9mbHVzaF9uZWlnaGJvcnMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9mbHVzaF9zeW5jIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfZmx1c2hpbmdfYXZnX2xvb3BzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfZm9yY2VfbG9hZF9jb3JydXB0ZWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9mb3JjZV9yZWNvdmVyeSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX2Z0X2NhY2hlX3NpemUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9mdF9lbmFibGVfZGlhZ19wcmludCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX2Z0X2VuYWJsZV9zdG9wd29yZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX2Z0X2lnbm9yZV9zdG9wd29yZHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9mdF9tYXhfdG9rZW5fc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX2Z0X21pbl90b2tlbl9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfZnRfbnVtX3dvcmRfb3B0aW1pemUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9mdF9yZXN1bHRfY2FjaGVfbGltaXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9mdF9zb3J0X3BsbF9kZWdyZWUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9mdF90b3RhbF9jYWNoZV9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfaW9fY2FwYWNpdHkiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9pb19jYXBhY2l0eV9tYXgiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9raWxsX2lkbGVfdHJhbnNhY3Rpb24iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9sYXJnZV9wcmVmaXgiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9sb2NrX3dhaXRfdGltZW91dCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX2xvY2tzX3Vuc2FmZV9mb3JfYmlubG9nIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfbG9nX2J1ZmZlcl9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfbG9nX2NoZWNrc3VtcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX2xvZ19jb21wcmVzc2VkX3BhZ2VzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfbG9nX2ZpbGVfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX2xvZ19maWxlc19pbl9ncm91cCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX2xvZ193cml0ZV9haGVhZF9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfbHJ1X3NjYW5fZGVwdGgiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9tYXhfYml0bWFwX2ZpbGVfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX21heF9jaGFuZ2VkX3BhZ2VzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfbWF4X2RpcnR5X3BhZ2VzX3BjdCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX21heF9kaXJ0eV9wYWdlc19wY3RfbHdtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfbWF4X3B1cmdlX2xhZyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX21heF9wdXJnZV9sYWdfZGVsYXkiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9tYXhfdW5kb19sb2dfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX21pbl9ibG9iX2NvbXByZXNzX2xlbmd0aCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX29sZF9ibG9ja3NfcGN0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfb2xkX2Jsb2Nrc190aW1lIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfb25saW5lX2FsdGVyX2xvZ19tYXhfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX29wZW5fZmlsZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9vcHRpbWl6ZV9mdWxsdGV4dF9vbmx5IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfcGFnZV9jbGVhbmVycyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX3BhZ2Vfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX3ByaW50X2FsbF9kZWFkbG9ja3MiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9wcmludF9sb2NrX3dhaXRfdGltZW91dF9pbmZvIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfcHVyZ2VfYmF0Y2hfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX3B1cmdlX3JzZWdfdHJ1bmNhdGVfZnJlcXVlbmN5IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfcHVyZ2VfdGhyZWFkcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX3JhbmRvbV9yZWFkX2FoZWFkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfcmVhZF9haGVhZF90aHJlc2hvbGQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9yZWFkX2lvX3RocmVhZHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9yZWFkX29ubHkiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9yZXBsaWNhdGlvbl9kZWxheSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX3JvbGxiYWNrX29uX3RpbWVvdXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9yb2xsYmFja19zZWdtZW50cyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX3Nob3dfbG9ja3NfaGVsZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX3Nob3dfdmVyYm9zZV9sb2NrcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX3NvcnRfYnVmZmVyX3NpemUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9zcGluX3dhaXRfZGVsYXkiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9zdGF0c19hdXRvX3JlY2FsYyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX3N0YXRzX2luY2x1ZGVfZGVsZXRlX21hcmtlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX3N0YXRzX29uX21ldGFkYXRhIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfc3RhdHNfcGVyc2lzdGVudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX3N0YXRzX3BlcnNpc3RlbnRfc2FtcGxlX3BhZ2VzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfc3RhdHNfc2FtcGxlX3BhZ2VzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfc3RhdHNfdHJhbnNpZW50X3NhbXBsZV9wYWdlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX3N0YXR1c19vdXRwdXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9zdGF0dXNfb3V0cHV0X2xvY2tzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfc3RyaWN0X21vZGUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl9zdXBwb3J0X3hhIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfc3luY19hcnJheV9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfc3luY19zcGluX2xvb3BzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfdGFibGVfbG9ja3MiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl90aHJlYWRfY29uY3VycmVuY3kiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl90aHJlYWRfc2xlZXBfZGVsYXkiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl90cmFja19jaGFuZ2VkX3BhZ2VzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfdW5kb19sb2dfdHJ1bmNhdGUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl91bmRvX2xvZ3MiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2lubm9kYl91bmRvX3RhYmxlc3BhY2VzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfdXNlX2dsb2JhbF9mbHVzaF9sb2dfYXRfdHJ4X2NvbW1pdCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW5ub2RiX3VzZV9uYXRpdmVfYWlvIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19pbm5vZGJfd3JpdGVfaW9fdGhyZWFkcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfaW50ZXJhY3RpdmVfdGltZW91dCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfam9pbl9idWZmZXJfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfa2VlcF9maWxlc19vbl9jcmVhdGUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2tleV9idWZmZXJfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfa2V5X2NhY2hlX2FnZV90aHJlc2hvbGQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2tleV9jYWNoZV9ibG9ja19zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19rZXlfY2FjaGVfZGl2aXNpb25fbGltaXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2tpbGxfaWRsZV90cmFuc2FjdGlvbiIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfbGFyZ2VfZmlsZXNfc3VwcG9ydCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfbGFyZ2VfcGFnZV9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19sYXJnZV9wYWdlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfbG9jYWxfaW5maWxlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19sb2NrX3dhaXRfdGltZW91dCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfbG9ja2VkX2luX21lbW9yeSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfbG9nX2JpbiIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfbG9nX2Jpbl9jb21wcmVzcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfbG9nX2Jpbl9jb21wcmVzc19taW5fbGVuIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19sb2dfYmluX3RydXN0X2Z1bmN0aW9uX2NyZWF0b3JzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19sb2dfYmluX3VzZV92MV9yb3dfZXZlbnRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19sb2dfYnVpbHRpbl9hc19pZGVudGlmaWVkX2J5X3Bhc3N3b3JkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19sb2dfZXJyb3JfdmVyYm9zaXR5IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19sb2dfcXVlcmllc19ub3RfdXNpbmdfaW5kZXhlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfbG9nX3NsYXZlX3VwZGF0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2xvZ19zbG93X2FkbWluX3N0YXRlbWVudHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2xvZ19zbG93X3JhdGVfbGltaXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2xvZ19zbG93X3NsYXZlX3N0YXRlbWVudHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2xvZ19zbG93X3NwX3N0YXRlbWVudHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2xvZ19zdGF0ZW1lbnRzX3Vuc2FmZV9mb3JfYmlubG9nIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19sb2dfc3lzbG9nIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19sb2dfc3lzbG9nX2luY2x1ZGVfcGlkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19sb2dfdGhyb3R0bGVfcXVlcmllc19ub3RfdXNpbmdfaW5kZXhlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfbG9nX3dhcm5pbmdzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19sb25nX3F1ZXJ5X3RpbWUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX2xvd19wcmlvcml0eV91cGRhdGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19sb3dlcl9jYXNlX2ZpbGVfc3lzdGVtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19sb3dlcl9jYXNlX3RhYmxlX25hbWVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19tYXN0ZXJfdmVyaWZ5X2NoZWNrc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19tYXhfYWxsb3dlZF9wYWNrZXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX21heF9iaW5sb2dfY2FjaGVfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfbWF4X2JpbmxvZ19maWxlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfbWF4X2JpbmxvZ19zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19tYXhfYmlubG9nX3N0bXRfY2FjaGVfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfbWF4X2Nvbm5lY3RfZXJyb3JzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19tYXhfY29ubmVjdGlvbnMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX21heF9kZWxheWVkX3RocmVhZHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX21heF9kaWdlc3RfbGVuZ3RoIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19tYXhfZXJyb3JfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX21heF9leGVjdXRpb25fdGltZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfbWF4X2hlYXBfdGFibGVfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfbWF4X2luc2VydF9kZWxheWVkX3RocmVhZHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX21heF9qb2luX3NpemUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX21heF9sZW5ndGhfZm9yX3NvcnRfZGF0YSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfbWF4X3BvaW50c19pbl9nZW9tZXRyeSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfbWF4X3ByZXBhcmVkX3N0bXRfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX21heF9yZWxheV9sb2dfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfbWF4X3NlZWtzX2Zvcl9rZXkiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX21heF9zbG93bG9nX2ZpbGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19tYXhfc2xvd2xvZ19zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19tYXhfc29ydF9sZW5ndGgiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX21heF9zcF9yZWN1cnNpb25fZGVwdGgiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX21heF90bXBfdGFibGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19tYXhfdXNlcl9jb25uZWN0aW9ucyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfbWF4X3dyaXRlX2xvY2tfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX21heF94YV9jb21taXRfbG9ncyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfbWV0YWRhdGFfbG9ja3NfY2FjaGVfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfbWV0YWRhdGFfbG9ja3NfaGFzaF9pbnN0YW5jZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX21pbl9leGFtaW5lZF9yb3dfbGltaXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX211bHRpX3JhbmdlX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19teWlzYW1fZGF0YV9wb2ludGVyX3NpemUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX215aXNhbV9tYXhfc29ydF9maWxlX3NpemUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX215aXNhbV9tbWFwX3NpemUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX215aXNhbV9yZWNvdmVyX29wdGlvbnMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX215aXNhbV9yZXBhaXJfdGhyZWFkcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfbXlpc2FtX3NvcnRfYnVmZmVyX3NpemUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX215aXNhbV91c2VfbW1hcCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfbXlzcWxfbmF0aXZlX3Bhc3N3b3JkX3Byb3h5X3VzZXJzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19uZXRfYnVmZmVyX2xlbmd0aCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfbmV0X3JlYWRfdGltZW91dCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfbmV0X3JldHJ5X2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19uZXRfd3JpdGVfdGltZW91dCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfbmV3IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19uZ3JhbV90b2tlbl9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19vZmZsaW5lX21vZGUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX29sZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfb2xkX2FsdGVyX3RhYmxlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19vbGRfcGFzc3dvcmRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19vcGVuX2ZpbGVzX2xpbWl0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19vcHRpbWl6ZXJfcHJ1bmVfbGV2ZWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX29wdGltaXplcl9zZWFyY2hfZGVwdGgiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX29wdGltaXplcl90cmFjZV9saW1pdCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfb3B0aW1pemVyX3RyYWNlX21heF9tZW1fc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfb3B0aW1pemVyX3RyYWNlX29mZnNldCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfcGFyc2VyX21heF9tZW1fc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfcGVyZm9ybWFuY2Vfc2NoZW1hIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19wZXJmb3JtYW5jZV9zY2hlbWFfYWNjb3VudHNfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfcGVyZm9ybWFuY2Vfc2NoZW1hX2RpZ2VzdHNfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfcGVyZm9ybWFuY2Vfc2NoZW1hX2V2ZW50c19zdGFnZXNfaGlzdG9yeV9sb25nX3NpemUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3BlcmZvcm1hbmNlX3NjaGVtYV9ldmVudHNfc3RhZ2VzX2hpc3Rvcnlfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfcGVyZm9ybWFuY2Vfc2NoZW1hX2V2ZW50c19zdGF0ZW1lbnRzX2hpc3RvcnlfbG9uZ19zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19wZXJmb3JtYW5jZV9zY2hlbWFfZXZlbnRzX3N0YXRlbWVudHNfaGlzdG9yeV9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19wZXJmb3JtYW5jZV9zY2hlbWFfZXZlbnRzX3RyYW5zYWN0aW9uc19oaXN0b3J5X2xvbmdfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfcGVyZm9ybWFuY2Vfc2NoZW1hX2V2ZW50c190cmFuc2FjdGlvbnNfaGlzdG9yeV9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19wZXJmb3JtYW5jZV9zY2hlbWFfZXZlbnRzX3dhaXRzX2hpc3RvcnlfbG9uZ19zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19wZXJmb3JtYW5jZV9zY2hlbWFfZXZlbnRzX3dhaXRzX2hpc3Rvcnlfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfcGVyZm9ybWFuY2Vfc2NoZW1hX2hvc3RzX3NpemUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3BlcmZvcm1hbmNlX3NjaGVtYV9tYXhfY29uZF9jbGFzc2VzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19wZXJmb3JtYW5jZV9zY2hlbWFfbWF4X2NvbmRfaW5zdGFuY2VzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19wZXJmb3JtYW5jZV9zY2hlbWFfbWF4X2RpZ2VzdF9sZW5ndGgiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3BlcmZvcm1hbmNlX3NjaGVtYV9tYXhfZmlsZV9jbGFzc2VzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19wZXJmb3JtYW5jZV9zY2hlbWFfbWF4X2ZpbGVfaGFuZGxlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfcGVyZm9ybWFuY2Vfc2NoZW1hX21heF9maWxlX2luc3RhbmNlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfcGVyZm9ybWFuY2Vfc2NoZW1hX21heF9pbmRleF9zdGF0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19wZXJmb3JtYW5jZV9zY2hlbWFfbWF4X21lbW9yeV9jbGFzc2VzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19wZXJmb3JtYW5jZV9zY2hlbWFfbWF4X21ldGFkYXRhX2xvY2tzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19wZXJmb3JtYW5jZV9zY2hlbWFfbWF4X211dGV4X2NsYXNzZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3BlcmZvcm1hbmNlX3NjaGVtYV9tYXhfbXV0ZXhfaW5zdGFuY2VzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19wZXJmb3JtYW5jZV9zY2hlbWFfbWF4X3ByZXBhcmVkX3N0YXRlbWVudHNfaW5zdGFuY2VzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19wZXJmb3JtYW5jZV9zY2hlbWFfbWF4X3Byb2dyYW1faW5zdGFuY2VzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19wZXJmb3JtYW5jZV9zY2hlbWFfbWF4X3J3bG9ja19jbGFzc2VzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19wZXJmb3JtYW5jZV9zY2hlbWFfbWF4X3J3bG9ja19pbnN0YW5jZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3BlcmZvcm1hbmNlX3NjaGVtYV9tYXhfc29ja2V0X2NsYXNzZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3BlcmZvcm1hbmNlX3NjaGVtYV9tYXhfc29ja2V0X2luc3RhbmNlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfcGVyZm9ybWFuY2Vfc2NoZW1hX21heF9zcWxfdGV4dF9sZW5ndGgiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3BlcmZvcm1hbmNlX3NjaGVtYV9tYXhfc3RhZ2VfY2xhc3NlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfcGVyZm9ybWFuY2Vfc2NoZW1hX21heF9zdGF0ZW1lbnRfY2xhc3NlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfcGVyZm9ybWFuY2Vfc2NoZW1hX21heF9zdGF0ZW1lbnRfc3RhY2siLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3BlcmZvcm1hbmNlX3NjaGVtYV9tYXhfdGFibGVfaGFuZGxlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfcGVyZm9ybWFuY2Vfc2NoZW1hX21heF90YWJsZV9pbnN0YW5jZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3BlcmZvcm1hbmNlX3NjaGVtYV9tYXhfdGFibGVfbG9ja19zdGF0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19wZXJmb3JtYW5jZV9zY2hlbWFfbWF4X3RocmVhZF9jbGFzc2VzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19wZXJmb3JtYW5jZV9zY2hlbWFfbWF4X3RocmVhZF9pbnN0YW5jZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3BlcmZvcm1hbmNlX3NjaGVtYV9zZXNzaW9uX2Nvbm5lY3RfYXR0cnNfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfcGVyZm9ybWFuY2Vfc2NoZW1hX3NldHVwX2FjdG9yc19zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19wZXJmb3JtYW5jZV9zY2hlbWFfc2V0dXBfb2JqZWN0c19zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19wZXJmb3JtYW5jZV9zY2hlbWFfdXNlcnNfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfcG9ydCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfcHJlbG9hZF9idWZmZXJfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfcHJvZmlsaW5nIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19wcm9maWxpbmdfaGlzdG9yeV9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19wcm90b2NvbF92ZXJzaW9uIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19xdWVyeV9hbGxvY19ibG9ja19zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19xdWVyeV9jYWNoZV9saW1pdCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfcXVlcnlfY2FjaGVfbWluX3Jlc191bml0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19xdWVyeV9jYWNoZV9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19xdWVyeV9jYWNoZV9zdHJpcF9jb21tZW50cyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfcXVlcnlfY2FjaGVfdHlwZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfcXVlcnlfY2FjaGVfd2xvY2tfaW52YWxpZGF0ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfcXVlcnlfcHJlYWxsb2Nfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfcXVlcnlfcmVzcG9uc2VfdGltZV9mbHVzaCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfcXVlcnlfcmVzcG9uc2VfdGltZV9yYW5nZV9iYXNlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19xdWVyeV9yZXNwb25zZV90aW1lX3N0YXRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19yYW5nZV9hbGxvY19ibG9ja19zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19yYW5nZV9vcHRpbWl6ZXJfbWF4X21lbV9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19yZWFkX2JpbmxvZ19zcGVlZF9saW1pdCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfcmVhZF9idWZmZXJfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfcmVhZF9vbmx5IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19yZWFkX3JuZF9idWZmZXJfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfcmVsYXlfbG9nX3B1cmdlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19yZWxheV9sb2dfcmVjb3ZlcnkiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3JlbGF5X2xvZ19zcGFjZV9saW1pdCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfcmVsYXlfbG9nX3VuY29tcHJlc3MiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3JlcG9ydF9wb3J0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19yZXF1aXJlX3NlY3VyZV90cmFuc3BvcnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3JwbF9zdG9wX3NsYXZlX3RpbWVvdXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3NlY3VyZV9hdXRoIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19zZXJ2ZXJfaWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3NlcnZlcl9pZF9iaXRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19zZXNzaW9uX3RyYWNrX2d0aWRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19zZXNzaW9uX3RyYWNrX3NjaGVtYSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfc2Vzc2lvbl90cmFja19zdGF0ZV9jaGFuZ2UiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3Nlc3Npb25fdHJhY2tfdHJhbnNhY3Rpb25faW5mbyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfc2hhMjU2X3Bhc3N3b3JkX2F1dG9fZ2VuZXJhdGVfcnNhX2tleXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3NoYTI1Nl9wYXNzd29yZF9wcm94eV91c2VycyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfc2hvd19jb21wYXRpYmlsaXR5XzU2IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19zaG93X29sZF90ZW1wb3JhbHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3NraXBfZXh0ZXJuYWxfbG9ja2luZyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfc2tpcF9uYW1lX3Jlc29sdmUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3NraXBfbmV0d29ya2luZyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfc2tpcF9zaG93X2RhdGFiYXNlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19zbGF2ZV9hbGxvd19iYXRjaGluZyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfc2xhdmVfY2hlY2twb2ludF9ncm91cCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfc2xhdmVfY2hlY2twb2ludF9wZXJpb2QiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3NsYXZlX2NvbXByZXNzZWRfcHJvdG9jb2wiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3NsYXZlX21heF9hbGxvd2VkX3BhY2tldCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfc2xhdmVfbmV0X3RpbWVvdXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3NsYXZlX3BhcmFsbGVsX3dvcmtlcnMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3NsYXZlX3BlbmRpbmdfam9ic19zaXplX21heCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfc2xhdmVfcHJlc2VydmVfY29tbWl0X29yZGVyIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19zbGF2ZV9za2lwX2Vycm9ycyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfc2xhdmVfc3FsX3ZlcmlmeV9jaGVja3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfc2xhdmVfdHJhbnNhY3Rpb25fcmV0cmllcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfc2xvd19sYXVuY2hfdGltZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfc2xvd19xdWVyeV9sb2ciLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3Nsb3dfcXVlcnlfbG9nX2Fsd2F5c193cml0ZV90aW1lIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19zb3J0X2J1ZmZlcl9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19zb3J0X3doZW5fcGFydGl0aW9uX3ByZWZpeF9vcmRlciIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfc3FsX2F1dG9faXNfbnVsbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfc3FsX2JpZ19zZWxlY3RzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19zcWxfYnVmZmVyX3Jlc3VsdCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfc3FsX2xvZ19iaW4iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3NxbF9sb2dfb2ZmIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19zcWxfbm90ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3NxbF9xdW90ZV9zaG93X2NyZWF0ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfc3FsX3NhZmVfdXBkYXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfc3FsX3NlbGVjdF9saW1pdCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfc3FsX3NsYXZlX3NraXBfY291bnRlciIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfc3FsX3dhcm5pbmdzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19zdG9yZWRfcHJvZ3JhbV9jYWNoZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfc3VwZXJfcmVhZF9vbmx5IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19zeW5jX2JpbmxvZyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfc3luY19mcm0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3N5bmNfbWFzdGVyX2luZm8iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3N5bmNfcmVsYXlfbG9nIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc19zeW5jX3JlbGF5X2xvZ19pbmZvIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc190YWJsZV9kZWZpbml0aW9uX2NhY2hlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc190YWJsZV9vcGVuX2NhY2hlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc190YWJsZV9vcGVuX2NhY2hlX2luc3RhbmNlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfdGhyZWFkX2NhY2hlX3NpemUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3RocmVhZF9wb29sX2hpZ2hfcHJpb190aWNrZXRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc190aHJlYWRfcG9vbF9pZGxlX3RpbWVvdXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3RocmVhZF9wb29sX21heF90aHJlYWRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc190aHJlYWRfcG9vbF9vdmVyc3Vic2NyaWJlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc190aHJlYWRfcG9vbF9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc190aHJlYWRfcG9vbF9zdGFsbF9saW1pdCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfdGhyZWFkX3N0YWNrIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc190aHJlYWRfc3RhdGlzdGljcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfdG1wX3RhYmxlX3NpemUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3RyYW5zYWN0aW9uX2FsbG9jX2Jsb2NrX3NpemUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3RyYW5zYWN0aW9uX3ByZWFsbG9jX3NpemUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3RyYW5zYWN0aW9uX3JlYWRfb25seSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfdHJhbnNhY3Rpb25fd3JpdGVfc2V0X2V4dHJhY3Rpb24iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3R4X3JlYWRfb25seSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfdW5pcXVlX2NoZWNrcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXNfdXBkYXRhYmxlX3ZpZXdzX3dpdGhfbGltaXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9nbG9iYWxfdmFyaWFibGVzX3VzZXJzdGF0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZ2xvYmFsX3ZhcmlhYmxlc193YWl0X3RpbWVvdXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX1dLCAidGFibGVfaWQiOiAiZXhwb3J0ZXJfZGJtX215c3FsZF9leHBvcnRlci5ndmFycyIsICJ0YWJsZV9kZXNjIjogIm15c3FsX2dsb2JhbF92YXJpYWJsZXMiLCAidGFibGVfbmFtZSI6ICJndmFycyJ9LCB7ImZpZWxkcyI6IFt7Im5hbWUiOiAibXlzcWxfZGF0YWRpcl9zaXplX2tiIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9XSwgInRhYmxlX2lkIjogImV4cG9ydGVyX2RibV9teXNxbGRfZXhwb3J0ZXIuZGF0YXNpemUiLCAidGFibGVfZGVzYyI6ICJteXNxbF9kYXRhZGlyX3NpemUiLCAidGFibGVfbmFtZSI6ICJkYXRhc2l6ZSJ9LCB7ImZpZWxkcyI6IFt7Im5hbWUiOiAibXlzcWxfaW5mb19zY2hlbWFfaW5ub2RiX21ldHJpY3NfYWRhcHRpdmVfaGFzaF9pbmRleF9hZGFwdGl2ZV9oYXNoX3NlYXJjaGVzX2J0cmVlX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfaW5mb19zY2hlbWFfaW5ub2RiX21ldHJpY3NfYWRhcHRpdmVfaGFzaF9pbmRleF9hZGFwdGl2ZV9oYXNoX3NlYXJjaGVzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfaW5mb19zY2hlbWFfaW5ub2RiX21ldHJpY3NfYnVmZmVyX2J1ZmZlcl9kYXRhX3JlYWRzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfaW5mb19zY2hlbWFfaW5ub2RiX21ldHJpY3NfYnVmZmVyX2J1ZmZlcl9kYXRhX3dyaXR0ZW5fdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9pbmZvX3NjaGVtYV9pbm5vZGJfbWV0cmljc19idWZmZXJfYnVmZmVyX3BhZ2VzX2NyZWF0ZWRfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9pbmZvX3NjaGVtYV9pbm5vZGJfbWV0cmljc19idWZmZXJfYnVmZmVyX3BhZ2VzX3JlYWRfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9pbmZvX3NjaGVtYV9pbm5vZGJfbWV0cmljc19idWZmZXJfYnVmZmVyX3BhZ2VzX3dyaXR0ZW5fdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9pbmZvX3NjaGVtYV9pbm5vZGJfbWV0cmljc19idWZmZXJfYnVmZmVyX3Bvb2xfYnl0ZXNfZGF0YSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2luZm9fc2NoZW1hX2lubm9kYl9tZXRyaWNzX2J1ZmZlcl9idWZmZXJfcG9vbF9ieXRlc19kaXJ0eSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2luZm9fc2NoZW1hX2lubm9kYl9tZXRyaWNzX2J1ZmZlcl9idWZmZXJfcG9vbF9yZWFkX2FoZWFkX2V2aWN0ZWRfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9pbmZvX3NjaGVtYV9pbm5vZGJfbWV0cmljc19idWZmZXJfYnVmZmVyX3Bvb2xfcmVhZF9haGVhZF90b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2luZm9fc2NoZW1hX2lubm9kYl9tZXRyaWNzX2J1ZmZlcl9idWZmZXJfcG9vbF9yZWFkX3JlcXVlc3RzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfaW5mb19zY2hlbWFfaW5ub2RiX21ldHJpY3NfYnVmZmVyX2J1ZmZlcl9wb29sX3JlYWRzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfaW5mb19zY2hlbWFfaW5ub2RiX21ldHJpY3NfYnVmZmVyX2J1ZmZlcl9wb29sX3dhaXRfZnJlZV90b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2luZm9fc2NoZW1hX2lubm9kYl9tZXRyaWNzX2J1ZmZlcl9idWZmZXJfcG9vbF93cml0ZV9yZXF1ZXN0c190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2luZm9fc2NoZW1hX2lubm9kYl9tZXRyaWNzX2J1ZmZlcl9wb29sX2RpcnR5X3BhZ2VzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfaW5mb19zY2hlbWFfaW5ub2RiX21ldHJpY3NfY2hhbmdlX2J1ZmZlcl9pYnVmX21lcmdlc19kZWxldGVfbWFya190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2luZm9fc2NoZW1hX2lubm9kYl9tZXRyaWNzX2NoYW5nZV9idWZmZXJfaWJ1Zl9tZXJnZXNfZGVsZXRlX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfaW5mb19zY2hlbWFfaW5ub2RiX21ldHJpY3NfY2hhbmdlX2J1ZmZlcl9pYnVmX21lcmdlc19kaXNjYXJkX2RlbGV0ZV9tYXJrX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfaW5mb19zY2hlbWFfaW5ub2RiX21ldHJpY3NfY2hhbmdlX2J1ZmZlcl9pYnVmX21lcmdlc19kaXNjYXJkX2RlbGV0ZV90b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2luZm9fc2NoZW1hX2lubm9kYl9tZXRyaWNzX2NoYW5nZV9idWZmZXJfaWJ1Zl9tZXJnZXNfZGlzY2FyZF9pbnNlcnRfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9pbmZvX3NjaGVtYV9pbm5vZGJfbWV0cmljc19jaGFuZ2VfYnVmZmVyX2lidWZfbWVyZ2VzX2luc2VydF90b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2luZm9fc2NoZW1hX2lubm9kYl9tZXRyaWNzX2NoYW5nZV9idWZmZXJfaWJ1Zl9tZXJnZXNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9pbmZvX3NjaGVtYV9pbm5vZGJfbWV0cmljc19jaGFuZ2VfYnVmZmVyX2lidWZfc2l6ZV90b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2luZm9fc2NoZW1hX2lubm9kYl9tZXRyaWNzX2RtbF9kbWxfZGVsZXRlc190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2luZm9fc2NoZW1hX2lubm9kYl9tZXRyaWNzX2RtbF9kbWxfaW5zZXJ0c190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2luZm9fc2NoZW1hX2lubm9kYl9tZXRyaWNzX2RtbF9kbWxfdXBkYXRlc190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2luZm9fc2NoZW1hX2lubm9kYl9tZXRyaWNzX2ZpbGVfc3lzdGVtX2ZpbGVfbnVtX29wZW5fZmlsZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9pbmZvX3NjaGVtYV9pbm5vZGJfbWV0cmljc19sb2NrX2xvY2tfZGVhZGxvY2tzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfaW5mb19zY2hlbWFfaW5ub2RiX21ldHJpY3NfbG9ja19sb2NrX3Jvd19sb2NrX2N1cnJlbnRfd2FpdHNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9pbmZvX3NjaGVtYV9pbm5vZGJfbWV0cmljc19sb2NrX2xvY2tfcm93X2xvY2tfdGltZV9hdmciLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9pbmZvX3NjaGVtYV9pbm5vZGJfbWV0cmljc19sb2NrX2xvY2tfcm93X2xvY2tfdGltZV9tYXgiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9pbmZvX3NjaGVtYV9pbm5vZGJfbWV0cmljc19sb2NrX2xvY2tfcm93X2xvY2tfdGltZV90b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2luZm9fc2NoZW1hX2lubm9kYl9tZXRyaWNzX2xvY2tfbG9ja19yb3dfbG9ja193YWl0c190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2luZm9fc2NoZW1hX2lubm9kYl9tZXRyaWNzX2xvY2tfbG9ja190aW1lb3V0c190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2luZm9fc2NoZW1hX2lubm9kYl9tZXRyaWNzX29zX29zX2RhdGFfZnN5bmNzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfaW5mb19zY2hlbWFfaW5ub2RiX21ldHJpY3Nfb3Nfb3NfZGF0YV9yZWFkc190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2luZm9fc2NoZW1hX2lubm9kYl9tZXRyaWNzX29zX29zX2RhdGFfd3JpdGVzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfaW5mb19zY2hlbWFfaW5ub2RiX21ldHJpY3Nfb3Nfb3NfbG9nX2J5dGVzX3dyaXR0ZW5fdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9pbmZvX3NjaGVtYV9pbm5vZGJfbWV0cmljc19vc19vc19sb2dfZnN5bmNzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfaW5mb19zY2hlbWFfaW5ub2RiX21ldHJpY3Nfb3Nfb3NfbG9nX3BlbmRpbmdfZnN5bmNzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfaW5mb19zY2hlbWFfaW5ub2RiX21ldHJpY3Nfb3Nfb3NfbG9nX3BlbmRpbmdfd3JpdGVzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfaW5mb19zY2hlbWFfaW5ub2RiX21ldHJpY3NfcmVjb3ZlcnlfbG9nX3BhZGRlZF90b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2luZm9fc2NoZW1hX2lubm9kYl9tZXRyaWNzX3JlY292ZXJ5X2xvZ193YWl0c190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2luZm9fc2NoZW1hX2lubm9kYl9tZXRyaWNzX3JlY292ZXJ5X2xvZ193cml0ZV9yZXF1ZXN0c190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2luZm9fc2NoZW1hX2lubm9kYl9tZXRyaWNzX3JlY292ZXJ5X2xvZ193cml0ZXNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9pbmZvX3NjaGVtYV9pbm5vZGJfbWV0cmljc19zZXJ2ZXJfYnVmZmVyX3Bvb2xfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2luZm9fc2NoZW1hX2lubm9kYl9tZXRyaWNzX3NlcnZlcl9pbm5vZGJfYWN0aXZpdHlfY291bnRfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9pbmZvX3NjaGVtYV9pbm5vZGJfbWV0cmljc19zZXJ2ZXJfaW5ub2RiX2RibHdyX3BhZ2VzX3dyaXR0ZW5fdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9pbmZvX3NjaGVtYV9pbm5vZGJfbWV0cmljc19zZXJ2ZXJfaW5ub2RiX2RibHdyX3dyaXRlc190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2luZm9fc2NoZW1hX2lubm9kYl9tZXRyaWNzX3NlcnZlcl9pbm5vZGJfcGFnZV9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfaW5mb19zY2hlbWFfaW5ub2RiX21ldHJpY3Nfc2VydmVyX2lubm9kYl9yd2xvY2tfc19vc193YWl0c190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2luZm9fc2NoZW1hX2lubm9kYl9tZXRyaWNzX3NlcnZlcl9pbm5vZGJfcndsb2NrX3Nfc3Bpbl9yb3VuZHNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9pbmZvX3NjaGVtYV9pbm5vZGJfbWV0cmljc19zZXJ2ZXJfaW5ub2RiX3J3bG9ja19zX3NwaW5fd2FpdHNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9pbmZvX3NjaGVtYV9pbm5vZGJfbWV0cmljc19zZXJ2ZXJfaW5ub2RiX3J3bG9ja19zeF9vc193YWl0c190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2luZm9fc2NoZW1hX2lubm9kYl9tZXRyaWNzX3NlcnZlcl9pbm5vZGJfcndsb2NrX3N4X3NwaW5fcm91bmRzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfaW5mb19zY2hlbWFfaW5ub2RiX21ldHJpY3Nfc2VydmVyX2lubm9kYl9yd2xvY2tfc3hfc3Bpbl93YWl0c190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2luZm9fc2NoZW1hX2lubm9kYl9tZXRyaWNzX3NlcnZlcl9pbm5vZGJfcndsb2NrX3hfb3Nfd2FpdHNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9pbmZvX3NjaGVtYV9pbm5vZGJfbWV0cmljc19zZXJ2ZXJfaW5ub2RiX3J3bG9ja194X3NwaW5fcm91bmRzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfaW5mb19zY2hlbWFfaW5ub2RiX21ldHJpY3Nfc2VydmVyX2lubm9kYl9yd2xvY2tfeF9zcGluX3dhaXRzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfaW5mb19zY2hlbWFfaW5ub2RiX21ldHJpY3NfdHJhbnNhY3Rpb25fdHJ4X3JzZWdfaGlzdG9yeV9sZW4iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9lbmdpbmVfaW5ub2RiX3F1ZXJpZXNfaW5fcXVldWUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9lbmdpbmVfaW5ub2RiX3F1ZXJpZXNfaW5zaWRlX2lubm9kYiIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2VuZ2luZV9pbm5vZGJfcmVhZF92aWV3c19vcGVuX2luc2lkZV9pbm5vZGIiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX1dLCAidGFibGVfaWQiOiAiZXhwb3J0ZXJfZGJtX215c3FsZF9leHBvcnRlci5pbm5vZGJtdHIiLCAidGFibGVfZGVzYyI6ICJteXNxbF9pbmZvX3NjaGVtYV9pbm5vZGJfbWV0cmljcyIsICJ0YWJsZV9uYW1lIjogImlubm9kYm10ciJ9LCB7ImZpZWxkcyI6IFt7Im5hbWUiOiAibXlzcWxfZW5naW5lX2lubm9kYl9sb2NrX3dhaXRzX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZW5naW5lX2lubm9kYl9sb2Nrc19jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2VuZ2luZV9pbm5vZGJfdHJ4X2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZW5naW5lX2lubm9kYl90cnhfbG9uZ19xdWVyeV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2VuZ2luZV9pbm5vZGJfdHJ4X2xvbmdfdGltZV9tYXgiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9lbmdpbmVfaW5ub2RiX3RyeF9pZGxlX3RpbWVfbWF4IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAidHJ4X3N0YXRlIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiY29tbWFuZCIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInN0YXRlIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAidXNlciIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfV0sICJ0YWJsZV9pZCI6ICJleHBvcnRlcl9kYm1fbXlzcWxkX2V4cG9ydGVyLmlubm9kYnRyeCIsICJ0YWJsZV9kZXNjIjogIm15c3FsX2VuZ2luZV9pbm5vZGJfbWV0cmljc19sb2NrcyIsICJ0YWJsZV9uYW1lIjogImlubm9kYnRyeCJ9LCB7ImZpZWxkcyI6IFt7Im5hbWUiOiAibXlzcWxfaW5mb19zY2hlbWFfcXVlcnlfcmVzcG9uc2VfdGltZV9zZWNvbmRzX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2luZm9fc2NoZW1hX3F1ZXJ5X3Jlc3BvbnNlX3RpbWVfc2Vjb25kc19jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2luZm9fc2NoZW1hX3JlYWRfcXVlcnlfcmVzcG9uc2VfdGltZV9zZWNvbmRzX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2luZm9fc2NoZW1hX3JlYWRfcXVlcnlfcmVzcG9uc2VfdGltZV9zZWNvbmRzX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfaW5mb19zY2hlbWFfd3JpdGVfcXVlcnlfcmVzcG9uc2VfdGltZV9zZWNvbmRzX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2luZm9fc2NoZW1hX3dyaXRlX3F1ZXJ5X3Jlc3BvbnNlX3RpbWVfc2Vjb25kc19jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2luZm9fc2NoZW1hX3F1ZXJ5X3Jlc3BvbnNlX3RpbWVfc2Vjb25kc19idWNrZXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJsZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfaW5mb19zY2hlbWFfcmVhZF9xdWVyeV9yZXNwb25zZV90aW1lX3NlY29uZHNfYnVja2V0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsibGUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2luZm9fc2NoZW1hX3dyaXRlX3F1ZXJ5X3Jlc3BvbnNlX3RpbWVfc2Vjb25kc19idWNrZXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJsZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibGUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX1dLCAidGFibGVfaWQiOiAiZXhwb3J0ZXJfZGJtX215c3FsZF9leHBvcnRlci5yZXNwdGltZSIsICJ0YWJsZV9kZXNjIjogIm15c3FsX2luZm9fc2NoZW1hX3F1ZXJ5X3Jlc3BvbnNlX3RpbWUiLCAidGFibGVfbmFtZSI6ICJyZXNwdGltZSJ9LCB7ImZpZWxkcyI6IFt7Im5hbWUiOiAibXlzcWxfaW5mb19zY2hlbWFfYXV0b19pbmNyZW1lbnRfY29sdW1uIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY29sdW1uIiwgInNjaGVtYSIsICJ0YWJsZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfaW5mb19zY2hlbWFfYXV0b19pbmNyZW1lbnRfY29sdW1uX21heCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNvbHVtbiIsICJzY2hlbWEiLCAidGFibGUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImNvbHVtbiIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInNjaGVtYSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInRhYmxlIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9XSwgInRhYmxlX2lkIjogImV4cG9ydGVyX2RibV9teXNxbGRfZXhwb3J0ZXIudGFibGVpbmNyIiwgInRhYmxlX2Rlc2MiOiAibXlzcWxfaW5mb19zY2hlbWFfYXV0b19pbmNyZW1lbnQiLCAidGFibGVfbmFtZSI6ICJ0YWJsZWluY3IifSwgeyJmaWVsZHMiOiBbeyJuYW1lIjogIm15c3FsX2luZm9fc2NoZW1hX3RhYmxlX3NpemUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjb21wb25lbnQiLCAic2NoZW1hIiwgInRhYmxlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9pbmZvX3NjaGVtYV90b3RhbF9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsic2NoZW1hIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9pbmZvX3NjaGVtYV90YWJsZV9yb3dzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsidGFibGUiLCAic2NoZW1hIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJjb21wb25lbnQiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJzY2hlbWEiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJ0YWJsZSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfV0sICJ0YWJsZV9pZCI6ICJleHBvcnRlcl9kYm1fbXlzcWxkX2V4cG9ydGVyLnRhYmxlc2l6ZSIsICJ0YWJsZV9kZXNjIjogIm15c3FsX2luZm9fc2NoZW1hX3RhYmxlX3NpemUgc2NoZW1hIiwgInRhYmxlX25hbWUiOiAidGFibGVzaXplIn0sIHsiZmllbGRzIjogW3sibmFtZSI6ICJteXNxbF9pbmZvX3NjaGVtYV90YWJsZV92ZXJzaW9uIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsic2NoZW1hIiwgInRhYmxlIiwgInR5cGUiLCAiY3JlYXRlX29wdGlvbnMiLCAiZW5naW5lIiwgInJvd19mb3JtYXQiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInNjaGVtYSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInRhYmxlIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAidHlwZSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImNyZWF0ZV9vcHRpb25zIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZW5naW5lIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicm93X2Zvcm1hdCIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImxlIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9XSwgInRhYmxlX2lkIjogImV4cG9ydGVyX2RibV9teXNxbGRfZXhwb3J0ZXIudGFibGVkZWYiLCAidGFibGVfZGVzYyI6ICJteXNxbF9pbmZvX3NjaGVtYV90YWJsZV92ZXJzaW9uIiwgInRhYmxlX25hbWUiOiAidGFibGVkZWYifSwgeyJmaWVsZHMiOiBbeyJuYW1lIjogIm15c3FsX3ZlcnNpb25faW5mbyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbInZlcnNpb24iLCAidmVyc2lvbl9jb21tZW50IiwgImlubm9kYl92ZXJzaW9uIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJ2ZXJzaW9uIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAidmVyc2lvbl9jb21tZW50IiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5ub2RiX3ZlcnNpb24iLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX1dLCAidGFibGVfaWQiOiAiZXhwb3J0ZXJfZGJtX215c3FsZF9leHBvcnRlci52ZXJzaW9uIiwgInRhYmxlX2Rlc2MiOiAibXlzcWxfdmVyc2lvbl9pbmZvIiwgInRhYmxlX25hbWUiOiAidmVyc2lvbiJ9LCB7ImZpZWxkcyI6IFt7Im5hbWUiOiAibXlzcWxfaW5mb19zY2hlbWFfcHJvY2Vzc2xpc3Rfc2Vjb25kcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNvbW1hbmQiLCAic3RhdGUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2luZm9fc2NoZW1hX3Byb2Nlc3NsaXN0X3RocmVhZHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjb21tYW5kIiwgInN0YXRlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9pbmZvX3NjaGVtYV9wcm9jZXNzbGlzdF9wcm9jZXNzZXNfYnlfdXNlciIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbIm15c3FsX3VzZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX3VzZXIiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJjb21tYW5kIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAic3RhdGUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX1dLCAidGFibGVfaWQiOiAiZXhwb3J0ZXJfZGJtX215c3FsZF9leHBvcnRlci5wbGlzdCIsICJ0YWJsZV9kZXNjIjogIm15c3FsX2luZm9fc2NoZW1hX3Byb2Nlc3NsaXN0IiwgInRhYmxlX25hbWUiOiAicGxpc3QifSwgeyJmaWVsZHMiOiBbeyJuYW1lIjogIm15c3FsX3NsYXZlX3N0YXR1c19hdXRvX3Bvc2l0aW9uIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2hhbm5lbF9uYW1lIiwgImNvbm5lY3Rpb25fbmFtZSIsICJtYXN0ZXJfaG9zdCIsICJtYXN0ZXJfdXVpZCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfc2xhdmVfc3RhdHVzX2Nvbm5lY3RfcmV0cnkiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjaGFubmVsX25hbWUiLCAiY29ubmVjdGlvbl9uYW1lIiwgIm1hc3Rlcl9ob3N0IiwgIm1hc3Rlcl91dWlkIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9zbGF2ZV9zdGF0dXNfZXhlY19tYXN0ZXJfbG9nX3BvcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNoYW5uZWxfbmFtZSIsICJjb25uZWN0aW9uX25hbWUiLCAibWFzdGVyX2hvc3QiLCAibWFzdGVyX3V1aWQiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX3NsYXZlX3N0YXR1c19sYXN0X2Vycm5vIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2hhbm5lbF9uYW1lIiwgImNvbm5lY3Rpb25fbmFtZSIsICJtYXN0ZXJfaG9zdCIsICJtYXN0ZXJfdXVpZCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfc2xhdmVfc3RhdHVzX2xhc3RfaW9fZXJybm8iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjaGFubmVsX25hbWUiLCAiY29ubmVjdGlvbl9uYW1lIiwgIm1hc3Rlcl9ob3N0IiwgIm1hc3Rlcl91dWlkIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9zbGF2ZV9zdGF0dXNfbGFzdF9zcWxfZXJybm8iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjaGFubmVsX25hbWUiLCAiY29ubmVjdGlvbl9uYW1lIiwgIm1hc3Rlcl9ob3N0IiwgIm1hc3Rlcl91dWlkIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9zbGF2ZV9zdGF0dXNfbWFzdGVyX3BvcnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjaGFubmVsX25hbWUiLCAiY29ubmVjdGlvbl9uYW1lIiwgIm1hc3Rlcl9ob3N0IiwgIm1hc3Rlcl91dWlkIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9zbGF2ZV9zdGF0dXNfbWFzdGVyX3JldHJ5X2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2hhbm5lbF9uYW1lIiwgImNvbm5lY3Rpb25fbmFtZSIsICJtYXN0ZXJfaG9zdCIsICJtYXN0ZXJfdXVpZCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfc2xhdmVfc3RhdHVzX21hc3Rlcl9zZXJ2ZXJfaWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjaGFubmVsX25hbWUiLCAiY29ubmVjdGlvbl9uYW1lIiwgIm1hc3Rlcl9ob3N0IiwgIm1hc3Rlcl91dWlkIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9zbGF2ZV9zdGF0dXNfbWFzdGVyX3NzbF9hbGxvd2VkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2hhbm5lbF9uYW1lIiwgImNvbm5lY3Rpb25fbmFtZSIsICJtYXN0ZXJfaG9zdCIsICJtYXN0ZXJfdXVpZCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfc2xhdmVfc3RhdHVzX21hc3Rlcl9zc2xfdmVyaWZ5X3NlcnZlcl9jZXJ0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2hhbm5lbF9uYW1lIiwgImNvbm5lY3Rpb25fbmFtZSIsICJtYXN0ZXJfaG9zdCIsICJtYXN0ZXJfdXVpZCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfc2xhdmVfc3RhdHVzX3JlYWRfbWFzdGVyX2xvZ19wb3MiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjaGFubmVsX25hbWUiLCAiY29ubmVjdGlvbl9uYW1lIiwgIm1hc3Rlcl9ob3N0IiwgIm1hc3Rlcl91dWlkIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9zbGF2ZV9zdGF0dXNfcmVsYXlfbG9nX3BvcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNoYW5uZWxfbmFtZSIsICJjb25uZWN0aW9uX25hbWUiLCAibWFzdGVyX2hvc3QiLCAibWFzdGVyX3V1aWQiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX3NsYXZlX3N0YXR1c19yZWxheV9sb2dfc3BhY2UiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjaGFubmVsX25hbWUiLCAiY29ubmVjdGlvbl9uYW1lIiwgIm1hc3Rlcl9ob3N0IiwgIm1hc3Rlcl91dWlkIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbF9zbGF2ZV9zdGF0dXNfc2Vjb25kc19iZWhpbmRfbWFzdGVyIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2hhbm5lbF9uYW1lIiwgImNvbm5lY3Rpb25fbmFtZSIsICJtYXN0ZXJfaG9zdCIsICJtYXN0ZXJfdXVpZCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfc2xhdmVfc3RhdHVzX3NraXBfY291bnRlciIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNoYW5uZWxfbmFtZSIsICJjb25uZWN0aW9uX25hbWUiLCAibWFzdGVyX2hvc3QiLCAibWFzdGVyX3V1aWQiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX3NsYXZlX3N0YXR1c19zbGF2ZV9pb19ydW5uaW5nIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2hhbm5lbF9uYW1lIiwgImNvbm5lY3Rpb25fbmFtZSIsICJtYXN0ZXJfaG9zdCIsICJtYXN0ZXJfdXVpZCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfc2xhdmVfc3RhdHVzX3NsYXZlX3NxbF9ydW5uaW5nIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2hhbm5lbF9uYW1lIiwgImNvbm5lY3Rpb25fbmFtZSIsICJtYXN0ZXJfaG9zdCIsICJtYXN0ZXJfdXVpZCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfc2xhdmVfc3RhdHVzX3NxbF9kZWxheSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNoYW5uZWxfbmFtZSIsICJjb25uZWN0aW9uX25hbWUiLCAibWFzdGVyX2hvc3QiLCAibWFzdGVyX3V1aWQiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX3NsYXZlX3N0YXR1c191bnRpbF9sb2dfcG9zIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2hhbm5lbF9uYW1lIiwgImNvbm5lY3Rpb25fbmFtZSIsICJtYXN0ZXJfaG9zdCIsICJtYXN0ZXJfdXVpZCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiY2hhbm5lbF9uYW1lIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiY29ubmVjdGlvbl9uYW1lIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibWFzdGVyX2hvc3QiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJtYXN0ZXJfdXVpZCIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfV0sICJ0YWJsZV9pZCI6ICJleHBvcnRlcl9kYm1fbXlzcWxkX2V4cG9ydGVyLnNsYXZlc3RhdCIsICJ0YWJsZV9kZXNjIjogIm15c3FsX3NsYXZlX3N0YXR1cyIsICJ0YWJsZV9uYW1lIjogInNsYXZlc3RhdCJ9LCB7ImZpZWxkcyI6IFt7Im5hbWUiOiAibXlzcWxfZXhwb3J0ZXJfY29sbGVjdG9yX2R1cmF0aW9uX3NlY29uZHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjb2xsZWN0b3IiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FsX2V4cG9ydGVyX2xhc3Rfc2NyYXBlX2Vycm9yIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibXlzcWxfZXhwb3J0ZXJfc2NyYXBlc190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImNvbGxlY3RvciIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfV0sICJ0YWJsZV9pZCI6ICJleHBvcnRlcl9kYm1fbXlzcWxkX2V4cG9ydGVyLmludGVybmFsIiwgInRhYmxlX2Rlc2MiOiAiZXhwb3J0ZXIgY29sbGVjdG9yIiwgInRhYmxlX25hbWUiOiAiaW50ZXJuYWwifV0sICJwbHVnaW5fdHlwZSI6ICJFeHBvcnRlciIsICJvc190eXBlX2xpc3QiOiBbImxpbnV4Il19LCAiY29sbGVjdF90eXBlIjogIkV4cG9ydGVyIiwgInRhcmdldF9ub2RlcyI6IFtdLCAidGFyZ2V0X25vZGVfdHlwZSI6ICJUT1BPIiwgInRhcmdldF9vYmplY3RfdHlwZSI6ICJTRVJWSUNFIn19 \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/collect/0.mysql.dbm_mysqlproxy_exporter.json b/dbm-ui/backend/db_monitor/tpls/collect/0.mysql.dbm_mysqlproxy_exporter.json new file mode 100644 index 0000000000..8d66cf7167 --- /dev/null +++ b/dbm-ui/backend/db_monitor/tpls/collect/0.mysql.dbm_mysqlproxy_exporter.json @@ -0,0 +1,278 @@ +{ + "bk_biz_id": 0, + "plugin_id": "dbm_mysqlproxy_exporter", + "db_type": "mysql", + "details": { + "name": "dbm_mysqlproxy_exporter", + "label": "component", + "params": { + "plugin": { + "-mysqlproxy.file": "/etc/{{ target.service.labels[\"exporter_conf_path\"]}}", + "-web.listen-address": "${host}:${port}", + "服务实例维度注入": { + "app": "app", + "instance": "instance", + "bk_app_code": "app_id", + "cluster_name": "cluster_name", + "cluster_type": "cluster_type", + "instance_bost": "instance_host", + "instance_role": "instance_role", + "cluster_domain": "cluster_domain" + } + }, + "collector": { + "host": "127.0.0.1", + "port": "7100", + "period": 60 + } + }, + "plugin_info": { + "plugin_id": "dbm_mysqlproxy_exporter", + "metric_json": [ + { + "fields": [ + { + "name": "mysqlproxy_max_connid", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysqlproxy_process_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysqlproxy_up", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_cpu_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_max_fds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_open_fds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_resident_memory_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_start_time_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_virtual_memory_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_virtual_memory_max_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_mysqlproxy_exporter.mproxy", + "table_desc": "mysql_proxy_exporter", + "table_name": "mproxy" + }, + { + "fields": [ + { + "name": "go_gc_duration_seconds_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_gc_duration_seconds_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_goroutines", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysqlproxy_exporter_last_scrape_duration_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysqlproxy_exporter_scrapes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "mysqlproxy_exporter_build_info", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "commit_sha", + "golang_version", + "version", + "build_date" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "commit_sha", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "golang_version", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "version", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "build_date", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_mysqlproxy_exporter.internal", + "table_desc": "internal", + "table_name": "internal" + } + ], + "plugin_type": "Exporter", + "os_type_list": [ + "linux" + ] + }, + "collect_type": "Exporter", + "target_nodes": [], + "target_node_type": "TOPO", + "target_object_type": "SERVICE" + } +} diff --git a/dbm-ui/backend/db_monitor/tpls/collect/0.mysql.dbm_mysqlproxy_exporter.tpl64 b/dbm-ui/backend/db_monitor/tpls/collect/0.mysql.dbm_mysqlproxy_exporter.tpl64 deleted file mode 100644 index 7648f624c2..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/collect/0.mysql.dbm_mysqlproxy_exporter.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAicGx1Z2luX2lkIjogImRibV9teXNxbHByb3h5X2V4cG9ydGVyIiwgImRiX3R5cGUiOiAibXlzcWwiLCAiZGV0YWlscyI6IHsibmFtZSI6ICJkYm1fbXlzcWxwcm94eV9leHBvcnRlciIsICJsYWJlbCI6ICJjb21wb25lbnQiLCAicGFyYW1zIjogeyJwbHVnaW4iOiB7Ii1teXNxbHByb3h5LmZpbGUiOiAiL2V0Yy97eyB0YXJnZXQuc2VydmljZS5sYWJlbHNbXCJleHBvcnRlcl9jb25mX3BhdGhcIl19fSIsICItd2ViLmxpc3Rlbi1hZGRyZXNzIjogIiR7aG9zdH06JHtwb3J0fSIsICJcdTY3MGRcdTUyYTFcdTViOWVcdTRmOGJcdTdlZjRcdTVlYTZcdTZjZThcdTUxNjUiOiB7ImFwcCI6ICJhcHAiLCAiaW5zdGFuY2UiOiAiaW5zdGFuY2UiLCAiYmtfYXBwX2NvZGUiOiAiYXBwX2lkIiwgImNsdXN0ZXJfbmFtZSI6ICJjbHVzdGVyX25hbWUiLCAiY2x1c3Rlcl90eXBlIjogImNsdXN0ZXJfdHlwZSIsICJpbnN0YW5jZV9ib3N0IjogImluc3RhbmNlX2hvc3QiLCAiaW5zdGFuY2Vfcm9sZSI6ICJpbnN0YW5jZV9yb2xlIiwgImNsdXN0ZXJfZG9tYWluIjogImNsdXN0ZXJfZG9tYWluIn19LCAiY29sbGVjdG9yIjogeyJob3N0IjogIjEyNy4wLjAuMSIsICJwb3J0IjogIjcxMDAiLCAicGVyaW9kIjogNjB9fSwgInBsdWdpbl9pbmZvIjogeyJwbHVnaW5faWQiOiAiZGJtX215c3FscHJveHlfZXhwb3J0ZXIiLCAibWV0cmljX2pzb24iOiBbeyJmaWVsZHMiOiBbeyJuYW1lIjogIm15c3FscHJveHlfbWF4X2Nvbm5pZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FscHJveHlfcHJvY2Vzc190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm15c3FscHJveHlfdXAiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jZXNzX2NwdV9zZWNvbmRzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY2Vzc19tYXhfZmRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY2Vzc19vcGVuX2ZkcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb2Nlc3NfcmVzaWRlbnRfbWVtb3J5X2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY2Vzc19zdGFydF90aW1lX3NlY29uZHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jZXNzX3ZpcnR1YWxfbWVtb3J5X2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY2Vzc192aXJ0dWFsX21lbW9yeV9tYXhfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX1dLCAidGFibGVfaWQiOiAiZXhwb3J0ZXJfZGJtX215c3FscHJveHlfZXhwb3J0ZXIubXByb3h5IiwgInRhYmxlX2Rlc2MiOiAibXlzcWxfcHJveHlfZXhwb3J0ZXIiLCAidGFibGVfbmFtZSI6ICJtcHJveHkifSwgeyJmaWVsZHMiOiBbeyJuYW1lIjogImdvX2djX2R1cmF0aW9uX3NlY29uZHNfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fZ2NfZHVyYXRpb25fc2Vjb25kc19jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX2dvcm91dGluZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbHByb3h5X2V4cG9ydGVyX2xhc3Rfc2NyYXBlX2R1cmF0aW9uX3NlY29uZHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbHByb3h5X2V4cG9ydGVyX3NjcmFwZXNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJteXNxbHByb3h5X2V4cG9ydGVyX2J1aWxkX2luZm8iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJjb21taXRfc2hhIiwgImdvbGFuZ192ZXJzaW9uIiwgInZlcnNpb24iLCAiYnVpbGRfZGF0ZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiY29tbWl0X3NoYSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvbGFuZ192ZXJzaW9uIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAidmVyc2lvbiIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJ1aWxkX2RhdGUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX1dLCAidGFibGVfaWQiOiAiZXhwb3J0ZXJfZGJtX215c3FscHJveHlfZXhwb3J0ZXIuaW50ZXJuYWwiLCAidGFibGVfZGVzYyI6ICJpbnRlcm5hbCIsICJ0YWJsZV9uYW1lIjogImludGVybmFsIn1dLCAicGx1Z2luX3R5cGUiOiAiRXhwb3J0ZXIiLCAib3NfdHlwZV9saXN0IjogWyJsaW51eCJdfSwgImNvbGxlY3RfdHlwZSI6ICJFeHBvcnRlciIsICJ0YXJnZXRfbm9kZXMiOiBbXSwgInRhcmdldF9ub2RlX3R5cGUiOiAiVE9QTyIsICJ0YXJnZXRfb2JqZWN0X3R5cGUiOiAiU0VSVklDRSJ9fQ== \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/collect/0.pulsar.dbm_pulsarbookkeeper_bkpull.json b/dbm-ui/backend/db_monitor/tpls/collect/0.pulsar.dbm_pulsarbookkeeper_bkpull.json new file mode 100644 index 0000000000..d4a003df95 --- /dev/null +++ b/dbm-ui/backend/db_monitor/tpls/collect/0.pulsar.dbm_pulsarbookkeeper_bkpull.json @@ -0,0 +1,4536 @@ +{ + "bk_biz_id": 0, + "plugin_id": "dbm_pulsarbookkeeper_bkpull", + "db_type": "pulsar", + "details": { + "name": "dbm_pulsarbookkeeper_bkpull", + "label": "component", + "params": { + "plugin": { + "服务实例维度注入": { + "app": "app", + "instance": "instance", + "cluster_name": "cluster_name", + "cluster_type": "cluster_type", + "instance_host": "instance_host", + "instance_port": "instance_port", + "instance_role": "instance_role", + "cluster_domain": "cluster_domain" + } + }, + "collector": { + "period": 60, + "timeout": 60, + "password": "", + "username": "", + "metrics_url": "http://{{ target.host.bk_host_innerip }}:8000/metrics" + }, + "target_node_type": "TOPO", + "target_object_type": "SERVICE" + }, + "plugin_info": { + "plugin_id": "dbm_pulsarbookkeeper_bkpull", + "metric_json": [ + { + "fields": [ + { + "name": "process_cpu_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_start_time_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_open_fds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_max_fds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_virtual_memory_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_resident_memory_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_gc_collection_seconds_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_gc_collection_seconds_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_memory_direct_bytes_max", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_memory_bytes_used", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_memory_bytes_committed", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_memory_bytes_max", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_memory_bytes_init", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_memory_pool_bytes_used", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_memory_pool_bytes_committed", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_memory_pool_bytes_max", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_memory_pool_bytes_init", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_threads_current", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_threads_daemon", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_threads_peak", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_threads_started_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_threads_deadlocked", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_threads_deadlocked_monitor", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "log4j2_appender_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_memory_direct_bytes_used", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_NUM_WRITABLE_BOOKIES_IN_DEFAULT_RACK", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieHighPriorityThread_3181_completed_tasks_4", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_completed_tasks_12", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_total_tasks_14", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieHighPriorityThread_3181_queue_2", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_dir__data_pulsardata_ledgers_usage", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieReadThreadPool_total_tasks_0", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_total_tasks_5", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_completed_tasks_6", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieReadThreadPool_completed_tasks_4", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieReadThreadPool_queue_1", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_queue_10", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_queue_3", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieHighPriorityThread_3181_total_tasks_3", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_completed_tasks_11", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_total_tasks_15", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieHighPriorityThread_3181_completed_tasks_3", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_total_tasks_6", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_ACTIVE_ENTRY_LOG_SPACE_BYTES", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_MEMORY_MAX", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieHighPriorityThread_3181_queue_3", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_ledgers_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_ledger_writable_dirs", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_read_cache_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieReadThreadPool_completed_tasks_3", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_completed_tasks_7", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_queue_4", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieHighPriorityThread_3181_total_tasks_2", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_ADD_ENTRY_BLOCKED", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_queue_11", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieReadThreadPool_queue_2", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieHighPriorityThread_3181_total_tasks_5", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_completed_tasks_10", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_total_tasks_12", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_ledger_dir__data_pulsardata_ledgers_usage", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_write_cache_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_MEMORY_USED", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieHighPriorityThread_3181_completed_tasks_6", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieReadThreadPool_total_tasks_7", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieHighPriorityThread_3181_queue_4", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_completed_tasks_4", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_total_tasks_3", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieReadThreadPool_completed_tasks_6", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_queue_5", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieReadThreadPool_queue_3", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_queue_12", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieHighPriorityThread_3181_total_tasks_4", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_ACTIVE_ENTRY_LOG_COUNT", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_total_tasks_13", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieHighPriorityThread_3181_completed_tasks_5", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieReadThreadPool_total_tasks_6", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieHighPriorityThread_3181_queue_5", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_total_tasks_4", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_completed_tasks_5", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieReadThreadPool_completed_tasks_5", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_queue_6", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_queue_13", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieReadThreadPool_queue_4", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieReadThreadPool_queue_6", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieHighPriorityThread_3181_completed_tasks_0", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieHighPriorityThread_3181_total_tasks_7", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_total_tasks_10", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_writable_dirs", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieReadThreadPool_total_tasks_5", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_total_tasks_9", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_entries_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_read_cache_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_total_tasks_1", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieHighPriorityThread_3181_queue_6", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_completed_tasks_2", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieReadThreadPool_completed_tasks_0", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_queue_7", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieReadThreadPool_queue_5", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_queue_14", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_total_tasks_11", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieReadThreadPool_queue_7", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieHighPriorityThread_3181_total_tasks_6", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_READ_ENTRY_IN_PROGRESS", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_completed_tasks_15", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_queue_8", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieReadThreadPool_total_tasks_4", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieHighPriorityThread_3181_completed_tasks_7", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_queue_15", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieHighPriorityThread_3181_queue_7", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_completed_tasks_3", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_total_tasks_2", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieReadThreadPool_total_tasks_3", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieReadThreadPool_completed_tasks_7", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_queue_0", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieHighPriorityThread_3181_completed_tasks_2", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_completed_tasks_14", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_queue_9", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieHighPriorityThread_3181_queue_0", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_ADD_ENTRY_IN_PROGRESS", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_total_tasks_7", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_completed_tasks_0", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieReadThreadPool_completed_tasks_2", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieReadThreadPool_total_tasks_2", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_completed_tasks_8", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieHighPriorityThread_3181_total_tasks_1", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_queue_1", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieHighPriorityThread_3181_completed_tasks_1", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_SERVER_STATUS", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_completed_tasks_13", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_total_tasks_8", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieHighPriorityThread_3181_queue_1", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_ACTIVE_LEDGER_COUNT", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_total_tasks_0", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_completed_tasks_1", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieReadThreadPool_completed_tasks_1", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieReadThreadPool_total_tasks_1", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_READ_ENTRY_BLOCKED", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieReadThreadPool_queue_0", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_completed_tasks_9", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieHighPriorityThread_3181_total_tasks_0", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_queue_2", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_write_cache_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_READ_BYTES", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_QUEUE_SIZE", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_replication_worker_NUM_ENTRIES_WRITTEN", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_MAJOR_COMPACTION_COUNT", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_READ_ENTRY_DM", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_WRITE_BYTES", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_watcher_state_SyncConnected", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_LAC_UPDATE_MISSES", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_replication_worker_NUM_FULL_OR_PARTIAL_LEDGERS_REPLICATED", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_MINOR_COMPACTION_COUNT", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_LAC_UPDATE_HITS", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_rejected_write_requests", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_NUM_ENSEMBLE_CHANGE", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_NUM_FLUSH_MAX_WAIT", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_throttled_write_requests", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_WRITE_BYTES", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_READ_LAST_ENTRY_NOENTRY_ERROR", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_RECLAIMED_COMPACTION_SPACE_BYTES", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_replication_worker_NUM_DEFER_LEDGER_LOCK_RELEASE_OF_FAILED_LEDGER", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_NUM_FLUSH_EMPTY_QUEUE", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_bookie_watcher_ENSEMBLE_NOT_ADHERING_TO_PLACEMENT_POLICY_COUNTER", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_NUM_FLUSH_MAX_OUTSTANDING_BYTES", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_RECLAIMED_DELETION_SPACE_BYTES", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_SPECULATIVE_READ_COUNT", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_replication_worker_NUM_ENTRIES_UNABLE_TO_READ_FOR_REPLICATION", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_replication_worker_NUM_ENTRIES_READ", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_bookie_watcher_state_SyncConnected", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_FORCE_WRITE_QUEUE_SIZE", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_BOOKIE_FORCE_LEDGER", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_DELETED_LEDGER_COUNT", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_FAILED_TO_RESOLVE_NETWORK_LOCATION_COUNTER", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_ADD_ENTRY_UR", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_WRITE_TIME_OUT_DUE_TO_NOT_ENOUGH_FAULT_DOMAINS", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_CB_QUEUE_SIZE", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_WRITE_DELAYED_DUE_TO_NOT_ENOUGH_FAULT_DOMAINS", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieHighPriorityThread_3181_task_queued_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieHighPriorityThread_3181_task_queued_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_READ_ENTRY_REQUEST_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_READ_ENTRY_REQUEST_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_zk_create_client_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_zk_create_client_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_WRITE_LAC_REQUEST_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_WRITE_LAC_REQUEST_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_bookie_zk_get_acl_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_bookie_zk_get_acl_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_READ_ENTRY_LONG_POLL_READ_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_READ_ENTRY_LONG_POLL_READ_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_bookie_zk_delete_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_bookie_zk_delete_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_GET_LIST_OF_ENTRIES_OF_LEDGER_REQUEST_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_GET_LIST_OF_ENTRIES_OF_LEDGER_REQUEST_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_replication_worker_NUM_BYTES_WRITTEN_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_replication_worker_NUM_BYTES_WRITTEN_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_QUEUE_LATENCY_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_QUEUE_LATENCY_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_QUEUE_LATENCY", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_WRITE_LAC_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_WRITE_LAC_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_read_entry_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_read_entry_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_LEDGER_OPEN_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_LEDGER_OPEN_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_bookie_zk_set_acl_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_bookie_zk_set_acl_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_SYNC_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_SYNC_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_SYNC", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_CLIENT_CHANNEL_WRITE_WAIT_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_CLIENT_CHANNEL_WRITE_WAIT_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_READ_ENTRY_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_READ_ENTRY_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_zk_get_children_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_zk_get_children_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_bookie_zk_get_children_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_bookie_zk_get_children_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_READ_ENTRY_FENCE_REQUEST_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_READ_ENTRY_FENCE_REQUEST_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_CHANNEL_WRITE_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_CHANNEL_WRITE_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_CHANNEL_WRITE", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_ADD_ENTRY_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_ADD_ENTRY_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieReadThreadPool_task_queued_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieReadThreadPool_task_queued_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_READ_LAST_CONFIRMED_AND_ENTRY_RESPONSE_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_READ_LAST_CONFIRMED_AND_ENTRY_RESPONSE_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_zk_multi_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_zk_multi_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieReadThreadPool_task_execution_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieReadThreadPool_task_execution_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_zk_set_data_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_zk_set_data_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_BOOKIE_ADD_ENTRY_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_BOOKIE_ADD_ENTRY_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_BOOKIE_ADD_ENTRY", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_READ_ENTRY_LONG_POLL_WAIT_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_READ_ENTRY_LONG_POLL_WAIT_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_BOOKIE_RECOVERY_ADD_ENTRY_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_BOOKIE_RECOVERY_ADD_ENTRY_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_BOOKIES_LEFT_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_BOOKIES_LEFT_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_readahead_batch_count_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_readahead_batch_count_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_BOOKIE_ADD_ENTRY_BYTES_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_BOOKIE_ADD_ENTRY_BYTES_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_BOOKIE_ADD_ENTRY_BYTES", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_bookie_zk_set_data_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_bookie_zk_set_data_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_read_cache_misses_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_read_cache_misses_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_READ_LAC_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_READ_LAC_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_BOOKIES_JOINED_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_BOOKIES_JOINED_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_bookie_zk_get_data_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_bookie_zk_get_data_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_zk_create_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_zk_create_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_WRITE_LAC_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_WRITE_LAC_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_READ_LAC_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_READ_LAC_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_task_queued_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_task_queued_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_replication_worker_WRITE_DATA_LATENCY_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_replication_worker_WRITE_DATA_LATENCY_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_zk_get_acl_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_zk_get_acl_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_flush_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_flush_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_flush", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_READ_REQUESTS_REORDERED_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_READ_REQUESTS_REORDERED_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_GET_BOOKIE_INFO_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_GET_BOOKIE_INFO_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_BOOKIE_READ_ENTRY_BYTES_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_BOOKIE_READ_ENTRY_BYTES_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_LEDGER_DELETE_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_LEDGER_DELETE_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_task_execution_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_BookKeeperClientWorker_task_execution_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_zk_exists_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_zk_exists_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_READ_ENTRY_FENCE_WAIT_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_READ_ENTRY_FENCE_WAIT_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_FORCE_WRITE_ENQUEUE_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_FORCE_WRITE_ENQUEUE_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_FORCE_WRITE_ENQUEUE", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_CREATION_LATENCY_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_CREATION_LATENCY_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_replication_worker_NUM_BYTES_READ_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_replication_worker_NUM_BYTES_READ_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_ADD_ENTRY_REQUEST_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_ADD_ENTRY_REQUEST_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_ADD_ENTRY_REQUEST", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieHighPriorityThread_3181_task_execution_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_BookieHighPriorityThread_3181_task_execution_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_ADD_ENTRY_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_ADD_ENTRY_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_ADD_ENTRY", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_FORCE_WRITE_BATCH_ENTRIES_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_FORCE_WRITE_BATCH_ENTRIES_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_FORCE_WRITE_BATCH_ENTRIES", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_zk_sync_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_zk_sync_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_FORCE_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_FORCE_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_zk_set_acl_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_zk_set_acl_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_ADD_ENTRY_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_ADD_ENTRY_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_ADD_ENTRY", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_zk_get_data_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_zk_get_data_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_replication_worker_READ_DATA_LATENCY_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_replication_worker_READ_DATA_LATENCY_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_replication_worker_rereplicate_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_replication_worker_rereplicate_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_READ_ENTRY_LONG_POLL_PRE_WAIT_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_READ_ENTRY_LONG_POLL_PRE_WAIT_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_READ_ENTRY_SCHEDULING_DELAY_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_READ_ENTRY_SCHEDULING_DELAY_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_THREAD_RUNTIME_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_THREAD_RUNTIME_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_PROCESS_TIME_LATENCY_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_PROCESS_TIME_LATENCY_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_PROCESS_TIME_LATENCY", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_FLUSH_LATENCY_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_FLUSH_LATENCY_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_FLUSH_LATENCY", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_GET_BOOKIE_INFO_REQUEST_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_GET_BOOKIE_INFO_REQUEST_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_LEDGER_CREATE_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_LEDGER_CREATE_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_BOOKIE_GET_LIST_OF_ENTRIES_OF_LEDGER_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_BOOKIE_GET_LIST_OF_ENTRIES_OF_LEDGER_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_READ_LAST_CONFIRMED_AND_ENTRY_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_READ_LAST_CONFIRMED_AND_ENTRY_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_readahead_batch_size_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_readahead_batch_size_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_READ_ENTRY_LONG_POLL_REQUEST_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_READ_ENTRY_LONG_POLL_REQUEST_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_read_cache_hits_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_read_cache_hits_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_READ_ENTRY_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_READ_ENTRY_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_FORCE_WRITE_GROUPING_COUNT_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_FORCE_WRITE_GROUPING_COUNT_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_FORCE_WRITE_GROUPING_COUNT", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_READ_ENTRY_BLOCKED_WAIT_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_READ_ENTRY_BLOCKED_WAIT_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_BOOKIE_READ_ENTRY_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_BOOKIE_READ_ENTRY_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_FORCE_LEDGER_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_FORCE_LEDGER_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_WRITE_DELAYED_DUE_TO_NOT_ENOUGH_FAULT_DOMAINS_LATENCY_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_WRITE_DELAYED_DUE_TO_NOT_ENOUGH_FAULT_DOMAINS_LATENCY_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_bookie_zk_sync_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_bookie_zk_sync_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_bookie_zk_exists_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_bookie_zk_exists_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_READ_LAC_REQUEST_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_READ_LAC_REQUEST_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_add_entry_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_add_entry_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_add_entry", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_READ_ENTRY_FENCE_READ_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_READ_ENTRY_FENCE_READ_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_bookie_zk_multi_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_bookie_zk_multi_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_FORCE_LEDGER_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_FORCE_LEDGER_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_flush_size_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_flush_size_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_flush_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_bookie_zk_create_client_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_bookie_zk_create_client_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_zk_delete_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_zk_delete_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_LEDGER_RECOVER_ADD_ENTRIES_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_LEDGER_RECOVER_ADD_ENTRIES_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_LEDGER_RECOVER_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_LEDGER_RECOVER_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_FORCE_WRITE_BATCH_BYTES_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_FORCE_WRITE_BATCH_BYTES_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_journal_JOURNAL_FORCE_WRITE_BATCH_BYTES", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_FORCE_LEDGER_REQUEST_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_FORCE_LEDGER_REQUEST_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_GET_LIST_OF_ENTRIES_OF_LEDGER_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_GET_LIST_OF_ENTRIES_OF_LEDGER_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_bookie_watcher_REPLACE_BOOKIE_TIME_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_bookie_watcher_REPLACE_BOOKIE_TIME_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_LEDGER_RECOVER_READ_ENTRIES_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_LEDGER_RECOVER_READ_ENTRIES_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_ADD_ENTRY_BLOCKED_WAIT_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookkeeper_server_ADD_ENTRY_BLOCKED_WAIT_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_bookie_zk_create_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bookie_bookie_zk_create_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_bookie_watcher_NEW_ENSEMBLE_TIME_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "replication_bookkeeper_client_bookkeeper_client_bookie_watcher_NEW_ENSEMBLE_TIME_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "gc", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "area", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "pool", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "level", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "success", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "quantile", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "pushgateway_dbm_pulsarbookkeeper_bkpull.group1", + "table_desc": "默认分组", + "table_name": "group1" + } + ], + "plugin_type": "Pushgateway", + "os_type_list": [ + "linux", + "windows", + "linux_aarch64" + ] + }, + "collect_type": "Pushgateway", + "target_nodes": [], + "target_node_type": "TOPO", + "target_object_type": "SERVICE" + } +} diff --git a/dbm-ui/backend/db_monitor/tpls/collect/0.pulsar.dbm_pulsarbookkeeper_bkpull.tpl64 b/dbm-ui/backend/db_monitor/tpls/collect/0.pulsar.dbm_pulsarbookkeeper_bkpull.tpl64 deleted file mode 100644 index d93363380a..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/collect/0.pulsar.dbm_pulsarbookkeeper_bkpull.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAicGx1Z2luX2lkIjogImRibV9wdWxzYXJib29ra2VlcGVyX2JrcHVsbCIsICJkYl90eXBlIjogInB1bHNhciIsICJkZXRhaWxzIjogeyJuYW1lIjogImRibV9wdWxzYXJib29ra2VlcGVyX2JrcHVsbCIsICJsYWJlbCI6ICJjb21wb25lbnQiLCAicGFyYW1zIjogeyJwbHVnaW4iOiB7Ilx1NjcwZFx1NTJhMVx1NWI5ZVx1NGY4Ylx1N2VmNFx1NWVhNlx1NmNlOFx1NTE2NSI6IHsiYXBwIjogImFwcCIsICJpbnN0YW5jZSI6ICJpbnN0YW5jZSIsICJjbHVzdGVyX25hbWUiOiAiY2x1c3Rlcl9uYW1lIiwgImNsdXN0ZXJfdHlwZSI6ICJjbHVzdGVyX3R5cGUiLCAiaW5zdGFuY2VfaG9zdCI6ICJpbnN0YW5jZV9ob3N0IiwgImluc3RhbmNlX3BvcnQiOiAiaW5zdGFuY2VfcG9ydCIsICJpbnN0YW5jZV9yb2xlIjogImluc3RhbmNlX3JvbGUiLCAiY2x1c3Rlcl9kb21haW4iOiAiY2x1c3Rlcl9kb21haW4ifX0sICJjb2xsZWN0b3IiOiB7InBlcmlvZCI6IDYwLCAidGltZW91dCI6IDYwLCAicGFzc3dvcmQiOiAiIiwgInVzZXJuYW1lIjogIiIsICJtZXRyaWNzX3VybCI6ICJodHRwOi8ve3sgdGFyZ2V0Lmhvc3QuYmtfaG9zdF9pbm5lcmlwIH19OjgwMDAvbWV0cmljcyJ9LCAidGFyZ2V0X25vZGVfdHlwZSI6ICJUT1BPIiwgInRhcmdldF9vYmplY3RfdHlwZSI6ICJTRVJWSUNFIn0sICJwbHVnaW5faW5mbyI6IHsicGx1Z2luX2lkIjogImRibV9wdWxzYXJib29ra2VlcGVyX2JrcHVsbCIsICJtZXRyaWNfanNvbiI6IFt7ImZpZWxkcyI6IFt7Im5hbWUiOiAicHJvY2Vzc19jcHVfc2Vjb25kc190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb2Nlc3Nfc3RhcnRfdGltZV9zZWNvbmRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY2Vzc19vcGVuX2ZkcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb2Nlc3NfbWF4X2ZkcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb2Nlc3NfdmlydHVhbF9tZW1vcnlfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jZXNzX3Jlc2lkZW50X21lbW9yeV9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImp2bV9nY19jb2xsZWN0aW9uX3NlY29uZHNfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJqdm1fZ2NfY29sbGVjdGlvbl9zZWNvbmRzX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImp2bV9tZW1vcnlfZGlyZWN0X2J5dGVzX21heCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImp2bV9tZW1vcnlfYnl0ZXNfdXNlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImp2bV9tZW1vcnlfYnl0ZXNfY29tbWl0dGVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAianZtX21lbW9yeV9ieXRlc19tYXgiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJqdm1fbWVtb3J5X2J5dGVzX2luaXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJqdm1fbWVtb3J5X3Bvb2xfYnl0ZXNfdXNlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImp2bV9tZW1vcnlfcG9vbF9ieXRlc19jb21taXR0ZWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJqdm1fbWVtb3J5X3Bvb2xfYnl0ZXNfbWF4IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAianZtX21lbW9yeV9wb29sX2J5dGVzX2luaXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJqdm1fdGhyZWFkc19jdXJyZW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAianZtX3RocmVhZHNfZGFlbW9uIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAianZtX3RocmVhZHNfcGVhayIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImp2bV90aHJlYWRzX3N0YXJ0ZWRfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJqdm1fdGhyZWFkc19kZWFkbG9ja2VkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAianZtX3RocmVhZHNfZGVhZGxvY2tlZF9tb25pdG9yIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibG9nNGoyX2FwcGVuZGVyX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAianZtX21lbW9yeV9kaXJlY3RfYnl0ZXNfdXNlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X2Jvb2trZWVwZXJfY2xpZW50X05VTV9XUklUQUJMRV9CT09LSUVTX0lOX0RFRkFVTFRfUkFDSyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX0Jvb2tpZUhpZ2hQcmlvcml0eVRocmVhZF8zMTgxX2NvbXBsZXRlZF90YXNrc180IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfQm9va0tlZXBlckNsaWVudFdvcmtlcl9jb21wbGV0ZWRfdGFza3NfMTIiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF9Cb29rS2VlcGVyQ2xpZW50V29ya2VyX3RvdGFsX3Rhc2tzXzE0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfQm9va2llSGlnaFByaW9yaXR5VGhyZWFkXzMxODFfcXVldWVfMiIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9kaXJfX2RhdGFfcHVsc2FyZGF0YV9sZWRnZXJzX3VzYWdlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfQm9va2llUmVhZFRocmVhZFBvb2xfdG90YWxfdGFza3NfMCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X0Jvb2tLZWVwZXJDbGllbnRXb3JrZXJfdG90YWxfdGFza3NfNSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X0Jvb2tLZWVwZXJDbGllbnRXb3JrZXJfY29tcGxldGVkX3Rhc2tzXzYiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29ra2VlcGVyX3NlcnZlcl9Cb29raWVSZWFkVGhyZWFkUG9vbF9jb21wbGV0ZWRfdGFza3NfNCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX0Jvb2tpZVJlYWRUaHJlYWRQb29sX3F1ZXVlXzEiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF9Cb29rS2VlcGVyQ2xpZW50V29ya2VyX3F1ZXVlXzEwIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfQm9va0tlZXBlckNsaWVudFdvcmtlcl9xdWV1ZV8zIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfQm9va2llSGlnaFByaW9yaXR5VGhyZWFkXzMxODFfdG90YWxfdGFza3NfMyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X0Jvb2tLZWVwZXJDbGllbnRXb3JrZXJfY29tcGxldGVkX3Rhc2tzXzExIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfQm9va0tlZXBlckNsaWVudFdvcmtlcl90b3RhbF90YXNrc18xNSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX0Jvb2tpZUhpZ2hQcmlvcml0eVRocmVhZF8zMTgxX2NvbXBsZXRlZF90YXNrc18zIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfQm9va0tlZXBlckNsaWVudFdvcmtlcl90b3RhbF90YXNrc182IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX0FDVElWRV9FTlRSWV9MT0dfU1BBQ0VfQllURVMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29raWVfam91cm5hbF9KT1VSTkFMX01FTU9SWV9NQVgiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29ra2VlcGVyX3NlcnZlcl9Cb29raWVIaWdoUHJpb3JpdHlUaHJlYWRfMzE4MV9xdWV1ZV8zIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX2xlZGdlcnNfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29raWVfbGVkZ2VyX3dyaXRhYmxlX2RpcnMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29raWVfcmVhZF9jYWNoZV9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfQm9va2llUmVhZFRocmVhZFBvb2xfY29tcGxldGVkX3Rhc2tzXzMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF9Cb29rS2VlcGVyQ2xpZW50V29ya2VyX2NvbXBsZXRlZF90YXNrc183IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfQm9va0tlZXBlckNsaWVudFdvcmtlcl9xdWV1ZV80IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfQm9va2llSGlnaFByaW9yaXR5VGhyZWFkXzMxODFfdG90YWxfdGFza3NfMiIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX0FERF9FTlRSWV9CTE9DS0VEIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfQm9va0tlZXBlckNsaWVudFdvcmtlcl9xdWV1ZV8xMSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX0Jvb2tpZVJlYWRUaHJlYWRQb29sX3F1ZXVlXzIiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29ra2VlcGVyX3NlcnZlcl9Cb29raWVIaWdoUHJpb3JpdHlUaHJlYWRfMzE4MV90b3RhbF90YXNrc181IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfQm9va0tlZXBlckNsaWVudFdvcmtlcl9jb21wbGV0ZWRfdGFza3NfMTAiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF9Cb29rS2VlcGVyQ2xpZW50V29ya2VyX3RvdGFsX3Rhc2tzXzEyIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX2xlZGdlcl9kaXJfX2RhdGFfcHVsc2FyZGF0YV9sZWRnZXJzX3VzYWdlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX3dyaXRlX2NhY2hlX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX2pvdXJuYWxfSk9VUk5BTF9NRU1PUllfVVNFRCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX0Jvb2tpZUhpZ2hQcmlvcml0eVRocmVhZF8zMTgxX2NvbXBsZXRlZF90YXNrc182IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfQm9va2llUmVhZFRocmVhZFBvb2xfdG90YWxfdGFza3NfNyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX0Jvb2tpZUhpZ2hQcmlvcml0eVRocmVhZF8zMTgxX3F1ZXVlXzQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF9Cb29rS2VlcGVyQ2xpZW50V29ya2VyX2NvbXBsZXRlZF90YXNrc180IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfQm9va0tlZXBlckNsaWVudFdvcmtlcl90b3RhbF90YXNrc18zIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfQm9va2llUmVhZFRocmVhZFBvb2xfY29tcGxldGVkX3Rhc2tzXzYiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF9Cb29rS2VlcGVyQ2xpZW50V29ya2VyX3F1ZXVlXzUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29ra2VlcGVyX3NlcnZlcl9Cb29raWVSZWFkVGhyZWFkUG9vbF9xdWV1ZV8zIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfQm9va0tlZXBlckNsaWVudFdvcmtlcl9xdWV1ZV8xMiIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX0Jvb2tpZUhpZ2hQcmlvcml0eVRocmVhZF8zMTgxX3RvdGFsX3Rhc2tzXzQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29raWVfQUNUSVZFX0VOVFJZX0xPR19DT1VOVCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X0Jvb2tLZWVwZXJDbGllbnRXb3JrZXJfdG90YWxfdGFza3NfMTMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29ra2VlcGVyX3NlcnZlcl9Cb29raWVIaWdoUHJpb3JpdHlUaHJlYWRfMzE4MV9jb21wbGV0ZWRfdGFza3NfNSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX0Jvb2tpZVJlYWRUaHJlYWRQb29sX3RvdGFsX3Rhc2tzXzYiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29ra2VlcGVyX3NlcnZlcl9Cb29raWVIaWdoUHJpb3JpdHlUaHJlYWRfMzE4MV9xdWV1ZV81IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfQm9va0tlZXBlckNsaWVudFdvcmtlcl90b3RhbF90YXNrc180IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfQm9va0tlZXBlckNsaWVudFdvcmtlcl9jb21wbGV0ZWRfdGFza3NfNSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX0Jvb2tpZVJlYWRUaHJlYWRQb29sX2NvbXBsZXRlZF90YXNrc181IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfQm9va0tlZXBlckNsaWVudFdvcmtlcl9xdWV1ZV82IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfQm9va0tlZXBlckNsaWVudFdvcmtlcl9xdWV1ZV8xMyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX0Jvb2tpZVJlYWRUaHJlYWRQb29sX3F1ZXVlXzQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29ra2VlcGVyX3NlcnZlcl9Cb29raWVSZWFkVGhyZWFkUG9vbF9xdWV1ZV82IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfQm9va2llSGlnaFByaW9yaXR5VGhyZWFkXzMxODFfY29tcGxldGVkX3Rhc2tzXzAiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29ra2VlcGVyX3NlcnZlcl9Cb29raWVIaWdoUHJpb3JpdHlUaHJlYWRfMzE4MV90b3RhbF90YXNrc183IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfQm9va0tlZXBlckNsaWVudFdvcmtlcl90b3RhbF90YXNrc18xMCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV93cml0YWJsZV9kaXJzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfQm9va2llUmVhZFRocmVhZFBvb2xfdG90YWxfdGFza3NfNSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X0Jvb2tLZWVwZXJDbGllbnRXb3JrZXJfdG90YWxfdGFza3NfOSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9lbnRyaWVzX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX3JlYWRfY2FjaGVfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF9Cb29rS2VlcGVyQ2xpZW50V29ya2VyX3RvdGFsX3Rhc2tzXzEiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29ra2VlcGVyX3NlcnZlcl9Cb29raWVIaWdoUHJpb3JpdHlUaHJlYWRfMzE4MV9xdWV1ZV82IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfQm9va0tlZXBlckNsaWVudFdvcmtlcl9jb21wbGV0ZWRfdGFza3NfMiIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX0Jvb2tpZVJlYWRUaHJlYWRQb29sX2NvbXBsZXRlZF90YXNrc18wIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfQm9va0tlZXBlckNsaWVudFdvcmtlcl9xdWV1ZV83IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfQm9va2llUmVhZFRocmVhZFBvb2xfcXVldWVfNSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X0Jvb2tLZWVwZXJDbGllbnRXb3JrZXJfcXVldWVfMTQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF9Cb29rS2VlcGVyQ2xpZW50V29ya2VyX3RvdGFsX3Rhc2tzXzExIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfQm9va2llUmVhZFRocmVhZFBvb2xfcXVldWVfNyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX0Jvb2tpZUhpZ2hQcmlvcml0eVRocmVhZF8zMTgxX3RvdGFsX3Rhc2tzXzYiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29ra2VlcGVyX3NlcnZlcl9SRUFEX0VOVFJZX0lOX1BST0dSRVNTIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfQm9va0tlZXBlckNsaWVudFdvcmtlcl9jb21wbGV0ZWRfdGFza3NfMTUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF9Cb29rS2VlcGVyQ2xpZW50V29ya2VyX3F1ZXVlXzgiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29ra2VlcGVyX3NlcnZlcl9Cb29raWVSZWFkVGhyZWFkUG9vbF90b3RhbF90YXNrc180IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfQm9va2llSGlnaFByaW9yaXR5VGhyZWFkXzMxODFfY29tcGxldGVkX3Rhc2tzXzciLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF9Cb29rS2VlcGVyQ2xpZW50V29ya2VyX3F1ZXVlXzE1IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfQm9va2llSGlnaFByaW9yaXR5VGhyZWFkXzMxODFfcXVldWVfNyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X0Jvb2tLZWVwZXJDbGllbnRXb3JrZXJfY29tcGxldGVkX3Rhc2tzXzMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF9Cb29rS2VlcGVyQ2xpZW50V29ya2VyX3RvdGFsX3Rhc2tzXzIiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29ra2VlcGVyX3NlcnZlcl9Cb29raWVSZWFkVGhyZWFkUG9vbF90b3RhbF90YXNrc18zIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfQm9va2llUmVhZFRocmVhZFBvb2xfY29tcGxldGVkX3Rhc2tzXzciLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF9Cb29rS2VlcGVyQ2xpZW50V29ya2VyX3F1ZXVlXzAiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29ra2VlcGVyX3NlcnZlcl9Cb29raWVIaWdoUHJpb3JpdHlUaHJlYWRfMzE4MV9jb21wbGV0ZWRfdGFza3NfMiIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X0Jvb2tLZWVwZXJDbGllbnRXb3JrZXJfY29tcGxldGVkX3Rhc2tzXzE0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfQm9va0tlZXBlckNsaWVudFdvcmtlcl9xdWV1ZV85IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfQm9va2llSGlnaFByaW9yaXR5VGhyZWFkXzMxODFfcXVldWVfMCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX0FERF9FTlRSWV9JTl9QUk9HUkVTUyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X0Jvb2tLZWVwZXJDbGllbnRXb3JrZXJfdG90YWxfdGFza3NfNyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X0Jvb2tLZWVwZXJDbGllbnRXb3JrZXJfY29tcGxldGVkX3Rhc2tzXzAiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29ra2VlcGVyX3NlcnZlcl9Cb29raWVSZWFkVGhyZWFkUG9vbF9jb21wbGV0ZWRfdGFza3NfMiIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX0Jvb2tpZVJlYWRUaHJlYWRQb29sX3RvdGFsX3Rhc2tzXzIiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF9Cb29rS2VlcGVyQ2xpZW50V29ya2VyX2NvbXBsZXRlZF90YXNrc184IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfQm9va2llSGlnaFByaW9yaXR5VGhyZWFkXzMxODFfdG90YWxfdGFza3NfMSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X0Jvb2tLZWVwZXJDbGllbnRXb3JrZXJfcXVldWVfMSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX0Jvb2tpZUhpZ2hQcmlvcml0eVRocmVhZF8zMTgxX2NvbXBsZXRlZF90YXNrc18xIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX1NFUlZFUl9TVEFUVVMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF9Cb29rS2VlcGVyQ2xpZW50V29ya2VyX2NvbXBsZXRlZF90YXNrc18xMyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X0Jvb2tLZWVwZXJDbGllbnRXb3JrZXJfdG90YWxfdGFza3NfOCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX0Jvb2tpZUhpZ2hQcmlvcml0eVRocmVhZF8zMTgxX3F1ZXVlXzEiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29raWVfQUNUSVZFX0xFREdFUl9DT1VOVCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X0Jvb2tLZWVwZXJDbGllbnRXb3JrZXJfdG90YWxfdGFza3NfMCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X0Jvb2tLZWVwZXJDbGllbnRXb3JrZXJfY29tcGxldGVkX3Rhc2tzXzEiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29ra2VlcGVyX3NlcnZlcl9Cb29raWVSZWFkVGhyZWFkUG9vbF9jb21wbGV0ZWRfdGFza3NfMSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX0Jvb2tpZVJlYWRUaHJlYWRQb29sX3RvdGFsX3Rhc2tzXzEiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29ra2VlcGVyX3NlcnZlcl9SRUFEX0VOVFJZX0JMT0NLRUQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29ra2VlcGVyX3NlcnZlcl9Cb29raWVSZWFkVGhyZWFkUG9vbF9xdWV1ZV8wIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfQm9va0tlZXBlckNsaWVudFdvcmtlcl9jb21wbGV0ZWRfdGFza3NfOSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX0Jvb2tpZUhpZ2hQcmlvcml0eVRocmVhZF8zMTgxX3RvdGFsX3Rhc2tzXzAiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF9Cb29rS2VlcGVyQ2xpZW50V29ya2VyX3F1ZXVlXzIiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29raWVfd3JpdGVfY2FjaGVfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9SRUFEX0JZVEVTIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX2pvdXJuYWxfSk9VUk5BTF9RVUVVRV9TSVpFIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fcmVwbGljYXRpb25fd29ya2VyX05VTV9FTlRSSUVTX1dSSVRURU4iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29raWVfTUFKT1JfQ09NUEFDVElPTl9DT1VOVCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X2Jvb2trZWVwZXJfY2xpZW50X1JFQURfRU5UUllfRE0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29raWVfam91cm5hbF9KT1VSTkFMX1dSSVRFX0JZVEVTIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfd2F0Y2hlcl9zdGF0ZV9TeW5jQ29ubmVjdGVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfYm9va2tlZXBlcl9jbGllbnRfTEFDX1VQREFURV9NSVNTRVMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9yZXBsaWNhdGlvbl93b3JrZXJfTlVNX0ZVTExfT1JfUEFSVElBTF9MRURHRVJTX1JFUExJQ0FURUQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29raWVfTUlOT1JfQ09NUEFDVElPTl9DT1VOVCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X2Jvb2trZWVwZXJfY2xpZW50X0xBQ19VUERBVEVfSElUUyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9yZWplY3RlZF93cml0ZV9yZXF1ZXN0cyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X2Jvb2trZWVwZXJfY2xpZW50X05VTV9FTlNFTUJMRV9DSEFOR0UiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29raWVfam91cm5hbF9KT1VSTkFMX05VTV9GTFVTSF9NQVhfV0FJVCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV90aHJvdHRsZWRfd3JpdGVfcmVxdWVzdHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29raWVfV1JJVEVfQllURVMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29ra2VlcGVyX3NlcnZlcl9SRUFEX0xBU1RfRU5UUllfTk9FTlRSWV9FUlJPUiIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9SRUNMQUlNRURfQ09NUEFDVElPTl9TUEFDRV9CWVRFUyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX3JlcGxpY2F0aW9uX3dvcmtlcl9OVU1fREVGRVJfTEVER0VSX0xPQ0tfUkVMRUFTRV9PRl9GQUlMRURfTEVER0VSIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX2pvdXJuYWxfSk9VUk5BTF9OVU1fRkxVU0hfRU1QVFlfUVVFVUUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF9ib29ra2VlcGVyX2NsaWVudF9ib29raWVfd2F0Y2hlcl9FTlNFTUJMRV9OT1RfQURIRVJJTkdfVE9fUExBQ0VNRU5UX1BPTElDWV9DT1VOVEVSIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX2pvdXJuYWxfSk9VUk5BTF9OVU1fRkxVU0hfTUFYX09VVFNUQU5ESU5HX0JZVEVTIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX1JFQ0xBSU1FRF9ERUxFVElPTl9TUEFDRV9CWVRFUyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X2Jvb2trZWVwZXJfY2xpZW50X1NQRUNVTEFUSVZFX1JFQURfQ09VTlQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9yZXBsaWNhdGlvbl93b3JrZXJfTlVNX0VOVFJJRVNfVU5BQkxFX1RPX1JFQURfRk9SX1JFUExJQ0FUSU9OIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fcmVwbGljYXRpb25fd29ya2VyX05VTV9FTlRSSUVTX1JFQUQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29raWVfYm9va2llX3dhdGNoZXJfc3RhdGVfU3luY0Nvbm5lY3RlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9qb3VybmFsX0pPVVJOQUxfRk9SQ0VfV1JJVEVfUVVFVUVfU0laRSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9CT09LSUVfRk9SQ0VfTEVER0VSIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX0RFTEVURURfTEVER0VSX0NPVU5UIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfYm9va2tlZXBlcl9jbGllbnRfRkFJTEVEX1RPX1JFU09MVkVfTkVUV09SS19MT0NBVElPTl9DT1VOVEVSIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfYm9va2tlZXBlcl9jbGllbnRfQUREX0VOVFJZX1VSIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfYm9va2tlZXBlcl9jbGllbnRfV1JJVEVfVElNRV9PVVRfRFVFX1RPX05PVF9FTk9VR0hfRkFVTFRfRE9NQUlOUyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9qb3VybmFsX0pPVVJOQUxfQ0JfUVVFVUVfU0laRSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X2Jvb2trZWVwZXJfY2xpZW50X1dSSVRFX0RFTEFZRURfRFVFX1RPX05PVF9FTk9VR0hfRkFVTFRfRE9NQUlOUyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX0Jvb2tpZUhpZ2hQcmlvcml0eVRocmVhZF8zMTgxX3Rhc2tfcXVldWVkX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfQm9va2llSGlnaFByaW9yaXR5VGhyZWFkXzMxODFfdGFza19xdWV1ZWRfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfUkVBRF9FTlRSWV9SRVFVRVNUX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfUkVBRF9FTlRSWV9SRVFVRVNUX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X3prX2NyZWF0ZV9jbGllbnRfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF96a19jcmVhdGVfY2xpZW50X3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX1dSSVRFX0xBQ19SRVFVRVNUX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfV1JJVEVfTEFDX1JFUVVFU1Rfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX2Jvb2tpZV96a19nZXRfYWNsX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX2Jvb2tpZV96a19nZXRfYWNsX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX1JFQURfRU5UUllfTE9OR19QT0xMX1JFQURfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29ra2VlcGVyX3NlcnZlcl9SRUFEX0VOVFJZX0xPTkdfUE9MTF9SRUFEX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9ib29raWVfemtfZGVsZXRlX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX2Jvb2tpZV96a19kZWxldGVfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfR0VUX0xJU1RfT0ZfRU5UUklFU19PRl9MRURHRVJfUkVRVUVTVF9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX0dFVF9MSVNUX09GX0VOVFJJRVNfT0ZfTEVER0VSX1JFUVVFU1Rfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fcmVwbGljYXRpb25fd29ya2VyX05VTV9CWVRFU19XUklUVEVOX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fcmVwbGljYXRpb25fd29ya2VyX05VTV9CWVRFU19XUklUVEVOX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9qb3VybmFsX0pPVVJOQUxfUVVFVUVfTEFURU5DWV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9qb3VybmFsX0pPVVJOQUxfUVVFVUVfTEFURU5DWV9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29raWVfam91cm5hbF9KT1VSTkFMX1FVRVVFX0xBVEVOQ1kiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF9ib29ra2VlcGVyX2NsaWVudF9XUklURV9MQUNfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF9ib29ra2VlcGVyX2NsaWVudF9XUklURV9MQUNfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX3JlYWRfZW50cnlfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29raWVfcmVhZF9lbnRyeV9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF9ib29ra2VlcGVyX2NsaWVudF9MRURHRVJfT1BFTl9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X2Jvb2trZWVwZXJfY2xpZW50X0xFREdFUl9PUEVOX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9ib29raWVfemtfc2V0X2FjbF9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9ib29raWVfemtfc2V0X2FjbF9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29raWVfam91cm5hbF9KT1VSTkFMX1NZTkNfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29raWVfam91cm5hbF9KT1VSTkFMX1NZTkNfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX2pvdXJuYWxfSk9VUk5BTF9TWU5DIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfYm9va2tlZXBlcl9jbGllbnRfQ0xJRU5UX0NIQU5ORUxfV1JJVEVfV0FJVF9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X2Jvb2trZWVwZXJfY2xpZW50X0NMSUVOVF9DSEFOTkVMX1dSSVRFX1dBSVRfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfUkVBRF9FTlRSWV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX1JFQURfRU5UUllfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfemtfZ2V0X2NoaWxkcmVuX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfemtfZ2V0X2NoaWxkcmVuX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9ib29raWVfemtfZ2V0X2NoaWxkcmVuX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX2Jvb2tpZV96a19nZXRfY2hpbGRyZW5fc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfUkVBRF9FTlRSWV9GRU5DRV9SRVFVRVNUX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfUkVBRF9FTlRSWV9GRU5DRV9SRVFVRVNUX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX0NIQU5ORUxfV1JJVEVfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29ra2VlcGVyX3NlcnZlcl9DSEFOTkVMX1dSSVRFX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX0NIQU5ORUxfV1JJVEUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF9ib29ra2VlcGVyX2NsaWVudF9BRERfRU5UUllfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF9ib29ra2VlcGVyX2NsaWVudF9BRERfRU5UUllfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfQm9va2llUmVhZFRocmVhZFBvb2xfdGFza19xdWV1ZWRfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29ra2VlcGVyX3NlcnZlcl9Cb29raWVSZWFkVGhyZWFkUG9vbF90YXNrX3F1ZXVlZF9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF9ib29ra2VlcGVyX2NsaWVudF9SRUFEX0xBU1RfQ09ORklSTUVEX0FORF9FTlRSWV9SRVNQT05TRV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X2Jvb2trZWVwZXJfY2xpZW50X1JFQURfTEFTVF9DT05GSVJNRURfQU5EX0VOVFJZX1JFU1BPTlNFX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X3prX211bHRpX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfemtfbXVsdGlfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfQm9va2llUmVhZFRocmVhZFBvb2xfdGFza19leGVjdXRpb25fY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29ra2VlcGVyX3NlcnZlcl9Cb29raWVSZWFkVGhyZWFkUG9vbF90YXNrX2V4ZWN1dGlvbl9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF96a19zZXRfZGF0YV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X3prX3NldF9kYXRhX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9CT09LSUVfQUREX0VOVFJZX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX0JPT0tJRV9BRERfRU5UUllfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX0JPT0tJRV9BRERfRU5UUlkiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29ra2VlcGVyX3NlcnZlcl9SRUFEX0VOVFJZX0xPTkdfUE9MTF9XQUlUX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfUkVBRF9FTlRSWV9MT05HX1BPTExfV0FJVF9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29raWVfQk9PS0lFX1JFQ09WRVJZX0FERF9FTlRSWV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9CT09LSUVfUkVDT1ZFUllfQUREX0VOVFJZX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X2Jvb2trZWVwZXJfY2xpZW50X0JPT0tJRVNfTEVGVF9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X2Jvb2trZWVwZXJfY2xpZW50X0JPT0tJRVNfTEVGVF9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29raWVfcmVhZGFoZWFkX2JhdGNoX2NvdW50X2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX3JlYWRhaGVhZF9iYXRjaF9jb3VudF9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29raWVfQk9PS0lFX0FERF9FTlRSWV9CWVRFU19jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9CT09LSUVfQUREX0VOVFJZX0JZVEVTX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9CT09LSUVfQUREX0VOVFJZX0JZVEVTIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX2Jvb2tpZV96a19zZXRfZGF0YV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9ib29raWVfemtfc2V0X2RhdGFfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX3JlYWRfY2FjaGVfbWlzc2VzX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX3JlYWRfY2FjaGVfbWlzc2VzX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X2Jvb2trZWVwZXJfY2xpZW50X1JFQURfTEFDX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfYm9va2tlZXBlcl9jbGllbnRfUkVBRF9MQUNfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfYm9va2tlZXBlcl9jbGllbnRfQk9PS0lFU19KT0lORURfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF9ib29ra2VlcGVyX2NsaWVudF9CT09LSUVTX0pPSU5FRF9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29raWVfYm9va2llX3prX2dldF9kYXRhX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX2Jvb2tpZV96a19nZXRfZGF0YV9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF96a19jcmVhdGVfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF96a19jcmVhdGVfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfV1JJVEVfTEFDX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfV1JJVEVfTEFDX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX1JFQURfTEFDX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfUkVBRF9MQUNfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfQm9va0tlZXBlckNsaWVudFdvcmtlcl90YXNrX3F1ZXVlZF9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X0Jvb2tLZWVwZXJDbGllbnRXb3JrZXJfdGFza19xdWV1ZWRfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fcmVwbGljYXRpb25fd29ya2VyX1dSSVRFX0RBVEFfTEFURU5DWV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX3JlcGxpY2F0aW9uX3dvcmtlcl9XUklURV9EQVRBX0xBVEVOQ1lfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfemtfZ2V0X2FjbF9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X3prX2dldF9hY2xfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX2ZsdXNoX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX2ZsdXNoX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9mbHVzaCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X2Jvb2trZWVwZXJfY2xpZW50X1JFQURfUkVRVUVTVFNfUkVPUkRFUkVEX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfYm9va2tlZXBlcl9jbGllbnRfUkVBRF9SRVFVRVNUU19SRU9SREVSRURfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfR0VUX0JPT0tJRV9JTkZPX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfR0VUX0JPT0tJRV9JTkZPX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9CT09LSUVfUkVBRF9FTlRSWV9CWVRFU19jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9CT09LSUVfUkVBRF9FTlRSWV9CWVRFU19zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF9ib29ra2VlcGVyX2NsaWVudF9MRURHRVJfREVMRVRFX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfYm9va2tlZXBlcl9jbGllbnRfTEVER0VSX0RFTEVURV9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF9Cb29rS2VlcGVyQ2xpZW50V29ya2VyX3Rhc2tfZXhlY3V0aW9uX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfQm9va0tlZXBlckNsaWVudFdvcmtlcl90YXNrX2V4ZWN1dGlvbl9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF96a19leGlzdHNfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF96a19leGlzdHNfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfUkVBRF9FTlRSWV9GRU5DRV9XQUlUX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfUkVBRF9FTlRSWV9GRU5DRV9XQUlUX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9qb3VybmFsX0pPVVJOQUxfRk9SQ0VfV1JJVEVfRU5RVUVVRV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9qb3VybmFsX0pPVVJOQUxfRk9SQ0VfV1JJVEVfRU5RVUVVRV9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29raWVfam91cm5hbF9KT1VSTkFMX0ZPUkNFX1dSSVRFX0VOUVVFVUUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29raWVfam91cm5hbF9KT1VSTkFMX0NSRUFUSU9OX0xBVEVOQ1lfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29raWVfam91cm5hbF9KT1VSTkFMX0NSRUFUSU9OX0xBVEVOQ1lfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fcmVwbGljYXRpb25fd29ya2VyX05VTV9CWVRFU19SRUFEX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fcmVwbGljYXRpb25fd29ya2VyX05VTV9CWVRFU19SRUFEX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX0FERF9FTlRSWV9SRVFVRVNUX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfQUREX0VOVFJZX1JFUVVFU1Rfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfQUREX0VOVFJZX1JFUVVFU1QiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29ra2VlcGVyX3NlcnZlcl9Cb29raWVIaWdoUHJpb3JpdHlUaHJlYWRfMzE4MV90YXNrX2V4ZWN1dGlvbl9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX0Jvb2tpZUhpZ2hQcmlvcml0eVRocmVhZF8zMTgxX3Rhc2tfZXhlY3V0aW9uX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9qb3VybmFsX0pPVVJOQUxfQUREX0VOVFJZX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX2pvdXJuYWxfSk9VUk5BTF9BRERfRU5UUllfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX2pvdXJuYWxfSk9VUk5BTF9BRERfRU5UUlkiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29raWVfam91cm5hbF9KT1VSTkFMX0ZPUkNFX1dSSVRFX0JBVENIX0VOVFJJRVNfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29raWVfam91cm5hbF9KT1VSTkFMX0ZPUkNFX1dSSVRFX0JBVENIX0VOVFJJRVNfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX2pvdXJuYWxfSk9VUk5BTF9GT1JDRV9XUklURV9CQVRDSF9FTlRSSUVTIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfemtfc3luY19jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X3prX3N5bmNfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfYm9va2tlZXBlcl9jbGllbnRfRk9SQ0VfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF9ib29ra2VlcGVyX2NsaWVudF9GT1JDRV9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF96a19zZXRfYWNsX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfemtfc2V0X2FjbF9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29ra2VlcGVyX3NlcnZlcl9BRERfRU5UUllfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29ra2VlcGVyX3NlcnZlcl9BRERfRU5UUllfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfQUREX0VOVFJZIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfemtfZ2V0X2RhdGFfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF96a19nZXRfZGF0YV9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9yZXBsaWNhdGlvbl93b3JrZXJfUkVBRF9EQVRBX0xBVEVOQ1lfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9yZXBsaWNhdGlvbl93b3JrZXJfUkVBRF9EQVRBX0xBVEVOQ1lfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fcmVwbGljYXRpb25fd29ya2VyX3JlcmVwbGljYXRlX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fcmVwbGljYXRpb25fd29ya2VyX3JlcmVwbGljYXRlX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX1JFQURfRU5UUllfTE9OR19QT0xMX1BSRV9XQUlUX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfUkVBRF9FTlRSWV9MT05HX1BPTExfUFJFX1dBSVRfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfUkVBRF9FTlRSWV9TQ0hFRFVMSU5HX0RFTEFZX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfUkVBRF9FTlRSWV9TQ0hFRFVMSU5HX0RFTEFZX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9USFJFQURfUlVOVElNRV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9USFJFQURfUlVOVElNRV9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29raWVfam91cm5hbF9KT1VSTkFMX1BST0NFU1NfVElNRV9MQVRFTkNZX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX2pvdXJuYWxfSk9VUk5BTF9QUk9DRVNTX1RJTUVfTEFURU5DWV9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29raWVfam91cm5hbF9KT1VSTkFMX1BST0NFU1NfVElNRV9MQVRFTkNZIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX2pvdXJuYWxfSk9VUk5BTF9GTFVTSF9MQVRFTkNZX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX2pvdXJuYWxfSk9VUk5BTF9GTFVTSF9MQVRFTkNZX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9qb3VybmFsX0pPVVJOQUxfRkxVU0hfTEFURU5DWSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX0dFVF9CT09LSUVfSU5GT19SRVFVRVNUX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfR0VUX0JPT0tJRV9JTkZPX1JFUVVFU1Rfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfYm9va2tlZXBlcl9jbGllbnRfTEVER0VSX0NSRUFURV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X2Jvb2trZWVwZXJfY2xpZW50X0xFREdFUl9DUkVBVEVfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX0JPT0tJRV9HRVRfTElTVF9PRl9FTlRSSUVTX09GX0xFREdFUl9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9CT09LSUVfR0VUX0xJU1RfT0ZfRU5UUklFU19PRl9MRURHRVJfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfYm9va2tlZXBlcl9jbGllbnRfUkVBRF9MQVNUX0NPTkZJUk1FRF9BTkRfRU5UUllfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF9ib29ra2VlcGVyX2NsaWVudF9SRUFEX0xBU1RfQ09ORklSTUVEX0FORF9FTlRSWV9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29raWVfcmVhZGFoZWFkX2JhdGNoX3NpemVfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29raWVfcmVhZGFoZWFkX2JhdGNoX3NpemVfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfUkVBRF9FTlRSWV9MT05HX1BPTExfUkVRVUVTVF9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX1JFQURfRU5UUllfTE9OR19QT0xMX1JFUVVFU1Rfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX3JlYWRfY2FjaGVfaGl0c19jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9yZWFkX2NhY2hlX2hpdHNfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfYm9va2tlZXBlcl9jbGllbnRfUkVBRF9FTlRSWV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X2Jvb2trZWVwZXJfY2xpZW50X1JFQURfRU5UUllfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX2pvdXJuYWxfSk9VUk5BTF9GT1JDRV9XUklURV9HUk9VUElOR19DT1VOVF9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9qb3VybmFsX0pPVVJOQUxfRk9SQ0VfV1JJVEVfR1JPVVBJTkdfQ09VTlRfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX2pvdXJuYWxfSk9VUk5BTF9GT1JDRV9XUklURV9HUk9VUElOR19DT1VOVCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX1JFQURfRU5UUllfQkxPQ0tFRF9XQUlUX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfUkVBRF9FTlRSWV9CTE9DS0VEX1dBSVRfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX0JPT0tJRV9SRUFEX0VOVFJZX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX0JPT0tJRV9SRUFEX0VOVFJZX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX0ZPUkNFX0xFREdFUl9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX0ZPUkNFX0xFREdFUl9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF9ib29ra2VlcGVyX2NsaWVudF9XUklURV9ERUxBWUVEX0RVRV9UT19OT1RfRU5PVUdIX0ZBVUxUX0RPTUFJTlNfTEFURU5DWV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X2Jvb2trZWVwZXJfY2xpZW50X1dSSVRFX0RFTEFZRURfRFVFX1RPX05PVF9FTk9VR0hfRkFVTFRfRE9NQUlOU19MQVRFTkNZX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9ib29raWVfemtfc3luY19jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9ib29raWVfemtfc3luY19zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29raWVfYm9va2llX3prX2V4aXN0c19jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9ib29raWVfemtfZXhpc3RzX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX1JFQURfTEFDX1JFUVVFU1RfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29ra2VlcGVyX3NlcnZlcl9SRUFEX0xBQ19SRVFVRVNUX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9hZGRfZW50cnlfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29raWVfYWRkX2VudHJ5X3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9hZGRfZW50cnkiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29ra2VlcGVyX3NlcnZlcl9SRUFEX0VOVFJZX0ZFTkNFX1JFQURfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29ra2VlcGVyX3NlcnZlcl9SRUFEX0VOVFJZX0ZFTkNFX1JFQURfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX2Jvb2tpZV96a19tdWx0aV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9ib29raWVfemtfbXVsdGlfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX2pvdXJuYWxfSk9VUk5BTF9GT1JDRV9MRURHRVJfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29raWVfam91cm5hbF9KT1VSTkFMX0ZPUkNFX0xFREdFUl9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29raWVfZmx1c2hfc2l6ZV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9mbHVzaF9zaXplX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9mbHVzaF9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX2Jvb2tpZV96a19jcmVhdGVfY2xpZW50X2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX2Jvb2tpZV96a19jcmVhdGVfY2xpZW50X3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X3prX2RlbGV0ZV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X3prX2RlbGV0ZV9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF9ib29ra2VlcGVyX2NsaWVudF9MRURHRVJfUkVDT1ZFUl9BRERfRU5UUklFU19jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X2Jvb2trZWVwZXJfY2xpZW50X0xFREdFUl9SRUNPVkVSX0FERF9FTlRSSUVTX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X2Jvb2trZWVwZXJfY2xpZW50X0xFREdFUl9SRUNPVkVSX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfYm9va2tlZXBlcl9jbGllbnRfTEVER0VSX1JFQ09WRVJfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX2pvdXJuYWxfSk9VUk5BTF9GT1JDRV9XUklURV9CQVRDSF9CWVRFU19jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2tpZV9qb3VybmFsX0pPVVJOQUxfRk9SQ0VfV1JJVEVfQkFUQ0hfQllURVNfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX2pvdXJuYWxfSk9VUk5BTF9GT1JDRV9XUklURV9CQVRDSF9CWVRFUyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX0ZPUkNFX0xFREdFUl9SRVFVRVNUX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfRk9SQ0VfTEVER0VSX1JFUVVFU1Rfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfR0VUX0xJU1RfT0ZfRU5UUklFU19PRl9MRURHRVJfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29ra2VlcGVyX3NlcnZlcl9HRVRfTElTVF9PRl9FTlRSSUVTX09GX0xFREdFUl9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF9ib29ra2VlcGVyX2NsaWVudF9ib29raWVfd2F0Y2hlcl9SRVBMQUNFX0JPT0tJRV9USU1FX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVwbGljYXRpb25fYm9va2tlZXBlcl9jbGllbnRfYm9va2tlZXBlcl9jbGllbnRfYm9va2llX3dhdGNoZXJfUkVQTEFDRV9CT09LSUVfVElNRV9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF9ib29ra2VlcGVyX2NsaWVudF9MRURHRVJfUkVDT1ZFUl9SRUFEX0VOVFJJRVNfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF9ib29ra2VlcGVyX2NsaWVudF9MRURHRVJfUkVDT1ZFUl9SRUFEX0VOVFJJRVNfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2tlZXBlcl9zZXJ2ZXJfQUREX0VOVFJZX0JMT0NLRURfV0FJVF9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImJvb2trZWVwZXJfc2VydmVyX0FERF9FTlRSWV9CTE9DS0VEX1dBSVRfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYm9va2llX2Jvb2tpZV96a19jcmVhdGVfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJib29raWVfYm9va2llX3prX2NyZWF0ZV9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXBsaWNhdGlvbl9ib29ra2VlcGVyX2NsaWVudF9ib29ra2VlcGVyX2NsaWVudF9ib29raWVfd2F0Y2hlcl9ORVdfRU5TRU1CTEVfVElNRV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcGxpY2F0aW9uX2Jvb2trZWVwZXJfY2xpZW50X2Jvb2trZWVwZXJfY2xpZW50X2Jvb2tpZV93YXRjaGVyX05FV19FTlNFTUJMRV9USU1FX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdjIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYXJlYSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInBvb2wiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJsZXZlbCIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInN1Y2Nlc3MiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJxdWFudGlsZSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfV0sICJ0YWJsZV9pZCI6ICJwdXNoZ2F0ZXdheV9kYm1fcHVsc2FyYm9va2tlZXBlcl9ia3B1bGwuZ3JvdXAxIiwgInRhYmxlX2Rlc2MiOiAiXHU5ZWQ4XHU4YmE0XHU1MjA2XHU3ZWM0IiwgInRhYmxlX25hbWUiOiAiZ3JvdXAxIn1dLCAicGx1Z2luX3R5cGUiOiAiUHVzaGdhdGV3YXkiLCAib3NfdHlwZV9saXN0IjogWyJsaW51eCIsICJ3aW5kb3dzIiwgImxpbnV4X2FhcmNoNjQiXX0sICJjb2xsZWN0X3R5cGUiOiAiUHVzaGdhdGV3YXkiLCAidGFyZ2V0X25vZGVzIjogW10sICJ0YXJnZXRfbm9kZV90eXBlIjogIlRPUE8iLCAidGFyZ2V0X29iamVjdF90eXBlIjogIlNFUlZJQ0UifX0= \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/collect/0.pulsar.dbm_pulsarbroker_bkpull.json b/dbm-ui/backend/db_monitor/tpls/collect/0.pulsar.dbm_pulsarbroker_bkpull.json new file mode 100644 index 0000000000..e20047ebdd --- /dev/null +++ b/dbm-ui/backend/db_monitor/tpls/collect/0.pulsar.dbm_pulsarbroker_bkpull.json @@ -0,0 +1,2651 @@ +{ + "bk_biz_id": 0, + "plugin_id": "dbm_pulsarbroker_bkpull", + "db_type": "pulsar", + "details": { + "name": "dbm_pulsarbroker_bkpull", + "label": "component", + "params": { + "plugin": { + "服务实例维度注入": { + "app": "app", + "instance": "instance", + "cluster_name": "cluster_name", + "cluster_type": "cluster_type", + "instance_host": "instance_host", + "instance_port": "instance_port", + "instance_role": "instance_role", + "cluster_domain": "cluster_domain" + } + }, + "collector": { + "period": 60, + "timeout": 60, + "password": "", + "username": "", + "metrics_url": "http://{{ target.host.bk_host_innerip }}:8080/metrics/" + }, + "target_node_type": "TOPO", + "target_object_type": "SERVICE" + }, + "plugin_info": { + "plugin_id": "dbm_pulsarbroker_bkpull", + "metric_json": [ + { + "fields": [ + { + "name": "_subscription", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "subscription", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "pulsar_subscription_back_log", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_subscription_back_log_no_delayed", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_subscription_delayed", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_subscription_msg_rate_redeliver", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_subscription_unacked_messages", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_subscription_blocked_on_unacked_messages", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_subscription_msg_rate_out", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_subscription_msg_ack_rate", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_subscription_msg_throughput_out", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_out_bytes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_out_messages_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_subscription_last_expire_timestamp", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_subscription_last_acked_timestamp", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_subscription_last_consumed_flow_timestamp", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_subscription_last_consumed_timestamp", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_subscription_last_mark_delete_advanced_timestamp", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_subscription_msg_rate_expired", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_subscription_total_msg_expired", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_subscription_msg_drop_rate", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_expired_token_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_cpu_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_start_time_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_open_fds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_max_fds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_virtual_memory_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_resident_memory_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_version_info", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_resource_group_aggregate_usage_secs", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_resource_group_aggregate_usage_secs_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_resource_group_aggregate_usage_secs_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_broker_throttled_connections_global_limit", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_web_executor_idle_threads", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_broker_lookup_pending_requests", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_memory_direct_bytes_used", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_web_executor_min_threads", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_info", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_web_executor_max_threads", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_broker_load_manager_bundle_assigment_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_broker_load_manager_bundle_assigment_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_resource_group_calculate_quota_secs", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_resource_group_calculate_quota_secs_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_resource_group_calculate_quota_secs_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_broker_topic_load_pending_requests", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_gc_collection_seconds_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_gc_collection_seconds_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_memory_bytes_used", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_memory_bytes_committed", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_memory_bytes_max", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_memory_bytes_init", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_memory_pool_bytes_used", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_memory_pool_bytes_committed", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_memory_pool_bytes_max", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_memory_pool_bytes_init", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "caffeine_cache_hit_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "caffeine_cache_miss_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "caffeine_cache_requests_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "caffeine_cache_eviction_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "caffeine_cache_eviction_weight", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "caffeine_cache_load_failure_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "caffeine_cache_loads_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "caffeine_cache_estimated_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "caffeine_cache_load_duration_seconds_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "caffeine_cache_load_duration_seconds_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_broker_lookup_failures", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_memory_direct_bytes_max", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_broker_publish_latency_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_broker_publish_latency_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_classes_loaded", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_classes_loaded_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_classes_unloaded_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "log4j2_appender_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "zk_read_latency_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "zk_read_latency_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_buffer_pool_used_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_buffer_pool_capacity_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_buffer_pool_used_buffers", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_broker_lookup_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_broker_lookup_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_threads_current", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_threads_daemon", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_threads_peak", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_threads_started_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_threads_deadlocked", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_threads_deadlocked_monitor", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jetty_requests_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jetty_requests_active", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jetty_requests_active_max", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jetty_request_time_max_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jetty_request_time_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jetty_dispatched_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jetty_dispatched_active", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jetty_dispatched_active_max", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jetty_dispatched_time_max", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jetty_dispatched_time_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jetty_async_requests_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jetty_async_requests_waiting", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jetty_async_requests_waiting_max", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jetty_async_dispatches_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jetty_expires_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jetty_responses_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jetty_stats_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jetty_responses_bytes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "topic_load_times_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "topic_load_times_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "zk_write_latency_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "zk_write_latency_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_web_executor_active_threads", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_authentication_success_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_broker_lookup_answers", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_broker_lookup_redirects", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_broker_throttled_connections", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_expiring_token_minutes_bucket", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_expiring_token_minutes_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_expiring_token_minutes_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_web_executor_current_threads", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_topics_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_subscriptions_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_producers_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_consumers_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_rate_in", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_rate_out", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_throughput_in", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_throughput_out", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_storage_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_storage_logical_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_storage_write_rate", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_storage_read_rate", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_msg_backlog", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_average_msg_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_storage_backlog_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_publish_rate_limit_times", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_storage_offloaded_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_storage_backlog_quota_limit", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_storage_backlog_quota_limit_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_storage_write_latency_le_0_5", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_storage_write_latency_le_1", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_storage_write_latency_le_5", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_storage_write_latency_le_10", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_storage_write_latency_le_20", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_storage_write_latency_le_50", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_storage_write_latency_le_100", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_storage_write_latency_le_200", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_storage_write_latency_le_1000", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_storage_write_latency_overflow", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_storage_write_latency_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_storage_write_latency_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_storage_ledger_write_latency_le_0_5", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_storage_ledger_write_latency_le_1", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_storage_ledger_write_latency_le_5", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_storage_ledger_write_latency_le_10", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_storage_ledger_write_latency_le_20", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_storage_ledger_write_latency_le_50", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_storage_ledger_write_latency_le_100", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_storage_ledger_write_latency_le_200", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_storage_ledger_write_latency_le_1000", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_storage_ledger_write_latency_overflow", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_storage_ledger_write_latency_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_storage_ledger_write_latency_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_entry_size_le_128", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_entry_size_le_512", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_entry_size_le_1_kb", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_entry_size_le_2_kb", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_entry_size_le_4_kb", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_entry_size_le_16_kb", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_entry_size_le_100_kb", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_entry_size_le_1_mb", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_entry_size_le_overflow", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_entry_size_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_entry_size_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_in_bytes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_in_messages_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_ml_cache_evictions", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_ml_cache_hits_rate", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_ml_cache_hits_throughput", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_ml_cache_misses_rate", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_ml_cache_misses_throughput", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_ml_cache_pool_active_allocations", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_ml_cache_pool_active_allocations_huge", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_ml_cache_pool_active_allocations_normal", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_ml_cache_pool_active_allocations_small", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_ml_cache_pool_allocated", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_ml_cache_pool_used", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_ml_cache_used_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_ml_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_ml_AddEntryBytesRate", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_ml_AddEntryErrors", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_ml_AddEntryLatencyBuckets", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_ml_AddEntryLatencyBuckets_OVERFLOW", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_ml_AddEntryMessagesRate", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_ml_AddEntrySucceed", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_ml_AddEntryWithReplicasBytesRate", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_ml_EntrySizeBuckets", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_ml_EntrySizeBuckets_OVERFLOW", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_ml_LedgerAddEntryLatencyBuckets", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_ml_LedgerAddEntryLatencyBuckets_OVERFLOW", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_ml_LedgerSwitchLatencyBuckets", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_ml_LedgerSwitchLatencyBuckets_OVERFLOW", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_ml_MarkDeleteRate", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_ml_NumberOfMessagesInBacklog", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_ml_ReadEntriesBytesRate", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_ml_ReadEntriesErrors", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_ml_ReadEntriesRate", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_ml_ReadEntriesSucceeded", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_ml_StoredMessagesSize", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_active_connections", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_connection_closed_total_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_connection_create_fail_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_connection_create_success_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_connection_created_total_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_lb_bandwidth_in_usage", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_lb_bandwidth_out_usage", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_lb_cpu_usage", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_lb_directMemory_usage", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_lb_memory_usage", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_lb_unload_broker_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_lb_unload_bundle_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pulsar_lb_bundles_split_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "cluster", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "version", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "commit", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "quantile", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "vendor", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "runtime", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "gc", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "area", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "pool", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "cache", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "level", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "code", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "provider_name", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "auth_method", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "le", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "namespace", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "partition", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "topic", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "metric", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "broker", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "pushgateway_dbm_pulsarbroker_bkpull.group1", + "table_desc": "默认分组", + "table_name": "group1" + } + ], + "plugin_type": "Pushgateway", + "os_type_list": [ + "linux", + "windows", + "linux_aarch64" + ] + }, + "collect_type": "Pushgateway", + "target_nodes": [], + "target_node_type": "TOPO", + "target_object_type": "SERVICE" + } +} diff --git a/dbm-ui/backend/db_monitor/tpls/collect/0.pulsar.dbm_pulsarbroker_bkpull.tpl64 b/dbm-ui/backend/db_monitor/tpls/collect/0.pulsar.dbm_pulsarbroker_bkpull.tpl64 deleted file mode 100644 index 2985432597..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/collect/0.pulsar.dbm_pulsarbroker_bkpull.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAicGx1Z2luX2lkIjogImRibV9wdWxzYXJicm9rZXJfYmtwdWxsIiwgImRiX3R5cGUiOiAicHVsc2FyIiwgImRldGFpbHMiOiB7Im5hbWUiOiAiZGJtX3B1bHNhcmJyb2tlcl9ia3B1bGwiLCAibGFiZWwiOiAiY29tcG9uZW50IiwgInBhcmFtcyI6IHsicGx1Z2luIjogeyJcdTY3MGRcdTUyYTFcdTViOWVcdTRmOGJcdTdlZjRcdTVlYTZcdTZjZThcdTUxNjUiOiB7ImFwcCI6ICJhcHAiLCAiaW5zdGFuY2UiOiAiaW5zdGFuY2UiLCAiY2x1c3Rlcl9uYW1lIjogImNsdXN0ZXJfbmFtZSIsICJjbHVzdGVyX3R5cGUiOiAiY2x1c3Rlcl90eXBlIiwgImluc3RhbmNlX2hvc3QiOiAiaW5zdGFuY2VfaG9zdCIsICJpbnN0YW5jZV9wb3J0IjogImluc3RhbmNlX3BvcnQiLCAiaW5zdGFuY2Vfcm9sZSI6ICJpbnN0YW5jZV9yb2xlIiwgImNsdXN0ZXJfZG9tYWluIjogImNsdXN0ZXJfZG9tYWluIn19LCAiY29sbGVjdG9yIjogeyJwZXJpb2QiOiA2MCwgInRpbWVvdXQiOiA2MCwgInBhc3N3b3JkIjogIiIsICJ1c2VybmFtZSI6ICIiLCAibWV0cmljc191cmwiOiAiaHR0cDovL3t7IHRhcmdldC5ob3N0LmJrX2hvc3RfaW5uZXJpcCB9fTo4MDgwL21ldHJpY3MvIn0sICJ0YXJnZXRfbm9kZV90eXBlIjogIlRPUE8iLCAidGFyZ2V0X29iamVjdF90eXBlIjogIlNFUlZJQ0UifSwgInBsdWdpbl9pbmZvIjogeyJwbHVnaW5faWQiOiAiZGJtX3B1bHNhcmJyb2tlcl9ia3B1bGwiLCAibWV0cmljX2pzb24iOiBbeyJmaWVsZHMiOiBbeyJuYW1lIjogIl9zdWJzY3JpcHRpb24iLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAic3Vic2NyaXB0aW9uIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfc3Vic2NyaXB0aW9uX2JhY2tfbG9nIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX3N1YnNjcmlwdGlvbl9iYWNrX2xvZ19ub19kZWxheWVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX3N1YnNjcmlwdGlvbl9kZWxheWVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX3N1YnNjcmlwdGlvbl9tc2dfcmF0ZV9yZWRlbGl2ZXIiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfc3Vic2NyaXB0aW9uX3VuYWNrZWRfbWVzc2FnZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfc3Vic2NyaXB0aW9uX2Jsb2NrZWRfb25fdW5hY2tlZF9tZXNzYWdlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9zdWJzY3JpcHRpb25fbXNnX3JhdGVfb3V0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX3N1YnNjcmlwdGlvbl9tc2dfYWNrX3JhdGUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfc3Vic2NyaXB0aW9uX21zZ190aHJvdWdocHV0X291dCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9vdXRfYnl0ZXNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfb3V0X21lc3NhZ2VzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX3N1YnNjcmlwdGlvbl9sYXN0X2V4cGlyZV90aW1lc3RhbXAiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfc3Vic2NyaXB0aW9uX2xhc3RfYWNrZWRfdGltZXN0YW1wIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX3N1YnNjcmlwdGlvbl9sYXN0X2NvbnN1bWVkX2Zsb3dfdGltZXN0YW1wIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX3N1YnNjcmlwdGlvbl9sYXN0X2NvbnN1bWVkX3RpbWVzdGFtcCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9zdWJzY3JpcHRpb25fbGFzdF9tYXJrX2RlbGV0ZV9hZHZhbmNlZF90aW1lc3RhbXAiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfc3Vic2NyaXB0aW9uX21zZ19yYXRlX2V4cGlyZWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfc3Vic2NyaXB0aW9uX3RvdGFsX21zZ19leHBpcmVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX3N1YnNjcmlwdGlvbl9tc2dfZHJvcF9yYXRlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX2V4cGlyZWRfdG9rZW5fY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jZXNzX2NwdV9zZWNvbmRzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY2Vzc19zdGFydF90aW1lX3NlY29uZHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jZXNzX29wZW5fZmRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY2Vzc19tYXhfZmRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY2Vzc192aXJ0dWFsX21lbW9yeV9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb2Nlc3NfcmVzaWRlbnRfbWVtb3J5X2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX3ZlcnNpb25faW5mbyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9yZXNvdXJjZV9ncm91cF9hZ2dyZWdhdGVfdXNhZ2Vfc2VjcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9yZXNvdXJjZV9ncm91cF9hZ2dyZWdhdGVfdXNhZ2Vfc2Vjc19jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9yZXNvdXJjZV9ncm91cF9hZ2dyZWdhdGVfdXNhZ2Vfc2Vjc19zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfYnJva2VyX3Rocm90dGxlZF9jb25uZWN0aW9uc19nbG9iYWxfbGltaXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfd2ViX2V4ZWN1dG9yX2lkbGVfdGhyZWFkcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9icm9rZXJfbG9va3VwX3BlbmRpbmdfcmVxdWVzdHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJqdm1fbWVtb3J5X2RpcmVjdF9ieXRlc191c2VkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX3dlYl9leGVjdXRvcl9taW5fdGhyZWFkcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImp2bV9pbmZvIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX3dlYl9leGVjdXRvcl9tYXhfdGhyZWFkcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9icm9rZXJfbG9hZF9tYW5hZ2VyX2J1bmRsZV9hc3NpZ21lbnRfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfYnJva2VyX2xvYWRfbWFuYWdlcl9idW5kbGVfYXNzaWdtZW50X3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9yZXNvdXJjZV9ncm91cF9jYWxjdWxhdGVfcXVvdGFfc2VjcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9yZXNvdXJjZV9ncm91cF9jYWxjdWxhdGVfcXVvdGFfc2Vjc19jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9yZXNvdXJjZV9ncm91cF9jYWxjdWxhdGVfcXVvdGFfc2Vjc19zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfYnJva2VyX3RvcGljX2xvYWRfcGVuZGluZ19yZXF1ZXN0cyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImp2bV9nY19jb2xsZWN0aW9uX3NlY29uZHNfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJqdm1fZ2NfY29sbGVjdGlvbl9zZWNvbmRzX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImp2bV9tZW1vcnlfYnl0ZXNfdXNlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImp2bV9tZW1vcnlfYnl0ZXNfY29tbWl0dGVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAianZtX21lbW9yeV9ieXRlc19tYXgiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJqdm1fbWVtb3J5X2J5dGVzX2luaXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJqdm1fbWVtb3J5X3Bvb2xfYnl0ZXNfdXNlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImp2bV9tZW1vcnlfcG9vbF9ieXRlc19jb21taXR0ZWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJqdm1fbWVtb3J5X3Bvb2xfYnl0ZXNfbWF4IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAianZtX21lbW9yeV9wb29sX2J5dGVzX2luaXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJjYWZmZWluZV9jYWNoZV9oaXRfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJjYWZmZWluZV9jYWNoZV9taXNzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiY2FmZmVpbmVfY2FjaGVfcmVxdWVzdHNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJjYWZmZWluZV9jYWNoZV9ldmljdGlvbl90b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImNhZmZlaW5lX2NhY2hlX2V2aWN0aW9uX3dlaWdodCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImNhZmZlaW5lX2NhY2hlX2xvYWRfZmFpbHVyZV90b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImNhZmZlaW5lX2NhY2hlX2xvYWRzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiY2FmZmVpbmVfY2FjaGVfZXN0aW1hdGVkX3NpemUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJjYWZmZWluZV9jYWNoZV9sb2FkX2R1cmF0aW9uX3NlY29uZHNfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJjYWZmZWluZV9jYWNoZV9sb2FkX2R1cmF0aW9uX3NlY29uZHNfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX2Jyb2tlcl9sb29rdXBfZmFpbHVyZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJqdm1fbWVtb3J5X2RpcmVjdF9ieXRlc19tYXgiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfYnJva2VyX3B1Ymxpc2hfbGF0ZW5jeV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9icm9rZXJfcHVibGlzaF9sYXRlbmN5X3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImp2bV9jbGFzc2VzX2xvYWRlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImp2bV9jbGFzc2VzX2xvYWRlZF90b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImp2bV9jbGFzc2VzX3VubG9hZGVkX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibG9nNGoyX2FwcGVuZGVyX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiemtfcmVhZF9sYXRlbmN5X2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiemtfcmVhZF9sYXRlbmN5X3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImp2bV9idWZmZXJfcG9vbF91c2VkX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAianZtX2J1ZmZlcl9wb29sX2NhcGFjaXR5X2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAianZtX2J1ZmZlcl9wb29sX3VzZWRfYnVmZmVycyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9icm9rZXJfbG9va3VwX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX2Jyb2tlcl9sb29rdXBfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAianZtX3RocmVhZHNfY3VycmVudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImp2bV90aHJlYWRzX2RhZW1vbiIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImp2bV90aHJlYWRzX3BlYWsiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJqdm1fdGhyZWFkc19zdGFydGVkX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAianZtX3RocmVhZHNfZGVhZGxvY2tlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImp2bV90aHJlYWRzX2RlYWRsb2NrZWRfbW9uaXRvciIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImpldHR5X3JlcXVlc3RzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiamV0dHlfcmVxdWVzdHNfYWN0aXZlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiamV0dHlfcmVxdWVzdHNfYWN0aXZlX21heCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImpldHR5X3JlcXVlc3RfdGltZV9tYXhfc2Vjb25kcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImpldHR5X3JlcXVlc3RfdGltZV9zZWNvbmRzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiamV0dHlfZGlzcGF0Y2hlZF90b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImpldHR5X2Rpc3BhdGNoZWRfYWN0aXZlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiamV0dHlfZGlzcGF0Y2hlZF9hY3RpdmVfbWF4IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiamV0dHlfZGlzcGF0Y2hlZF90aW1lX21heCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImpldHR5X2Rpc3BhdGNoZWRfdGltZV9zZWNvbmRzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiamV0dHlfYXN5bmNfcmVxdWVzdHNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJqZXR0eV9hc3luY19yZXF1ZXN0c193YWl0aW5nIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiamV0dHlfYXN5bmNfcmVxdWVzdHNfd2FpdGluZ19tYXgiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJqZXR0eV9hc3luY19kaXNwYXRjaGVzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiamV0dHlfZXhwaXJlc190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImpldHR5X3Jlc3BvbnNlc190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImpldHR5X3N0YXRzX3NlY29uZHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJqZXR0eV9yZXNwb25zZXNfYnl0ZXNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJ0b3BpY19sb2FkX3RpbWVzX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAidG9waWNfbG9hZF90aW1lc19zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJ6a193cml0ZV9sYXRlbmN5X2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiemtfd3JpdGVfbGF0ZW5jeV9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfd2ViX2V4ZWN1dG9yX2FjdGl2ZV90aHJlYWRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX2F1dGhlbnRpY2F0aW9uX3N1Y2Nlc3NfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfYnJva2VyX2xvb2t1cF9hbnN3ZXJzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX2Jyb2tlcl9sb29rdXBfcmVkaXJlY3RzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX2Jyb2tlcl90aHJvdHRsZWRfY29ubmVjdGlvbnMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfZXhwaXJpbmdfdG9rZW5fbWludXRlc19idWNrZXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfZXhwaXJpbmdfdG9rZW5fbWludXRlc19jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9leHBpcmluZ190b2tlbl9taW51dGVzX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl93ZWJfZXhlY3V0b3JfY3VycmVudF90aHJlYWRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX3RvcGljc19jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9zdWJzY3JpcHRpb25zX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX3Byb2R1Y2Vyc19jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9jb25zdW1lcnNfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfcmF0ZV9pbiIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9yYXRlX291dCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl90aHJvdWdocHV0X2luIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX3Rocm91Z2hwdXRfb3V0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX3N0b3JhZ2Vfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9zdG9yYWdlX2xvZ2ljYWxfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9zdG9yYWdlX3dyaXRlX3JhdGUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfc3RvcmFnZV9yZWFkX3JhdGUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfbXNnX2JhY2tsb2ciLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfYXZlcmFnZV9tc2dfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9zdG9yYWdlX2JhY2tsb2dfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9wdWJsaXNoX3JhdGVfbGltaXRfdGltZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfc3RvcmFnZV9vZmZsb2FkZWRfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9zdG9yYWdlX2JhY2tsb2dfcXVvdGFfbGltaXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfc3RvcmFnZV9iYWNrbG9nX3F1b3RhX2xpbWl0X3RpbWUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfc3RvcmFnZV93cml0ZV9sYXRlbmN5X2xlXzBfNSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9zdG9yYWdlX3dyaXRlX2xhdGVuY3lfbGVfMSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9zdG9yYWdlX3dyaXRlX2xhdGVuY3lfbGVfNSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9zdG9yYWdlX3dyaXRlX2xhdGVuY3lfbGVfMTAiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfc3RvcmFnZV93cml0ZV9sYXRlbmN5X2xlXzIwIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX3N0b3JhZ2Vfd3JpdGVfbGF0ZW5jeV9sZV81MCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9zdG9yYWdlX3dyaXRlX2xhdGVuY3lfbGVfMTAwIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX3N0b3JhZ2Vfd3JpdGVfbGF0ZW5jeV9sZV8yMDAiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfc3RvcmFnZV93cml0ZV9sYXRlbmN5X2xlXzEwMDAiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfc3RvcmFnZV93cml0ZV9sYXRlbmN5X292ZXJmbG93IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX3N0b3JhZ2Vfd3JpdGVfbGF0ZW5jeV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9zdG9yYWdlX3dyaXRlX2xhdGVuY3lfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX3N0b3JhZ2VfbGVkZ2VyX3dyaXRlX2xhdGVuY3lfbGVfMF81IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX3N0b3JhZ2VfbGVkZ2VyX3dyaXRlX2xhdGVuY3lfbGVfMSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9zdG9yYWdlX2xlZGdlcl93cml0ZV9sYXRlbmN5X2xlXzUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfc3RvcmFnZV9sZWRnZXJfd3JpdGVfbGF0ZW5jeV9sZV8xMCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9zdG9yYWdlX2xlZGdlcl93cml0ZV9sYXRlbmN5X2xlXzIwIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX3N0b3JhZ2VfbGVkZ2VyX3dyaXRlX2xhdGVuY3lfbGVfNTAiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfc3RvcmFnZV9sZWRnZXJfd3JpdGVfbGF0ZW5jeV9sZV8xMDAiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfc3RvcmFnZV9sZWRnZXJfd3JpdGVfbGF0ZW5jeV9sZV8yMDAiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfc3RvcmFnZV9sZWRnZXJfd3JpdGVfbGF0ZW5jeV9sZV8xMDAwIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX3N0b3JhZ2VfbGVkZ2VyX3dyaXRlX2xhdGVuY3lfb3ZlcmZsb3ciLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfc3RvcmFnZV9sZWRnZXJfd3JpdGVfbGF0ZW5jeV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9zdG9yYWdlX2xlZGdlcl93cml0ZV9sYXRlbmN5X3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9lbnRyeV9zaXplX2xlXzEyOCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9lbnRyeV9zaXplX2xlXzUxMiIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9lbnRyeV9zaXplX2xlXzFfa2IiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfZW50cnlfc2l6ZV9sZV8yX2tiIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX2VudHJ5X3NpemVfbGVfNF9rYiIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9lbnRyeV9zaXplX2xlXzE2X2tiIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX2VudHJ5X3NpemVfbGVfMTAwX2tiIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX2VudHJ5X3NpemVfbGVfMV9tYiIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9lbnRyeV9zaXplX2xlX292ZXJmbG93IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX2VudHJ5X3NpemVfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfZW50cnlfc2l6ZV9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfaW5fYnl0ZXNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfaW5fbWVzc2FnZXNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfbWxfY2FjaGVfZXZpY3Rpb25zIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX21sX2NhY2hlX2hpdHNfcmF0ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9tbF9jYWNoZV9oaXRzX3Rocm91Z2hwdXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfbWxfY2FjaGVfbWlzc2VzX3JhdGUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfbWxfY2FjaGVfbWlzc2VzX3Rocm91Z2hwdXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfbWxfY2FjaGVfcG9vbF9hY3RpdmVfYWxsb2NhdGlvbnMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfbWxfY2FjaGVfcG9vbF9hY3RpdmVfYWxsb2NhdGlvbnNfaHVnZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9tbF9jYWNoZV9wb29sX2FjdGl2ZV9hbGxvY2F0aW9uc19ub3JtYWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfbWxfY2FjaGVfcG9vbF9hY3RpdmVfYWxsb2NhdGlvbnNfc21hbGwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfbWxfY2FjaGVfcG9vbF9hbGxvY2F0ZWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfbWxfY2FjaGVfcG9vbF91c2VkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX21sX2NhY2hlX3VzZWRfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9tbF9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9tbF9BZGRFbnRyeUJ5dGVzUmF0ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9tbF9BZGRFbnRyeUVycm9ycyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9tbF9BZGRFbnRyeUxhdGVuY3lCdWNrZXRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX21sX0FkZEVudHJ5TGF0ZW5jeUJ1Y2tldHNfT1ZFUkZMT1ciLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfbWxfQWRkRW50cnlNZXNzYWdlc1JhdGUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfbWxfQWRkRW50cnlTdWNjZWVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX21sX0FkZEVudHJ5V2l0aFJlcGxpY2FzQnl0ZXNSYXRlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX21sX0VudHJ5U2l6ZUJ1Y2tldHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfbWxfRW50cnlTaXplQnVja2V0c19PVkVSRkxPVyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9tbF9MZWRnZXJBZGRFbnRyeUxhdGVuY3lCdWNrZXRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX21sX0xlZGdlckFkZEVudHJ5TGF0ZW5jeUJ1Y2tldHNfT1ZFUkZMT1ciLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfbWxfTGVkZ2VyU3dpdGNoTGF0ZW5jeUJ1Y2tldHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfbWxfTGVkZ2VyU3dpdGNoTGF0ZW5jeUJ1Y2tldHNfT1ZFUkZMT1ciLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfbWxfTWFya0RlbGV0ZVJhdGUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfbWxfTnVtYmVyT2ZNZXNzYWdlc0luQmFja2xvZyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9tbF9SZWFkRW50cmllc0J5dGVzUmF0ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9tbF9SZWFkRW50cmllc0Vycm9ycyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9tbF9SZWFkRW50cmllc1JhdGUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfbWxfUmVhZEVudHJpZXNTdWNjZWVkZWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfbWxfU3RvcmVkTWVzc2FnZXNTaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX2FjdGl2ZV9jb25uZWN0aW9ucyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9jb25uZWN0aW9uX2Nsb3NlZF90b3RhbF9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9jb25uZWN0aW9uX2NyZWF0ZV9mYWlsX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX2Nvbm5lY3Rpb25fY3JlYXRlX3N1Y2Nlc3NfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfY29ubmVjdGlvbl9jcmVhdGVkX3RvdGFsX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX2xiX2JhbmR3aWR0aF9pbl91c2FnZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9sYl9iYW5kd2lkdGhfb3V0X3VzYWdlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX2xiX2NwdV91c2FnZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9sYl9kaXJlY3RNZW1vcnlfdXNhZ2UiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfbGJfbWVtb3J5X3VzYWdlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHVsc2FyX2xiX3VubG9hZF9icm9rZXJfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwdWxzYXJfbGJfdW5sb2FkX2J1bmRsZV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInB1bHNhcl9sYl9idW5kbGVzX3NwbGl0X2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiY2x1c3RlciIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInZlcnNpb24iLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJjb21taXQiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJxdWFudGlsZSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInZlbmRvciIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJ1bnRpbWUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJnYyIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImFyZWEiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwb29sIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiY2FjaGUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJsZXZlbCIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImNvZGUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm92aWRlcl9uYW1lIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYXV0aF9tZXRob2QiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJsZSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm5hbWVzcGFjZSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInBhcnRpdGlvbiIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInRvcGljIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibWV0cmljIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYnJva2VyIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9XSwgInRhYmxlX2lkIjogInB1c2hnYXRld2F5X2RibV9wdWxzYXJicm9rZXJfYmtwdWxsLmdyb3VwMSIsICJ0YWJsZV9kZXNjIjogIlx1OWVkOFx1OGJhNFx1NTIwNlx1N2VjNCIsICJ0YWJsZV9uYW1lIjogImdyb3VwMSJ9XSwgInBsdWdpbl90eXBlIjogIlB1c2hnYXRld2F5IiwgIm9zX3R5cGVfbGlzdCI6IFsibGludXgiLCAid2luZG93cyIsICJsaW51eF9hYXJjaDY0Il19LCAiY29sbGVjdF90eXBlIjogIlB1c2hnYXRld2F5IiwgInRhcmdldF9ub2RlcyI6IFtdLCAidGFyZ2V0X25vZGVfdHlwZSI6ICJUT1BPIiwgInRhcmdldF9vYmplY3RfdHlwZSI6ICJTRVJWSUNFIn19 \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/collect/0.pulsar.dbm_pulsarzookeeper_bkpull.json b/dbm-ui/backend/db_monitor/tpls/collect/0.pulsar.dbm_pulsarzookeeper_bkpull.json new file mode 100644 index 0000000000..b33e05936a --- /dev/null +++ b/dbm-ui/backend/db_monitor/tpls/collect/0.pulsar.dbm_pulsarzookeeper_bkpull.json @@ -0,0 +1,3103 @@ +{ + "bk_biz_id": 0, + "plugin_id": "dbm_pulsarzookeeper_bkpull", + "db_type": "pulsar", + "details": { + "name": "dbm_pulsarzookeeper_bkpull", + "label": "component", + "params": { + "plugin": { + "服务实例维度注入": { + "app": "app", + "instance": "instance", + "cluster_name": "cluster_name", + "cluster_type": "cluster_type", + "instance_host": "instance_host", + "instance_port": "instance_port", + "instance_role": "instance_role", + "cluster_domain": "cluster_domain" + } + }, + "collector": { + "period": 60, + "timeout": 60, + "password": "", + "username": "", + "metrics_url": "http://{{ target.host.bk_host_innerip }}:8000/metrics" + }, + "target_node_type": "TOPO", + "target_object_type": "SERVICE" + }, + "plugin_info": { + "plugin_id": "dbm_pulsarzookeeper_bkpull", + "metric_json": [ + { + "fields": [ + { + "name": "prep_processor_queue_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "max_proposal_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "last_proposal_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pending_syncs", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "prep_processor_queue_time_ms", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "_key", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "key", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "learners", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "ack_latency", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "ack_latency_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "ack_latency_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "synced_followers", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "synced_non_voting_followers", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "min_proposal_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "learner_handler_qp_time_ms", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "learner_handler_qp_time_ms_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "learner_handler_qp_time_ms_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "learner_handler_qp_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "learner_handler_qp_size_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "learner_handler_qp_size_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "quorum_ack_latency", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "prep_process_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "leader_uptime", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "snapshottime_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "snapshottime_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "close_session_prep_time_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "close_session_prep_time_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_buffer_pool_used_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_buffer_pool_capacity_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_buffer_pool_used_buffers", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "write_batch_time_in_commit_processor", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "write_batch_time_in_commit_processor_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "write_batch_time_in_commit_processor_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "non_mtls_local_conn_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "connection_drop_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "inflight_diff_count_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "inflight_diff_count_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "outstanding_changes_queued", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "sync_processor_queue_flush_time_ms", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "sync_processor_queue_flush_time_ms_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "sync_processor_queue_flush_time_ms_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "prep_processor_queue_size_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "prep_processor_queue_size_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "startup_txns_load_time_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "startup_txns_load_time_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "tls_handshake_exceeded", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "local_sessions", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "synced_observers", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "looking_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "revalidate_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "watch_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "quit_leading_due_to_disloyal_voter", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "avg_latency", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "readlatency", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "readlatency_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "readlatency_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "add_dead_watcher_stall_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "time_waiting_empty_pool_in_commit_processor_read_ms", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "time_waiting_empty_pool_in_commit_processor_read_ms_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "time_waiting_empty_pool_in_commit_processor_read_ms_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "write_commit_proc_req_queued", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "write_commit_proc_req_queued_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "write_commit_proc_req_queued_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "startup_snap_load_time_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "startup_snap_load_time_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "auth_failed_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "sessionless_connections_expired", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "max_latency", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "proposal_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "prep_processor_queue_time_ms_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "prep_processor_queue_time_ms_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "commit_process_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "commit_process_time_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "commit_process_time_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "write_per_namespace_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "write_per_namespace_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "write_per_namespace", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "commit_commit_proc_req_queued", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "commit_commit_proc_req_queued_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "commit_commit_proc_req_queued_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_cpu_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_start_time_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_open_fds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_max_fds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_virtual_memory_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_resident_memory_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "connection_revalidate_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_gc_collection_seconds_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_gc_collection_seconds_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "proposal_latency", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "proposal_latency_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "proposal_latency_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "inflight_snap_count_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "inflight_snap_count_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_pause_time_ms_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_pause_time_ms_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "uptime", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "ensemble_auth_skip", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "response_packet_cache_hits", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "znode_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "learner_commit_received_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "stale_requests", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "max_client_response_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "connection_drop_probability", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_info", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "requests_in_session_queue", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "requests_in_session_queue_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "requests_in_session_queue_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_threads_current", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_threads_daemon", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_threads_peak", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_threads_started_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_threads_deadlocked", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_threads_deadlocked_monitor", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "election_time_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "election_time_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "digest_mismatches_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_classes_loaded", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_classes_loaded_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_classes_unloaded_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "min_client_response_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "outstanding_changes_removed", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "read_commitproc_time_ms", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "read_commitproc_time_ms_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "read_commitproc_time_ms_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "write_commit_proc_issued", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "write_commit_proc_issued_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "write_commit_proc_issued_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "connection_rejected", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "node_deleted_watch_count_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "node_deleted_watch_count_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "sync_processor_queue_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "sync_processor_queue_size_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "sync_processor_queue_size_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "request_throttle_wait_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "sync_processor_queue_time_ms", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "sync_processor_queue_time_ms_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "sync_processor_queue_time_ms_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "dead_watchers_cleared", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "stale_requests_dropped", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "unrecoverable_error_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "packets_received", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "commit_propagation_latency", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "commit_propagation_latency_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "commit_propagation_latency_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "connection_request_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "stale_replies", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "netty_queued_buffer_capacity_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "netty_queued_buffer_capacity_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "write_final_proc_time_ms", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "write_final_proc_time_ms_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "write_final_proc_time_ms_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "outstanding_tls_handshake", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "om_commit_process_time_ms_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "om_commit_process_time_ms_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "read_per_namespace_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "read_per_namespace_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "read_per_namespace", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "outstanding_requests", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "sync_processor_request_queued", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "log4j2_appender_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "local_write_committed_time_ms", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "local_write_committed_time_ms_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "local_write_committed_time_ms_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "read_final_proc_time_ms", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "read_final_proc_time_ms_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "read_final_proc_time_ms_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "max_file_descriptor_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "read_commit_proc_req_queued", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "read_commit_proc_req_queued_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "read_commit_proc_req_queued_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "sync_processor_queue_and_flush_time_ms", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "sync_processor_queue_and_flush_time_ms_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "sync_processor_queue_and_flush_time_ms_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "sync_processor_batch_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "sync_processor_batch_size_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "sync_processor_batch_size_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "sync_process_time", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "sync_process_time_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "sync_process_time_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "dbinittime_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "dbinittime_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "commit_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "proposal_ack_creation_latency", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "proposal_ack_creation_latency_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "proposal_ack_creation_latency_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "session_queues_drained", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "session_queues_drained_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "session_queues_drained_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "snap_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "fsynctime", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "fsynctime_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "fsynctime_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "response_packet_cache_misses", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "min_latency", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "packets_sent", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "dead_watchers_cleaner_latency_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "dead_watchers_cleaner_latency_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "write_commitproc_time_ms", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "write_commitproc_time_ms_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "write_commitproc_time_ms_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "response_packet_get_children_cache_hits", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "last_client_response_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "quorum_ack_latency_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "quorum_ack_latency_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "large_requests_rejected", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "node_created_watch_count_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "node_created_watch_count_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "non_mtls_remote_conn_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "reads_issued_from_session_queue", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "reads_issued_from_session_queue_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "reads_issued_from_session_queue_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "reads_after_write_in_session_queue", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "reads_after_write_in_session_queue_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "reads_after_write_in_session_queue_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pending_session_queue_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pending_session_queue_size_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pending_session_queue_size_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "global_sessions", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "propagation_latency", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "propagation_latency_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "propagation_latency_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "ensemble_auth_fail", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "startup_txns_loaded_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "startup_txns_loaded_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "ensemble_auth_success", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "prep_processor_request_queued", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "open_file_descriptor_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "server_write_committed_time_ms", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "server_write_committed_time_ms_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "server_write_committed_time_ms_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "dead_watchers_queued", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "request_commit_queued", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "diff_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "prep_process_time_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "prep_process_time_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "node_changed_watch_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "node_changed_watch_count_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "node_changed_watch_count_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "approximate_data_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "num_alive_connections", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "learner_proposal_received_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "read_commit_proc_issued", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "read_commit_proc_issued_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "read_commit_proc_issued_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "follower_sync_time_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "follower_sync_time_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "bytes_received_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "connection_token_deficit_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "connection_token_deficit_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "stale_sessions_expired", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "node_children_watch_count_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "node_children_watch_count_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "updatelatency", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "updatelatency_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "updatelatency_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "concurrent_request_processing_in_commit_processor_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "concurrent_request_processing_in_commit_processor_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "ephemerals_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_memory_bytes_used", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_memory_bytes_committed", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_memory_bytes_max", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_memory_bytes_init", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_memory_pool_bytes_used", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_memory_pool_bytes_committed", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_memory_pool_bytes_max", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "jvm_memory_pool_bytes_init", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "quorum_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "om_proposal_process_time_ms_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "om_proposal_process_time_ms_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "response_packet_get_children_cache_misses", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "pool", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "quantile", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "gc", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "version", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "vendor", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "runtime", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "level", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "area", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "pushgateway_dbm_pulsarzookeeper_bkpull.group1", + "table_desc": "默认分组", + "table_name": "group1" + } + ], + "plugin_type": "Pushgateway", + "os_type_list": [ + "linux", + "windows", + "linux_aarch64" + ] + }, + "collect_type": "Pushgateway", + "target_nodes": [], + "target_node_type": "TOPO", + "target_object_type": "SERVICE" + } +} diff --git a/dbm-ui/backend/db_monitor/tpls/collect/0.pulsar.dbm_pulsarzookeeper_bkpull.tpl64 b/dbm-ui/backend/db_monitor/tpls/collect/0.pulsar.dbm_pulsarzookeeper_bkpull.tpl64 deleted file mode 100644 index 5b9b593528..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/collect/0.pulsar.dbm_pulsarzookeeper_bkpull.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAicGx1Z2luX2lkIjogImRibV9wdWxzYXJ6b29rZWVwZXJfYmtwdWxsIiwgImRiX3R5cGUiOiAicHVsc2FyIiwgImRldGFpbHMiOiB7Im5hbWUiOiAiZGJtX3B1bHNhcnpvb2tlZXBlcl9ia3B1bGwiLCAibGFiZWwiOiAiY29tcG9uZW50IiwgInBhcmFtcyI6IHsicGx1Z2luIjogeyJcdTY3MGRcdTUyYTFcdTViOWVcdTRmOGJcdTdlZjRcdTVlYTZcdTZjZThcdTUxNjUiOiB7ImFwcCI6ICJhcHAiLCAiaW5zdGFuY2UiOiAiaW5zdGFuY2UiLCAiY2x1c3Rlcl9uYW1lIjogImNsdXN0ZXJfbmFtZSIsICJjbHVzdGVyX3R5cGUiOiAiY2x1c3Rlcl90eXBlIiwgImluc3RhbmNlX2hvc3QiOiAiaW5zdGFuY2VfaG9zdCIsICJpbnN0YW5jZV9wb3J0IjogImluc3RhbmNlX3BvcnQiLCAiaW5zdGFuY2Vfcm9sZSI6ICJpbnN0YW5jZV9yb2xlIiwgImNsdXN0ZXJfZG9tYWluIjogImNsdXN0ZXJfZG9tYWluIn19LCAiY29sbGVjdG9yIjogeyJwZXJpb2QiOiA2MCwgInRpbWVvdXQiOiA2MCwgInBhc3N3b3JkIjogIiIsICJ1c2VybmFtZSI6ICIiLCAibWV0cmljc191cmwiOiAiaHR0cDovL3t7IHRhcmdldC5ob3N0LmJrX2hvc3RfaW5uZXJpcCB9fTo4MDAwL21ldHJpY3MifSwgInRhcmdldF9ub2RlX3R5cGUiOiAiVE9QTyIsICJ0YXJnZXRfb2JqZWN0X3R5cGUiOiAiU0VSVklDRSJ9LCAicGx1Z2luX2luZm8iOiB7InBsdWdpbl9pZCI6ICJkYm1fcHVsc2Fyem9va2VlcGVyX2JrcHVsbCIsICJtZXRyaWNfanNvbiI6IFt7ImZpZWxkcyI6IFt7Im5hbWUiOiAicHJlcF9wcm9jZXNzb3JfcXVldWVfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm1heF9wcm9wb3NhbF9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibGFzdF9wcm9wb3NhbF9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicGVuZGluZ19zeW5jcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByZXBfcHJvY2Vzc29yX3F1ZXVlX3RpbWVfbXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJfa2V5IiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogImtleSIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibGVhcm5lcnMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJhY2tfbGF0ZW5jeSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImFja19sYXRlbmN5X2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYWNrX2xhdGVuY3lfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAic3luY2VkX2ZvbGxvd2VycyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInN5bmNlZF9ub25fdm90aW5nX2ZvbGxvd2VycyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm1pbl9wcm9wb3NhbF9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibGVhcm5lcl9oYW5kbGVyX3FwX3RpbWVfbXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJsZWFybmVyX2hhbmRsZXJfcXBfdGltZV9tc19jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImxlYXJuZXJfaGFuZGxlcl9xcF90aW1lX21zX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImxlYXJuZXJfaGFuZGxlcl9xcF9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibGVhcm5lcl9oYW5kbGVyX3FwX3NpemVfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJsZWFybmVyX2hhbmRsZXJfcXBfc2l6ZV9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJxdW9ydW1fYWNrX2xhdGVuY3kiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcmVwX3Byb2Nlc3NfdGltZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImxlYWRlcl91cHRpbWUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJzbmFwc2hvdHRpbWVfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJzbmFwc2hvdHRpbWVfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiY2xvc2Vfc2Vzc2lvbl9wcmVwX3RpbWVfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJjbG9zZV9zZXNzaW9uX3ByZXBfdGltZV9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJqdm1fYnVmZmVyX3Bvb2xfdXNlZF9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImp2bV9idWZmZXJfcG9vbF9jYXBhY2l0eV9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImp2bV9idWZmZXJfcG9vbF91c2VkX2J1ZmZlcnMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJ3cml0ZV9iYXRjaF90aW1lX2luX2NvbW1pdF9wcm9jZXNzb3IiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJ3cml0ZV9iYXRjaF90aW1lX2luX2NvbW1pdF9wcm9jZXNzb3JfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJ3cml0ZV9iYXRjaF90aW1lX2luX2NvbW1pdF9wcm9jZXNzb3Jfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibm9uX210bHNfbG9jYWxfY29ubl9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImNvbm5lY3Rpb25fZHJvcF9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImluZmxpZ2h0X2RpZmZfY291bnRfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJpbmZsaWdodF9kaWZmX2NvdW50X3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm91dHN0YW5kaW5nX2NoYW5nZXNfcXVldWVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAic3luY19wcm9jZXNzb3JfcXVldWVfZmx1c2hfdGltZV9tcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInN5bmNfcHJvY2Vzc29yX3F1ZXVlX2ZsdXNoX3RpbWVfbXNfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJzeW5jX3Byb2Nlc3Nvcl9xdWV1ZV9mbHVzaF90aW1lX21zX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByZXBfcHJvY2Vzc29yX3F1ZXVlX3NpemVfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcmVwX3Byb2Nlc3Nvcl9xdWV1ZV9zaXplX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInN0YXJ0dXBfdHhuc19sb2FkX3RpbWVfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJzdGFydHVwX3R4bnNfbG9hZF90aW1lX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInRsc19oYW5kc2hha2VfZXhjZWVkZWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJsb2NhbF9zZXNzaW9ucyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInN5bmNlZF9vYnNlcnZlcnMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJsb29raW5nX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmV2YWxpZGF0ZV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIndhdGNoX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicXVpdF9sZWFkaW5nX2R1ZV90b19kaXNsb3lhbF92b3RlciIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImF2Z19sYXRlbmN5IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVhZGxhdGVuY3kiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWFkbGF0ZW5jeV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlYWRsYXRlbmN5X3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImFkZF9kZWFkX3dhdGNoZXJfc3RhbGxfdGltZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInRpbWVfd2FpdGluZ19lbXB0eV9wb29sX2luX2NvbW1pdF9wcm9jZXNzb3JfcmVhZF9tcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInRpbWVfd2FpdGluZ19lbXB0eV9wb29sX2luX2NvbW1pdF9wcm9jZXNzb3JfcmVhZF9tc19jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInRpbWVfd2FpdGluZ19lbXB0eV9wb29sX2luX2NvbW1pdF9wcm9jZXNzb3JfcmVhZF9tc19zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJ3cml0ZV9jb21taXRfcHJvY19yZXFfcXVldWVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAid3JpdGVfY29tbWl0X3Byb2NfcmVxX3F1ZXVlZF9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIndyaXRlX2NvbW1pdF9wcm9jX3JlcV9xdWV1ZWRfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAic3RhcnR1cF9zbmFwX2xvYWRfdGltZV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInN0YXJ0dXBfc25hcF9sb2FkX3RpbWVfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYXV0aF9mYWlsZWRfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJzZXNzaW9ubGVzc19jb25uZWN0aW9uc19leHBpcmVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibWF4X2xhdGVuY3kiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9wb3NhbF9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByZXBfcHJvY2Vzc29yX3F1ZXVlX3RpbWVfbXNfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcmVwX3Byb2Nlc3Nvcl9xdWV1ZV90aW1lX21zX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImNvbW1pdF9wcm9jZXNzX3RpbWUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJjb21taXRfcHJvY2Vzc190aW1lX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiY29tbWl0X3Byb2Nlc3NfdGltZV9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJ3cml0ZV9wZXJfbmFtZXNwYWNlX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAid3JpdGVfcGVyX25hbWVzcGFjZV9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJ3cml0ZV9wZXJfbmFtZXNwYWNlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiY29tbWl0X2NvbW1pdF9wcm9jX3JlcV9xdWV1ZWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJjb21taXRfY29tbWl0X3Byb2NfcmVxX3F1ZXVlZF9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImNvbW1pdF9jb21taXRfcHJvY19yZXFfcXVldWVkX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb2Nlc3NfY3B1X3NlY29uZHNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jZXNzX3N0YXJ0X3RpbWVfc2Vjb25kcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb2Nlc3Nfb3Blbl9mZHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jZXNzX21heF9mZHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jZXNzX3ZpcnR1YWxfbWVtb3J5X2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY2Vzc19yZXNpZGVudF9tZW1vcnlfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJjb25uZWN0aW9uX3JldmFsaWRhdGVfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJqdm1fZ2NfY29sbGVjdGlvbl9zZWNvbmRzX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAianZtX2djX2NvbGxlY3Rpb25fc2Vjb25kc19zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9wb3NhbF9sYXRlbmN5IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvcG9zYWxfbGF0ZW5jeV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb3Bvc2FsX2xhdGVuY3lfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiaW5mbGlnaHRfc25hcF9jb3VudF9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImluZmxpZ2h0X3NuYXBfY291bnRfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAianZtX3BhdXNlX3RpbWVfbXNfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJqdm1fcGF1c2VfdGltZV9tc19zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJ1cHRpbWUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbnNlbWJsZV9hdXRoX3NraXAiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXNwb25zZV9wYWNrZXRfY2FjaGVfaGl0cyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInpub2RlX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibGVhcm5lcl9jb21taXRfcmVjZWl2ZWRfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJzdGFsZV9yZXF1ZXN0cyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm1heF9jbGllbnRfcmVzcG9uc2Vfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImNvbm5lY3Rpb25fZHJvcF9wcm9iYWJpbGl0eSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImp2bV9pbmZvIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVxdWVzdHNfaW5fc2Vzc2lvbl9xdWV1ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlcXVlc3RzX2luX3Nlc3Npb25fcXVldWVfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXF1ZXN0c19pbl9zZXNzaW9uX3F1ZXVlX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImp2bV90aHJlYWRzX2N1cnJlbnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJqdm1fdGhyZWFkc19kYWVtb24iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJqdm1fdGhyZWFkc19wZWFrIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAianZtX3RocmVhZHNfc3RhcnRlZF90b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImp2bV90aHJlYWRzX2RlYWRsb2NrZWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJqdm1fdGhyZWFkc19kZWFkbG9ja2VkX21vbml0b3IiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlbGVjdGlvbl90aW1lX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZWxlY3Rpb25fdGltZV9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJkaWdlc3RfbWlzbWF0Y2hlc19jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImp2bV9jbGFzc2VzX2xvYWRlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImp2bV9jbGFzc2VzX2xvYWRlZF90b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImp2bV9jbGFzc2VzX3VubG9hZGVkX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibWluX2NsaWVudF9yZXNwb25zZV9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAib3V0c3RhbmRpbmdfY2hhbmdlc19yZW1vdmVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVhZF9jb21taXRwcm9jX3RpbWVfbXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWFkX2NvbW1pdHByb2NfdGltZV9tc19jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlYWRfY29tbWl0cHJvY190aW1lX21zX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIndyaXRlX2NvbW1pdF9wcm9jX2lzc3VlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIndyaXRlX2NvbW1pdF9wcm9jX2lzc3VlZF9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIndyaXRlX2NvbW1pdF9wcm9jX2lzc3VlZF9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJjb25uZWN0aW9uX3JlamVjdGVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibm9kZV9kZWxldGVkX3dhdGNoX2NvdW50X2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibm9kZV9kZWxldGVkX3dhdGNoX2NvdW50X3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInN5bmNfcHJvY2Vzc29yX3F1ZXVlX3NpemUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJzeW5jX3Byb2Nlc3Nvcl9xdWV1ZV9zaXplX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAic3luY19wcm9jZXNzb3JfcXVldWVfc2l6ZV9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXF1ZXN0X3Rocm90dGxlX3dhaXRfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJzeW5jX3Byb2Nlc3Nvcl9xdWV1ZV90aW1lX21zIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAic3luY19wcm9jZXNzb3JfcXVldWVfdGltZV9tc19jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInN5bmNfcHJvY2Vzc29yX3F1ZXVlX3RpbWVfbXNfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZGVhZF93YXRjaGVyc19jbGVhcmVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAic3RhbGVfcmVxdWVzdHNfZHJvcHBlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInVucmVjb3ZlcmFibGVfZXJyb3JfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwYWNrZXRzX3JlY2VpdmVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiY29tbWl0X3Byb3BhZ2F0aW9uX2xhdGVuY3kiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJjb21taXRfcHJvcGFnYXRpb25fbGF0ZW5jeV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImNvbW1pdF9wcm9wYWdhdGlvbl9sYXRlbmN5X3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImNvbm5lY3Rpb25fcmVxdWVzdF9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInN0YWxlX3JlcGxpZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJuZXR0eV9xdWV1ZWRfYnVmZmVyX2NhcGFjaXR5X2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibmV0dHlfcXVldWVkX2J1ZmZlcl9jYXBhY2l0eV9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJ3cml0ZV9maW5hbF9wcm9jX3RpbWVfbXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJ3cml0ZV9maW5hbF9wcm9jX3RpbWVfbXNfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJ3cml0ZV9maW5hbF9wcm9jX3RpbWVfbXNfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAib3V0c3RhbmRpbmdfdGxzX2hhbmRzaGFrZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm9tX2NvbW1pdF9wcm9jZXNzX3RpbWVfbXNfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJvbV9jb21taXRfcHJvY2Vzc190aW1lX21zX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlYWRfcGVyX25hbWVzcGFjZV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlYWRfcGVyX25hbWVzcGFjZV9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWFkX3Blcl9uYW1lc3BhY2UiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJvdXRzdGFuZGluZ19yZXF1ZXN0cyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInN5bmNfcHJvY2Vzc29yX3JlcXVlc3RfcXVldWVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibG9nNGoyX2FwcGVuZGVyX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibG9jYWxfd3JpdGVfY29tbWl0dGVkX3RpbWVfbXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJsb2NhbF93cml0ZV9jb21taXR0ZWRfdGltZV9tc19jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImxvY2FsX3dyaXRlX2NvbW1pdHRlZF90aW1lX21zX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlYWRfZmluYWxfcHJvY190aW1lX21zIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVhZF9maW5hbF9wcm9jX3RpbWVfbXNfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWFkX2ZpbmFsX3Byb2NfdGltZV9tc19zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJtYXhfZmlsZV9kZXNjcmlwdG9yX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVhZF9jb21taXRfcHJvY19yZXFfcXVldWVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVhZF9jb21taXRfcHJvY19yZXFfcXVldWVkX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVhZF9jb21taXRfcHJvY19yZXFfcXVldWVkX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInN5bmNfcHJvY2Vzc29yX3F1ZXVlX2FuZF9mbHVzaF90aW1lX21zIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAic3luY19wcm9jZXNzb3JfcXVldWVfYW5kX2ZsdXNoX3RpbWVfbXNfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJzeW5jX3Byb2Nlc3Nvcl9xdWV1ZV9hbmRfZmx1c2hfdGltZV9tc19zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJzeW5jX3Byb2Nlc3Nvcl9iYXRjaF9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAic3luY19wcm9jZXNzb3JfYmF0Y2hfc2l6ZV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInN5bmNfcHJvY2Vzc29yX2JhdGNoX3NpemVfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAic3luY19wcm9jZXNzX3RpbWUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJzeW5jX3Byb2Nlc3NfdGltZV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInN5bmNfcHJvY2Vzc190aW1lX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImRiaW5pdHRpbWVfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJkYmluaXR0aW1lX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImNvbW1pdF9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb3Bvc2FsX2Fja19jcmVhdGlvbl9sYXRlbmN5IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvcG9zYWxfYWNrX2NyZWF0aW9uX2xhdGVuY3lfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9wb3NhbF9hY2tfY3JlYXRpb25fbGF0ZW5jeV9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJzZXNzaW9uX3F1ZXVlc19kcmFpbmVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAic2Vzc2lvbl9xdWV1ZXNfZHJhaW5lZF9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInNlc3Npb25fcXVldWVzX2RyYWluZWRfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAic25hcF9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImZzeW5jdGltZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImZzeW5jdGltZV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImZzeW5jdGltZV9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXNwb25zZV9wYWNrZXRfY2FjaGVfbWlzc2VzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibWluX2xhdGVuY3kiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwYWNrZXRzX3NlbnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJkZWFkX3dhdGNoZXJzX2NsZWFuZXJfbGF0ZW5jeV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImRlYWRfd2F0Y2hlcnNfY2xlYW5lcl9sYXRlbmN5X3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIndyaXRlX2NvbW1pdHByb2NfdGltZV9tcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIndyaXRlX2NvbW1pdHByb2NfdGltZV9tc19jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIndyaXRlX2NvbW1pdHByb2NfdGltZV9tc19zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXNwb25zZV9wYWNrZXRfZ2V0X2NoaWxkcmVuX2NhY2hlX2hpdHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJsYXN0X2NsaWVudF9yZXNwb25zZV9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicXVvcnVtX2Fja19sYXRlbmN5X2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicXVvcnVtX2Fja19sYXRlbmN5X3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImxhcmdlX3JlcXVlc3RzX3JlamVjdGVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibm9kZV9jcmVhdGVkX3dhdGNoX2NvdW50X2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibm9kZV9jcmVhdGVkX3dhdGNoX2NvdW50X3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm5vbl9tdGxzX3JlbW90ZV9jb25uX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVhZHNfaXNzdWVkX2Zyb21fc2Vzc2lvbl9xdWV1ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlYWRzX2lzc3VlZF9mcm9tX3Nlc3Npb25fcXVldWVfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWFkc19pc3N1ZWRfZnJvbV9zZXNzaW9uX3F1ZXVlX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlYWRzX2FmdGVyX3dyaXRlX2luX3Nlc3Npb25fcXVldWUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWFkc19hZnRlcl93cml0ZV9pbl9zZXNzaW9uX3F1ZXVlX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVhZHNfYWZ0ZXJfd3JpdGVfaW5fc2Vzc2lvbl9xdWV1ZV9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwZW5kaW5nX3Nlc3Npb25fcXVldWVfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInBlbmRpbmdfc2Vzc2lvbl9xdWV1ZV9zaXplX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicGVuZGluZ19zZXNzaW9uX3F1ZXVlX3NpemVfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ2xvYmFsX3Nlc3Npb25zIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvcGFnYXRpb25fbGF0ZW5jeSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb3BhZ2F0aW9uX2xhdGVuY3lfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9wYWdhdGlvbl9sYXRlbmN5X3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVuc2VtYmxlX2F1dGhfZmFpbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInN0YXJ0dXBfdHhuc19sb2FkZWRfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJzdGFydHVwX3R4bnNfbG9hZGVkX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImVuc2VtYmxlX2F1dGhfc3VjY2VzcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByZXBfcHJvY2Vzc29yX3JlcXVlc3RfcXVldWVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAib3Blbl9maWxlX2Rlc2NyaXB0b3JfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJzZXJ2ZXJfd3JpdGVfY29tbWl0dGVkX3RpbWVfbXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJzZXJ2ZXJfd3JpdGVfY29tbWl0dGVkX3RpbWVfbXNfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJzZXJ2ZXJfd3JpdGVfY29tbWl0dGVkX3RpbWVfbXNfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZGVhZF93YXRjaGVyc19xdWV1ZWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZXF1ZXN0X2NvbW1pdF9xdWV1ZWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJkaWZmX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJlcF9wcm9jZXNzX3RpbWVfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcmVwX3Byb2Nlc3NfdGltZV9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJub2RlX2NoYW5nZWRfd2F0Y2hfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJub2RlX2NoYW5nZWRfd2F0Y2hfY291bnRfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJub2RlX2NoYW5nZWRfd2F0Y2hfY291bnRfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYXBwcm94aW1hdGVfZGF0YV9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibnVtX2FsaXZlX2Nvbm5lY3Rpb25zIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibGVhcm5lcl9wcm9wb3NhbF9yZWNlaXZlZF9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlYWRfY29tbWl0X3Byb2NfaXNzdWVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVhZF9jb21taXRfcHJvY19pc3N1ZWRfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWFkX2NvbW1pdF9wcm9jX2lzc3VlZF9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJmb2xsb3dlcl9zeW5jX3RpbWVfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJmb2xsb3dlcl9zeW5jX3RpbWVfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYnl0ZXNfcmVjZWl2ZWRfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJjb25uZWN0aW9uX3Rva2VuX2RlZmljaXRfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJjb25uZWN0aW9uX3Rva2VuX2RlZmljaXRfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAic3RhbGVfc2Vzc2lvbnNfZXhwaXJlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm5vZGVfY2hpbGRyZW5fd2F0Y2hfY291bnRfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJub2RlX2NoaWxkcmVuX3dhdGNoX2NvdW50X3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInVwZGF0ZWxhdGVuY3kiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJ1cGRhdGVsYXRlbmN5X2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAidXBkYXRlbGF0ZW5jeV9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJjb25jdXJyZW50X3JlcXVlc3RfcHJvY2Vzc2luZ19pbl9jb21taXRfcHJvY2Vzc29yX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiY29uY3VycmVudF9yZXF1ZXN0X3Byb2Nlc3NpbmdfaW5fY29tbWl0X3Byb2Nlc3Nvcl9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJlcGhlbWVyYWxzX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAianZtX21lbW9yeV9ieXRlc191c2VkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAianZtX21lbW9yeV9ieXRlc19jb21taXR0ZWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJqdm1fbWVtb3J5X2J5dGVzX21heCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImp2bV9tZW1vcnlfYnl0ZXNfaW5pdCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImp2bV9tZW1vcnlfcG9vbF9ieXRlc191c2VkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAianZtX21lbW9yeV9wb29sX2J5dGVzX2NvbW1pdHRlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImp2bV9tZW1vcnlfcG9vbF9ieXRlc19tYXgiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJqdm1fbWVtb3J5X3Bvb2xfYnl0ZXNfaW5pdCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInF1b3J1bV9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAib21fcHJvcG9zYWxfcHJvY2Vzc190aW1lX21zX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAib21fcHJvcG9zYWxfcHJvY2Vzc190aW1lX21zX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlc3BvbnNlX3BhY2tldF9nZXRfY2hpbGRyZW5fY2FjaGVfbWlzc2VzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicG9vbCIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInF1YW50aWxlIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ2MiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJ2ZXJzaW9uIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAidmVuZG9yIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicnVudGltZSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImxldmVsIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYXJlYSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfV0sICJ0YWJsZV9pZCI6ICJwdXNoZ2F0ZXdheV9kYm1fcHVsc2Fyem9va2VlcGVyX2JrcHVsbC5ncm91cDEiLCAidGFibGVfZGVzYyI6ICJcdTllZDhcdThiYTRcdTUyMDZcdTdlYzQiLCAidGFibGVfbmFtZSI6ICJncm91cDEifV0sICJwbHVnaW5fdHlwZSI6ICJQdXNoZ2F0ZXdheSIsICJvc190eXBlX2xpc3QiOiBbImxpbnV4IiwgIndpbmRvd3MiLCAibGludXhfYWFyY2g2NCJdfSwgImNvbGxlY3RfdHlwZSI6ICJQdXNoZ2F0ZXdheSIsICJ0YXJnZXRfbm9kZXMiOiBbXSwgInRhcmdldF9ub2RlX3R5cGUiOiAiVE9QTyIsICJ0YXJnZXRfb2JqZWN0X3R5cGUiOiAiU0VSVklDRSJ9fQ== \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/collect/0.redis.dbm_predixy_exporter.json b/dbm-ui/backend/db_monitor/tpls/collect/0.redis.dbm_predixy_exporter.json new file mode 100644 index 0000000000..138ac67e2b --- /dev/null +++ b/dbm-ui/backend/db_monitor/tpls/collect/0.redis.dbm_predixy_exporter.json @@ -0,0 +1,1026 @@ +{ + "bk_biz_id": 0, + "plugin_id": "dbm_predixy_exporter", + "db_type": "redis", + "details": { + "name": "dbm_predixy", + "label": "component", + "params": { + "plugin": { + "-addr": "{{ target.host.bk_host_innerip }}:{{ target.process[\"redis\"].bind_info[0].port }}", + "-bind": "${host}:${port}", + "-password-file": "/home/mysql/.exporter/{{ target.process[\"redis\"].bind_info[0].port }}.conf", + "服务实例维度注入": { + "app": "app", + "instance": "instance", + "cluster_name": "cluster_name", + "cluster_type": "cluster_type", + "instance_host": "instance_host", + "instance_port": "instance_port", + "instance_role": "instance_role", + "cluster_domain": "cluster_domain" + } + }, + "collector": { + "host": "127.0.0.1", + "port": "9121", + "period": 60, + "timeout": 60 + }, + "target_node_type": "TOPO", + "target_object_type": "SERVICE" + }, + "plugin_info": { + "plugin_id": "dbm_predixy_exporter", + "metric_json": [ + { + "fields": [ + { + "name": "_server", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "server", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "go_gc_duration_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_gc_duration_seconds_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_gc_duration_seconds_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_goroutines", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_info", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_alloc_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_alloc_bytes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_buck_hash_sys_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_frees_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_gc_sys_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_heap_alloc_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_heap_idle_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_heap_inuse_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_heap_objects", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_heap_released_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_heap_sys_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_last_gc_time_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_lookups_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_mallocs_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_mcache_inuse_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_mcache_sys_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_mspan_inuse_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_mspan_sys_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_next_gc_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_other_sys_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_stack_inuse_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_stack_sys_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_sys_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_threads", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_exporter_build_info", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_info", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_up", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_uptime", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_worker_threads", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_cpu_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_max_fds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_open_fds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_resident_memory_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_start_time_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_virtual_memory_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_virtual_memory_max_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "promhttp_metric_handler_requests_in_flight", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "promhttp_metric_handler_requests_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_server_latency_bucket", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "latency", + "le", + "server", + "addr" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_latency_bucket", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "latency", + "le", + "addr" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_server_latency_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "latency", + "server", + "addr" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_server_latency_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "latency", + "server", + "addr" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_latency_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "addr", + "latency" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_latency_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "addr", + "latency" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_server_connect", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "addr", + "server" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_server_connections", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "addr", + "server" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_server_recv_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "addr", + "server" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_server_requests", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "addr", + "server" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_server_responses", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "addr", + "server" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_server_send_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "addr", + "server" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_cluster_accept", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "addr" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_cluster_connections", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "addr" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_cluster_max_memory", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "addr" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_cluster_max_rss", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "addr" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_cluster_total_recv_client_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "addr" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_cluster_total_recv_server_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "addr" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_cluster_total_requests", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "addr" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_cluster_total_respones", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "addr" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_cluster_total_send_client_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "addr" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_cluster_total_send_server_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "addr" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_cluster_used_cpu", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "addr" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_cluster_used_cpu_sys", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "addr" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_cluster_used_cpu_user", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "addr" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_cluster_used_memory", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "addr" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_total_recv_client_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cluster", + "addr" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_total_requests", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "addr", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_total_responses", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "addr", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_total_send_client_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "addr", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_used_cpu", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "addr", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "predixy_used_memory", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "addr", + "cluster" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "version", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "latency", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "le", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "addr", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "cluster", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_predixy_exporter.Group1", + "table_desc": "分组1", + "table_name": "Group1" + } + ], + "plugin_type": "Exporter", + "os_type_list": [ + "linux" + ] + }, + "collect_type": "Exporter", + "target_nodes": [], + "target_node_type": "TOPO", + "target_object_type": "SERVICE" + } +} diff --git a/dbm-ui/backend/db_monitor/tpls/collect/0.redis.dbm_predixy_exporter.tpl64 b/dbm-ui/backend/db_monitor/tpls/collect/0.redis.dbm_predixy_exporter.tpl64 deleted file mode 100644 index 9d4ce9a990..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/collect/0.redis.dbm_predixy_exporter.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAicGx1Z2luX2lkIjogImRibV9wcmVkaXh5X2V4cG9ydGVyIiwgImRiX3R5cGUiOiAicmVkaXMiLCAiZGV0YWlscyI6IHsibmFtZSI6ICJkYm1fcHJlZGl4eSIsICJsYWJlbCI6ICJjb21wb25lbnQiLCAicGFyYW1zIjogeyJwbHVnaW4iOiB7Ii1hZGRyIjogInt7IHRhcmdldC5ob3N0LmJrX2hvc3RfaW5uZXJpcCB9fTp7eyB0YXJnZXQucHJvY2Vzc1tcInJlZGlzXCJdLmJpbmRfaW5mb1swXS5wb3J0IH19IiwgIi1iaW5kIjogIiR7aG9zdH06JHtwb3J0fSIsICItcGFzc3dvcmQtZmlsZSI6ICIvaG9tZS9teXNxbC8uZXhwb3J0ZXIve3sgdGFyZ2V0LnByb2Nlc3NbXCJyZWRpc1wiXS5iaW5kX2luZm9bMF0ucG9ydCB9fS5jb25mIiwgIlx1NjcwZFx1NTJhMVx1NWI5ZVx1NGY4Ylx1N2VmNFx1NWVhNlx1NmNlOFx1NTE2NSI6IHsiYXBwIjogImFwcCIsICJpbnN0YW5jZSI6ICJpbnN0YW5jZSIsICJjbHVzdGVyX25hbWUiOiAiY2x1c3Rlcl9uYW1lIiwgImNsdXN0ZXJfdHlwZSI6ICJjbHVzdGVyX3R5cGUiLCAiaW5zdGFuY2VfaG9zdCI6ICJpbnN0YW5jZV9ob3N0IiwgImluc3RhbmNlX3BvcnQiOiAiaW5zdGFuY2VfcG9ydCIsICJpbnN0YW5jZV9yb2xlIjogImluc3RhbmNlX3JvbGUiLCAiY2x1c3Rlcl9kb21haW4iOiAiY2x1c3Rlcl9kb21haW4ifX0sICJjb2xsZWN0b3IiOiB7Imhvc3QiOiAiMTI3LjAuMC4xIiwgInBvcnQiOiAiOTEyMSIsICJwZXJpb2QiOiA2MCwgInRpbWVvdXQiOiA2MH0sICJ0YXJnZXRfbm9kZV90eXBlIjogIlRPUE8iLCAidGFyZ2V0X29iamVjdF90eXBlIjogIlNFUlZJQ0UifSwgInBsdWdpbl9pbmZvIjogeyJwbHVnaW5faWQiOiAiZGJtX3ByZWRpeHlfZXhwb3J0ZXIiLCAibWV0cmljX2pzb24iOiBbeyJmaWVsZHMiOiBbeyJuYW1lIjogIl9zZXJ2ZXIiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAic2VydmVyIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJnb19nY19kdXJhdGlvbl9zZWNvbmRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fZ2NfZHVyYXRpb25fc2Vjb25kc19zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJnb19nY19kdXJhdGlvbl9zZWNvbmRzX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fZ29yb3V0aW5lcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX2luZm8iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJnb19tZW1zdGF0c19hbGxvY19ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX2FsbG9jX2J5dGVzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fbWVtc3RhdHNfYnVja19oYXNoX3N5c19ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX2ZyZWVzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fbWVtc3RhdHNfZ2Nfc3lzX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fbWVtc3RhdHNfaGVhcF9hbGxvY19ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX2hlYXBfaWRsZV9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX2hlYXBfaW51c2VfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJnb19tZW1zdGF0c19oZWFwX29iamVjdHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJnb19tZW1zdGF0c19oZWFwX3JlbGVhc2VkX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fbWVtc3RhdHNfaGVhcF9zeXNfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJnb19tZW1zdGF0c19sYXN0X2djX3RpbWVfc2Vjb25kcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX2xvb2t1cHNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJnb19tZW1zdGF0c19tYWxsb2NzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fbWVtc3RhdHNfbWNhY2hlX2ludXNlX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fbWVtc3RhdHNfbWNhY2hlX3N5c19ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX21zcGFuX2ludXNlX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fbWVtc3RhdHNfbXNwYW5fc3lzX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fbWVtc3RhdHNfbmV4dF9nY19ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX290aGVyX3N5c19ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX3N0YWNrX2ludXNlX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fbWVtc3RhdHNfc3RhY2tfc3lzX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fbWVtc3RhdHNfc3lzX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fdGhyZWFkcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByZWRpeHlfZXhwb3J0ZXJfYnVpbGRfaW5mbyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByZWRpeHlfaW5mbyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByZWRpeHlfdXAiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcmVkaXh5X3VwdGltZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByZWRpeHlfd29ya2VyX3RocmVhZHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jZXNzX2NwdV9zZWNvbmRzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY2Vzc19tYXhfZmRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY2Vzc19vcGVuX2ZkcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb2Nlc3NfcmVzaWRlbnRfbWVtb3J5X2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY2Vzc19zdGFydF90aW1lX3NlY29uZHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jZXNzX3ZpcnR1YWxfbWVtb3J5X2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY2Vzc192aXJ0dWFsX21lbW9yeV9tYXhfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9taHR0cF9tZXRyaWNfaGFuZGxlcl9yZXF1ZXN0c19pbl9mbGlnaHQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9taHR0cF9tZXRyaWNfaGFuZGxlcl9yZXF1ZXN0c190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByZWRpeHlfc2VydmVyX2xhdGVuY3lfYnVja2V0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsibGF0ZW5jeSIsICJsZSIsICJzZXJ2ZXIiLCAiYWRkciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJlZGl4eV9sYXRlbmN5X2J1Y2tldCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImxhdGVuY3kiLCAibGUiLCAiYWRkciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJlZGl4eV9zZXJ2ZXJfbGF0ZW5jeV9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJsYXRlbmN5IiwgInNlcnZlciIsICJhZGRyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcmVkaXh5X3NlcnZlcl9sYXRlbmN5X2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsibGF0ZW5jeSIsICJzZXJ2ZXIiLCAiYWRkciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJlZGl4eV9sYXRlbmN5X3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImFkZHIiLCAibGF0ZW5jeSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJlZGl4eV9sYXRlbmN5X2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiYWRkciIsICJsYXRlbmN5Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcmVkaXh5X3NlcnZlcl9jb25uZWN0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiYWRkciIsICJzZXJ2ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByZWRpeHlfc2VydmVyX2Nvbm5lY3Rpb25zIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiYWRkciIsICJzZXJ2ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByZWRpeHlfc2VydmVyX3JlY3ZfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJhZGRyIiwgInNlcnZlciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJlZGl4eV9zZXJ2ZXJfcmVxdWVzdHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJhZGRyIiwgInNlcnZlciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJlZGl4eV9zZXJ2ZXJfcmVzcG9uc2VzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiYWRkciIsICJzZXJ2ZXIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByZWRpeHlfc2VydmVyX3NlbmRfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJhZGRyIiwgInNlcnZlciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJlZGl4eV9jbHVzdGVyX2FjY2VwdCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImFkZHIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByZWRpeHlfY2x1c3Rlcl9jb25uZWN0aW9ucyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImFkZHIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByZWRpeHlfY2x1c3Rlcl9tYXhfbWVtb3J5IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiYWRkciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJlZGl4eV9jbHVzdGVyX21heF9yc3MiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJhZGRyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcmVkaXh5X2NsdXN0ZXJfdG90YWxfcmVjdl9jbGllbnRfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJhZGRyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcmVkaXh5X2NsdXN0ZXJfdG90YWxfcmVjdl9zZXJ2ZXJfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJhZGRyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcmVkaXh5X2NsdXN0ZXJfdG90YWxfcmVxdWVzdHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJhZGRyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcmVkaXh5X2NsdXN0ZXJfdG90YWxfcmVzcG9uZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJhZGRyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcmVkaXh5X2NsdXN0ZXJfdG90YWxfc2VuZF9jbGllbnRfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJhZGRyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcmVkaXh5X2NsdXN0ZXJfdG90YWxfc2VuZF9zZXJ2ZXJfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJhZGRyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcmVkaXh5X2NsdXN0ZXJfdXNlZF9jcHUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJhZGRyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcmVkaXh5X2NsdXN0ZXJfdXNlZF9jcHVfc3lzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiYWRkciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJlZGl4eV9jbHVzdGVyX3VzZWRfY3B1X3VzZXIiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJhZGRyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcmVkaXh5X2NsdXN0ZXJfdXNlZF9tZW1vcnkiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJhZGRyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcmVkaXh5X3RvdGFsX3JlY3ZfY2xpZW50X2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY2x1c3RlciIsICJhZGRyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcmVkaXh5X3RvdGFsX3JlcXVlc3RzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiYWRkciIsICJjbHVzdGVyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcmVkaXh5X3RvdGFsX3Jlc3BvbnNlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImFkZHIiLCAiY2x1c3RlciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJlZGl4eV90b3RhbF9zZW5kX2NsaWVudF9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImFkZHIiLCAiY2x1c3RlciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJlZGl4eV91c2VkX2NwdSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImFkZHIiLCAiY2x1c3RlciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJlZGl4eV91c2VkX21lbW9yeSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImFkZHIiLCAiY2x1c3RlciJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAidmVyc2lvbiIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImxhdGVuY3kiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJsZSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImFkZHIiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJjbHVzdGVyIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9XSwgInRhYmxlX2lkIjogImV4cG9ydGVyX2RibV9wcmVkaXh5X2V4cG9ydGVyLkdyb3VwMSIsICJ0YWJsZV9kZXNjIjogIlx1NTIwNlx1N2VjNDEiLCAidGFibGVfbmFtZSI6ICJHcm91cDEifV0sICJwbHVnaW5fdHlwZSI6ICJFeHBvcnRlciIsICJvc190eXBlX2xpc3QiOiBbImxpbnV4Il19LCAiY29sbGVjdF90eXBlIjogIkV4cG9ydGVyIiwgInRhcmdldF9ub2RlcyI6IFtdLCAidGFyZ2V0X25vZGVfdHlwZSI6ICJUT1BPIiwgInRhcmdldF9vYmplY3RfdHlwZSI6ICJTRVJWSUNFIn19 \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/collect/0.redis.dbm_redis_exporter.json b/dbm-ui/backend/db_monitor/tpls/collect/0.redis.dbm_redis_exporter.json new file mode 100644 index 0000000000..2a92b02739 --- /dev/null +++ b/dbm-ui/backend/db_monitor/tpls/collect/0.redis.dbm_redis_exporter.json @@ -0,0 +1,4232 @@ +{ + "bk_biz_id": 0, + "plugin_id": "dbm_redis_exporter", + "db_type": "redis", + "details": { + "name": "dbm_redis", + "label": "component", + "params": { + "plugin": { + "-redis.addr": "{{ target.host.bk_host_innerip }}:{{ target.process[\"redis\"].bind_info[0].port }}", + "-config-command": "CONFXX", + "-web.listen-address": "${host}:${port}", + "-redis.password-file": "/home/mysql/.exporter/{{ target.process[\"redis\"].bind_info[0].port }}.conf", + "服务实例维度注入": { + "app": "app", + "instance": "instance", + "cluster_name": "cluster_name", + "cluster_type": "cluster_type", + "instance_host": "instance_host", + "instance_port": "instance_port", + "instance_role": "instance_role", + "cluster_domain": "cluster_domain" + } + }, + "collector": { + "host": "127.0.0.1", + "port": "9432", + "period": 60, + "timeout": 60 + }, + "target_node_type": "TOPO", + "target_object_type": "SERVICE" + }, + "plugin_info": { + "plugin_id": "dbm_redis_exporter", + "metric_json": [ + { + "fields": [ + { + "name": "redis_rocksdb_level_deletions", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "store", + "level" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_level_entries", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "level", + "store" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_level_files", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "level", + "store" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_level_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "store", + "level" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_gc_duration_seconds_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_gc_duration_seconds_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cluster_stats_messages_fail_received", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_exporter_scrape_duration_seconds_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_exporter_scrape_duration_seconds_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_binlogcf_sst_files_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_compaction_pending", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_cur_size_all_mem_tables", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_estimate_live_data_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_estimate_num_keys", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_estimate_pending_compaction_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_estimate_table_readers_mem", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_kvstore_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_live_sst_files_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_mem_table_flush_pending", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_total_memory", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_total_sst_files_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_total_commands_cost_ns_", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cluster_stats_messages_ping_received", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rdb_changes_since_last_save", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_number_merge_failures", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_total_expire_keys", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_dbstats_write_self", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_virtual_memory_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_memory_used_scripts_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_config_maxclients", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_number_db_prev_found", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_target_scrape_request_errors_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_loading_dump_file", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_dbstats_block_cache_data_miss", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rdb_bgsave_in_progress", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_goroutines", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_open_fds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cmdstat_backup", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_dbstats_number_db_seek_found", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_memory_used_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_getupdatessince_calls", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_dbstats_number_superversion_acquires", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_total_commands_send_packet_cost_ns_", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_repl_backlog_is_active", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_defrag_hits", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cpu_sys_main_thread_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_block_cache_index_bytes_insert", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_replica_resyncs_full", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_compaction_optimized_del_drop_obsolete", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_allocator_resident_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_repl_backlog_first_byte_offset", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_heap_objects", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_row_cache_miss", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_buck_hash_sys_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_memory_used_rss_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_net_input_bytes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_level_size_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_commands_in_queue", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_mspan_inuse_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_total_commands_workpool_execute_cost_ns_", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_dbstats_number_db_next_found", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_master_sync_in_progress", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "master_host", + "master_port" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_pubsub_channels", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_level_files_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_mcache_sys_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_block_cachecompressed_add", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_other_sys_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_number_superversion_releases", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_module_fork_in_progress", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_exporter_last_scrape_error", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "err" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_l0_hit", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_dbstats_number_db_next", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cluster_enabled", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_exporter_last_scrape_connect_time_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_aof_last_rewrite_duration_sec", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cluster_slots_assigend", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_commands_processed_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_module_fork_last_cow_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_db_mutex_wait_micros", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_keyspace_misses_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_dbstats_memtable_miss", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_block_cache_index_add", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rdb_last_bgsave_status", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cmdstat_auth", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_bloom_filter_prefix_useful", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_block_cache_index_hit", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rdb_current_bgsave_duration_sec", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_heap_idle_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cmdstat_select", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_blockcache_usage", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_commands_failed_calls_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cmd" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_block_cache_filter_add", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_blockcache_capacity", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_tracking_total_prefixes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_commands_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cmd" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_connections_received_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_mcache_inuse_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rdb_last_bgsave_duration_sec", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_write_timeout", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_connected_slave_binlog_lag", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "slave_ip", + "slave_port", + "slave_state" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_instance_info", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "process_id", + "run_id", + "tcp_port", + "redis_version", + "maxmemory_policy", + "os", + "redis_mode", + "redis_build_id", + "role" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_commands_executed_in_workpool_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_memory_used_lua_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_commands_rejected_calls_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cmd" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cpu_user_main_thread_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_sim_block_cache_miss", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_info", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "version" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_process_id", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_dbstats_block_cache_bytes_read", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_stack_sys_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_datadir_size_kb", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_dbstats_block_cache_data_hit", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_compact_write_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_replica_partial_resync_accepted", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_memory_used_overhead_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_connected_slaves", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_level_entries_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cpu_sys_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_block_cache_filter_hit", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_deleting_expire_keys", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cluster_stats_messages_ping_sent", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_dbstats_bytes_read", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_row_cache_hit", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rejected_connections_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_compaction_key_drop_user", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_slowlog_last_id", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_dbstats_l2andup_hit", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cluster_stats_messages_pong_sent", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cluster_stats_messages_meet_sent", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cluster_slots_ok", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_number_multiget_keys_read", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cmdstat_applybinlogsv2", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_blocked_clients", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_mem_clients_slaves", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_total_invalid_packets", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_compaction_filter_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_net_output_bytes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_replica_partial_resync_denied", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_aof_last_cow_size_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_aof_enabled", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_compaction_cancelled", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_dbstats_wal_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cmdstat_client", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cmdstat_ping", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_number_superversion_cleanups", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_keyspace_wrong_versionep", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_compaction_key_drop_range_del", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_expired_stale_percentage", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_up", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_filter_operation_time_nanos", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_db_avg_ttl_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "db" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_lookups_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_number_db_prev", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_config_maxmemory_policy", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_expired_time_cap_reached_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_dbstats_number_block_compressed", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_last_gc_time_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cmdstat_cluster", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_dbstats_bytes_written", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_heap_released_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_memory_max_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_second_repl_offset", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_clients_in_timeout_table", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_bloom_filter_prefix_checked", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_merge_operation_time_nanos", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_pubsub_patterns", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_aof_rewrite_in_progress", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_memtable_compaction_micros", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_dbstats_compaction_key_drop_new", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_allocator_rss_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_mem_fragmentation_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cmdstat_confxx", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cmdstat_unseen", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_total_stricky_packets", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rdb_last_cow_size_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_dbstats_db_iter_bytes_read", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_migrate_cached_sockets_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cmdstat_info", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_db_keys_expiring", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "db" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_master_link_up", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "master_host", + "master_port" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_read_amp_total_read_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cluster_slots_fail", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_memory_used_dataset_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_stall_micros", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cluster_slots_pfail", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_sim_block_cache_hit", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_total_error_replies", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cmdstat_slowlog", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_max_fds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_number_multiget_get", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_number_iter_skip", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_cpu_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_io_threaded_reads_processed", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cluster_connections", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_last_key_groups_scrape_duration_milliseconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cmdstat_binlog_heartbeat", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rdb_last_save_timestamp_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_no_file_closes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_block_cachecompressed_miss", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_dbstats_number_db_seek", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_l0_num_files_stall_micros", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_io_threads_active", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cmdstat_set", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_sys_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cluster_clients", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_block_cache_index_miss", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_tracking_total_items", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_defrag_misses", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_defrag_key_misses", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_io_threaded_writes_processed", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_config_maxmemory", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_exporter_scrape_duration_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_dbstats_compact_read_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_master_last_io_seconds_ago", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "master_host", + "master_port" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_slave_priority", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_level_deletions_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_total_writes_processed", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_mem_clients_normal", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_uptime_in_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_slave_info", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "master_host", + "master_port", + "read_only" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_block_cachecompressed_add_failures", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_slave_repl_offset", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "master_host", + "master_port" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_blockcache_pinnedusage", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_slave_expires_tracked_keys", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_allocator_allocated_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cluster_messages_sent_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_persistent_cache_hit", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_resident_memory_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_aof_buffer_length", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_total_commands_execute_cost_ns_", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_bloom_filter_useful", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_memory_used_peak_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_lazyfree_pending_objects", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_dbstats_block_cache_add_failures", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_aof_rewrite_scheduled", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_tracking_clients", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_mspan_sys_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_alloc_bytes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_number_rate_limiter_drains", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cluster_known_nodes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_aof_pending_rewrite", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_master_repl_offset", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cluster_messages_received_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_number_block_not_compressed", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_connected_slave_lag_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "slave_ip", + "slave_port", + "slave_state" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_aof_base_size_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_mem_fragmentation_ratio", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_dump_payload_sanitizations", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_dbstats_block_cache_add", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_defrag_key_hits", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_aof_last_bgrewrite_status", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cpu_user_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_slowlog_length", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_aof_last_write_status", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_connected_clients", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cluster_size", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_allocator_rss_ratio", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cmdstat_adminset", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_stack_inuse_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_l1_hit", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_heap_alloc_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_block_cache_filter_miss", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_next_gc_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_mem_not_counted_for_eviction_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_dbstats_block_cache_miss", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_heap_sys_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_aof_rewrite_buffer_length", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_aof_delayed_fsync", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_wal_synced", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_frees_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_start_time_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_latest_fork_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cluster_stats_messages_meet_received", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_dbstats_number_block_decompressed", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_number_direct_load_table_properties", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_dbstats_write_wal", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_write_other", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_total_commands_workpool_queue_cost_ns_", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cluster_current_epoch", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_memory_used_startup_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_block_cachecompressed_hit", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_compaction_kv_expired_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_bg_error_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_client_recent_max_input_buffer_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_expired_keys_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_active_defrag_running", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_compaction_range_del_drop_obsolete", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cpu_user_children_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_db_keys", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "db" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_number_reseeks_iteration", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_dbstats_number_keys_written", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_tracking_total_keys", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_block_cache_filter_bytes_insert", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cluster_state", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_commands_duration_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cmd" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_client_recent_max_output_buffer_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_dbstats_block_cache_data_bytes_insert", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_start_time_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_persistent_cache_miss", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_read_amp_estimate_useful_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_block_cache_filter_bytes_evict", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_allocator_frag_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_evicted_keys_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_connected_slave_offset_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "slave_ip", + "slave_port", + "slave_state" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cmdstat_readonly", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_dbstats_block_cache_hit", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_gc_sys_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_dbstats_number_keys_read", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_dbstats_block_cache_data_add", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_last_slow_execution_duration_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_repl_backlog_history_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_total_reads_processed", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_num_iterators", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_number_keys_updated", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_exporter_scrapes_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_replication_backlog_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_threads", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_aof_current_rewrite_duration_sec", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_flush_write_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cpu_sys_children_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_memtable_hit", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_exporter_last_scrape_duration_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_heap_inuse_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cluster_stats_messages_pong_received", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_keyspace_hits_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_no_file_errors", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_scanpoint", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_dbstats_no_file_opens", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_dbstats_compaction_key_drop_obsolete", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_exporter_build_info", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "golang_version", + "version", + "build_date", + "commit_sha" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_virtual_memory_max_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_errors_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "err" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cluster_my_epoch", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_alloc_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_rate_limit_delay_millis", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_number_deletes_filtered", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_number_multiget_bytes_read", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_aof_pending_bio_fsync", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_cmdstat_dbsize", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_config_io_threads", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_dbstats_block_cache_bytes_write", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_gc_duration_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "quantile" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_l0_slowdown_micros", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_rocksdb_block_cache_index_bytes_evict", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_allocator_frag_ratio", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_number_of_cached_scripts", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_aof_current_size_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "go_memstats_mallocs_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_allocator_active_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "redis_unexpected_error_replies", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "store", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "level", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "quantile", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "slave_ip", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "slave_state", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "maxmemory_policy", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "redis_mode", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "os", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "redis_build_id", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "redis_version", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "role", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "run_id", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "tcp_port", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "version", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "master_port", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "build_date", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "commit_sha", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "golang_version", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "master_host", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "err", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "cmd", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "slave_port", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "process_id", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "db", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "read_only", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_redis_exporter.group1", + "table_desc": "group1", + "table_name": "group1" + } + ], + "plugin_type": "Exporter", + "os_type_list": [ + "linux" + ] + }, + "collect_type": "Exporter", + "target_nodes": [], + "target_node_type": "TOPO", + "target_object_type": "SERVICE" + } +} diff --git a/dbm-ui/backend/db_monitor/tpls/collect/0.redis.dbm_redis_exporter.tpl64 b/dbm-ui/backend/db_monitor/tpls/collect/0.redis.dbm_redis_exporter.tpl64 deleted file mode 100644 index 43aa2a0104..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/collect/0.redis.dbm_redis_exporter.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAicGx1Z2luX2lkIjogImRibV9yZWRpc19leHBvcnRlciIsICJkYl90eXBlIjogInJlZGlzIiwgImRldGFpbHMiOiB7Im5hbWUiOiAiZGJtX3JlZGlzIiwgImxhYmVsIjogImNvbXBvbmVudCIsICJwYXJhbXMiOiB7InBsdWdpbiI6IHsiLXJlZGlzLmFkZHIiOiAie3sgdGFyZ2V0Lmhvc3QuYmtfaG9zdF9pbm5lcmlwIH19Ont7IHRhcmdldC5wcm9jZXNzW1wicmVkaXNcIl0uYmluZF9pbmZvWzBdLnBvcnQgfX0iLCAiLWNvbmZpZy1jb21tYW5kIjogIkNPTkZYWCIsICItd2ViLmxpc3Rlbi1hZGRyZXNzIjogIiR7aG9zdH06JHtwb3J0fSIsICItcmVkaXMucGFzc3dvcmQtZmlsZSI6ICIvaG9tZS9teXNxbC8uZXhwb3J0ZXIve3sgdGFyZ2V0LnByb2Nlc3NbXCJyZWRpc1wiXS5iaW5kX2luZm9bMF0ucG9ydCB9fS5jb25mIiwgIlx1NjcwZFx1NTJhMVx1NWI5ZVx1NGY4Ylx1N2VmNFx1NWVhNlx1NmNlOFx1NTE2NSI6IHsiYXBwIjogImFwcCIsICJpbnN0YW5jZSI6ICJpbnN0YW5jZSIsICJjbHVzdGVyX25hbWUiOiAiY2x1c3Rlcl9uYW1lIiwgImNsdXN0ZXJfdHlwZSI6ICJjbHVzdGVyX3R5cGUiLCAiaW5zdGFuY2VfaG9zdCI6ICJpbnN0YW5jZV9ob3N0IiwgImluc3RhbmNlX3BvcnQiOiAiaW5zdGFuY2VfcG9ydCIsICJpbnN0YW5jZV9yb2xlIjogImluc3RhbmNlX3JvbGUiLCAiY2x1c3Rlcl9kb21haW4iOiAiY2x1c3Rlcl9kb21haW4ifX0sICJjb2xsZWN0b3IiOiB7Imhvc3QiOiAiMTI3LjAuMC4xIiwgInBvcnQiOiAiOTQzMiIsICJwZXJpb2QiOiA2MCwgInRpbWVvdXQiOiA2MH0sICJ0YXJnZXRfbm9kZV90eXBlIjogIlRPUE8iLCAidGFyZ2V0X29iamVjdF90eXBlIjogIlNFUlZJQ0UifSwgInBsdWdpbl9pbmZvIjogeyJwbHVnaW5faWQiOiAiZGJtX3JlZGlzX2V4cG9ydGVyIiwgIm1ldHJpY19qc29uIjogW3siZmllbGRzIjogW3sibmFtZSI6ICJyZWRpc19yb2Nrc2RiX2xldmVsX2RlbGV0aW9ucyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbInN0b3JlIiwgImxldmVsIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX2xldmVsX2VudHJpZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJsZXZlbCIsICJzdG9yZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl9sZXZlbF9maWxlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImxldmVsIiwgInN0b3JlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX2xldmVsX3NpemUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJzdG9yZSIsICJsZXZlbCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fZ2NfZHVyYXRpb25fc2Vjb25kc19zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJnb19nY19kdXJhdGlvbl9zZWNvbmRzX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfY2x1c3Rlcl9zdGF0c19tZXNzYWdlc19mYWlsX3JlY2VpdmVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfZXhwb3J0ZXJfc2NyYXBlX2R1cmF0aW9uX3NlY29uZHNfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfZXhwb3J0ZXJfc2NyYXBlX2R1cmF0aW9uX3NlY29uZHNfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX2JpbmxvZ2NmX3NzdF9maWxlc19zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl9jb21wYWN0aW9uX3BlbmRpbmciLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX2N1cl9zaXplX2FsbF9tZW1fdGFibGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl9lc3RpbWF0ZV9saXZlX2RhdGFfc2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfZXN0aW1hdGVfbnVtX2tleXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX2VzdGltYXRlX3BlbmRpbmdfY29tcGFjdGlvbl9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfZXN0aW1hdGVfdGFibGVfcmVhZGVyc19tZW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX2t2c3RvcmVfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX2xpdmVfc3N0X2ZpbGVzX3NpemUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX21lbV90YWJsZV9mbHVzaF9wZW5kaW5nIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl90b3RhbF9tZW1vcnkiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX3RvdGFsX3NzdF9maWxlc19zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfdG90YWxfY29tbWFuZHNfY29zdF9uc18iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19jbHVzdGVyX3N0YXRzX21lc3NhZ2VzX3BpbmdfcmVjZWl2ZWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yZGJfY2hhbmdlc19zaW5jZV9sYXN0X3NhdmUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX251bWJlcl9tZXJnZV9mYWlsdXJlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3RvdGFsX2V4cGlyZV9rZXlzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl9kYnN0YXRzX3dyaXRlX3NlbGYiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jZXNzX3ZpcnR1YWxfbWVtb3J5X2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfbWVtb3J5X3VzZWRfc2NyaXB0c19ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2NvbmZpZ19tYXhjbGllbnRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl9udW1iZXJfZGJfcHJldl9mb3VuZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3RhcmdldF9zY3JhcGVfcmVxdWVzdF9lcnJvcnNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19sb2FkaW5nX2R1bXBfZmlsZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfZGJzdGF0c19ibG9ja19jYWNoZV9kYXRhX21pc3MiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yZGJfYmdzYXZlX2luX3Byb2dyZXNzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fZ29yb3V0aW5lcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb2Nlc3Nfb3Blbl9mZHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19jbWRzdGF0X2JhY2t1cCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfZGJzdGF0c19udW1iZXJfZGJfc2Vla19mb3VuZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX21lbW9yeV91c2VkX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl9nZXR1cGRhdGVzc2luY2VfY2FsbHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX2Ric3RhdHNfbnVtYmVyX3N1cGVydmVyc2lvbl9hY3F1aXJlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3RvdGFsX2NvbW1hbmRzX3NlbmRfcGFja2V0X2Nvc3RfbnNfIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcmVwbF9iYWNrbG9nX2lzX2FjdGl2ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2RlZnJhZ19oaXRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfY3B1X3N5c19tYWluX3RocmVhZF9zZWNvbmRzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl9ibG9ja19jYWNoZV9pbmRleF9ieXRlc19pbnNlcnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yZXBsaWNhX3Jlc3luY3NfZnVsbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfY29tcGFjdGlvbl9vcHRpbWl6ZWRfZGVsX2Ryb3Bfb2Jzb2xldGUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19hbGxvY2F0b3JfcmVzaWRlbnRfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yZXBsX2JhY2tsb2dfZmlyc3RfYnl0ZV9vZmZzZXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJnb19tZW1zdGF0c19oZWFwX29iamVjdHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX3Jvd19jYWNoZV9taXNzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fbWVtc3RhdHNfYnVja19oYXNoX3N5c19ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX21lbW9yeV91c2VkX3Jzc19ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX25ldF9pbnB1dF9ieXRlc190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfbGV2ZWxfc2l6ZV9zdW0iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19jb21tYW5kc19pbl9xdWV1ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX21zcGFuX2ludXNlX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfdG90YWxfY29tbWFuZHNfd29ya3Bvb2xfZXhlY3V0ZV9jb3N0X25zXyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfZGJzdGF0c19udW1iZXJfZGJfbmV4dF9mb3VuZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX21hc3Rlcl9zeW5jX2luX3Byb2dyZXNzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsibWFzdGVyX2hvc3QiLCAibWFzdGVyX3BvcnQiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3B1YnN1Yl9jaGFubmVscyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfbGV2ZWxfZmlsZXNfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fbWVtc3RhdHNfbWNhY2hlX3N5c19ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfYmxvY2tfY2FjaGVjb21wcmVzc2VkX2FkZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX290aGVyX3N5c19ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfbnVtYmVyX3N1cGVydmVyc2lvbl9yZWxlYXNlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX21vZHVsZV9mb3JrX2luX3Byb2dyZXNzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfZXhwb3J0ZXJfbGFzdF9zY3JhcGVfZXJyb3IiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJlcnIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfbDBfaGl0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl9kYnN0YXRzX251bWJlcl9kYl9uZXh0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfY2x1c3Rlcl9lbmFibGVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfZXhwb3J0ZXJfbGFzdF9zY3JhcGVfY29ubmVjdF90aW1lX3NlY29uZHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19hb2ZfbGFzdF9yZXdyaXRlX2R1cmF0aW9uX3NlYyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2NsdXN0ZXJfc2xvdHNfYXNzaWdlbmQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19jb21tYW5kc19wcm9jZXNzZWRfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19tb2R1bGVfZm9ya19sYXN0X2Nvd19zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl9kYl9tdXRleF93YWl0X21pY3JvcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2tleXNwYWNlX21pc3Nlc190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfZGJzdGF0c19tZW10YWJsZV9taXNzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl9ibG9ja19jYWNoZV9pbmRleF9hZGQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yZGJfbGFzdF9iZ3NhdmVfc3RhdHVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfY21kc3RhdF9hdXRoIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl9ibG9vbV9maWx0ZXJfcHJlZml4X3VzZWZ1bCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfYmxvY2tfY2FjaGVfaW5kZXhfaGl0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcmRiX2N1cnJlbnRfYmdzYXZlX2R1cmF0aW9uX3NlYyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX2hlYXBfaWRsZV9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2NtZHN0YXRfc2VsZWN0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl9ibG9ja2NhY2hlX3VzYWdlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfY29tbWFuZHNfZmFpbGVkX2NhbGxzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY21kIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX2Jsb2NrX2NhY2hlX2ZpbHRlcl9hZGQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX2Jsb2NrY2FjaGVfY2FwYWNpdHkiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc190cmFja2luZ190b3RhbF9wcmVmaXhlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2NvbW1hbmRzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY21kIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19jb25uZWN0aW9uc19yZWNlaXZlZF90b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX21jYWNoZV9pbnVzZV9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JkYl9sYXN0X2Jnc2F2ZV9kdXJhdGlvbl9zZWMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX3dyaXRlX3RpbWVvdXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19jb25uZWN0ZWRfc2xhdmVfYmlubG9nX2xhZyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbInNsYXZlX2lwIiwgInNsYXZlX3BvcnQiLCAic2xhdmVfc3RhdGUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2luc3RhbmNlX2luZm8iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJwcm9jZXNzX2lkIiwgInJ1bl9pZCIsICJ0Y3BfcG9ydCIsICJyZWRpc192ZXJzaW9uIiwgIm1heG1lbW9yeV9wb2xpY3kiLCAib3MiLCAicmVkaXNfbW9kZSIsICJyZWRpc19idWlsZF9pZCIsICJyb2xlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19jb21tYW5kc19leGVjdXRlZF9pbl93b3JrcG9vbF90b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX21lbW9yeV91c2VkX2x1YV9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2NvbW1hbmRzX3JlamVjdGVkX2NhbGxzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY21kIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19jcHVfdXNlcl9tYWluX3RocmVhZF9zZWNvbmRzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl9zaW1fYmxvY2tfY2FjaGVfbWlzcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX2luZm8iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJ2ZXJzaW9uIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19wcm9jZXNzX2lkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl9kYnN0YXRzX2Jsb2NrX2NhY2hlX2J5dGVzX3JlYWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJnb19tZW1zdGF0c19zdGFja19zeXNfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX2RhdGFkaXJfc2l6ZV9rYiIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfZGJzdGF0c19ibG9ja19jYWNoZV9kYXRhX2hpdCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfY29tcGFjdF93cml0ZV9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JlcGxpY2FfcGFydGlhbF9yZXN5bmNfYWNjZXB0ZWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19tZW1vcnlfdXNlZF9vdmVyaGVhZF9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2Nvbm5lY3RlZF9zbGF2ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX2xldmVsX2VudHJpZXNfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfY3B1X3N5c19zZWNvbmRzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl9ibG9ja19jYWNoZV9maWx0ZXJfaGl0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfZGVsZXRpbmdfZXhwaXJlX2tleXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19jbHVzdGVyX3N0YXRzX21lc3NhZ2VzX3Bpbmdfc2VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfZGJzdGF0c19ieXRlc19yZWFkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl9yb3dfY2FjaGVfaGl0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcmVqZWN0ZWRfY29ubmVjdGlvbnNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX2NvbXBhY3Rpb25fa2V5X2Ryb3BfdXNlciIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3Nsb3dsb2dfbGFzdF9pZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfZGJzdGF0c19sMmFuZHVwX2hpdCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2NsdXN0ZXJfc3RhdHNfbWVzc2FnZXNfcG9uZ19zZW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfY2x1c3Rlcl9zdGF0c19tZXNzYWdlc19tZWV0X3NlbnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19jbHVzdGVyX3Nsb3RzX29rIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl9udW1iZXJfbXVsdGlnZXRfa2V5c19yZWFkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfY21kc3RhdF9hcHBseWJpbmxvZ3N2MiIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2Jsb2NrZWRfY2xpZW50cyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX21lbV9jbGllbnRzX3NsYXZlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3RvdGFsX2ludmFsaWRfcGFja2V0cyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfY29tcGFjdGlvbl9maWx0ZXJfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19uZXRfb3V0cHV0X2J5dGVzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcmVwbGljYV9wYXJ0aWFsX3Jlc3luY19kZW5pZWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19hb2ZfbGFzdF9jb3dfc2l6ZV9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2FvZl9lbmFibGVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl9jb21wYWN0aW9uX2NhbmNlbGxlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfZGJzdGF0c193YWxfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19jbWRzdGF0X2NsaWVudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2NtZHN0YXRfcGluZyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfbnVtYmVyX3N1cGVydmVyc2lvbl9jbGVhbnVwcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2tleXNwYWNlX3dyb25nX3ZlcnNpb25lcCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfY29tcGFjdGlvbl9rZXlfZHJvcF9yYW5nZV9kZWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19leHBpcmVkX3N0YWxlX3BlcmNlbnRhZ2UiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc191cCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfZmlsdGVyX29wZXJhdGlvbl90aW1lX25hbm9zIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfZGJfYXZnX3R0bF9zZWNvbmRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZGIiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX2xvb2t1cHNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX251bWJlcl9kYl9wcmV2IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfY29uZmlnX21heG1lbW9yeV9wb2xpY3kiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19leHBpcmVkX3RpbWVfY2FwX3JlYWNoZWRfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX2Ric3RhdHNfbnVtYmVyX2Jsb2NrX2NvbXByZXNzZWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJnb19tZW1zdGF0c19sYXN0X2djX3RpbWVfc2Vjb25kcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2NtZHN0YXRfY2x1c3RlciIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfZGJzdGF0c19ieXRlc193cml0dGVuIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fbWVtc3RhdHNfaGVhcF9yZWxlYXNlZF9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX21lbW9yeV9tYXhfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19zZWNvbmRfcmVwbF9vZmZzZXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19jbGllbnRzX2luX3RpbWVvdXRfdGFibGUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX2Jsb29tX2ZpbHRlcl9wcmVmaXhfY2hlY2tlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfbWVyZ2Vfb3BlcmF0aW9uX3RpbWVfbmFub3MiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19wdWJzdWJfcGF0dGVybnMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19hb2ZfcmV3cml0ZV9pbl9wcm9ncmVzcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfbWVtdGFibGVfY29tcGFjdGlvbl9taWNyb3MiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX2Ric3RhdHNfY29tcGFjdGlvbl9rZXlfZHJvcF9uZXciLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19hbGxvY2F0b3JfcnNzX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfbWVtX2ZyYWdtZW50YXRpb25fYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19jbWRzdGF0X2NvbmZ4eCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2NtZHN0YXRfdW5zZWVuIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfdG90YWxfc3RyaWNreV9wYWNrZXRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcmRiX2xhc3RfY293X3NpemVfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX2Ric3RhdHNfZGJfaXRlcl9ieXRlc19yZWFkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfbWlncmF0ZV9jYWNoZWRfc29ja2V0c190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2NtZHN0YXRfaW5mbyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2RiX2tleXNfZXhwaXJpbmciLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJkYiJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfbWFzdGVyX2xpbmtfdXAiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJtYXN0ZXJfaG9zdCIsICJtYXN0ZXJfcG9ydCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl9yZWFkX2FtcF90b3RhbF9yZWFkX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfY2x1c3Rlcl9zbG90c19mYWlsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfbWVtb3J5X3VzZWRfZGF0YXNldF9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfc3RhbGxfbWljcm9zIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfY2x1c3Rlcl9zbG90c19wZmFpbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfc2ltX2Jsb2NrX2NhY2hlX2hpdCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3RvdGFsX2Vycm9yX3JlcGxpZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19jbWRzdGF0X3Nsb3dsb2ciLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jZXNzX21heF9mZHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX251bWJlcl9tdWx0aWdldF9nZXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX251bWJlcl9pdGVyX3NraXAiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jZXNzX2NwdV9zZWNvbmRzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfaW9fdGhyZWFkZWRfcmVhZHNfcHJvY2Vzc2VkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfY2x1c3Rlcl9jb25uZWN0aW9ucyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2xhc3Rfa2V5X2dyb3Vwc19zY3JhcGVfZHVyYXRpb25fbWlsbGlzZWNvbmRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfY21kc3RhdF9iaW5sb2dfaGVhcnRiZWF0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcmRiX2xhc3Rfc2F2ZV90aW1lc3RhbXBfc2Vjb25kcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfbm9fZmlsZV9jbG9zZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX2Jsb2NrX2NhY2hlY29tcHJlc3NlZF9taXNzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl9kYnN0YXRzX251bWJlcl9kYl9zZWVrIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl9sMF9udW1fZmlsZXNfc3RhbGxfbWljcm9zIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfaW9fdGhyZWFkc19hY3RpdmUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19jbWRzdGF0X3NldCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX3N5c19ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2NsdXN0ZXJfY2xpZW50cyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfYmxvY2tfY2FjaGVfaW5kZXhfbWlzcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3RyYWNraW5nX3RvdGFsX2l0ZW1zIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfZGVmcmFnX21pc3NlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2RlZnJhZ19rZXlfbWlzc2VzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfaW9fdGhyZWFkZWRfd3JpdGVzX3Byb2Nlc3NlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2NvbmZpZ19tYXhtZW1vcnkiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19leHBvcnRlcl9zY3JhcGVfZHVyYXRpb25fc2Vjb25kcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfZGJzdGF0c19jb21wYWN0X3JlYWRfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19tYXN0ZXJfbGFzdF9pb19zZWNvbmRzX2FnbyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbIm1hc3Rlcl9ob3N0IiwgIm1hc3Rlcl9wb3J0Il0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19zbGF2ZV9wcmlvcml0eSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfbGV2ZWxfZGVsZXRpb25zX3N1bSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3RvdGFsX3dyaXRlc19wcm9jZXNzZWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19tZW1fY2xpZW50c19ub3JtYWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc191cHRpbWVfaW5fc2Vjb25kcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3NsYXZlX2luZm8iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJtYXN0ZXJfaG9zdCIsICJtYXN0ZXJfcG9ydCIsICJyZWFkX29ubHkiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfYmxvY2tfY2FjaGVjb21wcmVzc2VkX2FkZF9mYWlsdXJlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3NsYXZlX3JlcGxfb2Zmc2V0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsibWFzdGVyX2hvc3QiLCAibWFzdGVyX3BvcnQiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfYmxvY2tjYWNoZV9waW5uZWR1c2FnZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3NsYXZlX2V4cGlyZXNfdHJhY2tlZF9rZXlzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfYWxsb2NhdG9yX2FsbG9jYXRlZF9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2NsdXN0ZXJfbWVzc2FnZXNfc2VudF90b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfcGVyc2lzdGVudF9jYWNoZV9oaXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jZXNzX3Jlc2lkZW50X21lbW9yeV9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2FvZl9idWZmZXJfbGVuZ3RoIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfdG90YWxfY29tbWFuZHNfZXhlY3V0ZV9jb3N0X25zXyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfYmxvb21fZmlsdGVyX3VzZWZ1bCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX21lbW9yeV91c2VkX3BlYWtfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19sYXp5ZnJlZV9wZW5kaW5nX29iamVjdHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX2Ric3RhdHNfYmxvY2tfY2FjaGVfYWRkX2ZhaWx1cmVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfYW9mX3Jld3JpdGVfc2NoZWR1bGVkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfdHJhY2tpbmdfY2xpZW50cyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX21zcGFuX3N5c19ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX2FsbG9jX2J5dGVzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl9udW1iZXJfcmF0ZV9saW1pdGVyX2RyYWlucyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2NsdXN0ZXJfa25vd25fbm9kZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19hb2ZfcGVuZGluZ19yZXdyaXRlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfbWFzdGVyX3JlcGxfb2Zmc2V0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfY2x1c3Rlcl9tZXNzYWdlc19yZWNlaXZlZF90b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfbnVtYmVyX2Jsb2NrX25vdF9jb21wcmVzc2VkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfY29ubmVjdGVkX3NsYXZlX2xhZ19zZWNvbmRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsic2xhdmVfaXAiLCAic2xhdmVfcG9ydCIsICJzbGF2ZV9zdGF0ZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfYW9mX2Jhc2Vfc2l6ZV9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX21lbV9mcmFnbWVudGF0aW9uX3JhdGlvIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfZHVtcF9wYXlsb2FkX3Nhbml0aXphdGlvbnMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX2Ric3RhdHNfYmxvY2tfY2FjaGVfYWRkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfZGVmcmFnX2tleV9oaXRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfYW9mX2xhc3RfYmdyZXdyaXRlX3N0YXR1cyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2NwdV91c2VyX3NlY29uZHNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19zbG93bG9nX2xlbmd0aCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2FvZl9sYXN0X3dyaXRlX3N0YXR1cyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2Nvbm5lY3RlZF9jbGllbnRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfY2x1c3Rlcl9zaXplIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfYWxsb2NhdG9yX3Jzc19yYXRpbyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2NtZHN0YXRfYWRtaW5zZXQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJnb19tZW1zdGF0c19zdGFja19pbnVzZV9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfbDFfaGl0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fbWVtc3RhdHNfaGVhcF9hbGxvY19ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfYmxvY2tfY2FjaGVfZmlsdGVyX21pc3MiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJnb19tZW1zdGF0c19uZXh0X2djX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfbWVtX25vdF9jb3VudGVkX2Zvcl9ldmljdGlvbl9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfZGJzdGF0c19ibG9ja19jYWNoZV9taXNzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fbWVtc3RhdHNfaGVhcF9zeXNfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19hb2ZfcmV3cml0ZV9idWZmZXJfbGVuZ3RoIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfYW9mX2RlbGF5ZWRfZnN5bmMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX3dhbF9zeW5jZWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJnb19tZW1zdGF0c19mcmVlc190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb2Nlc3Nfc3RhcnRfdGltZV9zZWNvbmRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfbGF0ZXN0X2Zvcmtfc2Vjb25kcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2NsdXN0ZXJfc3RhdHNfbWVzc2FnZXNfbWVldF9yZWNlaXZlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfZGJzdGF0c19udW1iZXJfYmxvY2tfZGVjb21wcmVzc2VkIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl9udW1iZXJfZGlyZWN0X2xvYWRfdGFibGVfcHJvcGVydGllcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfZGJzdGF0c193cml0ZV93YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX3dyaXRlX290aGVyIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfdG90YWxfY29tbWFuZHNfd29ya3Bvb2xfcXVldWVfY29zdF9uc18iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19jbHVzdGVyX2N1cnJlbnRfZXBvY2giLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19tZW1vcnlfdXNlZF9zdGFydHVwX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl9ibG9ja19jYWNoZWNvbXByZXNzZWRfaGl0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl9jb21wYWN0aW9uX2t2X2V4cGlyZWRfY291bnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX2JnX2Vycm9yX2NvdW50IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfY2xpZW50X3JlY2VudF9tYXhfaW5wdXRfYnVmZmVyX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfZXhwaXJlZF9rZXlzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfYWN0aXZlX2RlZnJhZ19ydW5uaW5nIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl9jb21wYWN0aW9uX3JhbmdlX2RlbF9kcm9wX29ic29sZXRlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfY3B1X3VzZXJfY2hpbGRyZW5fc2Vjb25kc190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2RiX2tleXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJkYiJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl9udW1iZXJfcmVzZWVrc19pdGVyYXRpb24iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX2Ric3RhdHNfbnVtYmVyX2tleXNfd3JpdHRlbiIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3RyYWNraW5nX3RvdGFsX2tleXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX2Jsb2NrX2NhY2hlX2ZpbHRlcl9ieXRlc19pbnNlcnQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19jbHVzdGVyX3N0YXRlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfY29tbWFuZHNfZHVyYXRpb25fc2Vjb25kc190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImNtZCJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfY2xpZW50X3JlY2VudF9tYXhfb3V0cHV0X2J1ZmZlcl9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfZGJzdGF0c19ibG9ja19jYWNoZV9kYXRhX2J5dGVzX2luc2VydCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3N0YXJ0X3RpbWVfc2Vjb25kcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfcGVyc2lzdGVudF9jYWNoZV9taXNzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl9yZWFkX2FtcF9lc3RpbWF0ZV91c2VmdWxfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX2Jsb2NrX2NhY2hlX2ZpbHRlcl9ieXRlc19ldmljdCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2FsbG9jYXRvcl9mcmFnX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfZXZpY3RlZF9rZXlzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfY29ubmVjdGVkX3NsYXZlX29mZnNldF9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbInNsYXZlX2lwIiwgInNsYXZlX3BvcnQiLCAic2xhdmVfc3RhdGUiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2NtZHN0YXRfcmVhZG9ubHkiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX2Ric3RhdHNfYmxvY2tfY2FjaGVfaGl0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fbWVtc3RhdHNfZ2Nfc3lzX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl9kYnN0YXRzX251bWJlcl9rZXlzX3JlYWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX2Ric3RhdHNfYmxvY2tfY2FjaGVfZGF0YV9hZGQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19sYXN0X3Nsb3dfZXhlY3V0aW9uX2R1cmF0aW9uX3NlY29uZHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yZXBsX2JhY2tsb2dfaGlzdG9yeV9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3RvdGFsX3JlYWRzX3Byb2Nlc3NlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfbnVtX2l0ZXJhdG9ycyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfbnVtYmVyX2tleXNfdXBkYXRlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2V4cG9ydGVyX3NjcmFwZXNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yZXBsaWNhdGlvbl9iYWNrbG9nX2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fdGhyZWFkcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2FvZl9jdXJyZW50X3Jld3JpdGVfZHVyYXRpb25fc2VjIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl9mbHVzaF93cml0ZV9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2NwdV9zeXNfY2hpbGRyZW5fc2Vjb25kc190b3RhbCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfbWVtdGFibGVfaGl0IiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfZXhwb3J0ZXJfbGFzdF9zY3JhcGVfZHVyYXRpb25fc2Vjb25kcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvX21lbXN0YXRzX2hlYXBfaW51c2VfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19jbHVzdGVyX3N0YXRzX21lc3NhZ2VzX3BvbmdfcmVjZWl2ZWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19rZXlzcGFjZV9oaXRzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl9ub19maWxlX2Vycm9ycyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3NjYW5wb2ludCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfZGJzdGF0c19ub19maWxlX29wZW5zIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl9kYnN0YXRzX2NvbXBhY3Rpb25fa2V5X2Ryb3Bfb2Jzb2xldGUiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19leHBvcnRlcl9idWlsZF9pbmZvIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZ29sYW5nX3ZlcnNpb24iLCAidmVyc2lvbiIsICJidWlsZF9kYXRlIiwgImNvbW1pdF9zaGEiXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb2Nlc3NfdmlydHVhbF9tZW1vcnlfbWF4X2J5dGVzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfZXJyb3JzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiZXJyIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19jbHVzdGVyX215X2Vwb2NoIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fbWVtc3RhdHNfYWxsb2NfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX3JhdGVfbGltaXRfZGVsYXlfbWlsbGlzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl9udW1iZXJfZGVsZXRlc19maWx0ZXJlZCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfbnVtYmVyX211bHRpZ2V0X2J5dGVzX3JlYWQiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19hb2ZfcGVuZGluZ19iaW9fZnN5bmMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19jbWRzdGF0X2Ric2l6ZSIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2NvbmZpZ19pb190aHJlYWRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfcm9ja3NkYl9kYnN0YXRzX2Jsb2NrX2NhY2hlX2J5dGVzX3dyaXRlIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZ29fZ2NfZHVyYXRpb25fc2Vjb25kcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbInF1YW50aWxlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19yb2Nrc2RiX2wwX3Nsb3dkb3duX21pY3JvcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3JvY2tzZGJfYmxvY2tfY2FjaGVfaW5kZXhfYnl0ZXNfZXZpY3QiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc19hbGxvY2F0b3JfZnJhZ19yYXRpbyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX251bWJlcl9vZl9jYWNoZWRfc2NyaXB0cyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX2FvZl9jdXJyZW50X3NpemVfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJnb19tZW1zdGF0c19tYWxsb2NzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfYWxsb2NhdG9yX2FjdGl2ZV9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlZGlzX3VuZXhwZWN0ZWRfZXJyb3JfcmVwbGllcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInN0b3JlIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAibGV2ZWwiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJxdWFudGlsZSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInNsYXZlX2lwIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAic2xhdmVfc3RhdGUiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJtYXhtZW1vcnlfcG9saWN5IiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfbW9kZSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm9zIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicmVkaXNfYnVpbGRfaWQiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJyZWRpc192ZXJzaW9uIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicm9sZSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJ1bl9pZCIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInRjcF9wb3J0IiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAidmVyc2lvbiIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm1hc3Rlcl9wb3J0IiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiYnVpbGRfZGF0ZSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImNvbW1pdF9zaGEiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJnb2xhbmdfdmVyc2lvbiIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogIm1hc3Rlcl9ob3N0IiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiZXJyIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiY21kIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAic2xhdmVfcG9ydCIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb2Nlc3NfaWQiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJkYiIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInJlYWRfb25seSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfV0sICJ0YWJsZV9pZCI6ICJleHBvcnRlcl9kYm1fcmVkaXNfZXhwb3J0ZXIuZ3JvdXAxIiwgInRhYmxlX2Rlc2MiOiAiZ3JvdXAxIiwgInRhYmxlX25hbWUiOiAiZ3JvdXAxIn1dLCAicGx1Z2luX3R5cGUiOiAiRXhwb3J0ZXIiLCAib3NfdHlwZV9saXN0IjogWyJsaW51eCJdfSwgImNvbGxlY3RfdHlwZSI6ICJFeHBvcnRlciIsICJ0YXJnZXRfbm9kZXMiOiBbXSwgInRhcmdldF9ub2RlX3R5cGUiOiAiVE9QTyIsICJ0YXJnZXRfb2JqZWN0X3R5cGUiOiAiU0VSVklDRSJ9fQ== \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/tpls/collect/0.redis.dbm_twemproxy_exporter.json b/dbm-ui/backend/db_monitor/tpls/collect/0.redis.dbm_twemproxy_exporter.json new file mode 100644 index 0000000000..482555aef5 --- /dev/null +++ b/dbm-ui/backend/db_monitor/tpls/collect/0.redis.dbm_twemproxy_exporter.json @@ -0,0 +1,398 @@ +{ + "bk_biz_id": 0, + "plugin_id": "dbm_twemproxy_exporter", + "db_type": "redis", + "details": { + "name": "dbm_twemproxy", + "label": "component", + "params": { + "plugin": { + "-addr": "{{ target.host.bk_host_innerip }}:{{ target.process[\"redis\"].bind_info[0].port }}", + "-bind": "${host}:${port}", + "-password-file": "/home/mysql/.exporter/{{ target.process[\"redis\"].bind_info[0].port }}.conf", + "服务实例维度注入": { + "app": "app", + "instance": "instance", + "cluster_name": "cluster_name", + "cluster_type": "cluster_type", + "instance_host": "instance_host", + "instance_port": "instance_port", + "instance_role": "instance_role", + "cluster_domain": "cluster_domain" + } + }, + "collector": { + "host": "127.0.0.1", + "port": "9121", + "period": 60, + "timeout": 60 + }, + "target_node_type": "TOPO", + "target_object_type": "SERVICE" + }, + "plugin_info": { + "plugin_id": "dbm_twemproxy_exporter", + "metric_json": [ + { + "fields": [ + { + "name": "process_cpu_seconds_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_max_fds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_open_fds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_resident_memory_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_start_time_seconds", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_virtual_memory_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "process_virtual_memory_max_bytes", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "promhttp_metric_handler_requests_in_flight", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "twemproxy_cmdstat_response_latency_sum", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "twemproxy_cmdstat_response_latency_count", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "twemproxy_connections_curr", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "twemproxy_connections_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "twemproxy_stat_up", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "twemproxy_up", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "twemproxy_uptime", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_twemproxy_exporter.Group1", + "table_desc": "分组1", + "table_name": "Group1" + }, + { + "fields": [ + { + "name": "twemproxy_exporter_build_info", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "build_date", + "commit_sha", + "golang_version", + "version" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "twemproxy_version", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "version" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "build_date", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "commit_sha", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "golang_version", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + }, + { + "name": "version", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_twemproxy_exporter.Group2", + "table_desc": "分组2", + "table_name": "Group2" + }, + { + "fields": [ + { + "name": "promhttp_metric_handler_requests_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "code" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "code", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_twemproxy_exporter.Group3", + "table_desc": "分组3", + "table_name": "Group3" + }, + { + "fields": [ + { + "name": "twemproxy_cmdstat_calls_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cmd" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "twemproxy_cmdstat_usec_total", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "cmd" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "cmd", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_twemproxy_exporter.Group4", + "table_desc": "分组4", + "table_name": "Group4" + }, + { + "fields": [ + { + "name": "twemproxy_cmdstat_response_latency_bucket", + "type": "double", + "unit": "none", + "is_active": true, + "dimensions": [ + "le" + ], + "description": "", + "source_name": "", + "monitor_type": "metric", + "is_diff_metric": false + }, + { + "name": "le", + "type": "string", + "unit": "none", + "is_active": true, + "description": "", + "source_name": "", + "monitor_type": "dimension", + "is_diff_metric": false + } + ], + "table_id": "exporter_dbm_twemproxy_exporter.Group5", + "table_desc": "分组5", + "table_name": "Group5" + } + ], + "plugin_type": "Exporter", + "os_type_list": [ + "linux" + ] + }, + "collect_type": "Exporter", + "target_nodes": [], + "target_node_type": "TOPO", + "target_object_type": "SERVICE" + } +} diff --git a/dbm-ui/backend/db_monitor/tpls/collect/0.redis.dbm_twemproxy_exporter.tpl64 b/dbm-ui/backend/db_monitor/tpls/collect/0.redis.dbm_twemproxy_exporter.tpl64 deleted file mode 100644 index 3bdba93091..0000000000 --- a/dbm-ui/backend/db_monitor/tpls/collect/0.redis.dbm_twemproxy_exporter.tpl64 +++ /dev/null @@ -1 +0,0 @@ -eyJia19iaXpfaWQiOiAwLCAicGx1Z2luX2lkIjogImRibV90d2VtcHJveHlfZXhwb3J0ZXIiLCAiZGJfdHlwZSI6ICJyZWRpcyIsICJkZXRhaWxzIjogeyJuYW1lIjogImRibV90d2VtcHJveHkiLCAibGFiZWwiOiAiY29tcG9uZW50IiwgInBhcmFtcyI6IHsicGx1Z2luIjogeyItYWRkciI6ICJ7eyB0YXJnZXQuaG9zdC5ia19ob3N0X2lubmVyaXAgfX06e3sgdGFyZ2V0LnByb2Nlc3NbXCJyZWRpc1wiXS5iaW5kX2luZm9bMF0ucG9ydCB9fSIsICItYmluZCI6ICIke2hvc3R9OiR7cG9ydH0iLCAiLXBhc3N3b3JkLWZpbGUiOiAiL2hvbWUvbXlzcWwvLmV4cG9ydGVyL3t7IHRhcmdldC5wcm9jZXNzW1wicmVkaXNcIl0uYmluZF9pbmZvWzBdLnBvcnQgfX0uY29uZiIsICJcdTY3MGRcdTUyYTFcdTViOWVcdTRmOGJcdTdlZjRcdTVlYTZcdTZjZThcdTUxNjUiOiB7ImFwcCI6ICJhcHAiLCAiaW5zdGFuY2UiOiAiaW5zdGFuY2UiLCAiY2x1c3Rlcl9uYW1lIjogImNsdXN0ZXJfbmFtZSIsICJjbHVzdGVyX3R5cGUiOiAiY2x1c3Rlcl90eXBlIiwgImluc3RhbmNlX2hvc3QiOiAiaW5zdGFuY2VfaG9zdCIsICJpbnN0YW5jZV9wb3J0IjogImluc3RhbmNlX3BvcnQiLCAiaW5zdGFuY2Vfcm9sZSI6ICJpbnN0YW5jZV9yb2xlIiwgImNsdXN0ZXJfZG9tYWluIjogImNsdXN0ZXJfZG9tYWluIn19LCAiY29sbGVjdG9yIjogeyJob3N0IjogIjEyNy4wLjAuMSIsICJwb3J0IjogIjkxMjEiLCAicGVyaW9kIjogNjAsICJ0aW1lb3V0IjogNjB9LCAidGFyZ2V0X25vZGVfdHlwZSI6ICJUT1BPIiwgInRhcmdldF9vYmplY3RfdHlwZSI6ICJTRVJWSUNFIn0sICJwbHVnaW5faW5mbyI6IHsicGx1Z2luX2lkIjogImRibV90d2VtcHJveHlfZXhwb3J0ZXIiLCAibWV0cmljX2pzb24iOiBbeyJmaWVsZHMiOiBbeyJuYW1lIjogInByb2Nlc3NfY3B1X3NlY29uZHNfdG90YWwiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jZXNzX21heF9mZHMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jZXNzX29wZW5fZmRzIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAicHJvY2Vzc19yZXNpZGVudF9tZW1vcnlfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jZXNzX3N0YXJ0X3RpbWVfc2Vjb25kcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb2Nlc3NfdmlydHVhbF9tZW1vcnlfYnl0ZXMiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJwcm9jZXNzX3ZpcnR1YWxfbWVtb3J5X21heF9ieXRlcyIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInByb21odHRwX21ldHJpY19oYW5kbGVyX3JlcXVlc3RzX2luX2ZsaWdodCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInR3ZW1wcm94eV9jbWRzdGF0X3Jlc3BvbnNlX2xhdGVuY3lfc3VtIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAidHdlbXByb3h5X2NtZHN0YXRfcmVzcG9uc2VfbGF0ZW5jeV9jb3VudCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbXSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogIm1ldHJpYyIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogInR3ZW1wcm94eV9jb25uZWN0aW9uc19jdXJyIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAidHdlbXByb3h5X2Nvbm5lY3Rpb25zX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAidHdlbXByb3h5X3N0YXRfdXAiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJ0d2VtcHJveHlfdXAiLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogW10sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJ0d2VtcHJveHlfdXB0aW1lIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFtdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9XSwgInRhYmxlX2lkIjogImV4cG9ydGVyX2RibV90d2VtcHJveHlfZXhwb3J0ZXIuR3JvdXAxIiwgInRhYmxlX2Rlc2MiOiAiXHU1MjA2XHU3ZWM0MSIsICJ0YWJsZV9uYW1lIjogIkdyb3VwMSJ9LCB7ImZpZWxkcyI6IFt7Im5hbWUiOiAidHdlbXByb3h5X2V4cG9ydGVyX2J1aWxkX2luZm8iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJidWlsZF9kYXRlIiwgImNvbW1pdF9zaGEiLCAiZ29sYW5nX3ZlcnNpb24iLCAidmVyc2lvbiJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAidHdlbXByb3h5X3ZlcnNpb24iLCAidHlwZSI6ICJkb3VibGUiLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkaW1lbnNpb25zIjogWyJ2ZXJzaW9uIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJidWlsZF9kYXRlIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiY29tbWl0X3NoYSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfSwgeyJuYW1lIjogImdvbGFuZ192ZXJzaW9uIiwgInR5cGUiOiAic3RyaW5nIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAiZGltZW5zaW9uIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAidmVyc2lvbiIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfV0sICJ0YWJsZV9pZCI6ICJleHBvcnRlcl9kYm1fdHdlbXByb3h5X2V4cG9ydGVyLkdyb3VwMiIsICJ0YWJsZV9kZXNjIjogIlx1NTIwNlx1N2VjNDIiLCAidGFibGVfbmFtZSI6ICJHcm91cDIifSwgeyJmaWVsZHMiOiBbeyJuYW1lIjogInByb21odHRwX21ldHJpY19oYW5kbGVyX3JlcXVlc3RzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY29kZSJdLCAiZGVzY3JpcHRpb24iOiAiIiwgInNvdXJjZV9uYW1lIjogIiIsICJtb25pdG9yX3R5cGUiOiAibWV0cmljIiwgImlzX2RpZmZfbWV0cmljIjogZmFsc2V9LCB7Im5hbWUiOiAiY29kZSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfV0sICJ0YWJsZV9pZCI6ICJleHBvcnRlcl9kYm1fdHdlbXByb3h5X2V4cG9ydGVyLkdyb3VwMyIsICJ0YWJsZV9kZXNjIjogIlx1NTIwNlx1N2VjNDMiLCAidGFibGVfbmFtZSI6ICJHcm91cDMifSwgeyJmaWVsZHMiOiBbeyJuYW1lIjogInR3ZW1wcm94eV9jbWRzdGF0X2NhbGxzX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY21kIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJ0d2VtcHJveHlfY21kc3RhdF91c2VjX3RvdGFsIiwgInR5cGUiOiAiZG91YmxlIiwgInVuaXQiOiAibm9uZSIsICJpc19hY3RpdmUiOiB0cnVlLCAiZGltZW5zaW9ucyI6IFsiY21kIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJjbWQiLCAidHlwZSI6ICJzdHJpbmciLCAidW5pdCI6ICJub25lIiwgImlzX2FjdGl2ZSI6IHRydWUsICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJkaW1lbnNpb24iLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX1dLCAidGFibGVfaWQiOiAiZXhwb3J0ZXJfZGJtX3R3ZW1wcm94eV9leHBvcnRlci5Hcm91cDQiLCAidGFibGVfZGVzYyI6ICJcdTUyMDZcdTdlYzQ0IiwgInRhYmxlX25hbWUiOiAiR3JvdXA0In0sIHsiZmllbGRzIjogW3sibmFtZSI6ICJ0d2VtcHJveHlfY21kc3RhdF9yZXNwb25zZV9sYXRlbmN5X2J1Y2tldCIsICJ0eXBlIjogImRvdWJsZSIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRpbWVuc2lvbnMiOiBbImxlIl0sICJkZXNjcmlwdGlvbiI6ICIiLCAic291cmNlX25hbWUiOiAiIiwgIm1vbml0b3JfdHlwZSI6ICJtZXRyaWMiLCAiaXNfZGlmZl9tZXRyaWMiOiBmYWxzZX0sIHsibmFtZSI6ICJsZSIsICJ0eXBlIjogInN0cmluZyIsICJ1bml0IjogIm5vbmUiLCAiaXNfYWN0aXZlIjogdHJ1ZSwgImRlc2NyaXB0aW9uIjogIiIsICJzb3VyY2VfbmFtZSI6ICIiLCAibW9uaXRvcl90eXBlIjogImRpbWVuc2lvbiIsICJpc19kaWZmX21ldHJpYyI6IGZhbHNlfV0sICJ0YWJsZV9pZCI6ICJleHBvcnRlcl9kYm1fdHdlbXByb3h5X2V4cG9ydGVyLkdyb3VwNSIsICJ0YWJsZV9kZXNjIjogIlx1NTIwNlx1N2VjNDUiLCAidGFibGVfbmFtZSI6ICJHcm91cDUifV0sICJwbHVnaW5fdHlwZSI6ICJFeHBvcnRlciIsICJvc190eXBlX2xpc3QiOiBbImxpbnV4Il19LCAiY29sbGVjdF90eXBlIjogIkV4cG9ydGVyIiwgInRhcmdldF9ub2RlcyI6IFtdLCAidGFyZ2V0X25vZGVfdHlwZSI6ICJUT1BPIiwgInRhcmdldF9vYmplY3RfdHlwZSI6ICJTRVJWSUNFIn19 \ No newline at end of file diff --git a/dbm-ui/backend/db_monitor/urls.py b/dbm-ui/backend/db_monitor/urls.py index 22b4ea9167..6e0d3086c8 100644 --- a/dbm-ui/backend/db_monitor/urls.py +++ b/dbm-ui/backend/db_monitor/urls.py @@ -8,13 +8,16 @@ 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 rest_framework.routers import DefaultRouter from backend.db_monitor.views.grafana import MonitorGrafanaViewSet +from backend.db_monitor.views.notice_group import MonitorNoticeGroupViewSet +from backend.db_monitor.views.policy import MonitorPolicyViewSet routers = DefaultRouter(trailing_slash=True) routers.register(r"grafana", MonitorGrafanaViewSet, basename="grafana") +routers.register(r"policy", MonitorPolicyViewSet, basename="policy") +routers.register(r"notice_group", MonitorNoticeGroupViewSet, basename="notice_group") urlpatterns = routers.urls diff --git a/dbm-ui/backend/db_monitor/views/grafana.py b/dbm-ui/backend/db_monitor/views/grafana.py index 45ac9c57b8..4d8e645c82 100644 --- a/dbm-ui/backend/db_monitor/views/grafana.py +++ b/dbm-ui/backend/db_monitor/views/grafana.py @@ -24,7 +24,7 @@ from backend.db_monitor.serializers import DashboardUrlSerializer, GetDashboardSerializer from backend.iam_app.handlers.drf_perm import DBManageIAMPermission -SWAGGER_TAG = _("监控告警管理") +from .. import constants class MonitorGrafanaViewSet(viewsets.SystemViewSet): @@ -39,7 +39,7 @@ def _get_custom_permissions(self): operation_summary=_("查询内嵌仪表盘地址"), query_serializer=GetDashboardSerializer, responses={status.HTTP_200_OK: DashboardUrlSerializer}, - tags=[SWAGGER_TAG], + tags=[constants.SWAGGER_TAG], ) @action(methods=["GET"], detail=False, serializer_class=GetDashboardSerializer, pagination_class=None) def get_dashboard(self, request): diff --git a/dbm-ui/backend/db_monitor/views/notice_group.py b/dbm-ui/backend/db_monitor/views/notice_group.py new file mode 100644 index 0000000000..00640aa352 --- /dev/null +++ b/dbm-ui/backend/db_monitor/views/notice_group.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import django_filters +from django.db.models import Q +from django.utils.decorators import method_decorator +from django.utils.translation import ugettext_lazy as _ +from rest_framework.decorators import action +from rest_framework.response import Response + +from backend.bk_web import viewsets +from backend.bk_web.swagger import common_swagger_auto_schema +from backend.components import CCApi, CmsiApi +from backend.configuration.constants import PLAT_BIZ_ID +from backend.db_monitor.models import NoticeGroup +from backend.db_monitor.serializers import NoticeGroupSerializer +from backend.iam_app.handlers.drf_perm import DBManageIAMPermission + +SWAGGER_TAG = _("监控告警组") + + +class MonitorPolicyListFilter(django_filters.FilterSet): + name = django_filters.CharFilter(field_name="name", lookup_expr="icontains", label=_("告警组名称")) + bk_biz_id = django_filters.NumberFilter(method="filter_bk_biz_id", label=_("业务ID")) + + def filter_bk_biz_id(self, queryset, name, value): + """ + 内置告警组优先使用业务配置, + """ + biz_built_in_group = queryset.filter(bk_biz_id=value, is_built_in=True) + db_types = set(list(biz_built_in_group.values_list("db_type", flat=True))) + plat_built_in_group_ids = ( + queryset.filter(bk_biz_id=PLAT_BIZ_ID, is_built_in=True) + .exclude(db_type__in=db_types) + .values_list("id", flat=True) + ) + return queryset.filter(Q(bk_biz_id=value) | Q(id__in=plat_built_in_group_ids)).order_by("is_built_in") + + class Meta: + model = NoticeGroup + fields = ["bk_biz_id", "name"] + + +@method_decorator( + name="list", + decorator=common_swagger_auto_schema(operation_summary=_("查询监控告警组列表"), tags=[SWAGGER_TAG]), +) +@method_decorator( + name="create", + decorator=common_swagger_auto_schema(operation_summary=_("新建监控告警组"), tags=[SWAGGER_TAG]), +) +@method_decorator( + name="retrieve", + decorator=common_swagger_auto_schema(operation_summary=_("获取监控告警组"), tags=[SWAGGER_TAG]), +) +@method_decorator( + name="update", + decorator=common_swagger_auto_schema(operation_summary=_("更新监控告警组"), tags=[SWAGGER_TAG]), +) +@method_decorator( + name="destroy", + decorator=common_swagger_auto_schema(operation_summary=_("删除监控告警组"), tags=[SWAGGER_TAG]), +) +class MonitorNoticeGroupViewSet(viewsets.AuditedModelViewSet): + """ + 监控告警组视图 + """ + + queryset = NoticeGroup.objects.all() + serializer_class = NoticeGroupSerializer + filter_class = MonitorPolicyListFilter + + def _get_custom_permissions(self): + return [DBManageIAMPermission()] + + @common_swagger_auto_schema(operation_summary=_("查询通知类型"), tags=[SWAGGER_TAG]) + @action(methods=["GET"], detail=False) + def get_msg_type(self, request, *args, **kwargs): + return Response(CmsiApi.get_msg_type()) diff --git a/dbm-ui/backend/db_monitor/views/policy.py b/dbm-ui/backend/db_monitor/views/policy.py new file mode 100644 index 0000000000..d38f475388 --- /dev/null +++ b/dbm-ui/backend/db_monitor/views/policy.py @@ -0,0 +1,166 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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.db.models import Q +from django.utils.decorators import method_decorator +from django.utils.translation import ugettext as _ +from django_filters import rest_framework as filters +from rest_framework import status +from rest_framework.decorators import action +from rest_framework.response import Response + +from backend.bk_web.swagger import common_swagger_auto_schema +from backend.bk_web.viewsets import AuditedModelViewSet + +from ...configuration.constants import PLAT_BIZ_ID +from .. import constants +from ..models import MonitorPolicy +from ..serializers import ( + MonitorPolicyCloneSerializer, + MonitorPolicyEmptySerializer, + MonitorPolicyListSerializer, + MonitorPolicySerializer, + MonitorPolicyUpdateSerializer, +) + + +class MonitorPolicyListFilter(filters.FilterSet): + name = filters.CharFilter(field_name="name", lookup_expr="icontains", label=_("策略名")) + updater = filters.CharFilter(lookup_expr="exact", label=_("更新人")) + creator = filters.CharFilter(lookup_expr="creator", label=_("创建人")) + db_type = filters.CharFilter(lookup_expr="exact", label=_("db类型")) + target_keyword = filters.CharFilter(lookup_expr="icontains", label=_("目标关键字检索")) + is_enabled = filters.BooleanFilter(label=_("是否启用")) + + bk_biz_id = filters.NumberFilter(method="filter_bk_biz_id", label=_("业务ID")) + + # 如果只需要开区间,可以简化配置,这里的注释留作学习示例 + # (create_at_after, create_at_before): create_at_after=2023-09-05 14:29:00&create_at_before=2023-09-05 14:30:05 + # create_at = filters.DateTimeFromToRangeFilter("create_at") + # 拆分rangeFilter,支持两端闭区间 + create_at_before = filters.DateTimeFilter(field_name="create_at", lookup_expr="lte") + create_at_after = filters.DateTimeFilter(field_name="create_at", lookup_expr="gte") + + # 需要利用Q查询 + notify_groups = filters.CharFilter(method="filter_notify_groups", label=_("告警组")) + + def filter_notify_groups(self, queryset, name, value): + """过滤多个告警组: value=1,2,3""" + + if name != "notify_groups": + return queryset + + qs = Q() + for group in map(lambda x: int(x), value.split(",")): + qs = qs | Q(notify_groups__contains=group) + + return queryset.filter(qs) + + def filter_bk_biz_id(self, queryset, name, value): + """默认包含平台告警策略""" + return queryset.filter(bk_biz_id__in=[PLAT_BIZ_ID, value]) + + class Meta: + model = MonitorPolicy + fields = [ + "bk_biz_id", + "name", + "db_type", + "updater", + "creator", + "create_at_before", + "create_at_after", + "is_enabled", + "target_keyword", + "notify_groups", + ] + + +@method_decorator( + name="list", + decorator=common_swagger_auto_schema( + tags=[constants.SWAGGER_TAG], responses={status.HTTP_200_OK: MonitorPolicyListSerializer()} + ), +) +@method_decorator( + name="retrieve", + decorator=common_swagger_auto_schema( + tags=[constants.SWAGGER_TAG], responses={status.HTTP_200_OK: MonitorPolicySerializer()} + ), +) +@method_decorator( + name="update", + decorator=common_swagger_auto_schema(tags=[constants.SWAGGER_TAG]), +) +@method_decorator( + name="destroy", + decorator=common_swagger_auto_schema(tags=[constants.SWAGGER_TAG]), +) +@method_decorator( + name="create", + decorator=common_swagger_auto_schema(tags=[constants.SWAGGER_TAG]), +) +class MonitorPolicyViewSet(AuditedModelViewSet): + """监控策略管理""" + + queryset = MonitorPolicy.objects.order_by("-create_at") + + http_method_names = ["get", "post", "delete"] + + # filter_fields = { + # "name": ["exact", "contains"], + # "bk_biz_id": ["exact", "in"], + # "db_type": ["exact", "in"], + # "is_enabled": ["exact"], + # "policy_status": ["exact", "in"], + # "create_at": ["lte", "gte"], + # } + filter_class = MonitorPolicyListFilter + ordering_fields = ("-create_at",) + + def get_serializer_class(self): + if self.action == "list": + return MonitorPolicyListSerializer + return MonitorPolicySerializer + + @common_swagger_auto_schema( + operation_summary=_("启用策略"), tags=[constants.SWAGGER_TAG], request_body=MonitorPolicyEmptySerializer() + ) + @action(methods=["POST"], detail=True, serializer_class=MonitorPolicyEmptySerializer) + def enable(self, request, *args, **kwargs): + return Response(self.get_object().enable()) + + @common_swagger_auto_schema( + operation_summary=_("停用策略"), tags=[constants.SWAGGER_TAG], request_body=MonitorPolicyEmptySerializer() + ) + @action(methods=["POST"], detail=True, serializer_class=MonitorPolicyEmptySerializer) + def disable(self, request, *args, **kwargs): + return Response(self.get_object().disable()) + + @common_swagger_auto_schema( + operation_summary=_("克隆策略"), tags=[constants.SWAGGER_TAG], request_body=MonitorPolicyCloneSerializer() + ) + @action(methods=["POST"], detail=False, serializer_class=MonitorPolicyCloneSerializer) + def clone_strategy(self, request, *args, **kwargs): + return Response(MonitorPolicy.clone(self.validated_data, request.user.username)) + + @common_swagger_auto_schema( + operation_summary=_("更新策略"), tags=[constants.SWAGGER_TAG], request_body=MonitorPolicyUpdateSerializer() + ) + @action(methods=["POST"], detail=True, serializer_class=MonitorPolicyUpdateSerializer) + def update_strategy(self, request, *args, **kwargs): + return Response(self.get_object().update(self.validated_data)) + + @common_swagger_auto_schema( + operation_summary=_("恢复默认策略"), tags=[constants.SWAGGER_TAG], request_body=MonitorPolicyEmptySerializer() + ) + @action(methods=["POST"], detail=True, serializer_class=MonitorPolicyEmptySerializer) + def reset(self, request, *args, **kwargs): + return Response(self.get_object().reset()) diff --git a/dbm-ui/backend/db_periodic_task/__init__.py b/dbm-ui/backend/db_periodic_task/__init__.py new file mode 100644 index 0000000000..aa5085c628 --- /dev/null +++ b/dbm-ui/backend/db_periodic_task/__init__.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" diff --git a/dbm-ui/backend/db_periodic_task/admin.py b/dbm-ui/backend/db_periodic_task/admin.py new file mode 100644 index 0000000000..90d025905e --- /dev/null +++ b/dbm-ui/backend/db_periodic_task/admin.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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.contrib import admin + +from . import models + + +@admin.register(models.DBPeriodicTask) +class DBPeriodicTaskAdmin(admin.ModelAdmin): + list_display = ["name", "task"] + search_fields = ["name"] + list_filter = ["task_type", "is_frozen"] + raw_id_fields = ["task"] diff --git a/dbm-ui/backend/db_periodic_task/apps.py b/dbm-ui/backend/db_periodic_task/apps.py new file mode 100644 index 0000000000..d01bbde940 --- /dev/null +++ b/dbm-ui/backend/db_periodic_task/apps.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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.apps import AppConfig + + +class PeriodicTaskConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "backend.db_periodic_task" + + def ready(self): + pass diff --git a/dbm-ui/backend/db_periodic_task/constants.py b/dbm-ui/backend/db_periodic_task/constants.py new file mode 100644 index 0000000000..c93a8d58cd --- /dev/null +++ b/dbm-ui/backend/db_periodic_task/constants.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 blue_krill.data_types.enum import EnumField, StructuredEnum +from django.utils.translation import ugettext_lazy as _ + +# 任务状态轮询最大次数 +MAX_QUERY_TASK_STATUS_TIMES = 5 +# 任务轮询间隔时间 +QUERY_TASK_STATUS_INTERVAL = 5 + + +class PeriodicTaskType(str, StructuredEnum): + REMOTE = EnumField("remote", _("远程 API 周期任务")) + LOCAL = EnumField("local", _("本地函数周期任务")) + + +class NoticeSignalEnum(str, StructuredEnum): + recovered = EnumField("recovered", _("告警恢复时")) + abnormal = EnumField("abnormal", _("告警触发时")) + closed = EnumField("closed", _("告警关闭时")) + ack = EnumField("ack", _("告警确认时")) + no_data = EnumField("no_data", _("无数据告警")) diff --git a/dbm-ui/backend/db_periodic_task/local_tasks/__init__.py b/dbm-ui/backend/db_periodic_task/local_tasks/__init__.py new file mode 100644 index 0000000000..afa727627e --- /dev/null +++ b/dbm-ui/backend/db_periodic_task/local_tasks/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 backend.db_periodic_task.local_tasks.db_meta import * +from backend.db_periodic_task.local_tasks.db_monitor import * +from backend.db_periodic_task.local_tasks.db_proxy import * +from backend.db_periodic_task.local_tasks.redis_autofix import * +from backend.db_periodic_task.local_tasks.ticket import * +from backend.db_periodic_task.models import DBPeriodicTask + +from ..constants import PeriodicTaskType +from .register import registered_local_tasks + +# 删除过期的本地周期任务 +DBPeriodicTask.delete_legacy_periodic_task(registered_local_tasks, PeriodicTaskType.LOCAL.value) diff --git a/dbm-ui/backend/db_periodic_task/local_tasks/db_meta.py b/dbm-ui/backend/db_periodic_task/local_tasks/db_meta.py new file mode 100644 index 0000000000..4edb4eb98d --- /dev/null +++ b/dbm-ui/backend/db_periodic_task/local_tasks/db_meta.py @@ -0,0 +1,155 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import datetime +import json +import logging + +from celery.schedules import crontab +from celery.task import periodic_task + +from backend import env +from backend.components import CCApi +from backend.db_meta.models import AppCache, Cluster, Machine +from backend.db_meta.models.cluster_monitor import SyncFailedMachine +from backend.db_periodic_task.local_tasks.register import register_periodic_task +from backend.dbm_init.constants import CC_APP_ABBR_ATTR, CC_HOST_DBM_ATTR + +logger = logging.getLogger("celery") + + +@register_periodic_task(run_every=5, args=[1], kwargs={"z": 3}) +def check_db_meta(x=None, y=None, z=None): + """ + 巡检校验元数据 + """ + print("x:", x) + print("y:", y) + print("z:", z) + + +@register_periodic_task(run_every=crontab(minute="*/2")) +def update_host_dbmeta(bk_biz_id=None, cluster_id=None, cluster_ips=None, dbm_meta=None): + """ + 更新集群主机的dbm_meta属性 + TODO 应该挪到转移主机的时候做,每两分钟对全量主机更新一次 db_meta,太频繁了 + """ + + now = datetime.datetime.now() + logger.info("[update_host_dbmeta] start update begin: %s", now) + + # 0为默认的无效machine + failed_host_ids = list(SyncFailedMachine.objects.all().values_list("bk_host_id", flat=True)) + [0] + + # 支持业务级别的更新 + machines = Machine.objects.all() + if bk_biz_id: + machines = machines.filter(bk_biz_id=bk_biz_id) + + machines = machines.exclude( + bk_host_id__in=failed_host_ids, + ).order_by("-create_at") + + if cluster_id: + cluster = Cluster.objects.get(pk=cluster_id) + cluster_bk_host_ids = set( + list(cluster.storageinstance_set.values_list("machine__bk_host_id", flat=True)) + + list(cluster.proxyinstance_set.values_list("machine__bk_host_id", flat=True)) + ) + machines = Machine.objects.filter(bk_host_id__in=cluster_bk_host_ids) + + if cluster_ips: + machines = machines.filter(ip__in=cluster_ips) + + # 批量更新接口限制最多500条,这里取456条 + STEP = 456 + updated_hosts, failed_updates = [], [] + machine_count = machines.count() + for step in range(machine_count // STEP + 1): + updates = [] + for machine in machines[step * STEP : (step + 1) * STEP]: + cc_dbm_meta = machine.dbm_meta if dbm_meta is None else dbm_meta + updates.append( + {"properties": {CC_HOST_DBM_ATTR: json.dumps(cc_dbm_meta)}, "bk_host_id": machine.bk_host_id} + ) + updated_hosts.extend(updates) + + try: + CCApi.batch_update_host({"update": updates}, use_admin=True) + except Exception as e: # pylint: disable=wildcard-import + failed_updates.extend(updates) + logger.error("[update_host_dbmeta] batch update exception: %s (%s)", updates, e) + + # 容错处理:逐个更新,避免批量更新误伤有效ip + for fail_update in failed_updates: + try: + CCApi.update_host( + {"bk_host_id": fail_update["bk_host_id"], "data": fail_update["properties"]}, use_admin=True + ) + except Exception as e: # pylint: disable=wildcard-import + # 记录异常ip,下次任务直接排除掉,尽量走批量更新 + SyncFailedMachine.objects.get_or_create(bk_host_id=fail_update["bk_host_id"], error=str(e)) + logger.error("[update_host_dbmeta] single update error: %s (%s)", fail_update, e) + + logger.info( + "[update_host_dbmeta] finish update end: %s, update_cnt: %s", + datetime.datetime.now() - now, + len(updated_hosts), + ) + + +@register_periodic_task(run_every=crontab(minute="*/20")) +def update_app_cache(): + """缓存空闲机拓扑""" + now = datetime.datetime.now() + logger.warning("[db_meta] start update app cache start: %s", now) + + bizs = CCApi.search_business().get("info", []) + + updated_hosts, created_hosts = [], [] + for biz in bizs: + try: + logger.warning("[db_meta] sync app : %s", biz["bk_biz_id"]) + bk_app_abbr = biz.get(env.BK_APP_ABBR, "") + db_app_abbr = biz.get(CC_APP_ABBR_ATTR, "").lower().replace(" ", "-").replace("_", "-") + + # 目标环境中存在bk_app_abbr,则同步过来 + if env.BK_APP_ABBR and env.BK_APP_ABBR != CC_APP_ABBR_ATTR: + # db_app_abbr为空才同步 + if not db_app_abbr and db_app_abbr != bk_app_abbr: + CCApi.update_business( + {"bk_biz_id": biz["bk_biz_id"], "data": {"db_app_abbr": bk_app_abbr}}, use_admin=True + ) + db_app_abbr = bk_app_abbr + + obj, created = AppCache.objects.update_or_create( + defaults={ + "db_app_abbr": db_app_abbr, + "bk_biz_name": biz["bk_biz_name"], + "language": biz["language"], + "time_zone": biz["time_zone"], + "bk_biz_maintainer": biz["bk_biz_maintainer"], + }, + bk_biz_id=biz["bk_biz_id"], + ) + + if created: + created_hosts.append(obj.bk_biz_id) + else: + updated_hosts.append(obj.bk_biz_id) + except Exception as e: # pylint: disable=wildcard-import + logger.error("[db_meta] cache app error: %s (%s)", biz, e) + + logger.warning( + "[db_meta] finish update app cache end: %s, create_cnt: %s, update_cnt: %s", + datetime.datetime.now() - now, + len(created_hosts), + len(updated_hosts), + ) diff --git a/dbm-ui/backend/db_periodic_task/local_tasks/db_monitor.py b/dbm-ui/backend/db_periodic_task/local_tasks/db_monitor.py new file mode 100644 index 0000000000..b1615b3e0a --- /dev/null +++ b/dbm-ui/backend/db_periodic_task/local_tasks/db_monitor.py @@ -0,0 +1,189 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import datetime +import glob +import json +import logging +import os + +from celery.schedules import crontab +from django.utils.crypto import get_random_string +from django.utils.translation import ugettext as _ + +from backend import env +from backend.components import BKMonitorV3Api +from backend.configuration.constants import DEFAULT_DB_ADMINISTRATORS +from backend.configuration.models import DBAdministrator +from backend.db_monitor.constants import TPLS_ALARM_DIR, TargetLevel, TargetPriority +from backend.db_monitor.models import MonitorPolicy, NoticeGroup +from backend.db_periodic_task.local_tasks import register_periodic_task + +# logger = logging.getLogger("celery") +logger = logging.getLogger("root") + + +@register_periodic_task(run_every=crontab(minute="*/2")) +def update_local_notice_group(): + """同步告警组""" + + now = datetime.datetime.now() + logger.info("[local_notice_group] start update local group at: %s", now) + + dbas = DBAdministrator.objects.all() + updated_groups, created_groups = 0, 0 + + for dba in dbas: + # 跳过不需要同步的告警组 + if NoticeGroup.objects.filter(db_type=dba.db_type, is_built_in=True, dba_sync=False).exists(): + continue + + obj, updated = NoticeGroup.objects.update_or_create( + defaults={"receivers": dba.users}, bk_biz_id=dba.bk_biz_id, db_type=dba.db_type, is_built_in=True + ) + + if updated: + updated_groups += 1 + else: + created_groups += 1 + + logger.info( + "[local_notice_group] finish update local group end: %s, create_cnt: %s, update_cnt: %s", + datetime.datetime.now() - now, + created_groups, + updated_groups, + ) + + +@register_periodic_task(run_every=crontab(minute="*/3")) +def update_remote_notice_group(): + """同步告警组""" + + now = datetime.datetime.now() + logger.info("[remote_notice_group] start update remote group start: %s", now) + + groups = NoticeGroup.objects.filter(dba_sync=True) + + updated_groups = 0 + for group in groups: + try: + logger.info("[remote_notice_group] update remote group: %s " % group.db_type) + group_users = set(group.receivers + DEFAULT_DB_ADMINISTRATORS) + group_params = { + "bk_biz_id": env.DBA_APP_BK_BIZ_ID, + "notice_receiver": [{"type": "user", "id": user} for user in group_users], + "name": f"{group.get_db_type_display()}_DBA_{group.bk_biz_id}", + "notice_way": {"1": ["rtx", "voice"], "2": ["rtx"], "3": ["rtx"]}, + "webhook_url": "", + "message": _("DBA系统专用"), + "wxwork_group": {}, + } + + # update or create + if group.monitor_group_id: + group_params["id"] = group.monitor_group_id + + res = BKMonitorV3Api.save_notice_group(group_params) + group.monitor_group_id = res["id"] + group.save() + + updated_groups += 1 + except Exception as e: # pylint: disable=wildcard-import + logger.error("[remote_notice_group] update remote group error: %s (%s)", group.db_type, e) + + logger.info( + "[remote_notice_group] finish update remote group end: %s, update_cnt: %s", + datetime.datetime.now() - now, + updated_groups, + ) + + +def get_bkm_strategy(name, bk_biz_id=env.DBA_APP_BK_BIZ_ID): + res = BKMonitorV3Api.search_alarm_strategy_v3( + { + "page": 1, + "page_size": 1, + "conditions": [{"key": "name", "value": name}], + "bk_biz_id": bk_biz_id, + "with_notice_group": False, + "with_notice_group_detail": False, + }, + use_admin=True, + ) + + # 批量获取策略 + strategy_config_list = res["strategy_config_list"] + return strategy_config_list[0] if strategy_config_list else None + + +@register_periodic_task(run_every=crontab(minute="*/3")) +def sync_plat_monitor_policy(): + """TODO: 同步平台告警策略""" + + now = datetime.datetime.now() + logger.warning("[sync_plat_monitor_policy] sync bkmonitor alarm start: %s", now) + + # 逐个json导入,本地+远程 + updated_policies = 0 + alarm_tpls = os.path.join(TPLS_ALARM_DIR, "*.json") + # todo: just for test + for alarm_tpl in glob.glob(alarm_tpls)[20:40]: + with open(alarm_tpl, "r") as f: + template_dict = json.loads(f.read()) + + # todo: just for test + template_dict["name"] = template_dict["name"] + "-" + get_random_string(5) + + # patch template + template_dict["details"]["labels"] = list(set(template_dict["details"]["labels"])) + template_dict["details"]["name"] = template_dict["name"] + template_dict["details"]["priority"] = TargetPriority.PLATFORM.value + + policy = MonitorPolicy(**template_dict) + + # try: + policy_name = policy.name + logger.info("[sync_plat_monitor_policy] start sync bkm alarm: %s " % policy_name) + try: + policy = MonitorPolicy.objects.get(bk_biz_id=policy.bk_biz_id, db_type=policy.db_type, name=policy_name) + + if policy.is_synced: + logger.info("[sync_plat_monitor_policy] skip synced bkm alarm: %s " % policy_name) + continue + + policy.details["id"] = policy.monitor_strategy_id + logger.info("[sync_plat_monitor_policy] update bkm alarm: %s " % policy.db_type) + except MonitorPolicy.DoesNotExist: + # 支持从监控反向同步 + bkm_strategy = get_bkm_strategy(policy_name) + if bkm_strategy: + policy.details = bkm_strategy + logger.info("[sync_plat_monitor_policy] sync and update bkm alarm: %s " % policy.db_type) + else: + logger.info("[sync_plat_monitor_policy] create bkm alarm: %s " % policy.db_type) + + # 新建导入补充 + create = False if policy.details.get("id") else True + if create: + # fetch targets/test_rules/notify_rules/notify_groups from parent details + for attr, value in policy.parse_details().items(): + setattr(policy, attr, value) + + policy.save() + updated_policies += 1 + + # except Exception as e: # pylint: disable=wildcard-import + # logger.error("[sync_plat_monitor_policy] sync bkm alarm exception: %s (%s)", policy.db_type, e) + + logger.warning( + "[sync_plat_monitor_policy] finish sync bkm alarm end: %s, update_cnt: %s", + datetime.datetime.now() - now, + updated_policies, + ) diff --git a/dbm-ui/backend/db_periodic_task/local_tasks/db_proxy.py b/dbm-ui/backend/db_periodic_task/local_tasks/db_proxy.py new file mode 100644 index 0000000000..d349f59c9c --- /dev/null +++ b/dbm-ui/backend/db_periodic_task/local_tasks/db_proxy.py @@ -0,0 +1,110 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +import base64 +import copy +import logging +from collections import defaultdict +from typing import Dict, List + +from celery.schedules import crontab +from django.utils.translation import ugettext as _ +from jinja2 import Environment + +from backend import env +from backend.components import JobApi +from backend.components.gse.client import GseApi +from backend.configuration.constants import DBType +from backend.core.consts import BK_PUSH_CONFIG_PAYLOAD +from backend.db_periodic_task.local_tasks import register_periodic_task +from backend.db_proxy import nginxconf_tpl +from backend.db_proxy.constants import JOB_INSTANCE_EXPIRE_TIME, NGINX_PUSH_TARGET_PATH, ExtensionType +from backend.db_proxy.exceptions import ProxyPassBaseException +from backend.db_proxy.models import ClusterExtension, DBCloudProxy, DBExtension +from backend.utils.redis import RedisConn + +logger = logging.getLogger("celery") + + +@register_periodic_task(run_every=crontab(minute="*/1")) +def fill_cluster_service_nginx_conf(): + """填充集群额外服务的配置信息""" + + def _job_push_config_file(_cloud_id, _file_list, _nginx): + # 如果当前nginx的机器agent异常,则抛出日志且不下发。避免阻塞job + nginx_ip_list = [{"bk_cloud_id": _cloud_id, "ip": _nginx.internal_address}] + status_map = GseApi.get_agent_status({"hosts": nginx_ip_list}) + if not status_map[f"{_cloud_id}:{_nginx.internal_address}"]["bk_agent_alive"]: + logger.error(_("nginx机器{}当前agent异常,跳过文件下发。请管理员检查机器运行状态").format(_nginx.internal_address)) + return None + + job_payload = copy.deepcopy(BK_PUSH_CONFIG_PAYLOAD) + job_payload["task_name"] = f"cloud_id({_cloud_id})_push_nginx_conf" + job_payload["file_target_path"] = NGINX_PUSH_TARGET_PATH + job_payload["file_list"] = _file_list + job_payload["target_server"]["ip_list"] = nginx_ip_list + job_payload["callback_url"] = f"{env.BK_SAAS_CALLBACK_URL}/apis/proxypass/push_conf_callback/" + + logger.info(_("[{}] nginx配置文件下发参数:{}").format(nginx.internal_address, job_payload)) + _resp = JobApi.push_config_file(job_payload, raw=True) + if not _resp["result"]: + raise ProxyPassBaseException(_("下发文件job启动失败,错误信息: {}").format(_resp["message"])) + + return _resp + + flush_extension = ClusterExtension.get_extension_by_flush(is_flush=False, is_deleted=False) + cloud__db_type__extension: Dict[int, Dict[DBType, List[ClusterExtension]]] = defaultdict(lambda: defaultdict(list)) + # 通过cloud_id和db_type进行聚合 + for extension in flush_extension: + cloud__db_type__extension[extension.bk_cloud_id][extension.db_type].append(extension) + + for cloud_id in cloud__db_type__extension.keys(): + # 获取下发nginx conf的机器 TODO: 后续要改为clb的地址进行转发 + nginx = DBCloudProxy.objects.filter(bk_cloud_id=cloud_id).last() + nginx_detail = DBExtension.get_latest_extension( + bk_cloud_id=cloud_id, extension_type=ExtensionType.NGINX + ).details + + file_list: List[Dict[str, str]] = [] + extension_ids: List[int] = [] + for db_type in cloud__db_type__extension[cloud_id].keys(): + conf_tpl = getattr(nginxconf_tpl, f"{db_type}_conf_tpl", None) + if not conf_tpl: + # 如果没有模板,则打印日志并跳过 + logger.warning(_("集群类型:{} 的nginx配置文件不存在,跳过对该nginx配置的下发").format(db_type)) + continue + + jinja_env = Environment() + template = jinja_env.from_string(conf_tpl) + + for extension in cloud__db_type__extension[cloud_id][db_type]: + conf_payload = { + "bk_biz_id": extension.bk_biz_id, + "bk_cloud_id": extension.bk_cloud_id, + "db_type": extension.db_type, + "cluster_name": extension.cluster_name, + "service_type": extension.service_type, + "service_url": f"http://{extension.ip}:{extension.port}", + } + file_name = f"{extension.bk_biz_id}_{extension.db_type}_{extension.cluster_name}_nginx.conf" + file_content = str(base64.b64encode(template.render(conf_payload).encode("utf-8")), "utf-8") + file_list.append({"file_name": file_name, "content": file_content}) + + # 这里先提前写入access url,至于是否执行成功根据is_flush + extension.save_access_url(nginx_url=f"{nginx.external_address}:{nginx_detail['manage_port']}") + extension_ids.append(extension.id) + + # 下发nginx服务配置 + resp = _job_push_config_file(_cloud_id=cloud_id, _file_list=file_list, _nginx=nginx) + if resp: + # 缓存inst_id和nginx id,用于回调job,默认缓存时间和定时周期一致 + RedisConn.lpush(resp["data"]["job_instance_id"], *extension_ids, nginx.id) + RedisConn.expire(resp["data"]["job_instance_id"], JOB_INSTANCE_EXPIRE_TIME) diff --git a/dbm-ui/backend/db_periodic_task/local_tasks/redis_autofix.py b/dbm-ui/backend/db_periodic_task/local_tasks/redis_autofix.py new file mode 100644 index 0000000000..a8339fee12 --- /dev/null +++ b/dbm-ui/backend/db_periodic_task/local_tasks/redis_autofix.py @@ -0,0 +1,78 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import json +import logging + +from celery.schedules import crontab +from celery.task import periodic_task +from django.utils import timezone as datetime + +from backend.db_periodic_task.local_tasks.register import register_periodic_task +from backend.db_services.redis.autofix.bill import generate_autofix_ticket +from backend.db_services.redis.autofix.enums import AutofixItem, AutofixStatus +from backend.db_services.redis.autofix.models import RedisAutofixCore, RedisAutofixCtl +from backend.db_services.redis.autofix.watcher import ( + get_4_next_watch_ID, + save_swithed_host_by_cluster, + watcher_get_by_hosts, +) +from backend.utils.time import datetime2str + +logger = logging.getLogger("celery") + + +@register_periodic_task(run_every=crontab(minute="*/1")) +def watch_dbha_switch(): + """监控DBHA切换日志列表""" + + try: + onoff = RedisAutofixCtl.objects.get(ctl_name=AutofixItem.AUTOFIX_ENABLE.value) + if onoff.ctl_value == "off": + logger.info("hi and bye ^_^") + return + except RedisAutofixCtl.DoesNotExist: + RedisAutofixCtl.objects.create( + bk_cloud_id=0, bk_biz_id=0, ctl_value="off", ctl_name=AutofixItem.AUTOFIX_ENABLE.value + ).save() + return + + # 获取切换列表 + current_id, switch_hosts = watcher_get_by_hosts() + if len(switch_hosts) == 0: + RedisAutofixCtl.objects.filter(ctl_name=AutofixItem.DBHA_ID.value).update( + ctl_value=current_id, update_at=datetime2str(datetime.datetime.now()) + ) + logger.info("no hosts switched , heartbeat update ! next sw_id: {}".format(current_id)) + return + + logger.info("query switch logs :: {}:{}".format(current_id, switch_hosts)) + next_id = get_4_next_watch_ID(current_id, switch_hosts) + RedisAutofixCtl.objects.filter(ctl_name=AutofixItem.DBHA_ID.value).update( + ctl_value=next_id, update_at=datetime2str(datetime.datetime.now()) + ) + # 以集群维度聚合 # AutofixStatus.AF_REQRES.value + save_swithed_host_by_cluster(next_id, switch_hosts) + + +@register_periodic_task(run_every=crontab(minute="*/1")) +def start_autofix_flow(): + """请求自愈需要的资源""" + + try: + fixlists = RedisAutofixCore.objects.filter(deal_status=AutofixStatus.AF_TICKET.value) + except RedisAutofixCore.DoesNotExist: + logger.info("waiting request resource items ... ") + return + if len(fixlists) == 0: + logger.info("waiting request resource items ... ") + return + + generate_autofix_ticket(fixlists) diff --git a/dbm-ui/backend/db_periodic_task/local_tasks/register.py b/dbm-ui/backend/db_periodic_task/local_tasks/register.py new file mode 100644 index 0000000000..fc89d4badf --- /dev/null +++ b/dbm-ui/backend/db_periodic_task/local_tasks/register.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 celery import shared_task + +from backend.db_periodic_task.constants import PeriodicTaskType +from backend.db_periodic_task.models import DBPeriodicTask + +registered_local_tasks = set() + + +def register_periodic_task(run_every, args=None, kwargs=None): + """ + 注册周期任务 + """ + + def inner_wrapper(wrapped_func): + name = f"{wrapped_func.__module__}.{wrapped_func.__name__}" + registered_local_tasks.add(name) + DBPeriodicTask.create_or_update_periodic_task( + name=name, task=name, task_type=PeriodicTaskType.LOCAL.value, run_every=run_every, args=args, kwargs=kwargs + ) + return shared_task(wrapped_func) + + return inner_wrapper diff --git a/dbm-ui/backend/db_periodic_task/local_tasks/ticket.py b/dbm-ui/backend/db_periodic_task/local_tasks/ticket.py new file mode 100644 index 0000000000..a65045a3ea --- /dev/null +++ b/dbm-ui/backend/db_periodic_task/local_tasks/ticket.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 celery.schedules import crontab +from celery.task import periodic_task + +from backend.db_periodic_task.local_tasks import register_periodic_task +from backend.ticket.tasks.ticket_tasks import TicketTask + + +@register_periodic_task(run_every=5) +def auto_retry_exclusive_inner_flow(): + TicketTask.retry_exclusive_inner_flow() + + +@register_periodic_task(run_every=crontab(minute=3, hour=2)) +def auto_create_data_repair_ticket(): + TicketTask.auto_create_data_repair_ticket() diff --git a/dbm-ui/backend/db_periodic_task/migrations/0001_initial.py b/dbm-ui/backend/db_periodic_task/migrations/0001_initial.py new file mode 100644 index 0000000000..1941d0a0f1 --- /dev/null +++ b/dbm-ui/backend/db_periodic_task/migrations/0001_initial.py @@ -0,0 +1,47 @@ +# Generated by Django 3.2.19 on 2023-08-27 15:05 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ("django_celery_beat", "0015_edit_solarschedule_events_choices"), + ] + + operations = [ + migrations.CreateModel( + name="DBPeriodicTask", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("creator", models.CharField(max_length=64, verbose_name="创建人")), + ("create_at", models.DateTimeField(auto_now_add=True, verbose_name="创建时间")), + ("updater", models.CharField(max_length=64, verbose_name="修改人")), + ("update_at", models.DateTimeField(auto_now=True, verbose_name="更新时间")), + ("name", models.CharField(max_length=255, unique=True, verbose_name="周期任务名称")), + ( + "task_type", + models.CharField( + choices=[("remote", "远程 API 周期任务"), ("local", "本地函数周期任务")], max_length=32, verbose_name="任务类型" + ), + ), + ("is_frozen", models.BooleanField(default=False, help_text="人工冻结此任务,将不受更新影响", verbose_name="是否冻结")), + ( + "task", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="django_celery_beat.periodictask", + verbose_name="celery 周期任务实例", + ), + ), + ], + options={ + "verbose_name": "周期任务 PeriodicTask", + "verbose_name_plural": "周期任务 PeriodicTask", + "ordering": ["-id"], + }, + ), + ] diff --git a/dbm-ui/backend/db_periodic_task/migrations/__init__.py b/dbm-ui/backend/db_periodic_task/migrations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/dbm-ui/backend/db_periodic_task/models.py b/dbm-ui/backend/db_periodic_task/models.py new file mode 100644 index 0000000000..cf84afe5ae --- /dev/null +++ b/dbm-ui/backend/db_periodic_task/models.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +import json +import logging + +from django.db import models, transaction +from django.utils.translation import ugettext_lazy as _ +from django_celery_beat.models import PeriodicTask +from django_celery_beat.schedulers import ModelEntry + +from backend.bk_web import constants +from backend.bk_web.models import AuditedModel +from backend.db_periodic_task.constants import PeriodicTaskType + +logger = logging.getLogger("root") + + +class PeriodicTaskManager(models.Manager): + pass + + +class DBPeriodicTask(AuditedModel): + name = models.CharField(_("周期任务名称"), max_length=constants.LEN_LONG, unique=True) + task = models.ForeignKey(PeriodicTask, verbose_name=_("celery 周期任务实例"), on_delete=models.CASCADE) + task_type = models.CharField(_("任务类型"), choices=PeriodicTaskType.get_choices(), max_length=constants.LEN_SHORT) + is_frozen = models.BooleanField(_("是否冻结"), help_text=_("人工冻结此任务,将不受更新影响"), default=False) + + objects = PeriodicTaskManager() + + class Meta: + verbose_name = _("周期任务 PeriodicTask") + verbose_name_plural = _("周期任务 PeriodicTask") + ordering = ["-id"] + + def __str__(self): + return self.name + + @classmethod + @transaction.atomic + def delete_legacy_periodic_task(cls, tasks, task_type): + # 本地周期任务,且不再注册,说明是历史废弃任务,需删除 + legacy_tasks = DBPeriodicTask.objects.filter(task_type=task_type).exclude(name__in=tasks) + celery_task_ids = legacy_tasks.values_list("task_id", flat=True) + PeriodicTask.objects.filter(id__in=celery_task_ids).delete() + legacy_tasks.delete() + + @classmethod + def create_or_update_periodic_task(cls, name, task, run_every, task_type, args=None, kwargs=None): + """ + 创建/更新任务 + """ + + # 转换执行周期 + model_schedule, model_field = ModelEntry.to_model_schedule(run_every) + # 转换执行参数 + _args = json.dumps(args or []) + _kwargs = json.dumps(kwargs or {}) + + try: + db_task = DBPeriodicTask.objects.get(name=name) + except DBPeriodicTask.DoesNotExist: + # 新建周期任务 + celery_task = PeriodicTask.objects.create( + name=name, task=task, args=_args, kwargs=_kwargs, **{model_field: model_schedule} + ) + DBPeriodicTask.objects.create(name=name, task=celery_task, task_type=task_type) + else: + # 未冻结的情况,需要更新执行周期和执行参数 + if not db_task.is_frozen: + celery_task = db_task.task + setattr(celery_task, model_field, model_schedule) + celery_task.args = _args + celery_task.kwargs = _kwargs + celery_task.save(update_fields=[model_field, "args", "kwargs"]) diff --git a/dbm-ui/backend/db_periodic_task/readme.md b/dbm-ui/backend/db_periodic_task/readme.md new file mode 100644 index 0000000000..38d1dfcbb2 --- /dev/null +++ b/dbm-ui/backend/db_periodic_task/readme.md @@ -0,0 +1,9 @@ +# 周期任务管理 + DBM 的周期任务分为两类 +- (不可变)系统内置,不期望用户修改的 +- (可变)系统初始化,后期允许用户修改参数、执行时间、执行执行周期等 + +根据任务类型又分两类 +- 远程任务:由 dbm-service/celery-service 提供 +- 函数任务:由 dbm-ui/backend django 工程提供 + diff --git a/dbm-ui/backend/db_periodic_task/remote_tasks/__init__.py b/dbm-ui/backend/db_periodic_task/remote_tasks/__init__.py new file mode 100644 index 0000000000..5eb4b5f423 --- /dev/null +++ b/dbm-ui/backend/db_periodic_task/remote_tasks/__init__.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 backend.db_periodic_task.constants import PeriodicTaskType +from backend.db_periodic_task.models import DBPeriodicTask +from backend.db_periodic_task.remote_tasks.register import register_from_remote, registered_remote_tasks + +# 注册远程周期任务,并删除过期任务 +register_from_remote() +DBPeriodicTask.delete_legacy_periodic_task(registered_remote_tasks, PeriodicTaskType.REMOTE.value) diff --git a/dbm-ui/backend/db_periodic_task/remote_tasks/register.py b/dbm-ui/backend/db_periodic_task/remote_tasks/register.py new file mode 100644 index 0000000000..eef57423b3 --- /dev/null +++ b/dbm-ui/backend/db_periodic_task/remote_tasks/register.py @@ -0,0 +1,97 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +import logging + +from celery import shared_task +from celery.schedules import crontab + +from backend.components.base import DataAPI +from backend.components.celery_service.client import CeleryServiceApi +from backend.db_periodic_task.constants import ( + MAX_QUERY_TASK_STATUS_TIMES, + QUERY_TASK_STATUS_INTERVAL, + PeriodicTaskType, +) +from backend.db_periodic_task.models import DBPeriodicTask +from backend.env import CELERY_SERVICE_APIGW_DOMAIN + +logger = logging.getLogger("root") +registered_remote_tasks = set() + + +def register_from_remote(): + """ + 从远端 API 注册周期任务 + """ + async_task_list = CeleryServiceApi.async_list() + remote_task_name = f"{remote_call.__module__}.{remote_call.__name__}" + for task_info in async_task_list: + if not task_info["enable"]: + continue + # 如无定义的执行时间,默认每天凌晨一点 + run_every = crontab(**task_info["crontab"]) if task_info.get("crontab") else crontab(minute=0, hour=1) + # 任务的组成由 集群类型:任务名 组成 + task_name = f"{task_info['cluster_type']}:{task_info['name']}" + task_info.update(task_name=task_name) + # 注册远程定时任务 + registered_remote_tasks.add(task_name) + DBPeriodicTask.create_or_update_periodic_task( + name=task_name, + task=remote_task_name, + task_type=PeriodicTaskType.REMOTE.value, + run_every=run_every, + args=[task_info], + ) + + +@shared_task +def remote_call(async_task_info): + """ + 远程调用注册的 API,并轮询执行状态 + """ + method, url = async_task_info.get("method", "POST"), async_task_info["url"] + celery_method_name = async_task_info["task_name"] + if not hasattr(CeleryServiceApi, celery_method_name): + setattr( + CeleryServiceApi, + celery_method_name, + DataAPI( + method=method, + base=CELERY_SERVICE_APIGW_DOMAIN, + url=f"/async/{url}", + module=CeleryServiceApi.MODULE, + freeze_params=True, + description=f"Dynamic Periodic Interface--{celery_method_name}--{url}", + ), + ) + + session_id = getattr(CeleryServiceApi, celery_method_name)(params=async_task_info["empty_param"]) + query_async_task_status(session_id, 1) + + +@shared_task +def query_async_task_status(session_id, query_times): + """ + 周期性轮询任务执行状态 + """ + if query_times > MAX_QUERY_TASK_STATUS_TIMES: + # TODO: 超过最大次数后需要kill任务吗? + logger.error(f"The current number of polls exceeds the limit {MAX_QUERY_TASK_STATUS_TIMES}") + return + + task_exc_data = CeleryServiceApi.async_query({"session_id": session_id})[0] + if task_exc_data["done"]: + # TODO: 考虑如何记录任务执行日志 + logger.info(task_exc_data) + return + + query_async_task_status.apply_async((session_id, query_times + 1), countdown=QUERY_TASK_STATUS_INTERVAL) diff --git a/dbm-ui/backend/db_proxy/apps.py b/dbm-ui/backend/db_proxy/apps.py new file mode 100644 index 0000000000..44c44a1ced --- /dev/null +++ b/dbm-ui/backend/db_proxy/apps.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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.apps import AppConfig + + +class DBProxyConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "backend.db_proxy" diff --git a/dbm-ui/backend/db_proxy/migrations/0002_auto_20230711_1147.py b/dbm-ui/backend/db_proxy/migrations/0002_auto_20230711_1147.py new file mode 100644 index 0000000000..145e04574a --- /dev/null +++ b/dbm-ui/backend/db_proxy/migrations/0002_auto_20230711_1147.py @@ -0,0 +1,21 @@ +# Generated by Django 3.2.19 on 2023-07-11 03:47 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("db_proxy", "0001_initial"), + ] + + operations = [ + migrations.AlterUniqueTogether( + name="clusterextension", + unique_together=set(), + ), + migrations.AlterIndexTogether( + name="clusterextension", + index_together={("bk_biz_id", "db_type", "cluster_name", "service_type")}, + ), + ] diff --git a/dbm-ui/backend/db_proxy/models.py b/dbm-ui/backend/db_proxy/models.py index 580eda7726..d3f2d03ded 100644 --- a/dbm-ui/backend/db_proxy/models.py +++ b/dbm-ui/backend/db_proxy/models.py @@ -13,12 +13,14 @@ from typing import Dict, Union from django.db import models +from django.db.models.manager import Manager from django.utils.translation import gettext_lazy as _ from backend.bk_web.constants import LEN_LONG, LEN_NORMAL, LEN_SHORT from backend.bk_web.models import AuditedModel from backend.configuration.constants import DBType from backend.db_proxy.constants import CLUSTER__SERVICE_MAP, ClusterServiceType, ExtensionServiceStatus, ExtensionType +from backend.db_proxy.exceptions import ProxyPassBaseException from backend.flow.consts import CloudDBHATypeEnum @@ -116,6 +118,28 @@ def update_details(self, **kwargs): self.save() +class ClusterExtensionManager(Manager): + def create(self, **kwargs): + try: + # 因为ClusterExtension是软删除,所以创建的时候要判断是否有同种配置记录 + # TODO: 后续需要支持定时删除带有软删除标记的nginx文件 + ext = ClusterExtension.objects.filter( + bk_biz_id=kwargs["bk_biz_id"], + db_type=kwargs["db_type"], + cluster_name=kwargs["cluster_name"], + service_type=kwargs["service_type"], + is_deleted=False, + ) + if ext.count(): + raise ProxyPassBaseException( + _("在业务{}下已经存在同种配置的服务组件记录,请检查是否在同一业务下部署了同名的集群").format(kwargs["bk_biz_id"]) + ) + except ClusterExtension.DoesNotExist: + pass + + super().create(**kwargs) + + class ClusterExtension(AuditedModel): """集群部署所带的额外服务组件记录,如es的kibana""" @@ -131,11 +155,12 @@ class ClusterExtension(AuditedModel): # 当定时任务执行的时候,需要拉去is_flush为False的进行操作,并刷新为True is_flush = models.BooleanField(verbose_name=_("是否刷新(该条记录是否执行)"), default=False) is_deleted = models.BooleanField(verbose_name=_("是否删除"), default=False) - access_url = models.TextField(verbose_name=_("服务访问地址"), default="") + objects = ClusterExtensionManager() + class Meta: - unique_together = ["bk_biz_id", "db_type", "cluster_name", "service_type"] + index_together = [("bk_biz_id", "db_type", "cluster_name", "service_type")] @classmethod def get_extension_by_flush(cls, is_flush: bool = False, is_deleted: bool = False): diff --git a/dbm-ui/backend/db_proxy/tasks.py b/dbm-ui/backend/db_proxy/tasks.py deleted file mode 100644 index aef206f16e..0000000000 --- a/dbm-ui/backend/db_proxy/tasks.py +++ /dev/null @@ -1,109 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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. -""" -import base64 -import copy -import logging -from collections import defaultdict -from typing import Dict, List - -from celery import shared_task -from django.utils.translation import ugettext as _ -from jinja2 import Environment - -from backend import env -from backend.configuration.constants import DBType -from backend.core.consts import BK_PUSH_CONFIG_PAYLOAD -from backend.db_proxy.models import ClusterExtension, DBCloudProxy, DBExtension -from backend.utils.redis import RedisConn - -from ..components import JobApi -from ..components.gse.client import GseApi -from . import nginxconf_tpl -from .constants import JOB_INSTANCE_EXPIRE_TIME, NGINX_PUSH_TARGET_PATH, ExtensionType -from .exceptions import ProxyPassBaseException - -logger = logging.getLogger("celery") - - -@shared_task -def fill_cluster_service_nginx_conf(): - """填充集群额外服务的配置信息""" - - def _job_push_config_file(_cloud_id, _file_list, _nginx): - # 如果当前nginx的机器agent异常,则抛出日志且不下发。避免阻塞job - nginx_ip_list = [{"bk_cloud_id": _cloud_id, "ip": _nginx.internal_address}] - status_map = GseApi.get_agent_status({"hosts": nginx_ip_list}) - if not status_map[f"{_cloud_id}:{_nginx.internal_address}"]["bk_agent_alive"]: - logger.error(_("nginx机器{}当前agent异常,跳过文件下发。请管理员检查机器运行状态").format(_nginx.internal_address)) - return None - - job_payload = copy.deepcopy(BK_PUSH_CONFIG_PAYLOAD) - job_payload["task_name"] = f"cloud_id({_cloud_id})_push_nginx_conf" - job_payload["file_target_path"] = NGINX_PUSH_TARGET_PATH - job_payload["file_list"] = _file_list - job_payload["target_server"]["ip_list"] = nginx_ip_list - job_payload["callback_url"] = f"{env.BK_SAAS_CALLBACK_URL}/apis/proxypass/push_conf_callback/" - - logger.info(_("[{}] nginx配置文件下发参数:{}").format(nginx.internal_address, job_payload)) - _resp = JobApi.push_config_file(job_payload, raw=True) - if not _resp["result"]: - raise ProxyPassBaseException(_("下发文件job启动失败,错误信息: {}").format(_resp["message"])) - - return _resp - - flush_extension = ClusterExtension.get_extension_by_flush(is_flush=False, is_deleted=False) - cloud__db_type__extension: Dict[int, Dict[DBType, List[ClusterExtension]]] = defaultdict(lambda: defaultdict(list)) - # 通过cloud_id和db_type进行聚合 - for extension in flush_extension: - cloud__db_type__extension[extension.bk_cloud_id][extension.db_type].append(extension) - - for cloud_id in cloud__db_type__extension.keys(): - # 获取下发nginx conf的机器 TODO: 后续要改为clb的地址进行转发 - nginx = DBCloudProxy.objects.filter(bk_cloud_id=cloud_id).last() - nginx_detail = DBExtension.get_latest_extension( - bk_cloud_id=cloud_id, extension_type=ExtensionType.NGINX - ).details - - file_list: List[Dict[str, str]] = [] - extension_ids: List[int] = [] - for db_type in cloud__db_type__extension[cloud_id].keys(): - conf_tpl = getattr(nginxconf_tpl, f"{db_type}_conf_tpl", None) - if not conf_tpl: - # 如果没有模板,则打印日志并跳过 - logger.warning(_("集群类型:{} 的nginx配置文件不存在,跳过对该nginx配置的下发").format(db_type)) - continue - - jinja_env = Environment() - template = jinja_env.from_string(conf_tpl) - - for extension in cloud__db_type__extension[cloud_id][db_type]: - conf_payload = { - "bk_biz_id": extension.bk_biz_id, - "bk_cloud_id": extension.bk_cloud_id, - "db_type": extension.db_type, - "cluster_name": extension.cluster_name, - "service_type": extension.service_type, - "service_url": f"http://{extension.ip}:{extension.port}", - } - file_name = f"{extension.bk_biz_id}_{extension.db_type}_{extension.cluster_name}_nginx.conf" - file_content = str(base64.b64encode(template.render(conf_payload).encode("utf-8")), "utf-8") - file_list.append({"file_name": file_name, "content": file_content}) - - # 这里先提前写入access url,至于是否执行成功根据is_flush - extension.save_access_url(nginx_url=f"{nginx.external_address}:{nginx_detail['manage_port']}") - extension_ids.append(extension.id) - - # 下发nginx服务配置 - resp = _job_push_config_file(_cloud_id=cloud_id, _file_list=file_list, _nginx=nginx) - if resp: - # 缓存inst_id和nginx id,用于回调job,默认缓存时间和定时周期一致 - RedisConn.lpush(resp["data"]["job_instance_id"], *extension_ids, nginx.id) - RedisConn.expire(resp["data"]["job_instance_id"], JOB_INSTANCE_EXPIRE_TIME) diff --git a/dbm-ui/backend/db_proxy/views/db_meta/serializers.py b/dbm-ui/backend/db_proxy/views/db_meta/serializers.py index 2aded71059..2fe04954d7 100644 --- a/dbm-ui/backend/db_proxy/views/db_meta/serializers.py +++ b/dbm-ui/backend/db_proxy/views/db_meta/serializers.py @@ -11,6 +11,7 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from backend.db_meta import api from backend.db_proxy.views import mock_data from backend.db_proxy.views.serialiers import BaseProxyPassSerialier @@ -113,3 +114,8 @@ class FakeResetTendbHACluster(BaseProxyPassSerialier): slave_instance = serializers.CharField(help_text=_("slave实例")) immute_domain = serializers.CharField(help_text=_("域名")) slave_domain = serializers.CharField(help_text=_("slave域名"), required=False) + + +class BizClusterSerializer(BaseProxyPassSerialier): + bk_biz_id = serializers.IntegerField(help_text=_("业务ID")) + immute_domains = serializers.ListField(help_text=_("域名列表"), child=serializers.CharField()) diff --git a/dbm-ui/backend/db_proxy/views/db_meta/views.py b/dbm-ui/backend/db_proxy/views/db_meta/views.py index cdf008809a..f256e5048e 100644 --- a/dbm-ui/backend/db_proxy/views/db_meta/views.py +++ b/dbm-ui/backend/db_proxy/views/db_meta/views.py @@ -20,6 +20,7 @@ from backend.db_meta.models import BKCity from backend.db_proxy.constants import SWAGGER_TAG from backend.db_proxy.views.db_meta.serializers import ( + BizClusterSerializer, BKCityNameSerializer, ClusterDetailSerializer, EntryDetailSerializer, @@ -164,9 +165,9 @@ def machines_detail(self, request): validated_data = self.params_validate(self.get_serializer_class()) return Response(DBHA.query_cluster_by_hosts(validated_data["hosts"])) - # 批量查询集群信息 + # 批量查询redis集群信息 @common_swagger_auto_schema( - operation_summary=_("[dbmeta]查询集群信息"), + operation_summary=_("[dbmeta]查询redis集群信息"), request_body=ClusterDetailSerializer(), tags=[SWAGGER_TAG], ) @@ -197,3 +198,22 @@ def fake_reset_tendbha_cluster(self, request): return Response({"msg": "", "code": 0, "data": api.fake.fake_reset_tendbha_cluster(**request.data)}) except Exception as e: # pylint: disable=broad-except return Response({"msg": "{}".format(e), "code": 1, "data": ""}) + + @common_swagger_auto_schema( + operation_summary=_("[dbmeta]根据域名查询集群信息"), + query_serializer=BizClusterSerializer(), + tags=[SWAGGER_TAG], + ) + @action( + methods=["GET"], + detail=False, + serializer_class=BizClusterSerializer, + url_path="dbmeta/meta/biz_clusters", + ) + def biz_clusters(self, request): + validated_data = self.params_validate(self.get_serializer_class()) + data = api.priv_manager.biz_clusters( + bk_biz_id=validated_data["bk_biz_id"], + immute_domains=validated_data["immute_domains"], + ) + return Response(data) diff --git a/dbm-ui/backend/db_proxy/views/redis_dts/serializers.py b/dbm-ui/backend/db_proxy/views/redis_dts/serializers.py index cb27dd4e8e..164c9267eb 100644 --- a/dbm-ui/backend/db_proxy/views/redis_dts/serializers.py +++ b/dbm-ui/backend/db_proxy/views/redis_dts/serializers.py @@ -18,12 +18,6 @@ class IsDtsserverInBlacklistSerializer(BaseProxyPassSerialier): ip = serializers.IPAddressField(help_text=_("DTS_server IP"), required=True) -class DtsHistoryJobsSerializer(BaseProxyPassSerialier): - user = serializers.CharField(help_text=_("创建人"), required=False) - start_time = serializers.CharField(help_text=_("开始时间"), required=False) - end_time = serializers.CharField(help_text=_("结束时间"), required=False) - - class DtsJobSerializer(BaseProxyPassSerialier): bill_id = serializers.IntegerField(help_text=_("任务ID"), required=True) src_cluster = serializers.CharField(help_text=_("源集群"), required=True) @@ -36,19 +30,6 @@ class DtsJobTasksSerializer(BaseProxyPassSerialier): dst_cluster = serializers.CharField(help_text=_("目标集群"), required=True) -class DtsTaskIDsSerializer(BaseProxyPassSerialier): - task_ids = serializers.ListField( - help_text=_("子任务ID列表"), child=serializers.IntegerField(), allow_empty=False, required=True - ) - - -class DtsTaskOperateSerializer(BaseProxyPassSerialier): - task_ids = serializers.ListField( - help_text=_("子任务ID列表"), child=serializers.IntegerField(), allow_empty=False, required=True - ) - operate = serializers.CharField(help_text=_("操作类型"), required=True) - - class DtsDistributeLockSerializer(BaseProxyPassSerialier): lockkey = serializers.CharField(help_text=_("锁key名"), required=True) holder = serializers.CharField(help_text=_("锁持有者"), required=True) @@ -114,3 +95,17 @@ class DtsTasksUpdateSerializer(BaseProxyPassSerialier): help_text=_("子任务ID列表"), child=serializers.IntegerField(), allow_empty=False, required=True ) col_to_val = serializers.DictField(child=serializers.CharField()) + + +class DtsDataCopyBaseItemSerializer(serializers.Serializer): + src_cluster = serializers.CharField(help_text=_("源集群"), required=True) + src_cluster_password = serializers.CharField(help_text=_("源集群密码"), allow_blank=True) + dst_cluster = serializers.CharField(help_text=_("目标集群"), required=True) + dst_cluster_password = serializers.CharField(help_text=_("目标集群密码"), allow_blank=True) + + +class DtsTestRedisConnectionSerializer(BaseProxyPassSerialier): + data_copy_type = serializers.CharField(help_text=_("数据复制类型"), required=True) + infos = serializers.ListField( + help_text=_("复制列表"), child=DtsDataCopyBaseItemSerializer(), allow_empty=False, required=True + ) diff --git a/dbm-ui/backend/db_proxy/views/redis_dts/views.py b/dbm-ui/backend/db_proxy/views/redis_dts/views.py index 531f607670..c3a3278dcd 100644 --- a/dbm-ui/backend/db_proxy/views/redis_dts/views.py +++ b/dbm-ui/backend/db_proxy/views/redis_dts/views.py @@ -16,7 +16,6 @@ from backend.db_proxy.constants import SWAGGER_TAG from backend.db_proxy.views.redis_dts.serializers import ( DtsDistributeLockSerializer, - DtsHistoryJobsSerializer, DtsJobSrcIPRunningTasksSerializer, DtsJobTasksSerializer, DtsJobToScheduleTasksSerializer, @@ -25,20 +24,16 @@ DtsServerMaxSyncPortSerializer, DtsServerMigatingTasksSerializer, DtsTaskByTaskIDSerializer, - DtsTaskIDsSerializer, - DtsTaskOperateSerializer, DtsTasksUpdateSerializer, + DtsTestRedisConnectionSerializer, IsDtsserverInBlacklistSerializer, ) from backend.db_proxy.views.views import BaseProxyPassViewSet -from backend.db_services.redis_dts.apis import ( +from backend.db_services.redis.redis_dts.apis import ( dts_distribute_trylock, dts_distribute_unlock, - dts_tasks_operate, - dts_tasks_restart, - dts_tasks_retry, dts_tasks_updates, - get_dts_history_jobs, + dts_test_redis_connections, get_dts_job_detail, get_dts_job_tasks, get_dts_server_max_sync_port, @@ -50,7 +45,6 @@ get_last_30days_to_schedule_jobs, is_dtsserver_in_blacklist, ) -from backend.utils.time import datetime2str, strptime class DtsApiProxyPassViewSet(BaseProxyPassViewSet): @@ -73,18 +67,6 @@ def is_dtsserver_in_blacklist(self, request): validated_data = self.params_validate(self.get_serializer_class()) return Response({"in": is_dtsserver_in_blacklist(validated_data)}) - @common_swagger_auto_schema( - operation_summary=_("获取DTS历史任务以及其对应task cnt"), - request_body=DtsHistoryJobsSerializer, - tags=[SWAGGER_TAG], - ) - @action( - methods=["POST"], detail=False, serializer_class=DtsHistoryJobsSerializer, url_path="redis_dts/history_jobs" - ) - def get_dts_history_jobs(self, request): - validated_data = self.params_validate(self.get_serializer_class()) - return Response(get_dts_history_jobs(validated_data)) - @common_swagger_auto_schema( operation_summary=_("获取dts任务详情"), request_body=DtsJobTasksSerializer, @@ -105,38 +87,6 @@ def get_dts_job_tasks(self, request): validated_data = self.params_validate(self.get_serializer_class()) return Response(get_dts_job_tasks(validated_data)) - @common_swagger_auto_schema( - operation_summary=_("dts task操作,目前支持 同步完成(syncStopTodo)、强制终止(ForceKillTaskTodo) 两个操作"), - request_body=DtsTaskOperateSerializer, - tags=[SWAGGER_TAG], - ) - @action( - methods=["POST"], detail=False, serializer_class=DtsTaskOperateSerializer, url_path="redis_dts/tasks_operate" - ) - def dts_task_operate(self, request): - validated_data = self.params_validate(self.get_serializer_class()) - return Response(dts_tasks_operate(validated_data)) - - @common_swagger_auto_schema( - operation_summary=_("dts tasks重新开始"), - request_body=DtsTaskIDsSerializer, - tags=[SWAGGER_TAG], - ) - @action(methods=["POST"], detail=False, serializer_class=DtsTaskIDsSerializer, url_path="redis_dts/tasks_restart") - def dts_task_restart(self, request): - validated_data = self.params_validate(self.get_serializer_class()) - return Response(dts_tasks_restart(validated_data)) - - @common_swagger_auto_schema( - operation_summary=_("dts tasks重试当前步骤"), - request_body=DtsTaskIDsSerializer, - tags=[SWAGGER_TAG], - ) - @action(methods=["POST"], detail=False, serializer_class=DtsTaskIDsSerializer, url_path="redis_dts/tasks_retry") - def dts_task_retry(self, request): - validated_data = self.params_validate(self.get_serializer_class()) - return Response(dts_tasks_retry(validated_data)) - @common_swagger_auto_schema( operation_summary=_("dts 分布式锁,trylock,成功返回True,失败返回False"), request_body=DtsDistributeLockSerializer, @@ -283,3 +233,18 @@ def get_task_by_task_id(self, request): def update_tasks(self, request): validated_data = self.params_validate(self.get_serializer_class()) return Response({"rows_affected": dts_tasks_updates(validated_data)}) + + @common_swagger_auto_schema( + operation_summary=_("redis 连接性测试"), + request_body=DtsTestRedisConnectionSerializer, + tags=[SWAGGER_TAG], + ) + @action( + methods=["POST"], + detail=False, + serializer_class=DtsTestRedisConnectionSerializer, + url_path="redis_dts/test_redis_connection", + ) + def test_redis_connection(self, request): + validated_data = self.params_validate(self.get_serializer_class()) + return Response(dts_test_redis_connections(validated_data)) diff --git a/dbm-ui/backend/db_services/bigdata/influxdb/query.py b/dbm-ui/backend/db_services/bigdata/influxdb/query.py index a56c1a66af..17aad18fd2 100644 --- a/dbm-ui/backend/db_services/bigdata/influxdb/query.py +++ b/dbm-ui/backend/db_services/bigdata/influxdb/query.py @@ -137,9 +137,9 @@ def _to_instance( "instance_address": f"{instance['machine__ip']}{IP_PORT_DIVIDER}{instance['port']}", "instance_name": instance["name"], "bk_host_id": instance["machine__bk_host_id"], - "bk_mem": host_info.get("bk_mem", ""), - "bk_cpu": host_info.get("bk_cpu", ""), - "bk_disk": host_info.get("bk_disk", ""), + "mem": host_info.get("bk_mem", ""), + "cpu": host_info.get("bk_cpu", ""), + "disk": host_info.get("bk_disk", ""), "bk_cloud_id": instance["machine__bk_cloud_id"], "bk_cloud_name": cloud_info[str(instance["machine__bk_cloud_id"])]["bk_cloud_name"], "role": instance["role"], diff --git a/dbm-ui/backend/db_services/cmdb/biz.py b/dbm-ui/backend/db_services/cmdb/biz.py index 9f526a530e..37601cddd8 100644 --- a/dbm-ui/backend/db_services/cmdb/biz.py +++ b/dbm-ui/backend/db_services/cmdb/biz.py @@ -74,3 +74,39 @@ def get_db_app_abbr(bk_biz_id: int) -> str: use_admin=True, )["info"][0].get(CC_APP_ABBR_ATTR, "") return abbr + + +def list_cc_obj_user(bk_biz_id: int) -> list: + # 查询 CC 的角色对象 + roles = { + attr["bk_property_id"]: attr["bk_property_name"] + for attr in CCApi.search_object_attribute({"bk_obj_id": "biz"}, use_admin=True) + if attr["bk_property_type"] == "objuser" + } + results = CCApi.search_business( + { + "fields": list(roles.keys()), + "biz_property_filter": { + "condition": "AND", + "rules": [{"field": "bk_biz_id", "operator": "equal", "value": int(bk_biz_id)}], + }, + }, + use_admin=True, + ).get("info", []) + try: + biz_info = results[0] + except IndexError: + biz_info = {} + cc_obj_users = [ + { + "id": role, + "display_name": role_display, + "logo": "", + "type": "group", + "members": [] if not biz_info.get(role) else [member for member in biz_info[role].split(",")], + } + for role, role_display in roles.items() + ] + # TODO dbm 角色录入 cmdb ?不合适, db type 会导致角色太多 + # 考虑以虚拟角色维护 DBA + return cc_obj_users diff --git a/dbm-ui/backend/db_services/cmdb/constants.py b/dbm-ui/backend/db_services/cmdb/constants.py new file mode 100644 index 0000000000..a6179e2f2b --- /dev/null +++ b/dbm-ui/backend/db_services/cmdb/constants.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +MAX_DB_MODULE_LIMIT = 63 +MAX_DB_APP_ABBR_LIMIT = 63 diff --git a/dbm-ui/backend/db_services/cmdb/serializers.py b/dbm-ui/backend/db_services/cmdb/serializers.py index 816918e3ed..e3e7a674f3 100644 --- a/dbm-ui/backend/db_services/cmdb/serializers.py +++ b/dbm-ui/backend/db_services/cmdb/serializers.py @@ -12,6 +12,7 @@ from rest_framework import serializers from backend.db_meta.enums import ClusterType +from backend.db_services.cmdb.constants import MAX_DB_APP_ABBR_LIMIT, MAX_DB_MODULE_LIMIT class BIZSLZ(serializers.Serializer): @@ -35,10 +36,22 @@ class CreateModuleSLZ(serializers.Serializer): db_module_name = serializers.CharField(help_text=_("DB模块名")) cluster_type = serializers.ChoiceField(help_text=_("集群类型"), choices=ClusterType.get_choices()) + def validate(self, attrs): + if len(attrs["db_module_name"]) > MAX_DB_MODULE_LIMIT: + raise serializers.ValidationError(_("请确保模块名称的长度不超过: {}").format(MAX_DB_MODULE_LIMIT)) + + return attrs + class SetBkAppAbbrSLZ(serializers.Serializer): db_app_abbr = serializers.CharField(help_text=_("英文缩写")) + def validate(self, attrs): + if len(attrs["db_app_abbr"]) > MAX_DB_APP_ABBR_LIMIT: + raise serializers.ValidationError(_("请确保业务CODE的长度不超过: {}").format(MAX_DB_APP_ABBR_LIMIT)) + + return attrs + class TopoSerializer(serializers.Serializer): bk_biz_id = serializers.IntegerField(help_text=_("业务ID")) diff --git a/dbm-ui/backend/db_services/cmdb/views.py b/dbm-ui/backend/db_services/cmdb/views.py index ddd52af25e..5a60ee5af6 100644 --- a/dbm-ui/backend/db_services/cmdb/views.py +++ b/dbm-ui/backend/db_services/cmdb/views.py @@ -77,3 +77,8 @@ def set_db_app_abbr(self, request, bk_biz_id): validated_data = self.params_validate(self.get_serializer_class()) biz.set_db_app_abbr(bk_biz_id, validated_data["db_app_abbr"], raise_exception=True) return Response() + + @common_swagger_auto_schema(operation_summary=_("查询 CC 角色对象"), tags=[SWAGGER_TAG]) + @action(methods=["GET"], detail=True) + def list_cc_obj_user(self, request, bk_biz_id): + return Response(biz.list_cc_obj_user(bk_biz_id)) diff --git a/dbm-ui/backend/db_services/dbbase/instances/__init__.py b/dbm-ui/backend/db_services/dbbase/instances/__init__.py new file mode 100644 index 0000000000..aa5085c628 --- /dev/null +++ b/dbm-ui/backend/db_services/dbbase/instances/__init__.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" diff --git a/dbm-ui/backend/db_services/dbbase/instances/constants.py b/dbm-ui/backend/db_services/dbbase/instances/constants.py new file mode 100644 index 0000000000..3ca5fc9760 --- /dev/null +++ b/dbm-ui/backend/db_services/dbbase/instances/constants.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +RESOURCE_TAG = "db_services/instances" diff --git a/dbm-ui/backend/db_services/dbbase/instances/handlers.py b/dbm-ui/backend/db_services/dbbase/instances/handlers.py new file mode 100644 index 0000000000..d4bd790059 --- /dev/null +++ b/dbm-ui/backend/db_services/dbbase/instances/handlers.py @@ -0,0 +1,141 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 dataclasses import asdict +from typing import Any, Dict, List, Union + +from django.db.models import F, Q + +from backend import env +from backend.constants import IP_PORT_DIVIDER +from backend.db_meta.models import Machine, ProxyInstance, StorageInstance +from backend.db_services.ipchooser.handlers.host_handler import HostHandler +from backend.db_services.ipchooser.query.resource import ResourceQueryHelper +from backend.db_services.mysql.cluster.handlers import ClusterServiceHandler +from backend.db_services.mysql.dataclass import DBInstance + + +class InstanceHandler: + def __init__(self, bk_biz_id: int): + self.bk_biz_id = bk_biz_id + + def check_instances(self, query_instances: List[Union[str, Dict]], cluster_ids: List[int] = None) -> List[dict]: + """ + 查询实例的详细信息(包括实例本身信息+主机信息+关联集群信息) + :param query_instances: ["0:127.0.0.1:10000", "127.0.0.1", "127.0.0.1:20000"]或者[{....}, [....}] + :param cluster_ids: 实例所属的集群ID + :return + """ + + if not query_instances: + return [] + + # 如果传来的inst是字典类型,默认它是已经查询出的实例,只需补充信息 + # 否则需要先查询实例,再补充相关信息 + if isinstance(query_instances[0], Dict): + storages_proxies_instances = query_instances + else: + query_conditions = Q() + for address in query_instances: + split_len = len(address.split(IP_PORT_DIVIDER)) + if split_len == 1: + # 只输入 IP + query_conditions |= Q(machine__ip=address) + elif split_len == 2: + # IP:PORT + ip, port = address.split(IP_PORT_DIVIDER) + query_conditions |= Q(machine__ip=ip, port=port) + else: + # Cloud:IP:PORT + bk_cloud_id, ip, port = address.split(IP_PORT_DIVIDER) + query_conditions |= Q(machine__ip=ip, port=port, cluster__bk_cloud_id=bk_cloud_id) + + if cluster_ids: + query_conditions &= Q(cluster__id__in=cluster_ids) + + # 由于不知道输入的是什么实例,因此把存储实例和 proxy 实例同时查询出来 + storages = ( + StorageInstance.objects.annotate(role=F("instance_inner_role")) + .select_related("machine") + .prefetch_related("cluster") + .filter(Q(bk_biz_id=self.bk_biz_id) & query_conditions) + ) + proxies = ( + ProxyInstance.objects.annotate(role=F("access_layer")) + .select_related("machine") + .prefetch_related("cluster") + .filter(Q(bk_biz_id=self.bk_biz_id) & query_conditions) + ) + + # 如果无法查询实例,则直接返回 + if not storages and not proxies: + return [] + + storages_proxies_instances = [*storages, *proxies] + + bk_host_ids: List[int] = [] + db_instances: List[DBInstance] = [] + host_id_instance_map: Dict[str, Dict] = {} + instance_related_clusters: List[Dict[str, Any]] = ClusterServiceHandler( + self.bk_biz_id + ).find_related_clusters_by_instances( + instances=[DBInstance.from_inst_obj(inst) for inst in storages_proxies_instances] + ) + inst_address__related_clusters_map: Dict[str, Dict[str, Any]] = { + info["instance_address"]: info for info in instance_related_clusters + } + cloud_info = ResourceQueryHelper.search_cc_cloud(get_cache=True) + + for inst in storages_proxies_instances: + db_inst = DBInstance.from_inst_obj(inst) + db_inst_address = f"{db_inst.ip}:{db_inst.port}" + db_inst_related_cluster = inst_address__related_clusters_map[db_inst_address] + host_id_instance_map[str(db_inst)] = { + **asdict(db_inst), + "bk_cloud_name": cloud_info[str(db_inst.bk_cloud_id)]["bk_cloud_name"], + "instance_address": f"{db_inst.ip}{IP_PORT_DIVIDER}{db_inst.port}", + "cluster_id": db_inst_related_cluster["cluster_info"]["id"], + "cluster_name": db_inst_related_cluster["cluster_info"]["cluster_name"], + "master_domain": db_inst_related_cluster["cluster_info"]["master_domain"], + # 目前的设计,instance_role 才能更好区分不通集群类型中机器的角色 + "create_at": inst["create_at"] if isinstance(inst, Dict) else inst.create_at, + "role": inst["role"] if isinstance(inst, Dict) else inst.role, + "status": inst["status"] if isinstance(inst, Dict) else inst.status, + "cluster_type": db_inst_related_cluster["cluster_info"]["cluster_type"], + # 实例的关联集群把本身集群和集群的关联集群合并到一起 + "related_clusters": [ + *db_inst_related_cluster["related_clusters"], + db_inst_related_cluster["cluster_info"], + ], + } + bk_host_ids.append(inst["bk_host_id"] if isinstance(inst, Dict) else inst.machine.bk_host_id) + db_instances.append(db_inst) + + # 查询补充主机信息 + host_infos = HostHandler.check( + [{"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "scope_type": "biz"}], [], [], bk_host_ids + ) + host_id_info_map = {host_info["host_id"]: host_info for host_info in host_infos} + return [ + {**host_id_instance_map[str(db_inst)], **{"host_info": host_id_info_map.get(db_inst.bk_host_id, {})}} + for db_inst in db_instances + ] + + def get_machine_by_instances(self, query_instances: List[str]) -> Dict[str, Machine]: + """根据ip:port查询实例的机器信息""" + + storage_instances = StorageInstance.find_insts_by_addresses(query_instances).filter(bk_biz_id=self.bk_biz_id) + proxy_instances = ProxyInstance.find_insts_by_addresses(query_instances).filter(bk_biz_id=self.bk_biz_id) + + address__machine_map: Dict[str, Machine] = {} + for instances in [storage_instances, proxy_instances]: + address__machine_map.update({inst.ip_port: inst.machine for inst in instances}) + + return address__machine_map diff --git a/dbm-ui/backend/db_services/dbbase/instances/serializers.py b/dbm-ui/backend/db_services/dbbase/instances/serializers.py new file mode 100644 index 0000000000..aa5085c628 --- /dev/null +++ b/dbm-ui/backend/db_services/dbbase/instances/serializers.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" diff --git a/dbm-ui/backend/db_services/dbbase/instances/views.py b/dbm-ui/backend/db_services/dbbase/instances/views.py new file mode 100644 index 0000000000..8d3d38cd62 --- /dev/null +++ b/dbm-ui/backend/db_services/dbbase/instances/views.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 as _ +from rest_framework import status +from rest_framework.decorators import action +from rest_framework.response import Response + +from backend.bk_web import viewsets +from backend.bk_web.swagger import common_swagger_auto_schema +from backend.db_services.dbbase.instances import constants +from backend.db_services.dbbase.instances.handlers import InstanceHandler +from backend.db_services.dbbase.instances.yasg_slz import CheckInstancesResSLZ, CheckInstancesSLZ +from backend.iam_app.handlers.drf_perm import DBManageIAMPermission + + +class InstanceViewSet(viewsets.SystemViewSet): + def _get_custom_permissions(self): + return [DBManageIAMPermission()] + + @common_swagger_auto_schema( + operation_summary=_("根据用户手动输入的 ip:port 查询真实的实例"), + request_body=CheckInstancesSLZ(), + tags=[constants.RESOURCE_TAG], + responses={status.HTTP_200_OK: CheckInstancesResSLZ()}, + ) + @action(methods=["POST"], detail=False, serializer_class=CheckInstancesSLZ) + def check_instances(self, request, bk_biz_id): + validated_data = self.params_validate(self.get_serializer_class()) + return Response( + InstanceHandler(bk_biz_id=bk_biz_id).check_instances( + query_instances=validated_data["instance_addresses"], cluster_ids=validated_data["cluster_ids"] + ) + ) diff --git a/dbm-ui/backend/db_services/dbbase/instances/yasg_slz.py b/dbm-ui/backend/db_services/dbbase/instances/yasg_slz.py new file mode 100644 index 0000000000..cbaa0e7590 --- /dev/null +++ b/dbm-ui/backend/db_services/dbbase/instances/yasg_slz.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 rest_framework import serializers + + +class CheckInstancesSLZ(serializers.Serializer): + instance_addresses = serializers.ListField( + help_text=_("实例地址列表"), child=serializers.CharField(help_text=_("实例地址(ip:port)"), required=True) + ) + cluster_ids = serializers.ListField( + help_text=_("实例所在的集群列表"), child=serializers.IntegerField(), required=False, default=[] + ) + + class Meta: + swagger_schema_fields = {"example": {"instance_addresses": ["127.0.0.1", "127.0.0.1:20000"]}} + + +class CheckInstancesResSLZ(serializers.Serializer): + class Meta: + swagger_schema_fields = { + "example": [ + { + "bk_host_id": 2000059062, + "ip": "127.0.0.1", + "bk_cloud_id": 0, + "port": 10000, + "cluster_id": 1, + "role": "proxy", + "meta": {"scope_type": "biz", "scope_id": "2005000194", "bk_biz_id": 2005000194}, + "host_id": 2000059062, + "ipv6": "", + "cloud_id": 0, + "cloud_vendor": "", + "agent_id": "", + "host_name": "", + "os_name": "1", + "alive": 1, + "cloud_area": {"id": 0, "name": "default area"}, + "biz": {"id": 2005000194, "name": _("测试业务")}, + "bk_mem": None, + "bk_disk": None, + "bk_cpu": None, + } + ] + } diff --git a/dbm-ui/backend/db_services/dbbase/resources/query.py b/dbm-ui/backend/db_services/dbbase/resources/query.py index a8aae7cd73..1339bc7436 100644 --- a/dbm-ui/backend/db_services/dbbase/resources/query.py +++ b/dbm-ui/backend/db_services/dbbase/resources/query.py @@ -43,17 +43,22 @@ def list_instances(cls, bk_biz_id: int, query_params: Dict, limit: int, offset: @classmethod def retrieve_instance(cls, bk_biz_id: int, cluster_id: int, ip: str, port: int) -> dict: - """查询实例详情. 具体方法在子类中实现""" + """查询实例详情. 具体方法可在子类中自定义""" instances = cls.list_instances(bk_biz_id, {"ip": ip, "port": port}, limit=1, offset=0) instance = instances.data[0] + return cls._fill_instance_info(instance, cluster_id) + + @classmethod + def _fill_instance_info(cls, instance, cluster_id): + """填充单个实例的相关信息""" host_detail = Machine.get_host_info_from_cmdb(instance["bk_host_id"]) instance.update(host_detail) # influxdb 目前不支持集群 cluster = Cluster.objects.filter(id=cluster_id).last() - instance["db_version"] = getattr(cluster, "db_version", None) + instance["db_version"] = getattr(cluster, "major_version", None) return instance diff --git a/dbm-ui/backend/db_services/dbresource/constants.py b/dbm-ui/backend/db_services/dbresource/constants.py index 1dfd507072..46ffe2ba54 100644 --- a/dbm-ui/backend/db_services/dbresource/constants.py +++ b/dbm-ui/backend/db_services/dbresource/constants.py @@ -12,11 +12,30 @@ from blue_krill.data_types.enum import EnumField, StructuredEnum from django.utils.translation import ugettext_lazy as _ +from backend.db_meta.enums import ClusterType +from backend.db_services.dbresource.handlers import ( + TenDBClusterSpecFilter, + TendisCacheSpecFilter, + TendisPlusSpecFilter, + TendisSSDSpecFilter, +) + SWAGGER_TAG = _("资源池") RESOURCE_IMPORT_TASK_FIELD = "{user}_resource_import_task_field" RESOURCE_IMPORT_EXPIRE_TIME = 7 * 24 * 60 * 60 +# gse正常的状态码为2 +GSE_AGENT_RUNNING_CODE = 2 + +# 集群对应的规格筛选类 +CLUSTER_TYPE__SPEC_FILTER = { + ClusterType.TenDBCluster: TenDBClusterSpecFilter, + ClusterType.TendisPredixyTendisplusCluster: TendisPlusSpecFilter, + ClusterType.TendisTwemproxyRedisInstance: TendisCacheSpecFilter, + ClusterType.TwemproxyTendisSSDInstance: TendisSSDSpecFilter, +} + class ResourceOperation(str, StructuredEnum): import_hosts = EnumField("imported", _("导入主机")) diff --git a/dbm-ui/backend/db_services/dbresource/exceptions.py b/dbm-ui/backend/db_services/dbresource/exceptions.py index c433e0595c..1728f4c16e 100644 --- a/dbm-ui/backend/db_services/dbresource/exceptions.py +++ b/dbm-ui/backend/db_services/dbresource/exceptions.py @@ -24,13 +24,19 @@ class ResourceApplyException(ResourcePoolBaseException): MESSAGE_TPL = _("资源池申请资源异常") +class ResourceApplyInsufficientException(ResourceApplyException): + ERROR_CODE = "004" + MESSAGE = _("资源池申请不足异常") + MESSAGE_TPL = _("资源池申请不足异常") + + class SpecOperateException(ResourcePoolBaseException): ERROR_CODE = "002" MESSAGE = _("规格操作失败") MESSAGE_TPL = _("规格操作失败") -class DeployPlanOperateException(ResourcePoolBaseException): +class SpecFilterClassDoesNotExistException(ResourcePoolBaseException): ERROR_CODE = "003" - MESSAGE = _("部署方案操作失败") - MESSAGE_TPL = _("部署方案操作失败") + MESSAGE = _("规格筛选类不存在") + MESSAGE_TPL = _("规格筛选类不存在") diff --git a/dbm-ui/backend/db_services/dbresource/filters.py b/dbm-ui/backend/db_services/dbresource/filters.py index 080639e2d5..884c0bcc51 100644 --- a/dbm-ui/backend/db_services/dbresource/filters.py +++ b/dbm-ui/backend/db_services/dbresource/filters.py @@ -12,7 +12,7 @@ from django.utils.translation import ugettext_lazy as _ from django_filters import rest_framework as filters -from backend.db_meta.models.spec import ClusterDeployPlan, Spec +from backend.db_meta.models.spec import Spec class SpecListFilter(filters.FilterSet): @@ -20,16 +20,12 @@ class SpecListFilter(filters.FilterSet): desc = filters.CharFilter(field_name="desc", lookup_expr="icontains", label=_("描述")) spec_cluster_type = filters.CharFilter(field_name="spec_cluster_type", lookup_expr="exact", label=_("规格集群类型")) spec_machine_type = filters.CharFilter(field_name="spec_machine_type", lookup_expr="exact", label=_("规格机器类型")) + update_at = filters.BooleanFilter(field_name="update_at", method="filter_update_at", label=_("根据时间正序/逆序")) - class Meta: - model = Spec - fields = ["spec_name", "spec_cluster_type", "spec_machine_type", "desc"] - - -class ClusterDeployPlanFilter(filters.FilterSet): - name = filters.CharFilter(field_name="name", lookup_expr="icontains", label=_("Redis部署方案名称")) - cluster_type = filters.CharFilter(field_name="cluster_type", lookup_expr="exact", label=_("Redis集群类型")) + def filter_update_at(self, queryset, name, value): + time_field = "update_at" if value else "-update_at" + return queryset.order_by(time_field) class Meta: - model = ClusterDeployPlan - fields = ["name", "cluster_type"] + model = Spec + fields = ["spec_name", "spec_cluster_type", "spec_machine_type", "desc", "update_at"] diff --git a/dbm-ui/backend/db_services/dbresource/handlers.py b/dbm-ui/backend/db_services/dbresource/handlers.py new file mode 100644 index 0000000000..32ebfd87e3 --- /dev/null +++ b/dbm-ui/backend/db_services/dbresource/handlers.py @@ -0,0 +1,215 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import math +from typing import Any, Dict, List + +from django.forms import model_to_dict + +from backend.components.dbresource.client import DBResourceApi +from backend.db_meta.models import Spec + + +class ClusterSpecFilter(object): + """集群规格的过滤器""" + + def __init__(self, capacity, future_capacity, qps, spec_cluster_type, spec_machine_type, shard_num=0): + # 用户的当前容量,期望容量,期望qps范围和分片数(可选) + self.capacity: int = capacity + self.future_capacity: int = future_capacity + self.qps: Dict = qps + self.filter_shard_num = shard_num + # 当前集群的筛选规格 + self.specs: List[Dict[str, Any]] = [ + {**model_to_dict(spec), "capacity": spec.capacity} + for spec in Spec.objects.filter(spec_machine_type=spec_machine_type, spec_cluster_type=spec_cluster_type) + ] + + def calc_machine_pair(self): + """计算每种规格所需的机器组数和集群总容量: 目标容量 / 规格容量""" + for spec in self.specs: + spec["machine_pair"] = math.ceil(self.capacity / spec["capacity"]) + # 集群容量:机器组数 * 规格容量;集群qps:机器组数 * 规格qps的min + spec["cluster_capacity"] = spec["machine_pair"] * spec["capacity"] + spec["cluster_qps"] = spec["machine_pair"] * spec["qps"]["min"] + + def calc_cluster_shard_num(self): + """计算每种规格的分片数, 根据不同的集群计算方式也不同""" + raise NotImplementedError() + + def _qps_check(self, user_qps_range, spec_qps_range): + """默认判断规则:当前qps与用户需要存在交集""" + if user_qps_range["min"] > spec_qps_range["max"] or user_qps_range["max"] < spec_qps_range["min"]: + return False + + return True + + def system_filter(self): + """系统自带的过滤:qps和分片数""" + valid_specs: List[Dict[str, Any]] = [] + for spec in self.specs: + qps_range = { + "min": spec["machine_pair"] * spec["qps"]["min"], + "max": spec["machine_pair"] * spec["qps"]["max"], + } + if not self._qps_check(self.qps, qps_range): + continue + + if self.filter_shard_num and spec["cluster_shard_num"] != self.filter_shard_num: + continue + + valid_specs.append(spec) + + self.specs = valid_specs + + def custom_filter(self): + """自定义过滤规则""" + pass + + def get_target_specs(self): + self.calc_machine_pair() + self.calc_cluster_shard_num() + self.system_filter() + self.custom_filter() + return self.specs + + +class TenDBClusterSpecFilter(ClusterSpecFilter): + """TendbCluster集群规格的过滤器""" + + def calc_cluster_shard_num(self): + for spec in self.specs: + # 一定要保证集群总分片数是机器组数的整数倍,因此单机分片数要上取整 + cluster_shard_num = math.ceil(self.future_capacity / spec["capacity"]) + single_machine_shard_num = math.ceil(cluster_shard_num / spec["machine_pair"]) + spec["cluster_shard_num"] = single_machine_shard_num * spec["machine_pair"] + + +class RedisSpecFilter(ClusterSpecFilter): + """Redis规格过滤器基类""" + + # 建议规格数,超出将淘汰末尾规格 + RECOMMEND_SPEC_NUM = 4 + # 淘汰规格比例 + DISUSE_SPEC_RATIO = 0.5 + # 按照机器组数正向/逆向排序 + MACHINE_PAIR_SORT = False + + def _qps_check(self, user_qps_range, spec_qps_range): + # redis可以接受规格qps过大,不接受规格qps小于用户的最小值 + if user_qps_range["min"] > spec_qps_range["max"]: + return False + + return True + + def custom_filter(self): + """对规格方案进行排序,如果存在大于4个方案,则按比例淘汰末尾规格方案""" + self.specs.sort(key=lambda x: x["machine_pair"], reverse=self.MACHINE_PAIR_SORT) + if len(self.specs) > self.RECOMMEND_SPEC_NUM: + self.specs = self.specs[: -int(len(self.specs) * self.DISUSE_SPEC_RATIO)] + + def filter_too_large_building_capacity(self): + """过滤掉过大的建设容量,当建设容量大于目标容量时,默认只保留最小的一个""" + exceed_target_capacity_specs: List[Dict[str, Any]] = [] + in_target_capacity_specs: List[Dict[str, Any]] = [] + for spec in self.specs: + # 首先筛选出建设容量超出目标容量的规格 + if spec["machine_pair"] * spec["capacity"] > self.capacity: + exceed_target_capacity_specs.append(spec) + else: + in_target_capacity_specs.append(spec) + + # 如果存在多个建设容量>目标容量的规格,则取最接近目标容量的规格 + if exceed_target_capacity_specs: + in_target_capacity_specs.append( + sorted(exceed_target_capacity_specs, key=lambda x: x["machine_pair"] * x["capacity"])[0] + ) + + self.specs = in_target_capacity_specs + + +class TendisPlusSpecFilter(RedisSpecFilter): + """TendisPlus集群规格过滤器""" + + # 最佳容量管理大小 300G + OPTIMAL_MANAGE_CAPACITY = 300 + + def _qps_check(self, user_qps_range, spec_qps_range): + # TendisPlus集群不需要qps过滤 + return True + + def calc_machine_pair(self): + """计算每种规格所需的机器组数,TendisPlus至少需要三组""" + for spec in self.specs: + spec["machine_pair"] = max(math.ceil(self.capacity / spec["capacity"]), 3) + spec["cluster_capacity"] = spec["machine_pair"] * spec["capacity"] + + def calc_cluster_shard_num(self): + for spec in self.specs: + spec["cluster_shard_num"] = max(3, math.ceil(self.capacity / self.OPTIMAL_MANAGE_CAPACITY)) + # 将分片数上取整为机器组数的倍数 + spec["cluster_shard_num"] = ( + math.ceil(spec["cluster_shard_num"] / spec["machine_pair"]) * spec["machine_pair"] + ) + + def custom_filter(self): + super().custom_filter() + + +class TendisSSDSpecFilter(RedisSpecFilter): + """TendisSSD集群规格过滤器""" + + # 单实例最大容量 50G + SINGLE_MAX_CAPACITY = 50 + MACHINE_PAIR_SORT = True + + def calc_cluster_shard_num(self): + for spec in self.specs: + # 计算单机分片数,容量/50-取整为最接近的偶数 + single_machine_shard_num = int(spec["capacity"] / self.SINGLE_MAX_CAPACITY) + single_machine_shard_num = max(single_machine_shard_num + (single_machine_shard_num & 1), 2) + spec["cluster_shard_num"] = single_machine_shard_num * spec["machine_pair"] + + def custom_filter(self): + super().custom_filter() + + +class TendisCacheSpecFilter(RedisSpecFilter): + """TendisCache集群规格过滤器""" + + def calc_cluster_shard_num(self): + for spec in self.specs: + # 计算单机分片数,CPU去中间数的偶数 + single_machine_shard_num = int((spec["cpu"]["min"] + spec["cpu"]["max"]) / 2) + single_machine_shard_num = max(single_machine_shard_num + (single_machine_shard_num & 1), 2) + spec["cluster_shard_num"] = single_machine_shard_num * spec["machine_pair"] + + def custom_filter(self): + super().filter_too_large_building_capacity() + super().custom_filter() + + +class ResourceHandler(object): + """资源池接口的处理函数""" + + @classmethod + def spec_resource_count(cls, bk_biz_id: int, resource_type: str, bk_cloud_id: int, spec_ids: List[int]): + # 构造申请参数 + spec_count_details = [ + spec.get_apply_params_detail(group_mark=str(spec.spec_id), count=0, bk_cloud_id=bk_cloud_id) + for spec in Spec.objects.filter(spec_id__in=spec_ids) + ] + spec_count_params = { + "bk_biz_id": bk_biz_id, + "resource_type": resource_type, + "bk_cloud_id": bk_cloud_id, + "details": spec_count_details, + } + return DBResourceApi.apply_count(params=spec_count_params) diff --git a/dbm-ui/backend/db_services/dbresource/mock.py b/dbm-ui/backend/db_services/dbresource/mock.py index ecf9dd05de..112cd6e656 100644 --- a/dbm-ui/backend/db_services/dbresource/mock.py +++ b/dbm-ui/backend/db_services/dbresource/mock.py @@ -59,3 +59,31 @@ "for_bizs": [{"bk_biz_id": 2005000100, "bk_biz_name": "xxxx"}], }, ] + +RECOMMEND_SPEC_DATA = [ + { + "spec_id": 1, + "creator": "admin", + "create_at": "2023-06-26 16:30:22", + "updater": "admin", + "update_at": "2023-06-26 16:30:22", + "spec_name": "mysql", + "spec_cluster_type": "tendbsingle", + "spec_machine_type": "backend", + "cpu": {"max": 10000, "min": 1}, + "mem": {"max": 10000, "min": 1}, + "device_class": [], + "storage_spec": [{"type": "", "max_size": 100000000, "min_size": 1, "mount_point": ""}], + "desc": "mysql", + "instance_num": 1, + } +] + +RESOURCE_UPDATE_PARAMS = { + "bk_host_ids": [192], + "for_bizs": [3], + "resource_types": ["tendbcluster"], + "set_empty_biz": False, + "set_empty_resource_type": False, + "storage_device": {"/data3": {"size": 200, "disk_type": "HDD"}}, +} diff --git a/dbm-ui/backend/db_services/dbresource/serializers.py b/dbm-ui/backend/db_services/dbresource/serializers.py index 388574cbaa..349184266a 100644 --- a/dbm-ui/backend/db_services/dbresource/serializers.py +++ b/dbm-ui/backend/db_services/dbresource/serializers.py @@ -9,15 +9,22 @@ specific language governing permissions and limitations under the License. """ +from django.db.models import Q from django.utils.translation import ugettext_lazy as _ from rest_framework import serializers from backend import env from backend.configuration.constants import DBType -from backend.db_meta.enums import InstanceRole -from backend.db_meta.models import ClusterDeployPlan, Spec +from backend.constants import INT_MAX +from backend.db_meta.enums import ClusterType, InstanceRole, MachineType +from backend.db_meta.models import Spec from backend.db_services.dbresource.constants import ResourceOperation -from backend.db_services.dbresource.mock import RESOURCE_LIST_DATA, SPEC_DATA +from backend.db_services.dbresource.mock import ( + RECOMMEND_SPEC_DATA, + RESOURCE_LIST_DATA, + RESOURCE_UPDATE_PARAMS, + SPEC_DATA, +) from backend.db_services.ipchooser.serializers.base import QueryHostsBaseSer from backend.ticket.constants import TicketStatus @@ -68,6 +75,7 @@ class ResourceLimitSerializer(serializers.Serializer): resource_types = serializers.CharField(help_text=_("专属DB"), required=False) device_class = serializers.CharField(help_text=_("机型"), required=False) hosts = serializers.CharField(help_text=_("主机IP列表"), required=False) + bk_cloud_ids = serializers.CharField(help_text=_("云区域ID列表"), required=False) city = serializers.CharField(help_text=_("城市"), required=False) subzones = serializers.CharField(help_text=_("园区"), required=False) @@ -76,6 +84,7 @@ class ResourceLimitSerializer(serializers.Serializer): disk = serializers.CharField(help_text=_("磁盘资源限制"), required=False) disk_type = serializers.CharField(help_text=_("磁盘类型"), required=False, allow_null=True, allow_blank=True) mount_point = serializers.CharField(help_text=_("磁盘挂载点"), required=False, allow_null=True, allow_blank=True) + spec_id = serializers.IntegerField(help_text=_("过滤的规格ID"), required=False) agent_status = serializers.BooleanField(help_text=_("agent状态"), required=False) labels = serializers.DictField(help_text=_("标签信息"), required=False) @@ -91,17 +100,51 @@ def format_fields(attrs, fields): if attrs.get(field): attrs[field] = attrs[field].split(divider) - # for_bizs 要转换为int - if field == "for_bizs": + # for_bizs,bk_cloud_ids 要转换为int + if field in ["for_bizs", "bk_cloud_ids"]: attrs[field] = list(map(int, attrs[field])) # cpu, mem, disk 需要转换为结构体 - elif field in ["cpu", "mem", "disk"]: - attrs[field] = {"min": int(attrs[field][0] or 0), "max": int(attrs[field][1] or (2 ** 31 - 1))} + elif field in ["mem"]: + attrs[field] = {"min": float(attrs[field][0] or 0), "max": float(attrs[field][1] or INT_MAX)} + elif field in ["cpu", "disk"]: + attrs[field] = {"min": int(attrs[field][0] or 0), "max": int(attrs[field][1] or INT_MAX)} + + # 转换规格查询参数 + if attrs.get("spec_id"): + spec = Spec.objects.get(spec_id=attrs["spec_id"]) + attrs["storage_spec"] = [ + { + "mount_point": storage_spec["mount_point"], + "disk_type": "" if storage_spec["type"] == "ALL" else storage_spec["type"], + "min": storage_spec["size"], + "max": INT_MAX, + } + for storage_spec in spec.storage_spec + ] + attrs["cpu"], attrs["mem"], attrs["device_class"] = spec.cpu, spec.mem, spec.device_class + + # 转换内存查询单位, GB --> MB + if attrs.get("mem"): + attrs["mem"] = {"min": int(attrs["mem"]["min"] * 1024), "max": int(attrs["mem"]["max"] * 1024)} + + # 格式化agent参数 + attrs["gse_agent_alive"] = str(attrs.get("agent_status", "")).lower() def validate(self, attrs): self.format_fields( attrs, - fields=["for_bizs", "resource_types", "device_class", "hosts", "city", "subzones", "cpu", "mem", "disk"], + fields=[ + "for_bizs", + "resource_types", + "device_class", + "hosts", + "city", + "subzones", + "cpu", + "mem", + "disk", + "bk_cloud_ids", + ], ) return attrs @@ -115,6 +158,10 @@ class ListDBAHostsSerializer(QueryHostsBaseSer): pass +class QueryDBAHostsSerializer(serializers.Serializer): + bk_host_ids = serializers.CharField(help_text=_("主机ID列表(逗号分隔)")) + + class ResourceConfirmSerializer(serializers.Serializer): request_id = serializers.CharField(help_text=_("资源申请的request_id")) host_ids = serializers.ListField(help_text=_("主机ID列表"), child=serializers.IntegerField()) @@ -125,42 +172,56 @@ class ResourceDeleteSerializer(serializers.Serializer): class ResourceUpdateSerializer(serializers.Serializer): - class UpdateDetailSerializer(serializers.Serializer): - bk_host_id = serializers.IntegerField(help_text=_("主机ID")) - labels = serializers.DictField(help_text=_("Labels"), required=False) - for_bizs = serializers.ListField(help_text=_("专用业务ID"), child=serializers.IntegerField(), required=False) - resource_types = serializers.ListField( - help_text=_("专属DB"), - child=serializers.ChoiceField(choices=DBType.get_choices()), - required=False, - ) - storage_device = serializers.JSONField(help_text=_("磁盘挂载点信息"), required=False) + bk_host_ids = serializers.ListField(help_text=_("主机ID列表"), child=serializers.IntegerField()) + labels = serializers.DictField(help_text=_("Labels"), required=False) + for_bizs = serializers.ListField(help_text=_("专用业务ID"), child=serializers.IntegerField(), required=False) + resource_types = serializers.ListField( + help_text=_("专属DB"), + child=serializers.ChoiceField(choices=DBType.get_choices()), + required=False, + ) + set_empty_biz = serializers.BooleanField(help_text=_("是否无专用业务"), required=False, default=False) + set_empty_resource_type = serializers.BooleanField(help_text=_("是否无专用资源类型"), required=False, default=False) + storage_device = serializers.JSONField(help_text=_("磁盘挂载点信息"), required=False) - data = serializers.ListSerializer(child=UpdateDetailSerializer()) + class Meta: + swagger_schema_fields = {"example": RESOURCE_UPDATE_PARAMS} class QueryOperationListSerializer(serializers.Serializer): operation_type = serializers.ChoiceField( help_text=_("操作类型"), choices=ResourceOperation.get_choices(), required=False ) + ticket_ids = serializers.CharField(help_text=_("过滤的单据ID列表"), required=False) + ticket_types = serializers.CharField(help_text=_("过滤的单据类型列表"), required=False) task_ids = serializers.CharField(help_text=_("过滤的任务ID列表"), required=False) + ip_list = serializers.CharField(help_text=_("过滤IP列表"), required=False) + update_time = serializers.BooleanField(help_text=_("时间排序模式"), required=False, default=False) + operator = serializers.CharField(help_text=_("操作者"), required=False) begin_time = serializers.CharField(help_text=_("操作开始时间"), required=False) end_time = serializers.CharField(help_text=_("操作结束时间"), required=False) status = serializers.ChoiceField(help_text=_("单据状态"), choices=TicketStatus.get_choices(), required=False) - page_size = serializers.IntegerField(help_text=_("分页大小"), required=False, default=10) - start = serializers.IntegerField(help_text=_("分页起始位置"), required=False, default=0) + limit = serializers.IntegerField(help_text=_("分页大小"), required=False, default=10) + offset = serializers.IntegerField(help_text=_("分页起始位置"), required=False, default=0) def validate(self, attrs): if attrs.get("ticket_ids"): attrs["bill_ids"] = attrs.pop("ticket_ids").split(",") + if attrs.get("ticket_types"): + attrs["bill_types"] = attrs.pop("ticket_types").split(",") + if attrs.get("task_ids"): attrs["task_ids"] = attrs["task_ids"].split(",") - attrs["offset"], attrs["limit"] = attrs.pop("start"), attrs.pop("page_size") + if attrs.get("ip_list"): + attrs["ip_list"] = attrs["ip_list"].split(",") + + attrs["orderby"] = "asc" if attrs["update_time"] else "desc" + return attrs @@ -171,6 +232,66 @@ class Meta: read_only_fields = ("spec_id",) + model.AUDITED_FIELDS swagger_schema_fields = {"example": SPEC_DATA} + def validate_valid_cpu_mem(self, attrs): + # 校验cpu, mem是正确的范围 + if attrs["cpu"]["min"] > attrs["cpu"]["max"] or attrs["mem"]["min"] > attrs["mem"]["max"]: + raise serializers.ValidationError(_("请保证CPU/MEM的最小最大范围合理")) + + def validate_only_spec(self, attrs): + # 校验规格的集群-角色-名字必须唯一 + unique_filter = ( + Q(spec_cluster_type=attrs["spec_cluster_type"]) + & Q(spec_machine_type=attrs["spec_machine_type"]) + & Q(spec_name=attrs["spec_name"]) + ) + specs = Spec.objects.filter(unique_filter) + # 排除掉更新的情况 + if specs.count() and getattr(self.instance, "spec_id", 0) != specs.first().spec_id: + raise serializers.ValidationError(_("已存在同名规格,请保证集群类型-规格类型-规格名称必须唯一")) + + unique_filter = ( + Q(spec_cluster_type=attrs["spec_cluster_type"]) + & Q(spec_machine_type=attrs["spec_machine_type"]) + & Q(cpu=attrs["cpu"]) + & Q(mem=attrs["mem"]) + & Q(device_class=attrs["device_class"]) + & Q(storage_spec=attrs["storage_spec"]) + & Q(instance_num=attrs.get("instance_num", 1)) + ) + specs = Spec.objects.filter(unique_filter) + if specs.count() and getattr(self.instance, "spec_id", 0) != specs.first().spec_id: + raise serializers.ValidationError(_("已存在同种规格配置,请不要在相同规格类型下重复录入")) + + def validate_data_points(self, attrs): + """校验磁盘的录入""" + standard_mount_points = ["/data", "/data1"] + mount_points = [storage["mount_point"] for storage in attrs["storage_spec"]] + # TenDBCluster 磁盘录入只能是/data or /data和/data1 + if attrs["spec_cluster_type"] == ClusterType.TenDBCluster and attrs["spec_machine_type"] == MachineType.REMOTE: + if not ("/data" in mount_points and set(mount_points).issubset(standard_mount_points)): + raise serializers.ValidationError( + _("【{}】后端磁盘挂载点必须包含/data,可选/data1").format(attrs["spec_cluster_type"]) + ) + # TendisPlus/TendisSSD 磁盘必须包含/data和/data1 + if ( + attrs["spec_cluster_type"] + in [ + ClusterType.TwemproxyTendisSSDInstance, + ClusterType.TendisPredixyTendisplusCluster, + ] + and attrs["spec_machine_type"] in [MachineType.TENDISPLUS, MachineType.TENDISSSD] + ): + if set(mount_points) != set(standard_mount_points): + raise serializers.ValidationError( + _("【{}】后端磁盘挂载点必须且只能包含/data,/data1").format(attrs["spec_cluster_type"]) + ) + + def validate(self, attrs): + self.validate_valid_cpu_mem(attrs) + self.validate_only_spec(attrs) + self.validate_data_points(attrs) + return attrs + class DeleteSpecSerializer(serializers.Serializer): spec_ids = serializers.ListField(help_text=_("规格id列表"), child=serializers.IntegerField()) @@ -179,25 +300,67 @@ class Meta: swagger_schema_fields = {"example": {"spec_ids": [1, 2, 3]}} -class DeleteDeployPlanSerializer(serializers.Serializer): - deploy_plan_ids = serializers.ListField(help_text=_("部署方案id列表"), child=serializers.IntegerField()) - - class Meta: - swagger_schema_fields = {"example": {"deploy_plan_ids": [1, 2, 3]}} +class VerifyDuplicatedSpecNameSerializer(serializers.Serializer): + spec_cluster_type = serializers.ChoiceField(help_text=_("集群类型"), choices=ClusterType.get_choices()) + spec_machine_type = serializers.ChoiceField(help_text=_("机器类型"), choices=MachineType.get_choices()) + spec_name = serializers.CharField(help_text=_("规格名称")) + spec_id = serializers.IntegerField(help_text=_("规格ID(更新时传递)"), required=False) class ListSubzonesSerializer(serializers.Serializer): citys = serializers.ListField(help_text=_("逻辑城市"), child=serializers.CharField()) -class ClusterDeployPlanSerializer(serializers.ModelSerializer): +class RecommendSpecSerializer(serializers.Serializer): + cluster_id = serializers.IntegerField(help_text=_("集群ID"), required=False) + instance_id = serializers.IntegerField(help_text=_("实例ID"), required=False) + role = serializers.ChoiceField(help_text=_("实例类型"), choices=InstanceRole.get_choices()) + + +class RecommendResponseSpecSerializer(serializers.Serializer): class Meta: - model = ClusterDeployPlan - fields = "__all__" - read_only_fields = ("id",) + model.AUDITED_FIELDS - swagger_schema_fields = {"example": {}} + swagger_schema_fields = {"example": RECOMMEND_SPEC_DATA} -class RecommendSpecSerializer(serializers.Serializer): - cluster_id = serializers.IntegerField(help_text=_("集群ID")) - role = serializers.ChoiceField(help_text=_("实例类型"), choices=InstanceRole.get_choices(), required=False) +class QueryQPSRangeSerializer(serializers.Serializer): + spec_cluster_type = serializers.ChoiceField(help_text=_("集群类型"), choices=ClusterType.get_choices()) + spec_machine_type = serializers.ChoiceField(help_text=_("角色类型"), choices=MachineType.get_choices()) + capacity = serializers.IntegerField(help_text=_("当前容量需求")) + future_capacity = serializers.IntegerField(help_text=_("未来容量需求")) + shard_num = serializers.IntegerField(help_text=_("所需分片数"), required=False, default=0) + + +class QueryQPSRangeResponseSerializer(serializers.Serializer): + class Meta: + swagger_schema_fields = {"example": {"min": 100, "max": 10000}} + + +class FilterClusterSpecSerializer(QueryQPSRangeSerializer): + qps = serializers.JSONField(help_text=_("qps范围")) + + +class FilterClusterSpecResponseSerializer(QueryQPSRangeSerializer): + class Meta: + swagger_schema_fields = {"example": ""} + + +class GetMountPointResponseSerializer(serializers.Serializer): + class Meta: + swagger_schema_fields = {"example": ["/data", "/data1"]} + + +class GetDiskTypeResponseSerializer(serializers.Serializer): + class Meta: + swagger_schema_fields = {"example": ["HDD", "SSD"]} + + +class SpecCountResourceSerializer(serializers.Serializer): + bk_biz_id = serializers.IntegerField(help_text=_("业务ID")) + resource_type = serializers.CharField(help_text=_("所属DB资源类型")) + bk_cloud_id = serializers.IntegerField(help_text=_("云区域ID")) + spec_ids = serializers.ListField(help_text=_("规格ID列表"), child=serializers.IntegerField()) + + +class SpecCountResourceResponseSerializer(serializers.Serializer): + class Meta: + swagger_schema_fields = {"example": {"spec1": 10, "spec2": 10}} diff --git a/dbm-ui/backend/db_services/dbresource/urls.py b/dbm-ui/backend/db_services/dbresource/urls.py index f7f22c2c72..a779c7feb5 100644 --- a/dbm-ui/backend/db_services/dbresource/urls.py +++ b/dbm-ui/backend/db_services/dbresource/urls.py @@ -10,7 +10,6 @@ """ from rest_framework.routers import DefaultRouter -from backend.db_services.dbresource.views.deploy_plan import ClusterDeployPlanViewSet from backend.db_services.dbresource.views.resource import DBResourceViewSet from backend.db_services.dbresource.views.sepc import DBSpecViewSet @@ -18,6 +17,5 @@ routers.register("resource", DBResourceViewSet, basename="resource") routers.register("spec", DBSpecViewSet, basename="spec") -routers.register("deploy_plan", ClusterDeployPlanViewSet, basename="deploy_plan") urlpatterns = routers.urls diff --git a/dbm-ui/backend/db_services/dbresource/views/deploy_plan.py b/dbm-ui/backend/db_services/dbresource/views/deploy_plan.py deleted file mode 100644 index 65f2eab1ee..0000000000 --- a/dbm-ui/backend/db_services/dbresource/views/deploy_plan.py +++ /dev/null @@ -1,89 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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 rest_framework.decorators import action -from rest_framework.response import Response - -from backend.bk_web import viewsets -from backend.bk_web.pagination import AuditedLimitOffsetPagination -from backend.bk_web.swagger import common_swagger_auto_schema -from backend.db_meta.models import Cluster -from backend.db_meta.models.spec import ClusterDeployPlan -from backend.db_services.dbresource.constants import SWAGGER_TAG -from backend.db_services.dbresource.exceptions import DeployPlanOperateException -from backend.db_services.dbresource.filters import ClusterDeployPlanFilter -from backend.db_services.dbresource.serializers import ClusterDeployPlanSerializer, DeleteDeployPlanSerializer -from backend.iam_app.handlers.drf_perm import GlobalManageIAMPermission - - -class DeployPlanViewSet(viewsets.AuditedModelViewSet): - - pagination_class = AuditedLimitOffsetPagination - view_name = "" - - def _get_custom_permissions(self): - return [GlobalManageIAMPermission()] - - @common_swagger_auto_schema( - operation_summary=_("新建{}部署方案").format(view_name), - tags=[SWAGGER_TAG], - ) - def create(self, request, *args, **kwargs): - return super().create(request, *args, **kwargs) - - @common_swagger_auto_schema( - operation_summary=_("更新{}部署方案").format(view_name), - tags=[SWAGGER_TAG], - ) - def update(self, request, *args, **kwargs): - deploy_plan_id = int(kwargs["pk"]) - if Cluster.is_refer_deploy_plan([deploy_plan_id]): - raise DeployPlanOperateException(_("部署方案: {} 正在被引用,无法修改相关参数").format(deploy_plan_id)) - return super().update(request, *args, **kwargs) - - @common_swagger_auto_schema( - operation_summary=_("查询{}部署方案列表").format(view_name), - tags=[SWAGGER_TAG], - ) - def list(self, request, *args, **kwargs): - return super().list(request, *args, **kwargs) - - @common_swagger_auto_schema( - operation_summary=_("删除{}部署方案").format(view_name), - tags=[SWAGGER_TAG], - ) - def destroy(self, request, *args, **kwargs): - deploy_plan_id = int(kwargs["pk"]) - if Cluster.is_refer_deploy_plan([deploy_plan_id]): - raise DeployPlanOperateException(_("部署方案: {} 正在被引用,无法删除").format(deploy_plan_id)) - super().destroy(request, *args, **kwargs) - return Response() - - @common_swagger_auto_schema( - operation_summary=_("批量删除{}部署方案").format(view_name), - request_body=DeleteDeployPlanSerializer(), - tags=[SWAGGER_TAG], - ) - @action(methods=["DELETE"], detail=False, serializer_class=DeleteDeployPlanSerializer) - def batch_delete(self, request, *args, **kwargs): - plan_ids = self.params_validate(self.get_serializer_class())["deploy_plan_ids"] - if Cluster.is_refer_deploy_plan(plan_ids): - raise DeployPlanOperateException(_("部署方案: {} 存在被引用,无法删除").format(plan_ids)) - return Response(self.deploy_plan_model.objects.filter(id__in=plan_ids).delete()) - - -class ClusterDeployPlanViewSet(DeployPlanViewSet): - - queryset = ClusterDeployPlan.objects.all() - serializer_class = ClusterDeployPlanSerializer - deploy_plan_model = ClusterDeployPlan - filter_class = ClusterDeployPlanFilter diff --git a/dbm-ui/backend/db_services/dbresource/views/resource.py b/dbm-ui/backend/db_services/dbresource/views/resource.py index 66596ac278..c5a375157a 100644 --- a/dbm-ui/backend/db_services/dbresource/views/resource.py +++ b/dbm-ui/backend/db_services/dbresource/views/resource.py @@ -15,7 +15,6 @@ import uuid from typing import Dict, List -from dateutil import parser from django.utils.translation import ugettext_lazy as _ from rest_framework import status from rest_framework.decorators import action @@ -24,16 +23,24 @@ from backend import env from backend.bk_web import viewsets from backend.bk_web.swagger import common_swagger_auto_schema +from backend.components import CCApi from backend.components.dbresource.client import DBResourceApi -from backend.db_meta.models import AppCache +from backend.configuration.constants import SystemSettingsEnum +from backend.configuration.models import SystemSettings +from backend.db_meta.models import AppCache, Spec from backend.db_services.dbresource.constants import ( + GSE_AGENT_RUNNING_CODE, RESOURCE_IMPORT_EXPIRE_TIME, RESOURCE_IMPORT_TASK_FIELD, SWAGGER_TAG, ) +from backend.db_services.dbresource.handlers import ResourceHandler from backend.db_services.dbresource.serializers import ( + GetDiskTypeResponseSerializer, + GetMountPointResponseSerializer, ListDBAHostsSerializer, ListSubzonesSerializer, + QueryDBAHostsSerializer, QueryOperationListSerializer, ResourceApplySerializer, ResourceConfirmSerializer, @@ -43,18 +50,22 @@ ResourceListResponseSerializer, ResourceListSerializer, ResourceUpdateSerializer, + SpecCountResourceResponseSerializer, + SpecCountResourceSerializer, ) from backend.db_services.ipchooser.constants import ModeType +from backend.db_services.ipchooser.handlers.host_handler import HostHandler from backend.db_services.ipchooser.handlers.topo_handler import TopoHandler from backend.db_services.ipchooser.query.resource import ResourceQueryHelper -from backend.flow.consts import SUCCEED_STATES +from backend.db_services.ipchooser.types import ScopeList +from backend.flow.consts import FAILED_STATES, SUCCEED_STATES from backend.flow.engine.controller.base import BaseController from backend.flow.models import FlowTree from backend.iam_app.handlers.drf_perm import GlobalManageIAMPermission -from backend.ticket.constants import TicketType +from backend.ticket.constants import BAMBOO_STATE__TICKET_STATE_MAP, TicketStatus, TicketType from backend.ticket.models import Ticket from backend.utils.redis import RedisConn -from backend.utils.time import datetime2str, remove_timezone +from backend.utils.time import remove_timezone class DBResourceViewSet(viewsets.SystemViewSet): @@ -69,35 +80,39 @@ def _get_custom_permissions(self): ) @action(detail=False, methods=["POST"], url_path="list", serializer_class=ResourceListSerializer) def resource_list(self, request): - resource_data = DBResourceApi.resource_list(params=self.params_validate(self.get_serializer_class())) - if not resource_data["details"]: - return Response({"count": 0, "results": []}) - - # 格式化资源列表信息,填充必要参数 - cloud_info = ResourceQueryHelper.search_cc_cloud(get_cache=True) - for_biz_ids = list(set(itertools.chain(*[data["for_bizs"] for data in resource_data["details"]]))) - for_biz_ids = list(map(int, for_biz_ids)) - for_biz_infos = AppCache.batch_get_app_attr(bk_biz_ids=for_biz_ids, attr_name="bk_biz_name") - for data in resource_data.get("details") or []: + def _format_resource_fields(data, _cloud_info, _for_biz_infos): data.update( { - "bk_cloud_name": cloud_info[str(data["bk_cloud_id"])]["bk_cloud_name"], + "bk_cloud_name": _cloud_info[str(data["bk_cloud_id"])]["bk_cloud_name"], "bk_host_innerip": data["ip"], - "bk_mem": data.pop("dram_cap") // 1024, + # 内存 MB --> GB + "bk_mem": data.pop("dram_cap"), "bk_cpu": data.pop("cpu_num"), "bk_disk": data.pop("total_storage_cap"), "resource_types": data.pop("resource_types"), "for_bizs": [ - {"bk_biz_id": int(bk_biz_id), "bk_biz_name": for_biz_infos[int(bk_biz_id)]} + {"bk_biz_id": int(bk_biz_id), "bk_biz_name": _for_biz_infos[int(bk_biz_id)]} for bk_biz_id in data.pop("for_bizs") ], + "agent_status": int((data.pop("gse_agent_status_code") == GSE_AGENT_RUNNING_CODE)), } ) + return data - # 填充agent信息 - resource_data["results"] = resource_data.pop("details") - ResourceQueryHelper.fill_agent_status(resource_data["results"], fill_key="agent_status") + resource_data = DBResourceApi.resource_list(params=self.params_validate(self.get_serializer_class())) + if not resource_data["details"]: + return Response({"count": 0, "results": []}) + + # 获取云区域信息和业务信息 + cloud_info = ResourceQueryHelper.search_cc_cloud(get_cache=True) + for_biz_ids = list(set(itertools.chain(*[data["for_bizs"] for data in resource_data["details"]]))) + for_biz_ids = list(map(int, for_biz_ids)) + for_biz_infos = AppCache.batch_get_app_attr(bk_biz_ids=for_biz_ids, attr_name="bk_biz_name") + # 格式化资源池字段信息 + for data in resource_data.get("details") or []: + _format_resource_fields(data, cloud_info, for_biz_infos) + resource_data["results"] = resource_data.pop("details") return Response(resource_data) @common_swagger_auto_schema( @@ -110,13 +125,35 @@ def list_dba_hosts(self, request): params = self.params_validate(self.get_serializer_class()) # 查询DBA空闲机模块的meta,构造查询空闲机参数的node_list - scope_list = [{"scope_id": env.DBA_APP_BK_BIZ_ID, "scope_type": "biz", "bk_biz_id": env.DBA_APP_BK_BIZ_ID}] - trees = TopoHandler.trees(all_scope=True, mode=ModeType.IDLE_ONLY.value, scope_list=scope_list) - node_list = [{"instance_id": trees[0]["instance_id"], "meta": trees[0]["meta"], "object_id": "module"}] + scope_list: ScopeList = [ + {"scope_id": env.DBA_APP_BK_BIZ_ID, "scope_type": "biz", "bk_biz_id": env.DBA_APP_BK_BIZ_ID} + ] + trees: List[Dict] = TopoHandler.trees(all_scope=True, mode=ModeType.IDLE_ONLY.value, scope_list=scope_list) + node_list: ScopeList = [ + {"instance_id": trees[0]["instance_id"], "meta": trees[0]["meta"], "object_id": "module"} + ] params.update(readable_node_list=node_list) - # 查询DBA业务下的空闲机 - return Response(TopoHandler.query_hosts(**params)) + # 查询DBA业务下的空闲机,并排除掉已经在资源池的空闲机 + resource_hosts = DBResourceApi.resource_list_all()["details"] or [] + resource_host_ids = [host["bk_host_id"] for host in resource_hosts] + host_infos = TopoHandler.query_hosts(**params) + for host in host_infos["data"]: + host.update(occupancy=(host["host_id"] in resource_host_ids)) + + return Response(host_infos) + + @common_swagger_auto_schema( + operation_summary=_("查询DBA业务下的主机信息"), + query_serializer=QueryDBAHostsSerializer, + tags=[SWAGGER_TAG], + ) + @action(detail=False, methods=["GET"], url_path="query_dba_hosts", serializer_class=QueryDBAHostsSerializer) + def query_dba_hosts(self, request): + host_ids = self.params_validate(self.get_serializer_class())["bk_host_ids"].split(",") + host_list = [{"host_id": int(host_id)} for host_id in host_ids] + scope_list: ScopeList = [{"bk_biz_id": env.DBA_APP_BK_BIZ_ID}] + return Response(HostHandler.details(scope_list=scope_list, host_list=host_list)) @common_swagger_auto_schema( operation_summary=_("资源导入"), @@ -136,6 +173,7 @@ def resource_import(self, request): uid=None, # 额外补充资源池导入的参数,用于记录操作日志 bill_id=None, + bill_type=None, task_id=root_id, operator=request.user.username, ) @@ -166,8 +204,7 @@ def query_resource_import_tasks(self, request): flow_tree_list = FlowTree.objects.filter(root_id__in=task_ids) done_tasks, processing_tasks = [], [] for tree in flow_tree_list: - if tree.status in SUCCEED_STATES: - # TODO: tree.status in FAILED_STATES + if tree.status in [*FAILED_STATES, *SUCCEED_STATES]: done_tasks.append(tree.root_id) else: processing_tasks.append(tree.root_id) @@ -176,7 +213,7 @@ def query_resource_import_tasks(self, request): if done_tasks: RedisConn.zrem(cache_key, *done_tasks) - return Response(processing_tasks) + return Response({"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "task_ids": processing_tasks}) @common_swagger_auto_schema( operation_summary=_("资源申请"), @@ -190,6 +227,7 @@ def resource_apply(self, request): @common_swagger_auto_schema( operation_summary=_("获取挂载点"), + responses={status.HTTP_200_OK: GetMountPointResponseSerializer()}, tags=[SWAGGER_TAG], ) @action(detail=False, methods=["GET"]) @@ -198,6 +236,7 @@ def get_mountpoints(self, request): @common_swagger_auto_schema( operation_summary=_("获取磁盘类型"), + responses={status.HTTP_200_OK: GetDiskTypeResponseSerializer()}, tags=[SWAGGER_TAG], ) @action(detail=False, methods=["GET"]) @@ -252,7 +291,22 @@ def resource_confirm(self, request): @action(detail=False, methods=["POST"], url_path="delete", serializer_class=ResourceDeleteSerializer) def resource_delete(self, request): validated_data = self.params_validate(self.get_serializer_class()) - return Response(DBResourceApi.resource_delete(params=validated_data)) + # 从资源池删除机器 + resp = DBResourceApi.resource_delete(params=validated_data) + # 将在资源池模块的机器移到空闲机,若机器处于其他模块,则忽略 + move_idle_hosts: List[int] = [] + resource_topo = SystemSettings.get_setting_value(key=SystemSettingsEnum.MANAGE_TOPO.value) + for topo in CCApi.find_host_biz_relations({"bk_host_id": validated_data["bk_host_ids"]}): + if ( + topo["bk_set_id"] == resource_topo["set_id"] + and topo["bk_module_id"] == resource_topo["resource_module_id"] + ): + move_idle_hosts.append(topo["bk_host_id"]) + + if move_idle_hosts: + CCApi.transfer_host_to_idlemodule({"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "bk_host_id": move_idle_hosts}) + + return Response(resp) @common_swagger_auto_schema( operation_summary=_("资源更新"), @@ -261,8 +315,8 @@ def resource_delete(self, request): ) @action(detail=False, methods=["POST"], url_path="update", serializer_class=ResourceUpdateSerializer) def resource_update(self, request): - validated_data = self.params_validate(self.get_serializer_class()) - return Response(DBResourceApi.resource_update(params=validated_data)) + update_params = self.params_validate(self.get_serializer_class()) + return Response(DBResourceApi.resource_batch_update(params=update_params)) @common_swagger_auto_schema( operation_summary=_("获取资源导入相关链接"), @@ -280,23 +334,32 @@ def resource_import_urls(self, request): tags=[SWAGGER_TAG], ) @action( - detail=False, methods=["GET"], url_path="query_operation_list", serializer_class=QueryOperationListSerializer + detail=False, + methods=["GET"], + url_path="query_operation_list", + serializer_class=QueryOperationListSerializer, + pagination_class=None, ) def query_operation_list(self, request): query_params = self.params_validate(self.get_serializer_class()) operation_list = DBResourceApi.operation_list(query_params) operation_list["results"] = operation_list.pop("details") or [] - ticket_ids: List[int] = [op["bill_id"] for op in operation_list["results"] if op["bill_id"]] - ticket_id__status_map: Dict[int, Ticket] = { - ticket.id: ticket.status for ticket in Ticket.objects.filter(id__in=ticket_ids) + task_ids: List[int] = [op["task_id"] for op in operation_list["results"]] + task_id__task: Dict[int, Ticket] = { + task.root_id: task for task in FlowTree.objects.filter(root_id__in=task_ids) } for op in operation_list["results"]: + # 格式化操作记录参数 + op["create_time"] = remove_timezone(op["update_time"]) + op["update_time"] = remove_timezone(op["update_time"]) + op["ticket_id"] = int(op.pop("bill_id") or 0) - op["create_time"], op["update_time"] = remove_timezone(op["update_time"]), remove_timezone( - op["update_time"] - ) - op["status"] = ticket_id__status_map.get(op["ticket_id"]) or "" + op["ticket_type"] = op.pop("bill_type", "") + op["ticket_type_display"] = TicketType.get_choice_label(op["ticket_type"]) + op["bk_biz_id"] = getattr(task_id__task.get(op["task_id"]), "bk_biz_id", env.DBA_APP_BK_BIZ_ID) + task_status = getattr(task_id__task.get(op["task_id"]), "status", "") + op["status"] = BAMBOO_STATE__TICKET_STATE_MAP.get(task_status, TicketStatus.RUNNING) # 过滤单据状态的操作记录 operation_list["results"] = [ @@ -306,3 +369,20 @@ def query_operation_list(self, request): ] return Response(operation_list) + + @common_swagger_auto_schema( + operation_summary=_("规格数量的预计"), + request_body=SpecCountResourceSerializer(), + responses={status.HTTP_200_OK: SpecCountResourceResponseSerializer()}, + tags=[SWAGGER_TAG], + ) + @action( + detail=False, + methods=["POST"], + url_path="spec_resource_count", + serializer_class=SpecCountResourceSerializer, + pagination_class=None, + ) + def spec_resource_count(self, request): + apply_params = self.params_validate(self.get_serializer_class()) + return Response(ResourceHandler.spec_resource_count(**apply_params)) diff --git a/dbm-ui/backend/db_services/dbresource/views/sepc.py b/dbm-ui/backend/db_services/dbresource/views/sepc.py index b5b7a549ae..e801f785bb 100644 --- a/dbm-ui/backend/db_services/dbresource/views/sepc.py +++ b/dbm-ui/backend/db_services/dbresource/views/sepc.py @@ -8,9 +8,11 @@ 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. """ +import math from django.db.models import F, Q from django.utils.translation import ugettext_lazy as _ +from rest_framework import status from rest_framework.decorators import action from rest_framework.response import Response @@ -18,12 +20,23 @@ from backend.bk_web.models import AuditedModel from backend.bk_web.pagination import AuditedLimitOffsetPagination from backend.bk_web.swagger import common_swagger_auto_schema +from backend.db_meta.enums import InstanceRole, MachineType from backend.db_meta.models import Cluster, Machine, ProxyInstance, StorageInstance from backend.db_meta.models.spec import Spec -from backend.db_services.dbresource.constants import SWAGGER_TAG -from backend.db_services.dbresource.exceptions import SpecOperateException +from backend.db_services.dbresource.constants import CLUSTER_TYPE__SPEC_FILTER, SWAGGER_TAG +from backend.db_services.dbresource.exceptions import SpecFilterClassDoesNotExistException, SpecOperateException from backend.db_services.dbresource.filters import SpecListFilter -from backend.db_services.dbresource.serializers import DeleteSpecSerializer, RecommendSpecSerializer, SpecSerializer +from backend.db_services.dbresource.serializers import ( + DeleteSpecSerializer, + FilterClusterSpecResponseSerializer, + FilterClusterSpecSerializer, + QueryQPSRangeResponseSerializer, + QueryQPSRangeSerializer, + RecommendResponseSpecSerializer, + RecommendSpecSerializer, + SpecSerializer, + VerifyDuplicatedSpecNameSerializer, +) from backend.iam_app.handlers.drf_perm import GlobalManageIAMPermission @@ -38,8 +51,28 @@ class DBSpecViewSet(viewsets.AuditedModelViewSet): filter_class = SpecListFilter def _get_custom_permissions(self): + if self.action in [ + DBSpecViewSet.list.__name__, + DBSpecViewSet.recommend_spec.__name__, + DBSpecViewSet.query_qps_range.__name__, + DBSpecViewSet.filter_cluster_spec.__name__, + ]: + return [] + return [GlobalManageIAMPermission()] + def _remove_spec_fields(self, machine_type, data): + """移除无需的字段""" + remove_fields = [] + if machine_type != MachineType.ES_DATANODE: + remove_fields.append("instance_num") + + # TODO: 后续可增加其他特定字段排除 + + for d in data: + for field in remove_fields: + d.pop(field) + @common_swagger_auto_schema( operation_summary=_("新建规格"), tags=[SWAGGER_TAG], @@ -47,6 +80,13 @@ def _get_custom_permissions(self): def create(self, request, *args, **kwargs): return super().create(request, *args, **kwargs) + @common_swagger_auto_schema( + operation_summary=_("规格详情"), + tags=[SWAGGER_TAG], + ) + def retrieve(self, request, *args, **kwargs): + return super().retrieve(request, *args, **kwargs) + @common_swagger_auto_schema( operation_summary=_("更新规格"), tags=[SWAGGER_TAG], @@ -58,8 +98,8 @@ def update(self, request, *args, **kwargs): if Machine.is_refer_spec([spec_id]): spec = Spec.objects.get(spec_id=spec_id) for key in update_data: - # 如果是可更新字段,则忽略 - if key in ["desc", *AuditedModel.AUDITED_FIELDS]: + # 如果是可更新字段或不存在字段,则忽略 + if key in ["desc", "spec_name", *AuditedModel.AUDITED_FIELDS] or key not in spec.__dict__: continue # 如果更新机型字段,则只允许拓展机型 elif key == "device_class": @@ -80,7 +120,14 @@ def update(self, request, *args, **kwargs): tags=[SWAGGER_TAG], ) def list(self, request, *args, **kwargs): - return super().list(request, *args, **kwargs) + resp = super().list(request, *args, **kwargs) + spec_ids = [spec["spec_id"] for spec in resp.data["results"]] + # 为规格添加是否引用字段 + already_refer_spec_ids = list(Machine.objects.filter(spec_id__in=spec_ids).values_list("spec_id", flat=True)) + for spec_data in resp.data["results"]: + spec_data["is_refer"] = spec_data["spec_id"] in already_refer_spec_ids + + return resp @common_swagger_auto_schema( operation_summary=_("删除规格"), @@ -94,6 +141,20 @@ def destroy(self, request, *args, **kwargs): super().destroy(request, *args, **kwargs) return Response() + @common_swagger_auto_schema( + operation_summary=_("校验是否存在同名规格"), + request_body=VerifyDuplicatedSpecNameSerializer(), + tags=[SWAGGER_TAG], + ) + @action(methods=["POST"], detail=False, serializer_class=VerifyDuplicatedSpecNameSerializer) + def verify_duplicated_spec_name(self, request, *args, **kwargs): + params = self.params_validate(self.get_serializer_class()) + verify_spec_id = params.pop("spec_id", -1) + # 如果存在同名规格,并且不是对自己更新,则认为规格重复 + spec = Spec.objects.filter(**params) + is_duplicated = spec.exists() and (spec.first().spec_id != verify_spec_id) + return Response(is_duplicated) + @common_swagger_auto_schema( operation_summary=_("批量删除规格"), request_body=DeleteSpecSerializer(), @@ -109,20 +170,104 @@ def batch_delete(self, request, *args, **kwargs): @common_swagger_auto_schema( operation_summary=_("获取推荐规格"), query_serializer=RecommendSpecSerializer(), + responses={status.HTTP_200_OK: RecommendResponseSpecSerializer()}, tags=[SWAGGER_TAG], ) - @action(methods=["GET"], detail=False, serializer_class=RecommendSpecSerializer) + @action( + methods=["GET"], + detail=False, + serializer_class=RecommendSpecSerializer, + filter_class=None, + pagination_class=None, + ) def recommend_spec(self, request): data = self.params_validate(self.get_serializer_class()) - cluster = Cluster.objects.get(id=data["cluster_id"]) - filter_params = Q(cluster=cluster) & Q(role=data["role"]) + if data["role"] == InstanceRole.INFLUXDB: + # 如果是influxdb,则直接通过id查询即可,并设置cluster为空 + cluster = None + filter_params = Q(role=data["role"]) & Q(id=data["instance_id"]) + else: + cluster = Cluster.objects.get(id=data["cluster_id"]) + filter_params = Q(cluster=cluster) & Q(role=data["role"]) - spec_ids = list( - StorageInstance.objects.annotate(role=F("instance_inner_role")) + # 根据角色获取StorageInstance关联的规格 + storage_spec_ids = ( + StorageInstance.objects.annotate(role=F("instance_role")) .filter(filter_params) - .union(ProxyInstance.objects.annotate(role=F("access_layer")).filter(filter_params)) .values_list("machine__spec_id", flat=True) ) - spec_data = SpecSerializer(Spec.objects.filter(spec_id__in=spec_ids), many=True).data + + # 根据角色获取ProxyInstance关联的规格 + proxy_spec_ids = ( + ProxyInstance.objects.annotate(role=F("access_layer")) + .filter(filter_params) + .values_list("machine__spec_id", flat=True) + ) + + # 根据spider角色获取接入层关联的规格 + spider_spec_ids = ( + ProxyInstance.objects.prefetch_related("tendbclusterspiderext") + .filter(cluster=cluster, tendbclusterspiderext__spider_role=data["role"]) + .values_list("machine__spec_id", flat=True) + ) + + spec_data = SpecSerializer( + Spec.objects.filter(spec_id__in=[*storage_spec_ids, *proxy_spec_ids, *spider_spec_ids]), many=True + ).data return Response(spec_data) + + @common_swagger_auto_schema( + operation_summary=_("获取qps的范围"), + query_serializer=QueryQPSRangeSerializer(), + responses={status.HTTP_200_OK: QueryQPSRangeResponseSerializer()}, + tags=[SWAGGER_TAG], + ) + @action( + methods=["GET"], + detail=False, + serializer_class=QueryQPSRangeSerializer, + filter_class=None, + pagination_class=None, + ) + def query_qps_range(self, request, *args, **kwargs): + data = self.params_validate(self.get_serializer_class()) + specs = Spec.objects.filter( + spec_machine_type=data["spec_machine_type"], spec_cluster_type=data["spec_cluster_type"] + ) + if not specs.exists(): + raise SpecFilterClassDoesNotExistException( + _("集群: {}后端没有配置任何规格,请前往规格页面配置").format(data["spec_cluster_type"]) + ) + + # 获取每个规格的qps的最小值和最大值 + qps_min_max_map = { + spec.qps.get("min", 0) + * math.ceil(data["capacity"] / spec.capacity): spec.qps.get("max", 0) + * math.ceil(data["capacity"] / spec.capacity) + for spec in specs + } + # 获取总qps的range + return Response({"min": min(qps_min_max_map.keys()), "max": max(qps_min_max_map.values())}) + + @common_swagger_auto_schema( + operation_summary=_("筛选集群部署规格方案"), + request_body=FilterClusterSpecSerializer(), + responses={status.HTTP_200_OK: FilterClusterSpecResponseSerializer()}, + tags=[SWAGGER_TAG], + ) + @action( + methods=["POST"], + detail=False, + serializer_class=FilterClusterSpecSerializer, + filter_class=None, + pagination_class=None, + ) + def filter_cluster_spec(self, request, *args, **kwargs): + data = self.params_validate(self.get_serializer_class()) + try: + specs = CLUSTER_TYPE__SPEC_FILTER[data["spec_cluster_type"]](**data).get_target_specs() + except KeyError: + raise SpecFilterClassDoesNotExistException(_("集群:{}的规格筛选类不存在,请实现相应接口").format(data["spec_cluster_type"])) + + return Response(specs) diff --git a/dbm-ui/backend/db_services/ipchooser/constants.py b/dbm-ui/backend/db_services/ipchooser/constants.py index 4f628ad8ee..3e7982200a 100644 --- a/dbm-ui/backend/db_services/ipchooser/constants.py +++ b/dbm-ui/backend/db_services/ipchooser/constants.py @@ -112,5 +112,10 @@ def _get_member__alias_map(cls) -> Dict[Enum, str]: FAULT_HOST_MODULE = 2 # 故障机 RECYCLE_HOST_MODULE = 3 # 待回收 +# DBM管理的CC集群名 +DB_MANAGE_SET = "db.manage.set" +RESOURCE_MODULE = "resource.idle.module" +DIRTY_MODULE = "dirty.module" + # 磁盘类型,目前固定写死 DEVICE_CLASS = ["SSD", "HDD", "ALL"] diff --git a/dbm-ui/backend/db_services/ipchooser/handlers/host_handler.py b/dbm-ui/backend/db_services/ipchooser/handlers/host_handler.py index c6b9bf5821..ab82f18966 100644 --- a/dbm-ui/backend/db_services/ipchooser/handlers/host_handler.py +++ b/dbm-ui/backend/db_services/ipchooser/handlers/host_handler.py @@ -42,6 +42,7 @@ def details_base( "bk_biz_id": bk_biz_id, "fields": [ "bk_host_id", + "bk_agent_id", "bk_host_name", "bk_os_type", "bk_host_innerip", diff --git a/dbm-ui/backend/db_services/ipchooser/handlers/topo_handler.py b/dbm-ui/backend/db_services/ipchooser/handlers/topo_handler.py index e772833cbf..91f956f962 100644 --- a/dbm-ui/backend/db_services/ipchooser/handlers/topo_handler.py +++ b/dbm-ui/backend/db_services/ipchooser/handlers/topo_handler.py @@ -163,7 +163,7 @@ def query_host_id_infos( conditions, start, page_size, - ["bk_host_id", "bk_host_innerip", "bk_host_innerip_v6", "bk_cloud_id"], + ["bk_host_id", "bk_host_innerip", "bk_host_innerip_v6", "bk_cloud_id", "bk_agent_id"], ) return {"total": resp["count"], "data": BaseHandler.format_host_id_infos(resp["info"], tree_node["bk_biz_id"])} diff --git a/dbm-ui/backend/db_services/ipchooser/mock_data.py b/dbm-ui/backend/db_services/ipchooser/mock_data.py index c5dcded49f..f6dfa38de6 100644 --- a/dbm-ui/backend/db_services/ipchooser/mock_data.py +++ b/dbm-ui/backend/db_services/ipchooser/mock_data.py @@ -183,7 +183,7 @@ API_HOST_TOPO_INFOS_REQUEST = { "bk_biz_id": 200050000, - "filter_conditions": {"bk_host_innerip": ["127.0.0.1", "127.0.0.2"]}, + "filter_conditions": {"bk_host_innerip": ["0:127.0.0.1", "0:127.0.0.2"]}, } API_HOST_TOPO_INFOS_RESPONSE = { diff --git a/dbm-ui/backend/db_services/ipchooser/query/resource.py b/dbm-ui/backend/db_services/ipchooser/query/resource.py index e0026b2899..44a9dc5e62 100644 --- a/dbm-ui/backend/db_services/ipchooser/query/resource.py +++ b/dbm-ui/backend/db_services/ipchooser/query/resource.py @@ -17,6 +17,7 @@ from django.core.cache import cache from django.utils.translation import ugettext as _ +from backend import env from backend.bk_web.constants import CACHE_1D from backend.components import CCApi from backend.components.gse.client import GseApi @@ -193,10 +194,8 @@ def query_cc_hosts( return resp @staticmethod - def fill_agent_status(cc_hosts, fill_key="status"): - - if not cc_hosts: - return + def query_agent_one_status(cc_hosts, fill_key="status"): + """查询agent1.0状态""" index = 0 hosts, host_map = [], {} @@ -211,9 +210,44 @@ def fill_agent_status(cc_hosts, fill_key="status"): status_map = GseApi.get_agent_status({"hosts": hosts}) for ip_cloud, detail in status_map.items(): + # agent在线状态,0为不在线,1为在线 cc_hosts[host_map[ip_cloud]][fill_key] = detail["bk_agent_alive"] except KeyError as e: - logger.error("fill_agent_status exception: %s", e) + logger.error("query_agent_one_status exception: %s", e) + + @staticmethod + def query_agent_two_status(cc_hosts, fill_key="status"): + """查询agent2.0状态""" + index = 0 + agent_id_list, host_map = [], {} + for cc_host in cc_hosts: + # bk_agent_id不存在(agent1.0)的情况下可以使用bk_cloud_id和bk_host_innerip来兼容查询 + bk_agent_id = cc_host.get("bk_agent_id") or f'{cc_host["bk_cloud_id"]}:{cc_host["bk_host_innerip"]}' + agent_id_list.append(bk_agent_id) + host_map[bk_agent_id] = index + index += 1 + + try: + status_list = GseApi.list_agent_state({"agent_id_list": agent_id_list}) + + for status in status_list: + # Agent当前运行状态码, -1:未知 0:初始安装 1:启动中 2:运行中 3:有损状态 4:繁忙状态 5:升级中 6:停止中 7:解除安装 + cc_hosts[host_map[status["bk_agent_id"]]][fill_key] = 1 if status["status_code"] == 2 else 0 + except KeyError as e: + logger.error("query_agent_two_status exception: %s", e) + + @staticmethod + def fill_agent_status(cc_hosts, fill_key="status"): + if not cc_hosts: + return + + if env.GSE_AGENT_VERSION == "1.0": + ResourceQueryHelper.query_agent_one_status(cc_hosts, fill_key) + return + + if env.GSE_AGENT_VERSION == "2.0": + ResourceQueryHelper.query_agent_two_status(cc_hosts, fill_key) + return @staticmethod def fill_cloud_name(cc_hosts): @@ -452,7 +486,7 @@ def _split_keyword(_keyword: str) -> List: @classmethod @func_cache_decorator(cache_time=60 * 60) - def search_cc_cloud(cls): + def search_cc_cloud(cls, fields=None): """ 查询云区域信息 """ @@ -462,7 +496,9 @@ def search_cc_cloud(cls): params={}, get_data=lambda x: x["info"], ) - cloud_id__cloud_info = {str(info["bk_cloud_id"]): info for info in resp} + cloud_id__cloud_info = { + str(info["bk_cloud_id"]): {f: info[f] for f in fields} if fields else info for info in resp + } # 命名要求 default_area ---> Direct Mode cloud_id__cloud_info[str(0)]["bk_cloud_name"] = _("直连区域") return cloud_id__cloud_info diff --git a/dbm-ui/backend/db_services/mysql/cluster/handlers.py b/dbm-ui/backend/db_services/mysql/cluster/handlers.py index 6d2496922a..50f20759d1 100644 --- a/dbm-ui/backend/db_services/mysql/cluster/handlers.py +++ b/dbm-ui/backend/db_services/mysql/cluster/handlers.py @@ -8,14 +8,25 @@ 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. """ +import itertools +import operator from collections import defaultdict +from functools import reduce from typing import Any, Dict, List, Set -from django.db.models import Prefetch, Q +from django.db.models import ExpressionWrapper, F, IntegerField, Prefetch, Q, Value +from django.db.models.query import QuerySet from django.forms import model_to_dict from django.utils.translation import ugettext_lazy as _ -from backend.db_meta.enums import AccessLayer, ClusterType, InstanceInnerRole +from backend.db_meta.enums import ( + AccessLayer, + ClusterType, + InstanceInnerRole, + InstanceRole, + InstanceStatus, + TenDBClusterSpiderRole, +) from backend.db_meta.exceptions import InstanceNotExistException from backend.db_meta.models import Cluster, DBModule, ProxyInstance, StorageInstance from backend.db_meta.models.machine import Machine @@ -86,22 +97,11 @@ def find_related_clusters_by_instances(self, instances: List[DBInstance]) -> Lis input: instances=[cluster1.proxy3] output: [cluster3] """ - inst_cluster_map = {} - host_id_related_cluster = defaultdict(list) + inst_cluster_map: Dict[str, Dict] = {} + host_id_related_cluster: Dict[int, List] = defaultdict(list) + # 基于 存储实例 和 Proxy 不会混部 的原则 - bk_host_ids = [instance.bk_host_id for instance in instances] - instance_objs = [ - *list( - StorageInstance.objects.select_related("machine") - .prefetch_related("cluster") - .filter(machine__bk_host_id__in=bk_host_ids) - ), - *list( - ProxyInstance.objects.select_related("machine") - .prefetch_related("cluster") - .filter(machine__bk_host_id__in=bk_host_ids) - ), - ] + instance_objs = self._get_instance_objs(instances) for inst_obj in instance_objs: inst_data = DBInstance.from_inst_obj(inst_obj) inst_cluster_map[str(inst_data)] = model_to_dict(inst_obj.cluster.first()) @@ -126,15 +126,55 @@ def query_clusters(self, cluster_filters: List[ClusterFilter]) -> List[Dict[str, 根据过滤条件查询集群,默认是精确匹配 """ + def _fill_mysql_instance_info(_cluster: Cluster, _cluster_info: Dict): + _cluster_info["masters"] = [] + _cluster_info["slaves"] = [] + _cluster_info["repeaters"] = [] + # 录入proxy instance的信息和storage instance的信息 + _cluster_info["proxies"] = [inst.simple_desc for inst in _cluster.proxyinstance_set.all()] + for inst in _cluster.storageinstance_set.all(): + role = ( + "masters" if _cluster.cluster_type == ClusterType.TenDBSingle else f"{inst.instance_inner_role}s" + ) + _cluster_info[role].append(inst.simple_desc) + + def _fill_spider_instance_info(_cluster: Cluster, _cluster_info: Dict): + # 更新remote db/remote dr的信息 + _cluster_info.update( + { + "remote_db": [ + m.simple_desc + for m in _cluster.storageinstance_set.all() + if m.instance_inner_role == InstanceInnerRole.MASTER + ], + "remote_dr": [ + m.simple_desc + for m in _cluster.storageinstance_set.all() + if m.instance_inner_role == InstanceInnerRole.SLAVE + ], + } + ) + # 更新spider角色信息 + _cluster_info.update( + { + role: [ + inst.simple_desc + for inst in _cluster.proxyinstance_set.all() + if inst.tendbclusterspiderext.spider_role == role + ] + for role in TenDBClusterSpiderRole.get_values() + } + ) + filter_conditions = Q() for cluster_filter in cluster_filters: filter_conditions |= Q(**cluster_filter.export_filter_conditions()) - clusters = Cluster.objects.prefetch_related("storageinstance_set", "proxyinstance_set").filter( + clusters: QuerySet = Cluster.objects.prefetch_related("storageinstance_set", "proxyinstance_set").filter( filter_conditions ) - cluster_db_module_ids = [cluster.db_module_id for cluster in clusters] - db_module_names = { + cluster_db_module_ids: List[int] = [cluster.db_module_id for cluster in clusters] + db_module_names: Dict[int, str] = { module.db_module_id: module.db_module_name for module in DBModule.objects.filter(db_module_id__in=cluster_db_module_ids) } @@ -148,21 +188,20 @@ def query_clusters(self, cluster_filters: List[ClusterFilter]) -> List[Dict[str, cluster_info["instance_count"] = ( cluster.storageinstance_set.all().count() + cluster.proxyinstance_set.all().count() ) - cluster_info["masters"] = [] - cluster_info["slaves"] = [] - cluster_info["repeaters"] = [] - # 录入proxy instance的信息和storage instance的信息 - cluster_info["proxies"] = [inst.simple_desc for inst in cluster.proxyinstance_set.all()] - for inst in cluster.storageinstance_set.all(): - role = "masters" if cluster.cluster_type == ClusterType.TenDBSingle else f"{inst.instance_inner_role}s" - cluster_info[role].append(inst.simple_desc) + if cluster.cluster_type == ClusterType.TenDBCluster: + _fill_spider_instance_info(cluster, cluster_info) + else: + _fill_mysql_instance_info(cluster, cluster_info) filter_cluster_list.append(cluster_info) return filter_cluster_list - def get_intersected_machines_from_clusters(self, cluster_ids: List[int], role: str): + def get_intersected_machines_from_clusters(self, cluster_ids: List[int], role: str, is_stand_by: bool): """ + @param cluster_ids: 查询的集群ID列表 + @param role: 角色 + @param is_stand_by: 是否只过滤is_stand_by标志的实例,仅用于slave 获取关联集群特定实例角色的交集 cluster1: slave1, slave2, slave3 cluster2: slave1, slave2 @@ -184,6 +223,9 @@ def get_intersected_machines_from_clusters(self, cluster_ids: List[int], role: s instances = StorageInstance.objects.select_related("machine").filter( cluster__id__in=cluster_ids, instance_inner_role=role ) + if is_stand_by: + # 如果带有is_stand_by标志,则过滤出可用于切换的slave实例 + instances = instances.filter(is_stand_by=True, status=InstanceStatus.RUNNING) clusters: List[Cluster] = Cluster.objects.prefetch_related( Prefetch(lookup_field, queryset=instances, to_attr="instances") @@ -205,8 +247,162 @@ def get_intersected_machines_from_clusters(self, cluster_ids: List[int], role: s return intersected_machines_info + def get_remote_machine_pairs(self, cluster_ids: List[int]): + """ + 根据tendbcluster集群查询remote机器信息 + @param cluster_ids: 集群的ID列表 + """ + # 获取remote master对应的机器 + clusters: QuerySet[Cluster] = Cluster.objects.prefetch_related("storageinstance_set").filter( + id__in=cluster_ids + ) + machine_ids: List[int] = list( + itertools.chain( + *[ + list( + cluster.storageinstance_set.select_related("machine") + .filter(instance_role=InstanceRole.REMOTE_MASTER) + .values_list("machine__bk_host_id", flat=True) + ) + for cluster in clusters + ] + ) + ) + master_machines: QuerySet[Machine] = Machine.objects.prefetch_related("storageinstance_set").filter( + bk_host_id__in=machine_ids + ) + + cluster_id__remote_machines: Dict[int, List[Dict]] = defaultdict(list) + for master_machine in master_machines: + # 查询master machine和slave machine + slave_instance: StorageInstance = master_machine.storageinstance_set.first().as_ejector.first().receiver + slave_machine: Machine = slave_instance.machine + cluster_id: int = slave_instance.cluster.first().id + cluster_id__remote_machines[cluster_id].append( + {"remote_master": model_to_dict(master_machine), "remote_slave": model_to_dict(slave_machine)} + ) + + remote_machine_infos: List[Dict[str, Any]] = [ + {"cluster_id": cluster_id, "remote_machines": cluster_id__remote_machines[cluster_id]} + for cluster_id in cluster_id__remote_machines.keys() + ] + return remote_machine_infos + + def get_remote_pairs(self, cluster_ids: List[int]): + """ + 根据tendbcluster集群查询remote db/remote dr + @param cluster_ids: 集群的ID列表 + """ + # 获取集群和对应的remote db实例 + clusters: QuerySet[Cluster] = Cluster.objects.prefetch_related("storageinstance_set").filter( + id__in=cluster_ids + ) + master_insts = StorageInstance.objects.prefetch_related("as_ejector").filter( + cluster__in=clusters, instance_role=InstanceRole.REMOTE_MASTER + ) + + # 根据remote db实例查询remote dr实例 + cluster_id__remote_pairs: Dict[int, List[Dict]] = defaultdict(list) + for master in master_insts: + cluster_id = master.cluster.first().id + slave: StorageInstance = master.as_ejector.first().receiver + cluster_id__remote_pairs[cluster_id].append( + {"remote_db": master.simple_desc, "remote_dr": slave.simple_desc} + ) + + remote_pair_infos: List[Dict[str, Any]] = [ + {"cluster_id": cluster_id, "remote_pairs": cluster_id__remote_pairs[cluster_id]} + for cluster_id in cluster_id__remote_pairs.keys() + ] + return remote_pair_infos + + def get_remote_machine_instance_pair(self, query: Dict[str, List[str]]): + """ + 查询remote的主机/实例关联的主机/实例, 仅用于查询spider的主从对关系 + """ + + def _get_related_instance(inst: StorageInstance): + # 这里remote的tuple表示一对db/dr + if inst.as_ejector.exists(): + return inst.as_ejector.first().receiver + if inst.as_receiver.exists(): + return inst.as_receiver.first().ejector + return inst + + related_pairs: Dict[str, Dict] = defaultdict(dict) + # 查询关联的实例信息 + if query.get("instances"): + instance_filters = reduce( + operator.or_, + [Q(machine__ip=inst.split(":")[0], port=inst.split(":")[1]) for inst in query["instances"]], + ) + insts = StorageInstance.objects.prefetch_related("as_ejector", "as_receiver").filter(instance_filters) + related_pairs["instances"] = {inst.ip_port: _get_related_instance(inst).simple_desc for inst in insts} + + # 查询关联的机器信息 + if query.get("machines"): + machine_filters = reduce( + operator.or_, + [Q(bk_cloud_id=machine.split(":")[0], ip=machine.split(":")[1]) for machine in query["machines"]], + ) + machines = Machine.objects.prefetch_related("storageinstance_set").filter(machine_filters) + related_pairs["machines"] = { + f"{machine.bk_cloud_id}:{machine.ip}": _get_related_instance( + machine.storageinstance_set.first() + ).machine.simple_desc + for machine in machines + if machine.storageinstance_set.exists() + } + + return related_pairs + def _format_cluster_field(self, cluster_info: Dict[str, Any]): cluster_info["cluster_name"] = cluster_info.pop("name") cluster_info["master_domain"] = cluster_info.pop("immute_domain") return cluster_info + + def _get_instance_objs(self, instances: List[DBInstance]): + """ + 根据instance(属DBInstance类)查询数据库实例,注意这里要考虑混布的情况 + eg: Tendbcluster的中控节点和spider master节点就是混布 + """ + bk_host_ids = [instance.bk_host_id for instance in instances] + # 获得基本的instance_objs + instance_objs = [ + *list( + StorageInstance.objects.select_related("machine") + .prefetch_related("cluster") + .filter(machine__bk_host_id__in=bk_host_ids) + ), + *list( + ProxyInstance.objects.select_related("machine") + .prefetch_related("cluster") + .filter(machine__bk_host_id__in=bk_host_ids) + ), + ] + + if not instance_objs: + return instance_objs + + # 获取当前实例的集群类型 ---> 这些实例集合所对应的集群类型应该是相同的 + cluster_type = instance_objs[0].cluster.first().cluster_type + + # 如果是Tendbcluster,则中控节点混布,需补充 + if cluster_type == ClusterType.TenDBCluster: + controller_instances = list( + ProxyInstance.objects.select_related("machine") + .prefetch_related("cluster") + .filter( + Q(machine__bk_host_id__in=bk_host_ids) + & Q(tendbclusterspiderext__spider_role=TenDBClusterSpiderRole.SPIDER_MASTER.value) + ) + ) + # 覆写port为admin port + for instance in controller_instances: + instance.port = instance.admin_port + instance_objs.extend(controller_instances) + + # TODO: 其他集群混布情况需补充 + + return instance_objs diff --git a/dbm-ui/backend/db_services/mysql/cluster/mock_data.py b/dbm-ui/backend/db_services/mysql/cluster/mock_data.py index b20313e347..27abbb7720 100644 --- a/dbm-ui/backend/db_services/mysql/cluster/mock_data.py +++ b/dbm-ui/backend/db_services/mysql/cluster/mock_data.py @@ -44,3 +44,113 @@ {"ip": "1.1.1.1", "bk_host_id": 1, "bk_cloud_id": 0, "bk_biz_id": 2000522222}, {"ip": "1.1.1.2", "bk_host_id": 2, "bk_cloud_id": 0, "bk_biz_id": 2000522222}, ] + +GET_TENDB_RELATED_MACHINES_RESPONSE_DATA = [ + { + "cluster_id": 28, + "remote_machines": [ + { + "remote_master": { + "creator": "admin", + "updater": "", + "ip": "127.0.0.1", + "bk_biz_id": 3, + "db_module_id": 4, + "access_layer": "storage", + "machine_type": "remote", + "cluster_type": "tendbcluster", + "...": "...", + "spec_id": 89, + "spec_config": {}, + }, + "remote_slave": { + "creator": "admin", + "updater": "", + "ip": "127.0.0.1", + "bk_biz_id": 3, + "db_module_id": 4, + "access_layer": "storage", + "machine_type": "remote", + "cluster_type": "tendbcluster", + "...": "...", + "spec_id": 89, + "spec_config": {}, + }, + } + ], + } +] + +GET_TENDB_REMOTE_PAIRS_RESPONSE_DATA = [ + { + "cluster_id": 29, + "remote_pairs": [ + { + "remote_db": { + "name": "", + "ip": "127.0.0.1", + "port": 20001, + "instance": "127.0.0.1:20001", + "status": "running", + "phase": "online", + "bk_instance_id": 1945, + "bk_host_id": 231, + "bk_cloud_id": 0, + "spec_config": {}, + "bk_biz_id": 3, + }, + "remote_dr": { + "name": "", + "ip": "127.0.0.2", + "port": 20001, + "instance": "127.0.0.2:20001", + "status": "running", + "phase": "online", + "bk_instance_id": 1944, + "bk_host_id": 232, + "bk_cloud_id": 0, + "spec_config": {}, + "bk_biz_id": 3, + }, + } + ], + }, + {"cluster_id": 30, "remote_pairs": ["..."]}, +] + +GET_TENDB_MACHINE_INSTANCE_PAIR_REQUEST_DATA = { + "machines": ["0:127.0.0.1"], + "instances": ["127.0.0.1:20000", "127.0.0.2:20001"], +} + +GET_TENDB_MACHINE_INSTANCE_PAIR_RESPONSE_DATA = { + "instances": { + "127.0.0.1:20001": { + "name": "", + "ip": "127.0.0.2", + "port": 20001, + "instance": "127.0.0.2:20001", + "status": "running", + "phase": "online", + "bk_instance_id": 2180, + "bk_host_id": 231, + "bk_cloud_id": 0, + "spec_config": {}, + "bk_biz_id": 3, + } + }, + "machines": { + "0:127.0.0.1": { + "creator": "admin", + "updater": "", + "ip": "127.0.0.2", + "bk_biz_id": 3, + "db_module_id": 4, + "access_layer": "storage", + "machine_type": "remote", + "cluster_type": "tendbcluster", + "....": ".....", + "spec_config": {}, + } + }, +} diff --git a/dbm-ui/backend/db_services/mysql/cluster/serializers.py b/dbm-ui/backend/db_services/mysql/cluster/serializers.py index db142a208b..f4ad6e1135 100644 --- a/dbm-ui/backend/db_services/mysql/cluster/serializers.py +++ b/dbm-ui/backend/db_services/mysql/cluster/serializers.py @@ -12,12 +12,17 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from backend.db_meta.enums import InstanceRole from backend.db_services.mysql.cluster.mock_data import ( FIND_RELATED_CLUSTERS_BY_ID_REQUEST_DATA, FIND_RELATED_CLUSTERS_BY_ID_RESPONSE_DATA, FIND_RELATED_CLUSTERS_BY_INSTANCE_REQUEST_DATA, FIND_RELATED_CLUSTERS_BY_INSTANCE_RESPONSE_DATA, GET_INTERSECTED_SLAVE_MACHINES_RESPONSE_DATA, + GET_TENDB_MACHINE_INSTANCE_PAIR_REQUEST_DATA, + GET_TENDB_MACHINE_INSTANCE_PAIR_RESPONSE_DATA, + GET_TENDB_RELATED_MACHINES_RESPONSE_DATA, + GET_TENDB_REMOTE_PAIRS_RESPONSE_DATA, QUERY_CLUSTERS_REQUEST_DATA, QUERY_CLUSTERS_RESPONSE_DATA, ) @@ -77,6 +82,9 @@ class Meta: class GetIntersectedSlavaMachinesSerializer(serializers.Serializer): cluster_ids = serializers.ListField(help_text=_("集群ID列表"), child=serializers.IntegerField()) + is_stand_by = serializers.BooleanField( + help_text=_("is_stand_by标志(默认获取带有is_stand_by标志的slave)"), required=False, default=True + ) class Meta: swagger_schema_fields = {"example": FIND_RELATED_CLUSTERS_BY_ID_REQUEST_DATA} @@ -85,3 +93,34 @@ class Meta: class GetIntersectedSlavaMachinesResponseSerializer(serializers.Serializer): class Meta: swagger_schema_fields = {"example": GET_INTERSECTED_SLAVE_MACHINES_RESPONSE_DATA} + + +class GetTendbRemoteMachinesSerializer(serializers.Serializer): + cluster_ids = serializers.ListField(help_text=_("集群ID列表"), child=serializers.IntegerField()) + + +class GetTendbRemoteMachinesResponseSerializer(serializers.Serializer): + class Meta: + swagger_schema_fields = {"example": GET_TENDB_RELATED_MACHINES_RESPONSE_DATA} + + +class GetTendbRemotePairsSerializer(serializers.Serializer): + cluster_ids = serializers.ListField(help_text=_("集群ID列表"), child=serializers.IntegerField()) + + +class GetTendbRemotePairsResponseSerializer(serializers.Serializer): + class Meta: + swagger_schema_fields = {"example": GET_TENDB_REMOTE_PAIRS_RESPONSE_DATA} + + +class GetTendbMachineInstancePairSerializer(serializers.Serializer): + instances = serializers.ListField(help_text=_("查询的实例列表"), required=False, child=serializers.CharField()) + machines = serializers.ListField(help_text=_("查询的机器列表"), required=False, child=serializers.CharField()) + + class Meta: + swagger_schema_fields = {"example": GET_TENDB_MACHINE_INSTANCE_PAIR_REQUEST_DATA} + + +class GetTendbMachineInstancePairResponseSerializer(serializers.Serializer): + class Meta: + swagger_schema_fields = {"example": GET_TENDB_MACHINE_INSTANCE_PAIR_RESPONSE_DATA} diff --git a/dbm-ui/backend/db_services/mysql/cluster/views.py b/dbm-ui/backend/db_services/mysql/cluster/views.py index 60974242e4..247a9730d6 100644 --- a/dbm-ui/backend/db_services/mysql/cluster/views.py +++ b/dbm-ui/backend/db_services/mysql/cluster/views.py @@ -24,6 +24,12 @@ FindRelatedClustersByInstancesResponseSerializer, GetIntersectedSlavaMachinesResponseSerializer, GetIntersectedSlavaMachinesSerializer, + GetTendbMachineInstancePairResponseSerializer, + GetTendbMachineInstancePairSerializer, + GetTendbRemoteMachinesResponseSerializer, + GetTendbRemoteMachinesSerializer, + GetTendbRemotePairsResponseSerializer, + GetTendbRemotePairsSerializer, QueryClustersRequestSerializer, QueryClustersResponseSerializer, ) @@ -91,6 +97,43 @@ def get_intersected_slave_machines_from_clusters(self, request, bk_biz_id): validated_data = self.params_validate(self.get_serializer_class()) return Response( ClusterServiceHandler(bk_biz_id).get_intersected_machines_from_clusters( - cluster_ids=validated_data["cluster_ids"], role=InstanceInnerRole.SLAVE.value + cluster_ids=validated_data["cluster_ids"], + role=InstanceInnerRole.SLAVE.value, + is_stand_by=validated_data["is_stand_by"], ) ) + + @common_swagger_auto_schema( + operation_summary=_("查询tendbcluster集群的remote相关角色机器"), + request_body=GetTendbRemoteMachinesSerializer(), + tags=[SWAGGER_TAG], + responses={status.HTTP_200_OK: GetTendbRemoteMachinesResponseSerializer()}, + ) + @action(methods=["POST"], detail=False, serializer_class=GetTendbRemoteMachinesSerializer) + def get_remote_machine_pairs(self, request, bk_biz_id): + validated_data = self.params_validate(self.get_serializer_class()) + return Response( + ClusterServiceHandler(bk_biz_id).get_remote_machine_pairs(cluster_ids=validated_data["cluster_ids"]) + ) + + @common_swagger_auto_schema( + operation_summary=_("查询tendbcluster集群的remote_db/remote_dr"), + request_body=GetTendbRemotePairsSerializer(), + tags=[SWAGGER_TAG], + responses={status.HTTP_200_OK: GetTendbRemotePairsResponseSerializer()}, + ) + @action(methods=["POST"], detail=False, serializer_class=GetTendbRemotePairsSerializer) + def get_remote_pairs(self, request, bk_biz_id): + validated_data = self.params_validate(self.get_serializer_class()) + return Response(ClusterServiceHandler(bk_biz_id).get_remote_pairs(cluster_ids=validated_data["cluster_ids"])) + + @common_swagger_auto_schema( + operation_summary=_("[tendbcluster]根据实例/机器查询关联对"), + request_body=GetTendbMachineInstancePairSerializer(), + tags=[SWAGGER_TAG], + responses={status.HTTP_200_OK: GetTendbMachineInstancePairResponseSerializer()}, + ) + @action(methods=["POST"], detail=False, serializer_class=GetTendbMachineInstancePairSerializer) + def get_remote_machine_instance_pair(self, request, bk_biz_id): + validated_data = self.params_validate(self.get_serializer_class()) + return Response(ClusterServiceHandler(bk_biz_id).get_remote_machine_instance_pair(validated_data)) diff --git a/dbm-ui/backend/db_services/mysql/constants.py b/dbm-ui/backend/db_services/mysql/constants.py index 469ed4b43a..4b66a4d54b 100644 --- a/dbm-ui/backend/db_services/mysql/constants.py +++ b/dbm-ui/backend/db_services/mysql/constants.py @@ -16,3 +16,15 @@ # 默认起始端口 DEFAULT_ORIGIN_PROXY_PORT = 10000 DEFAULT_ORIGIN_MYSQL_PORT = 20000 + +# 闪回查询库表sql语句 +QUERY_SCHEMA_DBS_SQL = ( + "SELECT SCHEMA_NAME from information_schema.SCHEMATA WHERE {db_sts} and SCHEMA_NAME not in {sys_db_list}" +) +QUERY_SCHEMA_TABLES_SQL = ( + "SELECT TABLE_SCHEMA,TABLE_NAME FROM information_schema.TABLES " + "WHERE TABLE_TYPE='BASE TABLE' AND (TABLE_SCHEMA IN {db_list}) AND {table_sts}" +) + +# 根据库名查询表名的sql语句 +QUERY_TABLES_FROM_DB_SQL = "select table_schema, table_name from information_schema.tables where {db_sts}" diff --git a/dbm-ui/backend/db_services/mysql/dataclass.py b/dbm-ui/backend/db_services/mysql/dataclass.py index abfb398b4b..0f7ef816eb 100644 --- a/dbm-ui/backend/db_services/mysql/dataclass.py +++ b/dbm-ui/backend/db_services/mysql/dataclass.py @@ -21,13 +21,26 @@ class DBInstance: ip: str bk_cloud_id: int port: int + spec_config: dict @classmethod def from_inst_obj(cls, inst_obj: Union[Dict, StorageInstance, ProxyInstance]) -> "DBInstance": if isinstance(inst_obj, Dict): - return cls(inst_obj["bk_host_id"], inst_obj["ip"], inst_obj["bk_cloud_id"], inst_obj["port"]) + return cls( + inst_obj["bk_host_id"], + inst_obj["ip"], + inst_obj["bk_cloud_id"], + inst_obj["port"], + inst_obj["spec_config"], + ) - return cls(inst_obj.machine.bk_host_id, inst_obj.machine.ip, inst_obj.machine.bk_cloud_id, inst_obj.port) + return cls( + inst_obj.machine.bk_host_id, + inst_obj.machine.ip, + inst_obj.machine.bk_cloud_id, + inst_obj.port, + inst_obj.machine.spec_config, + ) def __str__(self): return f"{self.bk_host_id}-{self.bk_cloud_id}-{self.ip}-{self.port}" diff --git a/dbm-ui/backend/db_services/mysql/fixpoint_rollback/handlers.py b/dbm-ui/backend/db_services/mysql/fixpoint_rollback/handlers.py index 9d2ced42d2..809963a34d 100644 --- a/dbm-ui/backend/db_services/mysql/fixpoint_rollback/handlers.py +++ b/dbm-ui/backend/db_services/mysql/fixpoint_rollback/handlers.py @@ -26,7 +26,7 @@ from backend.components import JobApi from backend.components.bklog.client import BKLogApi from backend.constants import DATETIME_PATTERN -from backend.db_meta.enums import InstanceInnerRole +from backend.db_meta.enums import ClusterType, InstanceInnerRole from backend.db_meta.models import StorageInstance from backend.db_meta.models.cluster import Cluster from backend.db_services.mysql.fixpoint_rollback.constants import BACKUP_LOG_ROLLBACK_TIME_RANGE_DAYS @@ -99,8 +99,15 @@ def _batch_make_job_requests(job_func: Callable, job_payloads: List[Dict]): return task_results - @staticmethod - def _format_job_backup_log(raw_backup_logs: List[str]) -> List[Dict[str, Any]]: + def _check_fixpoint_backup_log(self, log) -> bool: + if str(log["data_schema_grant"]).lower() == "all" or ( + "schema" in str(log["data_schema_grant"]).lower() and "data" in str(log["data_schema_grant"]).lower() + ): + return True + + return False + + def _format_job_backup_log(self, raw_backup_logs: List[str]) -> List[Dict[str, Any]]: """ 格式化本地备份记录日志 :param raw_backup_logs: 原始日志信息 @@ -114,10 +121,7 @@ def _format_job_backup_log(raw_backup_logs: List[str]) -> List[Dict[str, Any]]: log["mysql_role"] = log.pop("db_role") # 过滤适用于定点回档的备份 - if str(log["data_schema_grant"]).lower() == "all" or ( - "schema" in str(log["data_schema_grant"]).lower() - and "data" in str(log["data_schema_grant"]).lower() - ): + if self._check_fixpoint_backup_log(log): backup_logs.append(log) return backup_logs @@ -151,9 +155,36 @@ def _get_log_from_bklog(self, collector, start_time, end_time, query_string="*") return backup_logs @staticmethod - def aggregate_backup_log_by_id(backup_logs: List[Dict[str, Any]]) -> List[Dict[str, Any]]: + def _format_backup_for_tendb(raw_log: Dict[str, Any], backup_log: Dict[str, Any]) -> Dict[str, Any]: + """ + 对tendb cluster的日志进行进一步的格式化 + @param backup_log: 通过aggregate_backup_log_by_id已经聚合后的日志 """ - 按照backup_id聚合备份记录 + # 初始化相关角色集合,并舍弃无用的字段信息 + if "remote_node" not in backup_log: + backup_log["remote_node"] = {} + backup_log["spider_node"], backup_log["spider_slave"] = [], [] + delete_fields = [ + "mysql_host", + "mysql_port", + "master_host", + "master_port", + "mysql_role", + "binlog_info", + "data_schema_grant", + ] + for field in delete_fields: + backup_log.pop(field) + + # 同一个backid中,取最小的backup_begin_time,最大的backup_end_time和最大的consistent_backup_time。 + # 因为是字典序比较,所以可以直接用来比较时间 + backup_log["backup_begin_time"] = min(backup_log["backup_begin_time"], raw_log["backup_begin_time"]) + backup_log["backup_end_time"] = min(backup_log["backup_end_time"], raw_log["backup_end_time"]) + backup_log["backup_time"] = max(backup_log["backup_time"], raw_log["consistent_backup_time"]) + + def aggregate_backup_log_by_id(self, backup_logs: List[Dict[str, Any]]) -> List[Dict[str, Any]]: + """ + 按照backup_id聚合mysql备份记录 :param backup_logs: 备份记录列表 """ backup_id__backup_logs_map = defaultdict(dict) @@ -166,7 +197,7 @@ def aggregate_backup_log_by_id(backup_logs: List[Dict[str, Any]]) -> List[Dict[s backup_id__backup_logs_map[backup_id]["file_list_details"] = [] # 丢弃一些聚合后无用字段 - delete_fields = ["consistent_backup_time", "file_name", "file_size", "task_id"] + delete_fields = ["consistent_backup_time", "file_name", "file_size", "task_id", "file_type"] for field in delete_fields: backup_id__backup_logs_map[backup_id].pop(field) @@ -175,11 +206,140 @@ def aggregate_backup_log_by_id(backup_logs: List[Dict[str, Any]]) -> List[Dict[s {"file_name": file_name, "size": log["file_size"], "task_id": log["task_id"]} ) - if file_name.split(".")[-1] == "index": - backup_id__backup_logs_map[log["backup_id"]]["index_file"] = file_name + if log["file_type"] in ["index", "priv"]: + backup_id__backup_logs_map[log["backup_id"]][log["file_type"]] = file_name return list(backup_id__backup_logs_map.values()) + def aggregate_tendb_backup_log_by_id(self, backup_logs: List[Dict[str, Any]]) -> List[Dict[str, Any]]: + """ + 按照backup_id聚合tendb备份记录 + :param backup_logs: 备份记录列表 + """ + + def insert_time_field(_back_log, _log): + # 如果不具有时间字段则插入,否则更新 + if "backup_begin_time" not in _back_log: + _back_log["backup_begin_time"] = _log["backup_begin_time"] + _back_log["backup_end_time"] = _log["backup_end_time"] + _back_log["backup_time"] = _log["backup_time"] + else: + _back_log["backup_begin_time"] = min(_back_log["backup_begin_time"], _log["backup_begin_time"]) + _back_log["backup_end_time"] = max(_back_log["backup_end_time"], _log["backup_end_time"]) + _back_log["backup_time"] = max(_back_log["backup_time"], _log["backup_time"]) + + def insert_log_into_node(_backup_node, _log): + if _log["mysql_role"] in ["master", "slave"] and not self._check_fixpoint_backup_log(log): + return None + + if not _backup_node or ( + _log["mysql_host"] not in _backup_node + and ( + # 能覆盖的条件: + # 1. 如果是remote角色,则master能覆盖slave记录。同种角色可以时间接近rollback_time可以覆盖 + # 2. 如果是spider角色,则时间接近rollback_time可以覆盖 + ( + _log["mysql_role"] in ["spider_master", "spider_slave"] + and _log["consistent_backup_time"] > _backup_node["backup_time"] + ) + or ( + _log["mysql_role"] in ["master", "slave"] + and _log["mysql_role"] == "master" + and _backup_node.get("mysql_role", "slave") == "slave" + ) + or ( + _log["mysql_role"] in ["master", "slave"] + and _log["mysql_role"] == _backup_node["mysql_role"] + and _log["consistent_backup_time"] > _backup_node.get("backup_time", "") + ) + ) + ): + # 初始化该角色的备份信息 + insert_time_field(_backup_node, _log) + _backup_node["mysql_role"] = _log["mysql_role"] + _backup_node["host"], _backup_node["port"] = _log["mysql_host"], _log["mysql_port"] + _backup_node["file_list_details"] = [] + + # 更新备份时间,并插入文件列表信息 + insert_time_field(_backup_node, _log) + file_info = {"file_name": log["file_name"], "size": log["file_size"], "task_id": log["task_id"]} + _backup_node["file_list_details"].append(file_info) + # 如果是index/priv文件 则额外记录 + if log["file_type"] in ["index", "priv"]: + _backup_node[log["file_type"]] = file_info + + return _backup_node + + backup_id__backup_logs_map = defaultdict(dict) + for log in backup_logs: + # 如果存在单据号,证明不是例行备份,需排除 + if log["bill_id"]: + continue + + backup_id, log["backup_time"] = log["backup_id"], log["consistent_backup_time"] + if not backup_id__backup_logs_map.get(backup_id): + backup_id__backup_logs_map[backup_id].update(copy.deepcopy(log)) + # 初始化整体的角色信息 + backup_id__backup_logs_map[backup_id]["spider_node"] = {} + backup_id__backup_logs_map[backup_id]["spider_slave"] = {} + backup_id__backup_logs_map[backup_id]["remote_node"] = defaultdict(dict) + # 丢弃一些聚合后无用字段 + delete_fields = [ + "consistent_backup_time", + "file_name", + "file_size", + "task_id", + "file_type", + "shard_value", + "mysql_host", + "mysql_port", + "master_host", + "master_port", + "binlog_info", + "data_schema_grant", + "backup_type", + ] + for field in delete_fields: + backup_id__backup_logs_map[backup_id].pop(field) + + # 把该日志插入对应的角色字典中 + if log["mysql_role"] in ["master", "slave"]: + backup_node = backup_id__backup_logs_map[backup_id]["remote_node"][log["shard_value"]] + backup_node = insert_log_into_node(backup_node, log) + else: + node_role = "spider_node" if log["mysql_role"] == "spider_master" else "spider_slave" + backup_node = backup_id__backup_logs_map[backup_id][node_role] + backup_node = insert_log_into_node(backup_node, log) + + # 更新备份时间 + if backup_node: + insert_time_field(backup_id__backup_logs_map[backup_id], backup_node) + + # 获取合法的备份记录 + cluster_shard_num = self.cluster.tendbclusterstorageset_set.count() + backup_id__valid_backup_logs = defaultdict(dict) + for backup_id, backup_log in backup_id__backup_logs_map.items(): + # 获取合法分片ID,如果分片数不完整,则忽略 + shard_value_list = [ + shard_value + for shard_value in backup_log["remote_node"].keys() + if backup_log["remote_node"][shard_value] + ] + if sorted(shard_value_list) != list(range(0, cluster_shard_num)): + continue + + # 如果不存在spider master记录,则忽略 + if not backup_log["spider_node"]: + continue + + # 如果存在多条完整的backup记录,则保留最接近rollback time的记录 + if backup_id not in backup_id__valid_backup_logs or ( + backup_id__valid_backup_logs[backup_id]["backup_time"] < backup_log["backup_time"] + ): + backup_id__valid_backup_logs[backup_id] = backup_log + + return list(backup_id__valid_backup_logs.values()) + def query_backup_log_from_bklog(self, start_time: str, end_time: str) -> List[Dict]: """ 通过日志平台查询集群的时间范围内的备份记录 @@ -194,26 +354,40 @@ def query_backup_log_from_bklog(self, start_time: str, end_time: str) -> List[Di end_time=end_time, query_string=f'log: "cluster_address: \\"{cluster_domain}\\""', ) - return self.aggregate_backup_log_by_id(backup_logs) - def query_binlog_from_bklog(self, start_time: datetime, end_time: datetime, host_ip: str = None) -> Dict: + if self.cluster.cluster_type == ClusterType.TenDBCluster: + return self.aggregate_tendb_backup_log_by_id(backup_logs) + else: + return self.aggregate_backup_log_by_id(backup_logs) + + def query_binlog_from_bklog( + self, + start_time: Union[datetime, str], + end_time: Union[datetime, str], + host_ip: str = None, + port: int = None, + minute_range: int = 20, + ) -> Dict: """ 通过日志平台查询集群的时间范围内的binlog记录 :param start_time: 开始时间 :param end_time: 结束时间 :param host_ip: 过滤的主机IP + :param port: 端口 + :param minute_range: 放大的前后时间范围 """ start_time, end_time = str2datetime(start_time), str2datetime(end_time) if not host_ip: - host_ip = self.cluster.storageinstance_set.get(instance_inner_role=InstanceInnerRole.MASTER).machine.ip + master = self.cluster.storageinstance_set.get(instance_inner_role=InstanceInnerRole.MASTER) + host_ip, port = master.machine.ip, master.port binlogs = self._get_log_from_bklog( collector="mysql_binlog_result", - # 时间范围前后放大20min,避免日志平台上传延迟 - start_time=datetime2str(start_time - timedelta(minutes=20)), - end_time=datetime2str(end_time + timedelta(minutes=20)), - query_string=f'log: "host: \\"{host_ip}\\""', + # 时间范围前后放大避免日志平台上传延迟 + start_time=datetime2str(start_time - timedelta(minutes=minute_range)), + end_time=datetime2str(end_time + timedelta(minutes=minute_range)), + query_string=f"host: {host_ip} AND port: {port}", ) if not binlogs: @@ -227,7 +401,7 @@ def query_binlog_from_bklog(self, start_time: datetime, end_time: datetime, host "port": binlogs[0]["port"], "file_list_details": [], } - collector_fields = ["file_mtime", "start_time", "stop_time", "size", "backup_taskid"] + collector_fields = ["file_mtime", "start_time", "stop_time", "size", "task_id"] for log in binlogs: if str2datetime(log["stop_time"]) > end_time or str2datetime(log["stop_time"]) < start_time: continue @@ -323,6 +497,9 @@ def query_latest_backup_log(self, rollback_time: datetime, job_instance_id: int start_time=datetime2str(start_time), end_time=datetime2str(end_time) ) + if not backup_logs: + return None + backup_logs.sort(key=lambda x: x["backup_time"]) time_keys = [log["backup_time"] for log in backup_logs] try: diff --git a/dbm-ui/backend/db_services/mysql/fixpoint_rollback/mock_data.py b/dbm-ui/backend/db_services/mysql/fixpoint_rollback/mock_data.py new file mode 100644 index 0000000000..b147c80583 --- /dev/null +++ b/dbm-ui/backend/db_services/mysql/fixpoint_rollback/mock_data.py @@ -0,0 +1,194 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +FIXPOINT_LOG_DATA = { + "count": 10, + "results": [ + { + "databases": ["xxx123"], + "databases_ignore": [], + "tables": ["xxx1"], + "tables_ignore": [], + "source_cluster": { + "id": 32, + "name": "XXX-test-1", + "immute_domain": "spider.XXX-test-1.dba.db", + }, + "target_cluster": { + "cluster_id": 1, + "nodes": { + "spider": [ + {"ip": "127.0.0.1", "xxx": "xxx"}, + {"ip": "127.0.0.1", "xxx": "xxx"}, + ], + "backend_group": [ + { + "slave": {"ip": "127.0.0.1", "xxx": "xxx"}, + "master": {"ip": "127.0.0.1", "xxx": "xxx"}, + }, + { + "slave": {"ip": "127.0.0.1", "xxx": "xxx"}, + "master": {"ip": "127.0.0.1", "xxx": "xxx"}, + }, + ], + }, + }, + "ticket_id": 324, + "rollback_type": "REMOTE_AND_TIME", + "rollback_time": "2023-07-31 12:12:01", + "backupinfo": "", + } + ], +} + +MYSQL_BACKUP_LOG_FROM_BKLOG = [ + { + "backup_id": "c4676a9d-4841-11ee-8977-5254002c7706", + "bill_id": "", + "bk_biz_id": 3, + "bk_cloud_id": 0, + "time_zone": "CST", + "cluster_id": 22, + "cluster_address": "mysql80db.xxxxx.dba.db", + "shard_value": -1, + "mysql_host": "127.0.0.1", + "mysql_port": 20000, + "master_host": "127.0.0.1", + "master_port": 20000, + "mysql_role": "master", + "binlog_info": { + "show_master_status": { + "binlog_file": "binlog20000.000003", + "binlog_pos": "226547225", + "gtid": "", + "master_host": "127.0.0.1", + "master_port": 20000, + }, + "show_slave_status": None, + }, + "backup_begin_time": "2023-09-01 05:03:00", + "backup_end_time": "2023-09-01 05:03:00", + "data_schema_grant": "schema", + "backup_type": "logical", + "backup_time": "2023-09-01 05:03:00", + "file_list": [ + "3_22_127.0.0.x_20000_20230901_050300_logical.index", + "3_22_127.0.0.x_20000_20230901_050300_logical_0.tar", + ], + "file_list_details": [ + { + "file_name": "3_22_127.0.0.x_20000_20230901_050300_logical.index", + "size": 1063, + "task_id": "1693515780268496841-0160387296-29220-0", + }, + { + "file_name": "3_22_127.0.0.x_20000_20230901_050300_logical_0.tar", + "size": 3584, + "task_id": "1693515780326947415-0160387296-29227-0", + }, + ], + "index": "3_22_127.0.0.x_20000_20230901_050300_logical.index", + } +] + +TENDBCLUSTER_BACKUP_LOG_FROM_BKLOG = [ + { + "backup_id": "99ffaed9-4778-11ee-927d-5254000355cf", + "bill_id": "", + "bk_biz_id": 3, + "bk_cloud_id": 0, + "time_zone": "CST", + "cluster_id": 63, + "cluster_address": "spider.lucky.dba.db", + "mysql_role": "master", + "backup_begin_time": "2023-08-31 05:04:00", + "backup_end_time": "2023-08-31 05:04:00", + "backup_time": "2023-08-31 05:04:00", + "spider_node": { + "backup_begin_time": "2023-08-31 05:04:00", + "backup_end_time": "2023-08-31 05:04:00", + "backup_time": "2023-08-31 05:04:00", + "mysql_role": "spider_master", + "host": "127.0.0.1", + "port": 26000, + "file_list_details": [ + { + "file_name": "3_63_127.0.0.x_26000_20230831_050400_logical.index", + "size": 1899, + "task_id": "1693429440429266047-0160208206-16030-0", + }, + ], + "index": { + "file_name": "3_63_127.0.0.x_25000_20230831_050400_logical.index", + "size": 1882, + "task_id": "1693429440172235501-0160208206-15900-0", + }, + "priv": { + "file_name": "3_63_127.0.0.x_25000_20230831_050400_logical.priv", + "size": 2164, + "task_id": "1693429440216214985-0160208206-15906-0", + }, + }, + "spider_slave": {}, + "remote_node": { + "0": { + "backup_begin_time": "2023-08-31 05:04:00", + "backup_end_time": "2023-08-31 05:04:00", + "backup_time": "2023-08-31 05:04:00", + "mysql_role": "slave", + "host": "127.0.0.2", + "port": 20000, + "file_list_details": [ + { + "file_name": "3_63_127.0.0.x_20000_20230831_050400_logical.index", + "size": 1965, + "task_id": "1693429440284831420-0160210690-32194-0", + } + ], + "index": { + "file_name": "3_63_127.0.0.x_20000_20230831_050400_logical.index", + "size": 1965, + "task_id": "1693429440284831420-0160210690-32194-0", + }, + "priv": { + "file_name": "3_63_127.0.0.x_20000_20230831_050400_logical.priv", + "size": 3206, + "task_id": "1693429440333168971-0160210690-32200-0", + }, + }, + "1": { + "backup_begin_time": "2023-08-31 05:04:00", + "backup_end_time": "2023-08-31 05:04:00", + "backup_time": "2023-08-31 05:04:00", + "mysql_role": "slave", + "host": "127.0.0.3", + "port": 20001, + "file_list_details": [ + { + "file_name": "3_63_127.0.0.x_20001_20230831_050400_logical.index", + "size": 1965, + "task_id": "1693429440699256245-0160210690-32240-0", + } + ], + "index": { + "file_name": "3_63_127.0.0.x_20001_20230831_050400_logical.index", + "size": 1965, + "task_id": "1693429440699256245-0160210690-32240-0", + }, + "priv": { + "file_name": "3_63_127.0.0.x_20001_20230831_050400_logical.priv", + "size": 3206, + "task_id": "1693429440750115729-0160210690-32247-0", + }, + }, + }, + } +] diff --git a/dbm-ui/backend/db_services/mysql/fixpoint_rollback/serializers.py b/dbm-ui/backend/db_services/mysql/fixpoint_rollback/serializers.py index e28981dfda..d05092ee7d 100644 --- a/dbm-ui/backend/db_services/mysql/fixpoint_rollback/serializers.py +++ b/dbm-ui/backend/db_services/mysql/fixpoint_rollback/serializers.py @@ -14,18 +14,50 @@ from backend.db_services.mysql.fixpoint_rollback.constants import BACKUP_LOG_RANGE_DAYS +from . import mock_data + class BackupLogSerializer(serializers.Serializer): cluster_id = serializers.IntegerField(help_text=_("集群ID")) days = serializers.IntegerField(help_text=_("查询时间间隔"), default=BACKUP_LOG_RANGE_DAYS, required=False) -class BackupLogRollbackTimeSerialzier(serializers.Serializer): +class BackupLogTendbResponseSerializer(serializers.Serializer): + class Meta: + swagger_schema_fields = {"example": mock_data.TENDBCLUSTER_BACKUP_LOG_FROM_BKLOG} + + +class BackupLogMySQLResponseSerializer(serializers.Serializer): + class Meta: + swagger_schema_fields = {"example": mock_data.MYSQL_BACKUP_LOG_FROM_BKLOG} + + +class BackupLogRollbackTimeSerializer(serializers.Serializer): cluster_id = serializers.IntegerField(help_text=_("集群ID")) rollback_time = serializers.DateTimeField(help_text=_("回档时间")) job_instance_id = serializers.IntegerField(help_text=_("JOB实例ID"), required=False) +class BackupLogRollbackTimeTendbResponseSerializer(serializers.Serializer): + class Meta: + swagger_schema_fields = {"example": mock_data.TENDBCLUSTER_BACKUP_LOG_FROM_BKLOG[0]} + + +class BackupLogRollbackTimeMySQLResponseSerializer(serializers.Serializer): + class Meta: + swagger_schema_fields = {"example": mock_data.MYSQL_BACKUP_LOG_FROM_BKLOG[0]} + + class QueryBackupLogJobSerializer(serializers.Serializer): cluster_id = serializers.IntegerField(help_text=_("集群ID")) job_instance_id = serializers.IntegerField(help_text=_("JOB实例ID")) + + +class QueryFixpointLogSerializer(serializers.Serializer): + limit = serializers.IntegerField(help_text=_("分页限制"), required=False, default=10) + offset = serializers.IntegerField(help_text=_("分页起始"), required=False, default=0) + + +class QueryFixpointLogResponseSerializer(serializers.Serializer): + class Meta: + swagger_schema_fields = {"example": mock_data.FIXPOINT_LOG_DATA} diff --git a/dbm-ui/backend/db_services/mysql/fixpoint_rollback/views.py b/dbm-ui/backend/db_services/mysql/fixpoint_rollback/views.py index 08e5940060..94f1a5d64c 100644 --- a/dbm-ui/backend/db_services/mysql/fixpoint_rollback/views.py +++ b/dbm-ui/backend/db_services/mysql/fixpoint_rollback/views.py @@ -9,20 +9,33 @@ specific language governing permissions and limitations under the License. """ from datetime import datetime, timedelta +from typing import Any, Dict, List from django.utils.translation import ugettext as _ +from rest_framework import status from rest_framework.decorators import action from rest_framework.response import Response from backend.bk_web import viewsets from backend.bk_web.swagger import common_swagger_auto_schema +from backend.db_meta.enums import ClusterStatus, ClusterType +from backend.db_meta.enums.comm import SystemTagEnum +from backend.db_meta.models import Cluster from backend.db_services.mysql.fixpoint_rollback.handlers import FixPointRollbackHandler from backend.db_services.mysql.fixpoint_rollback.serializers import ( - BackupLogRollbackTimeSerialzier, + BackupLogMySQLResponseSerializer, + BackupLogRollbackTimeMySQLResponseSerializer, + BackupLogRollbackTimeSerializer, + BackupLogRollbackTimeTendbResponseSerializer, BackupLogSerializer, + BackupLogTendbResponseSerializer, QueryBackupLogJobSerializer, + QueryFixpointLogResponseSerializer, + QueryFixpointLogSerializer, ) from backend.iam_app.handlers.drf_perm import DBManageIAMPermission +from backend.ticket.constants import TicketType +from backend.ticket.models import ClusterOperateRecord from backend.utils.time import datetime2str SWAGGER_TAG = "db_services/fixpoint_rollback" @@ -35,6 +48,10 @@ def _get_custom_permissions(self): @common_swagger_auto_schema( operation_summary=_("通过日志平台获取集群备份记录"), query_serializer=BackupLogSerializer(), + responses={ + status.HTTP_200_OK: BackupLogTendbResponseSerializer(), + status.HTTP_202_ACCEPTED: BackupLogMySQLResponseSerializer(), + }, tags=[SWAGGER_TAG], ) @action(methods=["GET"], detail=False, serializer_class=BackupLogSerializer) @@ -73,10 +90,14 @@ def query_backup_log_job(self, requests, *args, **kwargs): @common_swagger_auto_schema( operation_summary=_("查询小于回档时间点最近的备份记录"), - query_serializer=BackupLogRollbackTimeSerialzier(), + query_serializer=BackupLogRollbackTimeSerializer(), + responses={ + status.HTTP_200_OK: BackupLogRollbackTimeTendbResponseSerializer(), + status.HTTP_202_ACCEPTED: BackupLogRollbackTimeMySQLResponseSerializer(), + }, tags=[SWAGGER_TAG], ) - @action(methods=["GET"], detail=False, serializer_class=BackupLogRollbackTimeSerialzier) + @action(methods=["GET"], detail=False, serializer_class=BackupLogRollbackTimeSerializer) def query_latest_backup_log(self, requests, *args, **kwargs): validated_data = self.params_validate(self.get_serializer_class()) return Response( @@ -85,3 +106,50 @@ def query_latest_backup_log(self, requests, *args, **kwargs): job_instance_id=validated_data.get("job_instance_id", None), ) ) + + @common_swagger_auto_schema( + operation_summary=_("获取定点构造记录"), + query_serializer=QueryFixpointLogSerializer(), + responses={status.HTTP_200_OK: QueryFixpointLogResponseSerializer()}, + tags=[SWAGGER_TAG], + ) + @action(methods=["GET"], detail=False, serializer_class=QueryFixpointLogSerializer, pagination_class=None) + def query_fixpoint_log(self, requests, *args, **kwargs): + validated_data = self.params_validate(self.get_serializer_class()) + limit, offset = validated_data["limit"], validated_data["offset"] + + # 查询目前定点回档临时集群 + temp_clusters = Cluster.objects.filter( + cluster_type=ClusterType.TenDBCluster, tag__name=SystemTagEnum.TEMPORARY + ) + temp_clusters_count = temp_clusters.count() + # 查询定点回档记录 + temp_clusters = temp_clusters[offset : limit + offset] + temp_cluster_ids = [cluster.id for cluster in temp_clusters] + records = ClusterOperateRecord.objects.select_related("ticket").filter( + cluster_id__in=temp_cluster_ids, ticket__ticket_type=TicketType.TENDBCLUSTER_ROLLBACK_CLUSTER + ) + # 填充定点回档实例记录 + fixpoint_logs: List[Dict[str, Any]] = [] + for record in records: + ticket_data = record.ticket.details + fixpoint_logs.append( + { + "databases": ticket_data["databases"], + "databases_ignore": ticket_data["databases_ignore"], + "tables": ticket_data["tables"], + "tables_ignore": ticket_data["tables_ignore"], + "source_cluster": ticket_data["clusters"][str(ticket_data["cluster_id"])], + "target_cluster": { + "cluster_id": record.cluster_id, + "nodes": ticket_data["nodes"], + "operations": ClusterOperateRecord.objects.get_cluster_operations(record.cluster_id), + }, + "ticket_id": record.ticket.id, + "rollback_type": ticket_data["rollback_type"], + "rollback_time": ticket_data.get("rollback_time", ""), + "backupinfo": ticket_data.get("backupinfo", {}).get("backup_time", ""), + } + ) + + return Response({"count": temp_clusters_count, "results": fixpoint_logs}) diff --git a/dbm-ui/backend/db_services/mysql/instance/handlers.py b/dbm-ui/backend/db_services/mysql/instance/handlers.py deleted file mode 100644 index db55b10936..0000000000 --- a/dbm-ui/backend/db_services/mysql/instance/handlers.py +++ /dev/null @@ -1,132 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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 dataclasses import asdict -from typing import Any, Dict, List, Union - -from django.db.models import F, Q - -from backend import env -from backend.constants import IP_PORT_DIVIDER -from backend.db_meta.models import Machine, ProxyInstance, StorageInstance -from backend.db_services.ipchooser.handlers.host_handler import HostHandler -from backend.db_services.mysql.cluster.handlers import ClusterServiceHandler -from backend.db_services.mysql.dataclass import DBInstance - - -class InstanceHandler: - def __init__(self, bk_biz_id: int): - self.bk_biz_id = bk_biz_id - - def check_instances(self, query_instances: List[Union[str, Dict]]) -> List[dict]: - """ - 查询实例的详细信息(包括实例本身信息+主机信息+关联集群信息) - :param query_instances: ["0:127.0.0.1:10000", "127.0.0.1", "127.0.0.1:20000"]或者[{....}, [....}] - :return - """ - - if not query_instances: - return [] - - # 如果传来的inst是字典类型,默认它是已经查询出的实例,只需补充信息 - # 否则需要先查询实例,再补充相关信息 - if isinstance(query_instances[0], Dict): - storages_proxies_instances = query_instances - else: - query_conditions = Q() - for address in query_instances: - split_len = len(address.split(IP_PORT_DIVIDER)) - if split_len == 1: - # 只输入 IP - query_conditions |= Q(machine__ip=address) - elif split_len == 2: - # IP:PORT - ip, port = address.split(IP_PORT_DIVIDER) - query_conditions |= Q(machine__ip=ip, port=port) - else: - # Cloud:IP:PORT - bk_cloud_id, ip, port = address.split(IP_PORT_DIVIDER) - query_conditions |= Q(machine__ip=ip, port=port, cluster__bk_cloud_id=bk_cloud_id) - - # 由于不知道输入的是什么实例,因此把存储实例和 proxy 实例同时查询出来 - storages = ( - StorageInstance.objects.annotate(role=F("instance_inner_role")) - .select_related("machine") - .prefetch_related("cluster") - .filter(Q(bk_biz_id=self.bk_biz_id) & query_conditions) - ) - proxies = ( - ProxyInstance.objects.annotate(role=F("access_layer")) - .select_related("machine") - .prefetch_related("cluster") - .filter(Q(bk_biz_id=self.bk_biz_id) & query_conditions) - ) - - # 如果无法查询实例,则直接返回 - if not storages and not proxies: - return [] - - storages_proxies_instances = [*storages, *proxies] - - bk_host_ids: List[int] = [] - db_instances: List[DBInstance] = [] - host_id_instance_map: Dict[str, Dict] = {} - instance_related_clusters: List[Dict[str, Any]] = ClusterServiceHandler( - self.bk_biz_id - ).find_related_clusters_by_instances( - instances=[DBInstance.from_inst_obj(inst) for inst in storages_proxies_instances] - ) - inst_address__related_clusters_map: Dict[str, Dict[str, Any]] = { - info["instance_address"]: info for info in instance_related_clusters - } - - for inst in storages_proxies_instances: - db_inst = DBInstance.from_inst_obj(inst) - db_inst_address = f"{db_inst.ip}:{db_inst.port}" - db_inst_related_cluster = inst_address__related_clusters_map[db_inst_address] - host_id_instance_map[str(db_inst)] = { - **asdict(db_inst), - "instance_address": f"{db_inst.ip}:{db_inst.port}", - "cluster_id": db_inst_related_cluster["cluster_info"]["id"], - "master_domain": db_inst_related_cluster["cluster_info"]["master_domain"], - # 目前的设计,instance_role 才能更好区分不通集群类型中机器的角色 - "role": inst["role"] if isinstance(inst, Dict) else inst.role, - "status": inst["status"] if isinstance(inst, Dict) else inst.status, - "cluster_type": db_inst_related_cluster["cluster_info"]["cluster_type"], - # 实例的关联集群把本身集群和集群的关联集群合并到一起 - "related_clusters": [ - *db_inst_related_cluster["related_clusters"], - db_inst_related_cluster["cluster_info"], - ], - } - bk_host_ids.append(inst["bk_host_id"] if isinstance(inst, Dict) else inst.machine.bk_host_id) - db_instances.append(db_inst) - - # 查询补充主机信息 - host_infos = HostHandler.check( - [{"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "scope_type": "biz"}], [], [], bk_host_ids - ) - host_id_info_map = {host_info["host_id"]: host_info for host_info in host_infos} - return [ - {**host_id_instance_map[str(db_inst)], **{"host_info": host_id_info_map.get(db_inst.bk_host_id, {})}} - for db_inst in db_instances - ] - - def get_machine_by_instances(self, query_instances: List[str]) -> Dict[str, Machine]: - """根据ip:port查询实例的机器信息""" - - storage_instances = StorageInstance.find_insts_by_addresses(query_instances).filter(bk_biz_id=self.bk_biz_id) - proxy_instances = ProxyInstance.find_insts_by_addresses(query_instances).filter(bk_biz_id=self.bk_biz_id) - - address__machine_map: Dict[str, Machine] = {} - for instances in [storage_instances, proxy_instances]: - address__machine_map.update({inst.ip_port: inst.machine for inst in instances}) - - return address__machine_map diff --git a/dbm-ui/backend/db_services/mysql/instance/mock_data.py b/dbm-ui/backend/db_services/mysql/instance/mock_data.py deleted file mode 100644 index 276320c49d..0000000000 --- a/dbm-ui/backend/db_services/mysql/instance/mock_data.py +++ /dev/null @@ -1,29 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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. -""" - -FIND_RELATED_CLUSTERS_BY_ID_REQUEST_DATA = {"cluster_ids": [1, 2]} - -FIND_RELATED_CLUSTERS_BY_ID_RESPONSE_DATA = [ - {"cluster_id": 1, "cluster_info": {}, "related_clusters": []}, - {"cluster_id": 2, "cluster_info": {}, "related_clusters": []}, -] - -FIND_RELATED_CLUSTERS_BY_INSTANCE_REQUEST_DATA = { - "instances": [ - {"bk_host_id": 1, "bk_cloud_id": 0, "ip": "127.0.0.1", "port": 20000}, - {"bk_host_id": 2, "bk_cloud_id": 0, "ip": "127.0.0.2", "port": 20001}, - ] -} - -FIND_RELATED_CLUSTERS_BY_INSTANCE_RESPONSE_DATA = [ - {"bk_host_id": 1, "cluster_info": {}, "related_clusters": []}, - {"bk_host_id": 2, "cluster_info": {}, "related_clusters": []}, -] diff --git a/dbm-ui/backend/db_services/mysql/instance/serializers.py b/dbm-ui/backend/db_services/mysql/instance/serializers.py deleted file mode 100644 index 7344474681..0000000000 --- a/dbm-ui/backend/db_services/mysql/instance/serializers.py +++ /dev/null @@ -1,50 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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 gettext_lazy as _ -from rest_framework import serializers - -from backend.db_services.mysql.cluster.mock_data import ( - FIND_RELATED_CLUSTERS_BY_ID_REQUEST_DATA, - FIND_RELATED_CLUSTERS_BY_ID_RESPONSE_DATA, - FIND_RELATED_CLUSTERS_BY_INSTANCE_REQUEST_DATA, - FIND_RELATED_CLUSTERS_BY_INSTANCE_RESPONSE_DATA, -) - - -class FindRelatedClustersByClusterIdRequestSerializer(serializers.Serializer): - cluster_ids = serializers.ListField(help_text=_("集群ID列表"), child=serializers.IntegerField()) - - class Meta: - swagger_schema_fields = {"example": FIND_RELATED_CLUSTERS_BY_ID_REQUEST_DATA} - - -class FindRelatedClustersByClusterIdResponseSerializer(serializers.Serializer): - class Meta: - swagger_schema_fields = {"example": FIND_RELATED_CLUSTERS_BY_ID_RESPONSE_DATA} - - -class FindRelatedClustersByInstancesRequestSerializer(serializers.Serializer): - class InstanceSerializer(serializers.Serializer): - bk_cloud_id = serializers.IntegerField(help_text=_("云区域ID")) - ip = serializers.CharField(help_text=_("IP地址")) - bk_host_id = serializers.IntegerField(help_text=_("主机ID")) - port = serializers.IntegerField(help_text=_("端口号")) - - instances = serializers.ListField(help_text=_("实例列表"), child=InstanceSerializer()) - - class Meta: - swagger_schema_fields = {"example": FIND_RELATED_CLUSTERS_BY_INSTANCE_REQUEST_DATA} - - -class FindRelatedClustersByInstancesResponseSerializer(serializers.Serializer): - class Meta: - swagger_schema_fields = {"example": FIND_RELATED_CLUSTERS_BY_INSTANCE_RESPONSE_DATA} diff --git a/dbm-ui/backend/db_services/mysql/instance/urls.py b/dbm-ui/backend/db_services/mysql/instance/urls.py index f2158d052e..4740f97928 100644 --- a/dbm-ui/backend/db_services/mysql/instance/urls.py +++ b/dbm-ui/backend/db_services/mysql/instance/urls.py @@ -11,7 +11,7 @@ from rest_framework.routers import DefaultRouter -from backend.db_services.mysql.instance.views import InstanceViewSet +from backend.db_services.dbbase.instances.views import InstanceViewSet router = DefaultRouter(trailing_slash=True) router.register(r"instance", InstanceViewSet, basename="instance") diff --git a/dbm-ui/backend/db_services/mysql/instance/views.py b/dbm-ui/backend/db_services/mysql/instance/views.py deleted file mode 100644 index c8e5428c18..0000000000 --- a/dbm-ui/backend/db_services/mysql/instance/views.py +++ /dev/null @@ -1,39 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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 as _ -from rest_framework import status -from rest_framework.decorators import action -from rest_framework.response import Response - -from backend.bk_web import viewsets -from backend.bk_web.swagger import common_swagger_auto_schema -from backend.db_services.dbbase.resources import constants -from backend.db_services.mysql.instance.handlers import InstanceHandler -from backend.db_services.mysql.resources.tendbha.yasg_slz import CheckInstancesResSLZ, CheckInstancesSLZ -from backend.iam_app.handlers.drf_perm import DBManageIAMPermission - -SWAGGER_TAG = "db_services/mysql/instance" - - -class InstanceViewSet(viewsets.SystemViewSet): - def _get_custom_permissions(self): - return [DBManageIAMPermission()] - - @common_swagger_auto_schema( - operation_summary=_("根据用户手动输入的 ip:port 查询真实的实例"), - request_body=CheckInstancesSLZ(), - tags=[constants.RESOURCE_TAG], - responses={status.HTTP_200_OK: CheckInstancesResSLZ()}, - ) - @action(methods=["POST"], detail=False, serializer_class=CheckInstancesSLZ) - def check_instances(self, request, bk_biz_id): - validated_data = self.params_validate(self.get_serializer_class()) - return Response(InstanceHandler(bk_biz_id=bk_biz_id).check_instances(validated_data["instance_addresses"])) diff --git a/dbm-ui/backend/db_services/mysql/permission/authorize/handlers.py b/dbm-ui/backend/db_services/mysql/permission/authorize/handlers.py index 0e78609c08..a529a0b6fb 100644 --- a/dbm-ui/backend/db_services/mysql/permission/authorize/handlers.py +++ b/dbm-ui/backend/db_services/mysql/permission/authorize/handlers.py @@ -118,9 +118,11 @@ def pre_check_excel_rules(self, excel_authorize: ExcelAuthorizeMeta) -> Dict: pre_check: bool = True for future in as_completed(tasks): + # 获取线程执行的授权结果 task_result = future.result() uid, __, index = task_result["authorize_uid"], task_result["message"], task_result["task_index"] + # 将缓存数据取出放到excel缓存数据的切片中 data = cache.get(uid)[0] pre_check &= task_result["pre_check"] @@ -128,10 +130,9 @@ def pre_check_excel_rules(self, excel_authorize: ExcelAuthorizeMeta) -> Dict: to_cache_data_list[index] = data raw_authorize_data_list[index] = task_result["authorize_data"] - # 缓存授权数据,用于授权单据创建,并返回校验结果 + # 缓存excel授权数据,删除线程中pre_check产生的缓存,并返回校验结果 cache.delete_many(to_delete_cache_uid_list) authorize_uid = data_cache(key=None, data=to_cache_data_list, cache_time=AUTHORIZE_DATA_EXPIRE_TIME) - excel_url = ( f"{env.BK_SAAS_HOST}/apis/mysql/bizs/{self.bk_biz_id}/permission/authorize" f"/get_authorize_info_excel/?authorize_uid={authorize_uid}" diff --git a/dbm-ui/backend/db_services/mysql/permission/clone/handlers.py b/dbm-ui/backend/db_services/mysql/permission/clone/handlers.py index 68effeaa6a..e106e24c8f 100644 --- a/dbm-ui/backend/db_services/mysql/permission/clone/handlers.py +++ b/dbm-ui/backend/db_services/mysql/permission/clone/handlers.py @@ -17,7 +17,8 @@ from backend import env from backend.components.mysql_priv_manager.client import MySQLPrivManagerApi -from backend.db_services.mysql.instance.handlers import InstanceHandler +from backend.db_meta.models import Machine +from backend.db_services.dbbase.instances.handlers import InstanceHandler from backend.db_services.mysql.permission.clone.dataclass import CloneMeta from backend.db_services.mysql.permission.clone.models import MySQLPermissionCloneRecord from backend.db_services.mysql.permission.constants import ( @@ -35,22 +36,26 @@ class CloneHandler(object): 封装权限克隆相关的处理操作 """ - def __init__(self, bk_biz_id: int, operator: str, clone_type: str, context: Dict = None): + def __init__(self, bk_biz_id: int, operator: str, clone_type: str, clone_cluster_type: str, context: Dict = None): """ - :param bk_biz_id: 业务ID - :param context: 上下文数据 + @param bk_biz_id: 业务ID + @param operator: 操作者 + @param clone_type: 克隆类型 + @param clone_cluster_type: 克隆集群类型(这里其实代表的是DB类型,枚举值是mysql/tendbcluster) + @param context: 上下文数据 """ self.bk_biz_id = bk_biz_id self.clone_type = clone_type + self.cluster_type = clone_cluster_type self.context = context self.operator = operator - def get_address__machine_map(self, clone_instance_list): + def get_address__machine_map(self, clone_instance_list) -> Dict[str, Machine]: """根据克隆数据得到address/ip_port与机器信息的字典关系""" # 如果不存在相应的数据和键,则直接返回 if not clone_instance_list or "source" not in clone_instance_list[0]: - return None + return {} address_list = [] for clone_data in clone_instance_list: @@ -88,41 +93,42 @@ def _get_instance_check_records(self, clone_data_list): ] return clone_priv_records - def pre_check_clone(self, clone: CloneMeta) -> Dict: + def pre_check_clone(self, clone: CloneMeta) -> Dict[str, Any]: """ - - 实例间权限克隆前置检查 + - 客户端/实例 权限克隆前置检查 :param clone: 实例间权限克隆元数据 """ # 前置校验参数字段名格式化 - pre_check, message = True, "ok" if self.clone_type == CloneType.CLIENT: clone_priv_records = self._get_client_check_records(clone.clone_list) else: clone_priv_records = self._get_instance_check_records(clone.clone_list) # 对克隆参数进行前置校验 - clone_api_field = f"pre_check_clone_{self.clone_type}" - clone_priv_records_field = f"clone_{self.clone_type}_priv_records" - params = {"bk_biz_id": self.bk_biz_id, clone_priv_records_field: clone_priv_records} + params: Dict[str, Any] = { + "bk_biz_id": self.bk_biz_id, + "cluster_type": self.cluster_type, + f"clone_{self.clone_type}_priv_records": clone_priv_records, + } try: - raw_resp = getattr(MySQLPrivManagerApi, clone_api_field)(params=params, raw=True) + raw_resp = getattr(MySQLPrivManagerApi, f"pre_check_clone_{self.clone_type}")(params=params, raw=True) if raw_resp["message"]: # 捕获接口返回结果异常,更新克隆权限错误信息 pre_check, message = False, raw_resp["message"] - error_pattern = re.compile(r"line ([0-9]+):(.*)") - error_msg_list = error_pattern.findall(message) + error_msg_list: List[str] = re.compile(r"line ([0-9]+):(.*)").findall(message) for err_index, err_msg in error_msg_list: clone.clone_list[int(err_index) - 1].update({"message": err_msg}) - except Exception as e: # pylint: disable=broad-except # 捕获接口其他未知异常 pre_check, message = False, _("「接口调用异常」{}").format(e) for clone_data in clone.clone_list: clone_data.update({"message": message}) + else: + pre_check, message = True, "ok" - clone_uid = data_cache(key=None, data=clone.clone_list, cache_time=CLONE_DATA_EXPIRE_TIME) - + # 缓存后返回当前的校验结果 + clone_uid: str = data_cache(key=None, data=clone.clone_list, cache_time=CLONE_DATA_EXPIRE_TIME) return { "pre_check": pre_check, "message": message, diff --git a/dbm-ui/backend/db_services/mysql/permission/clone/serializers.py b/dbm-ui/backend/db_services/mysql/permission/clone/serializers.py index 1b914e597a..3a3bfc2023 100644 --- a/dbm-ui/backend/db_services/mysql/permission/clone/serializers.py +++ b/dbm-ui/backend/db_services/mysql/permission/clone/serializers.py @@ -12,24 +12,27 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from backend.configuration.constants import DBType from backend.db_meta.request_validator import validate_instance_in_biz from backend.db_services.mysql.permission.clone import mock_data -from backend.db_services.mysql.permission.constants import CLONE_EXCEL_HEADER_MAP, CloneType +from backend.db_services.mysql.permission.constants import CLONE_EXCEL_HEADER_MAP, CloneClusterType, CloneType from backend.utils.excel import ExcelHandler -class CloneElementSerializer(serializers.Serializer): - source = serializers.CharField(help_text=_("旧实例/旧客户端IP")) - target = serializers.CharField(help_text=_("新实例/新客户端IP")) - bk_cloud_id = serializers.IntegerField(help_text=_("云区域ID")) - module = serializers.CharField(help_text=_("模块名"), required=False) - cluster_domain = serializers.CharField(help_text=_("集群域名"), required=False) - - class Meta: - swagger_schema_fields = {"example": mock_data.CLONE_INSTANCE_DATA} - - class PreCheckCloneSerializer(serializers.Serializer): + class CloneElementSerializer(serializers.Serializer): + source = serializers.CharField(help_text=_("旧实例/旧客户端IP")) + target = serializers.CharField(help_text=_("新实例/新客户端IP")) + bk_cloud_id = serializers.IntegerField(help_text=_("云区域ID")) + module = serializers.CharField(help_text=_("模块名"), required=False) + cluster_domain = serializers.CharField(help_text=_("集群域名"), required=False) + + class Meta: + swagger_schema_fields = {"example": mock_data.CLONE_INSTANCE_DATA} + + clone_cluster_type = serializers.ChoiceField( + help_text=_("集群类型"), choices=CloneClusterType.get_choices(), required=False, default=CloneClusterType.MYSQL + ) clone_type = serializers.ChoiceField(help_text=_("权限克隆类型"), choices=CloneType.get_choices()) clone_list = serializers.ListField( help_text=_("克隆元素列表"), child=CloneElementSerializer(help_text=_("克隆元素信息")), min_length=1 @@ -82,6 +85,9 @@ class Meta: class PreCheckExcelCloneSerializere(serializers.Serializer): clone_file = serializers.FileField(help_text=_("克隆实例/客户端excel文件")) clone_type = serializers.ChoiceField(help_text=_("权限克隆类型"), choices=CloneType.get_choices()) + clone_cluster_type = serializers.ChoiceField( + help_text=_("集群类型"), choices=CloneClusterType.get_choices(), required=False, default=CloneClusterType.MYSQL + ) def validate(self, attrs): clone_excel_file = attrs["clone_file"].file diff --git a/dbm-ui/backend/db_services/mysql/permission/clone/views.py b/dbm-ui/backend/db_services/mysql/permission/clone/views.py index 03eb9ae002..ddfd2a2d38 100644 --- a/dbm-ui/backend/db_services/mysql/permission/clone/views.py +++ b/dbm-ui/backend/db_services/mysql/permission/clone/views.py @@ -52,6 +52,7 @@ def _view_common_handler( "bk_biz_id": bk_biz_id, "operator": request.user.username, "clone_type": validated_data["clone_type"], + "clone_cluster_type": validated_data.pop("clone_cluster_type", None), "context": {}, } meta_init_data = meta.from_dict(validated_data) diff --git a/dbm-ui/backend/db_services/mysql/permission/constants.py b/dbm-ui/backend/db_services/mysql/permission/constants.py index 969271d8d3..23e32cd965 100644 --- a/dbm-ui/backend/db_services/mysql/permission/constants.py +++ b/dbm-ui/backend/db_services/mysql/permission/constants.py @@ -45,6 +45,14 @@ class AccountType(str, StructuredEnum): """账号类型枚举""" MYSQL = EnumField("mysql", _("MySQL")) + TENDB = EnumField("tendbcluster", _("TendbCluster")) + + +class CloneClusterType(str, StructuredEnum): + """克隆的集群类型""" + + MYSQL = EnumField("mysql", _("MySQL")) + TENDB = EnumField("tendbcluster", _("TendbCluster")) class AuthorizeExcelTypeID(str, StructuredEnum): diff --git a/dbm-ui/backend/db_services/mysql/permission/db_account/handlers.py b/dbm-ui/backend/db_services/mysql/permission/db_account/handlers.py index 914c017a75..f75e398761 100644 --- a/dbm-ui/backend/db_services/mysql/permission/db_account/handlers.py +++ b/dbm-ui/backend/db_services/mysql/permission/db_account/handlers.py @@ -15,6 +15,7 @@ from backend.configuration.models.password_policy import PasswordPolicy from backend.core.encrypt.constants import RSAConfigType from backend.core.encrypt.handlers import RSAHandler +from backend.db_meta.enums import ClusterType from backend.db_services.mysql.permission.constants import AccountType from backend.db_services.mysql.permission.db_account.dataclass import AccountMeta, AccountRuleMeta from backend.db_services.mysql.permission.db_account.policy import DBPasswordPolicy @@ -25,26 +26,26 @@ class AccountHandler(object): 封装账号相关的处理操作 """ - def __init__(self, bk_biz_id: int, operator: str = None, context: Dict = None): + def __init__(self, bk_biz_id: int, account_type: AccountType, operator: str = None, context: Dict = None): """ - :param bk_biz_id: 业务ID - :param operator: 操作者 - :param context: 上下文数据 + @param bk_biz_id: 业务ID + @param account_type: 账号类型,目前区分与mysql和tendbcluster + @param operator: 操作者 + @param context: 上下文数据 """ - self.bk_biz_id = bk_biz_id + self.account_type = account_type self.operator = operator self.context = context @staticmethod - def _check_pwd_strength(password: str, rule_data: Dict) -> Tuple[bool, Dict[str, bool]]: + def _check_password_strength(password: str, rule_data: Dict) -> Tuple[bool, Dict[str, bool]]: """ - 检查密码是否符合平台预设强度 - :param password: 待校验密码 - :param rule_data: 密码强度规则 - :returns: 密码校验是否成功和校验信息 + @param password: 待校验密码 + @param rule_data: 密码强度规则 + @returns: 密码校验是否成功和校验信息 """ - # 完善密码强度规则信息 follow = rule_data.pop("follow") for rule in follow.keys(): @@ -54,34 +55,29 @@ def _check_pwd_strength(password: str, rule_data: Dict) -> Tuple[bool, Dict[str, rule_data[f"follow_{rule}"] = follow["limit"] if follow[rule] else rule_data["max_length"] policy = DBPasswordPolicy(**rule_data) - - is_validity = policy.validate(password) - validity_map = policy.get_validity_map() + is_validity, validity_map = policy.validate(password), policy.get_validity_map() return is_validity, validity_map @staticmethod - def _cipher_password(password: str) -> str: + def _encrypt_password(password: str) -> str: """ - - 将password利用公钥加密 + - 获取后台公钥,将password利用公钥加密 :param password: 待加密密码 """ - public_key = MySQLPrivManagerApi.fetch_public_key() return RSAHandler.encrypt_password(public_key=public_key, password=password, salt=None) @staticmethod def _decrypt_password(password: str) -> str: """ - - 将password利用私钥解密 + - 获取saas侧私钥,将password利用私钥解密 :param password: 待解密密码 """ - rsa_private_key = RSAHandler.get_or_generate_rsa_in_db(name=RSAConfigType.MYSQL.value).rsa_private_key return RSAHandler.decrypt_password(private_key=rsa_private_key.content, password=password, salt=None) def _format_account_rules(self, account_rules_list: Dict) -> Dict: """格式化账号权限列表信息""" - for account_rules in account_rules_list["items"]: account_rules["account"]["account_id"] = account_rules["account"].pop("id") @@ -92,18 +88,16 @@ def _format_account_rules(self, account_rules_list: Dict) -> Dict: return account_rules_list - @classmethod - def verify_password_strength(cls, account: AccountMeta) -> Dict: + def verify_password_strength(self, account: AccountMeta) -> Dict: """ - 校验密码强度 :param account: 账号元信息 """ - - password = cls._decrypt_password(account.password) - password_policy = PasswordPolicy.safe_get(AccountType.MYSQL.value) + password = self._decrypt_password(account.password) + password_policy = PasswordPolicy.safe_get(self.account_type) if password_policy: - is_strength, password_verify_info = cls._check_pwd_strength(password, password_policy.policy) + is_strength, password_verify_info = self._check_password_strength(password, password_policy.policy) return {"is_strength": is_strength, "password_verify_info": password_verify_info, "password": password} return {"is_strength": True, "password_verify_info": {}, "password": password} @@ -111,15 +105,15 @@ def verify_password_strength(cls, account: AccountMeta) -> Dict: def create_account(self, account: AccountMeta) -> Optional[Any]: """ - 新建一个账号 - :param account: 账号元信息 + @param account: 账号元信息 """ - resp = MySQLPrivManagerApi.create_account( { + "cluster_type": self.account_type, "bk_biz_id": self.bk_biz_id, "operator": self.operator, "user": account.user, - "psw": self._cipher_password(account.password), + "psw": self._encrypt_password(account.password), } ) return resp @@ -127,26 +121,30 @@ def create_account(self, account: AccountMeta) -> Optional[Any]: def delete_account(self, account: AccountMeta) -> Optional[Any]: """ - 删除账号(仅在账号不存在存量规则时) - :param account: 账号元信息 + @param account: 账号元信息 """ - resp = MySQLPrivManagerApi.delete_account( - {"bk_biz_id": self.bk_biz_id, "operator": self.operator, "id": account.account_id} + { + "bk_biz_id": self.bk_biz_id, + "operator": self.operator, + "cluster_type": self.account_type, + "id": account.account_id, + } ) return resp def update_password(self, account: AccountMeta) -> Optional[Any]: """ - 修改账号密码 - :param account: 账号元信息 + @param account: 账号元信息 """ - resp = MySQLPrivManagerApi.update_password( { + "cluster_type": self.account_type, "bk_biz_id": self.bk_biz_id, "operator": self.operator, "id": account.account_id, - "psw": self._cipher_password(account.password), + "psw": self._encrypt_password(account.password), } ) return resp @@ -154,13 +152,13 @@ def update_password(self, account: AccountMeta) -> Optional[Any]: def add_account_rule(self, account_rule: AccountRuleMeta) -> Optional[Any]: """ - 添加账号规则 - :param account_rule: 账号规则元信息 + @param account_rule: 账号规则元信息 """ - resp = MySQLPrivManagerApi.add_account_rule( { "bk_biz_id": self.bk_biz_id, "creator": self.operator, + "cluster_type": self.account_type, "account_id": account_rule.account_id, "priv": account_rule.privilege, "dbname": account_rule.access_db, @@ -171,7 +169,9 @@ def add_account_rule(self, account_rule: AccountRuleMeta) -> Optional[Any]: def query_account_rules(self, account_rule: AccountRuleMeta): """查询某个账号下的权限""" - account_rules_list = MySQLPrivManagerApi.list_account_rules({"bk_biz_id": self.bk_biz_id}) + account_rules_list = MySQLPrivManagerApi.list_account_rules( + {"bk_biz_id": self.bk_biz_id, "cluster_type": self.account_type} + ) account_rules_list = self._format_account_rules(account_rules_list) # 根据账号名和准许db过滤规则 @@ -192,7 +192,9 @@ def query_account_rules(self, account_rule: AccountRuleMeta): def list_account_rules(self, rule_filter: AccountRuleMeta) -> Dict: """列举规则清单""" - account_rules_list = MySQLPrivManagerApi.list_account_rules({"bk_biz_id": self.bk_biz_id}) + account_rules_list = MySQLPrivManagerApi.list_account_rules( + {"bk_biz_id": self.bk_biz_id, "cluster_type": self.account_type} + ) account_rules_list = self._format_account_rules(account_rules_list) # 不存在过滤条件则直接返回 @@ -235,6 +237,7 @@ def modify_account_rule(self, account_rule: AccountRuleMeta) -> Optional[Any]: { "bk_biz_id": self.bk_biz_id, "operator": self.operator, + "cluster_type": self.account_type, "id": account_rule.rule_id, "account_id": account_rule.account_id, "dbname": account_rule.access_db, @@ -253,6 +256,7 @@ def delete_account_rule(self, account_rule: AccountRuleMeta) -> Optional[Any]: { "bk_biz_id": self.bk_biz_id, "operator": self.operator, + "cluster_type": self.account_type, "id": [account_rule.rule_id], } ) diff --git a/dbm-ui/backend/db_services/mysql/permission/db_account/serializers.py b/dbm-ui/backend/db_services/mysql/permission/db_account/serializers.py index 2217b5df8b..a174cc9167 100644 --- a/dbm-ui/backend/db_services/mysql/permission/db_account/serializers.py +++ b/dbm-ui/backend/db_services/mysql/permission/db_account/serializers.py @@ -14,35 +14,36 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers -from backend.db_services.mysql.permission.constants import PrivilegeType +from backend.db_services.mysql.permission.constants import AccountType, PrivilegeType from backend.db_services.mysql.permission.db_account import mock_data from backend.db_services.mysql.permission.db_account.dataclass import AccountMeta from backend.db_services.mysql.permission.db_account.handlers import AccountHandler class DBAccountBaseSerializer(serializers.Serializer): - user = serializers.CharField(help_text=_("账号名称")) + user = serializers.CharField(help_text=_("账号名称"), required=False) password = serializers.CharField(help_text=_("账号密码")) + account_type = serializers.ChoiceField( + help_text=_("账号类型"), choices=AccountType.get_choices(), default=AccountType.MYSQL + ) - def validate_user(self, value): - """校验账号是否符合规则""" - - pattern = re.compile(r"^[0-9a-zA-Z][0-9a-zA-Z\-._]{0,31}$") - - if not re.match(pattern, value): - raise serializers.ValidationError(_("账号名称不符合要求, 请重新账号名")) - - return value - - def validate_password(self, value): - """将密码进行解密并校验密码强度""" - - account = AccountMeta(password=value) - verify_result = AccountHandler.verify_password_strength(account) + def validate(self, attrs): + # 校验账号是否符合规则 + if attrs.get("user"): + user_pattern = re.compile(r"^[0-9a-zA-Z][0-9a-zA-Z\-._]{0,31}$") + if not re.match(user_pattern, attrs["user"]): + raise serializers.ValidationError(_("账号名称不符合要求, 请重新账号名")) + + # 将密码进行解密并校验密码强度 + account = AccountMeta(password=attrs["password"]) + verify_result = AccountHandler(bk_biz_id=0, account_type=attrs["account_type"]).verify_password_strength( + account + ) if not verify_result["is_strength"]: - raise serializers.ValidationError(_("密码强度不符合要求, 请重新输入密码")) + raise serializers.ValidationError(_("密码强度不符合要求,请重新输入密码。")) - return verify_result["password"] + attrs["password"] = verify_result["password"] + return attrs class CreateMySQLAccountSerializer(DBAccountBaseSerializer): @@ -51,6 +52,9 @@ class Meta: class VerifyPasswordStrengthSerializer(serializers.Serializer): + account_type = serializers.ChoiceField( + help_text=_("账号类型(默认为mysql)"), choices=AccountType.get_choices(), required=False, default=AccountType.MYSQL + ) password = serializers.CharField(help_text=_("待校验密码")) class Meta: @@ -67,6 +71,9 @@ class Meta: class DeleteMySQLAccountSerializer(serializers.Serializer): account_id = serializers.IntegerField(help_text=_("账号ID")) + account_type = serializers.ChoiceField( + help_text=_("账号类型"), choices=AccountType.get_choices(), default=AccountType.MYSQL + ) class Meta: swagger_schema_fields = {"example": mock_data.DELETE_ACCOUNT_REQUEST} @@ -80,25 +87,23 @@ class Meta: swagger_schema_fields = {"example": mock_data.UPDATE_ACCOUNT_REQUEST} -class MySQLAccountInfoSerializer(DBAccountBaseSerializer): - bk_biz_id = serializers.IntegerField(help_text=_("业务ID")) - user = serializers.CharField(help_text=_("账号名称")) - account_id = serializers.IntegerField(help_text=_("账号ID")) - creator = serializers.CharField(help_text=_("创建者")) - create_time = serializers.DateTimeField(help_text=_("创建时间")) - - -class MySQLAccountRulesInfoSerializer(serializers.Serializer): - rule_id = serializers.IntegerField(help_text=_("规则ID")) - account_id = serializers.IntegerField(help_text=_("账号ID")) - bk_biz_id = serializers.IntegerField(help_text=_("业务ID")) - access_db = serializers.CharField(help_text=_("访问DB")) - privilege = serializers.CharField(help_text=_("规则列表")) - creator = serializers.CharField(help_text=_("创建者")) - create_time = serializers.DateTimeField(help_text=_("创建时间")) - - class MySQLAccountRulesDetailSerializer(serializers.Serializer): + class MySQLAccountInfoSerializer(DBAccountBaseSerializer): + bk_biz_id = serializers.IntegerField(help_text=_("业务ID")) + user = serializers.CharField(help_text=_("账号名称")) + account_id = serializers.IntegerField(help_text=_("账号ID")) + creator = serializers.CharField(help_text=_("创建者")) + create_time = serializers.DateTimeField(help_text=_("创建时间")) + + class MySQLAccountRulesInfoSerializer(serializers.Serializer): + rule_id = serializers.IntegerField(help_text=_("规则ID")) + account_id = serializers.IntegerField(help_text=_("账号ID")) + bk_biz_id = serializers.IntegerField(help_text=_("业务ID")) + access_db = serializers.CharField(help_text=_("访问DB")) + privilege = serializers.CharField(help_text=_("规则列表")) + creator = serializers.CharField(help_text=_("创建者")) + create_time = serializers.DateTimeField(help_text=_("创建时间")) + account = MySQLAccountInfoSerializer(help_text=_("账号信息")) rules = serializers.ListSerializer( help_text=_("权限列表信息"), allow_empty=True, child=MySQLAccountRulesInfoSerializer() @@ -109,11 +114,17 @@ class FilterMySQLAccountRulesSerializer(serializers.Serializer): user = serializers.CharField(help_text=_("账号名称"), required=False) access_db = serializers.CharField(help_text=_("访问DB"), required=False) privilege = serializers.CharField(help_text=_("规则列表"), required=False) + account_type = serializers.ChoiceField( + help_text=_("账号类型"), choices=AccountType.get_choices(), default=AccountType.MYSQL + ) class QueryMySQLAccountRulesSerializer(serializers.Serializer): user = serializers.CharField(help_text=_("账号名称")) access_dbs = serializers.ListField(help_text=_("访问DB列表"), child=serializers.CharField(), required=False) + account_type = serializers.ChoiceField( + help_text=_("账号类型"), choices=AccountType.get_choices(), default=AccountType.MYSQL + ) class ListMySQLAccountRulesSerializer(serializers.Serializer): @@ -126,22 +137,26 @@ class Meta: swagger_schema_fields = {"example": mock_data.LIST_MYSQL_ACCOUNT_RULE_RESPONSE} -class MySQLRuleTypeSerializer(serializers.Serializer): - dml = serializers.ListField( - help_text=_("dml"), child=serializers.ChoiceField(choices=PrivilegeType.DML.get_choices()), required=False - ) - ddl = serializers.ListField( - help_text=_("dml"), child=serializers.ChoiceField(choices=PrivilegeType.DDL.get_choices()), required=False - ) - glob = serializers.ListField( - help_text=_("glob"), child=serializers.ChoiceField(choices=PrivilegeType.GLOBAL.get_choices()), required=False - ) - - class AddMySQLAccountRuleSerializer(serializers.Serializer): + class MySQLRuleTypeSerializer(serializers.Serializer): + dml = serializers.ListField( + help_text=_("dml"), child=serializers.ChoiceField(choices=PrivilegeType.DML.get_choices()), required=False + ) + ddl = serializers.ListField( + help_text=_("dml"), child=serializers.ChoiceField(choices=PrivilegeType.DDL.get_choices()), required=False + ) + glob = serializers.ListField( + help_text=_("glob"), + child=serializers.ChoiceField(choices=PrivilegeType.GLOBAL.get_choices()), + required=False, + ) + account_id = serializers.IntegerField(help_text=_("账号ID")) access_db = serializers.CharField(help_text=_("访问DB")) privilege = MySQLRuleTypeSerializer() + account_type = serializers.ChoiceField( + help_text=_("账号类型"), choices=AccountType.get_choices(), default=AccountType.MYSQL + ) class Meta: swagger_schema_fields = {"example": mock_data.ADD_MYSQL_ACCOUNT_RULE_REQUEST} @@ -156,6 +171,9 @@ class Meta: class DeleteMySQLAccountRuleSerializer(serializers.Serializer): rule_id = serializers.IntegerField(help_text=_("规则ID")) + account_type = serializers.ChoiceField( + help_text=_("账号类型"), choices=AccountType.get_choices(), default=AccountType.MYSQL + ) class Meta: swagger_schema_fields = {"example": mock_data.DELETE_MYSQL_ACCOUNT_RULE_REQUEST} diff --git a/dbm-ui/backend/db_services/mysql/permission/db_account/views.py b/dbm-ui/backend/db_services/mysql/permission/db_account/views.py index aaec38dab8..2145eeb2d5 100644 --- a/dbm-ui/backend/db_services/mysql/permission/db_account/views.py +++ b/dbm-ui/backend/db_services/mysql/permission/db_account/views.py @@ -51,9 +51,13 @@ def _view_common_handler( :param meta: 元信息数据结构 :param func: handler的回调函数名称 """ - - base_info = {"bk_biz_id": bk_biz_id, "operator": request.user.username, "context": {}} validated_data = self.params_validate(self.get_serializer_class()) + base_info = { + "bk_biz_id": bk_biz_id, + "operator": request.user.username, + "account_type": validated_data.pop("account_type", None), + "context": {}, + } meta_init_data = meta.from_dict(validated_data) return Response(getattr(AccountHandler(**base_info), func)(meta_init_data)) diff --git a/dbm-ui/backend/db_services/mysql/remote_service/exceptions.py b/dbm-ui/backend/db_services/mysql/remote_service/exceptions.py new file mode 100644 index 0000000000..40f27de925 --- /dev/null +++ b/dbm-ui/backend/db_services/mysql/remote_service/exceptions.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import functools +import types + +from django.utils.translation import ugettext_lazy as _ + +from backend.exceptions import AppBaseException, ErrorCode + + +class RemoteServiceBaseException(AppBaseException): + MODULE_CODE = ErrorCode.DB_REMOTE_SERVICE_CODE + MESSAGE = _("DRS接口请求通用异常") diff --git a/dbm-ui/backend/db_services/mysql/remote_service/handlers.py b/dbm-ui/backend/db_services/mysql/remote_service/handlers.py index 6f8609ea97..c54ab1f529 100644 --- a/dbm-ui/backend/db_services/mysql/remote_service/handlers.py +++ b/dbm-ui/backend/db_services/mysql/remote_service/handlers.py @@ -8,13 +8,16 @@ 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. """ +import itertools from collections import defaultdict from itertools import chain -from typing import Dict, List, Union +from typing import Any, Dict, List, Union + +from django.utils.translation import ugettext as _ from backend.components import DRSApi -from backend.constants import IP_PORT_DIVIDER from backend.db_meta.api.cluster.base.handler import ClusterHandler +from backend.db_services.mysql.constants import QUERY_SCHEMA_DBS_SQL, QUERY_SCHEMA_TABLES_SQL, QUERY_TABLES_FROM_DB_SQL from backend.flow.consts import SYSTEM_DBS @@ -22,13 +25,27 @@ class RemoteServiceHandler: def __init__(self, bk_biz_id: int): self.bk_biz_id = bk_biz_id - def show_databases(self, cluster_ids: List[int]) -> List[Dict[str, Union[int, List[str]]]]: - """批量查询集群的数据库列表""" + def _get_cluster_address(self, cluster_id__role_map, cluster_id): + cluster_handler = ClusterHandler.get_exact_handler(bk_biz_id=self.bk_biz_id, cluster_id=cluster_id) + if cluster_id__role_map.get(cluster_id): + return cluster_handler, cluster_handler.get_remote_address(cluster_id__role_map[cluster_id]) + + return cluster_handler, cluster_handler.get_remote_address() + + def show_databases( + self, cluster_ids: List[int], cluster_id__role_map: Dict[int, str] = None + ) -> List[Dict[str, Union[int, List[str]]]]: + """ + 批量查询集群的数据库列表 + @param cluster_ids: 集群ID列表 + @param cluster_id__role_map: (可选)集群ID和对应查询库表角色的映射表 + """ # 如果集群列表为空,则提前返回 if not cluster_ids: return [] + cluster_id__role_map = cluster_id__role_map or {} cloud_addresses = defaultdict(list) cluster_databases = [] address_cluster_id_map = defaultdict(dict) @@ -36,11 +53,9 @@ def show_databases(self, cluster_ids: List[int]) -> List[Dict[str, Union[int, Li # 查询各个集群可执行的实例地址 for cluster_id in cluster_ids: - cluster_handler = ClusterHandler.get_exact_handler(bk_biz_id=self.bk_biz_id, cluster_id=cluster_id) - inst = cluster_handler.get_exec_inst() + cluster_handler, address = self._get_cluster_address(cluster_id__role_map, cluster_id) bk_cloud_id = cluster_handler.cluster.bk_cloud_id - address = f"{inst.machine.ip}{IP_PORT_DIVIDER}{inst.port}" - cloud_addresses[bk_cloud_id].append(f"{inst.machine.ip}{IP_PORT_DIVIDER}{inst.port}") + cloud_addresses[bk_cloud_id].append(address) address_cluster_id_map[bk_cloud_id][address] = cluster_id # 批量查询实例地址对应的数据库列表 @@ -63,52 +78,141 @@ def show_databases(self, cluster_ids: List[int]) -> List[Dict[str, Union[int, Li ) return cluster_databases - def check_cluster_database(self, cluster_ids: List[int], db_names: List[str]) -> Dict[str, Dict]: + def show_tables( + self, cluster_db_infos: List[Dict], cluster_id__role_map: Dict[int, str] = None + ) -> List[Dict[str, Union[str, List]]]: + """ + 批量查询集群的数据库列表 + @param cluster_db_infos: 集群DB信息 + @param cluster_id__role_map: (可选)集群ID和对应查询库表角色的映射表 + """ + cluster_id__role_map = cluster_id__role_map or {} + + cluster_table_infos: List[Dict[str, Union[str, List]]] = [] + for info in cluster_db_infos: + cluster_handler, address = self._get_cluster_address(cluster_id__role_map, info["cluster_id"]) + + # 构造数据表查询语句 + db_sts = " or ".join([f"table_schema='{db}'" for db in info["dbs"]]) + query_table_sql = QUERY_TABLES_FROM_DB_SQL.format(db_sts=db_sts) + + # 执行DRS,并聚合库所包含的表数据 + bk_cloud_id = cluster_handler.cluster.bk_cloud_id + rpc_results = DRSApi.rpc({"bk_cloud_id": bk_cloud_id, "addresses": [address], "cmds": [query_table_sql]}) + table_data = rpc_results[0]["cmd_results"][0]["table_data"] + aggregate_table_data: Dict[str, List[str]] = {db: [] for db in info["dbs"]} + for data in table_data: + aggregate_table_data[data["table_schema"]].append(data["table_name"]) + + cluster_table_infos.append({"cluster_id": cluster_handler.cluster_id, "table_data": aggregate_table_data}) + + return cluster_table_infos + + def check_cluster_database(self, check_infos: List[Dict[str, Any]]) -> List[Dict[str, Dict]]: """ 批量校验集群下的DB是否存在 + @param check_infos: 校验库表的信息 + """ + + cluster_id__check_info = {info["cluster_id"]: info for info in check_infos} + cluster_database_infos = self.show_databases(cluster_ids=list(cluster_id__check_info.keys())) + + for db_info in cluster_database_infos: + check_info = { + db_name: (db_name in db_info["databases"]) + for db_name in cluster_id__check_info[db_info["cluster_id"]]["db_names"] + } + cluster_id__check_info[db_info["cluster_id"]].update(check_info=check_info) + + return list(cluster_id__check_info.values()) + + def check_flashback_database(self, flashback_infos: List[Dict[str, Any]]): + """ + 批量校验闪回库表是否存在 + @param flashback_infos: 闪回信息 [ { - "address": "127.0.0.1", - "cmd_results": [ - { - "cmd": "select schema_name as db_name from information_schema.schemata - where schema_name in ('test','sys','bk-dbm-test')", - "table_data": [{"db_name": "sys"},{"db_name": "test"}], - "rows_affected": 0, - "error_msg": "" - } - ], - "error_msg": "" + "cluster_id": 17, + "databases": "db%", + "databases_ignore": "tb%", + "tables": "db1", + "tables_ignore": "tb1", } ] """ - master_inst__cluster_map: Dict[str, int] = {} - for cluster_id in cluster_ids: - cluster_handler = ClusterHandler.get_exact_handler(bk_biz_id=self.bk_biz_id, cluster_id=cluster_id) - master_inst__cluster_map[cluster_handler.get_exec_inst().ip_port] = cluster_id + def _format_db_tb_name(_data_names): + if "*" in _data_names: + # 如果带*,直接返回空,后续认为永真 + return [] + for index in range(len(_data_names)): + # mysql模糊匹配单个字符,用_,原本字符串里带的_,要\_转义 + _data_names[index] = _data_names[index].replace("_", "\_").replace("?", "_") + return _data_names - raw_db_names = [f"'{db_name}'" for db_name in db_names] - rpc_results = DRSApi.rpc( - { - "addresses": list(master_inst__cluster_map.keys()), - "cmds": [ - f"select schema_name as db_name " - f"from information_schema.schemata where schema_name in ({','.join(raw_db_names)});" - ], + def _get_db_tb_sts(_data_names, key, default): + _data_names = _format_db_tb_name(_data_names) + _sts = "(" + " or ".join([f"{key} like '{name}'" for name in _data_names]) + ")" + _sts = f"({default})" if _sts == "()" else _sts + return _sts + + def _get_db_table_list(_bk_cloud_id, _address, _cmds, key): + _rpc_results = DRSApi.rpc({"bk_cloud_id": _bk_cloud_id, "addresses": [_address], "cmds": _cmds}) + _cmd__datalist = { + _result["cmd"]: [data[key] for data in _result["table_data"]] + for _result in _rpc_results[0]["cmd_results"] } - ) + return _cmd__datalist + + sys_db_list = "(" + ",".join([f"'{db}'" for db in SYSTEM_DBS]) + ")" + for info in flashback_infos: + cluster_handler, address = self._get_cluster_address( + cluster_id__role_map={}, cluster_id=info["cluster_id"] + ) - # 获取每个集群包含的校验DB列表 - cluster_check_info: Dict[int, List[str]] = {} - for result in rpc_results: - cluster_check_info[master_inst__cluster_map[result["address"]]] = [ - db["db_name"] for db in result["cmd_results"][0]["table_data"] - ] + # 构造查询库的sql语句 + db_sts, ignore_sts = _get_db_tb_sts(info["databases"], "SCHEMA_NAME", 1), _get_db_tb_sts( + info["databases_ignore"], "SCHEMA_NAME", 0 + ) + query_schema_dbs_sql = QUERY_SCHEMA_DBS_SQL.format(db_sts=db_sts, sys_db_list=sys_db_list) + query_ignore_schema_dbs_sql = QUERY_SCHEMA_DBS_SQL.format(db_sts=ignore_sts, sys_db_list=sys_db_list) + + # 查询库,得到闪回的操作库 + query_dbs_list = _get_db_table_list( + _bk_cloud_id=cluster_handler.cluster.bk_cloud_id, + _address=address, + _cmds=[query_schema_dbs_sql, query_ignore_schema_dbs_sql], + key="SCHEMA_NAME", + ) + databases = set(query_dbs_list[query_schema_dbs_sql]) - set(query_dbs_list[query_ignore_schema_dbs_sql]) + if not databases: + info.update(message=_("不存在可用于闪回的库")) + continue + + # 构造查询表的sql语句 + db_list = "(" + ",".join([f"'{db}'" for db in databases]) + ")" + table_sts, ignore_table_sts = _get_db_tb_sts(info["tables"], "TABLE_NAME", 1), _get_db_tb_sts( + info["tables_ignore"], "TABLE_NAME", 0 + ) + query_schema_tables_sql = QUERY_SCHEMA_TABLES_SQL.format(db_list=db_list, table_sts=table_sts) + query_ignore_schema_tables_sql = QUERY_SCHEMA_TABLES_SQL.format( + db_list=db_list, table_sts=ignore_table_sts + ) + + # 查询表,得到闪回的操作库 + query_tbs_list = _get_db_table_list( + _bk_cloud_id=cluster_handler.cluster.bk_cloud_id, + _address=address, + _cmds=[query_schema_tables_sql, query_ignore_schema_tables_sql], + key="TABLE_NAME", + ) + databases = set(query_tbs_list[query_schema_tables_sql]) - set( + query_tbs_list[query_ignore_schema_tables_sql] + ) + if not databases: + info.update(message=_("不存在可用于闪回的表")) + continue - # 校验DB是否在集群中出现。 - # 这里的判断逻辑是只要DB在校验集群的其中一个出现,就判定为合法 - db_exist_list = set(chain(*list(cluster_check_info.values()))) - db_check_info = {db_name: (db_name in db_exist_list and db_name not in SYSTEM_DBS) for db_name in db_names} + info.update(message="") - return {"cluster_check_info": cluster_check_info, "db_check_info": db_check_info} + return flashback_infos diff --git a/dbm-ui/backend/db_services/mysql/remote_service/mock_data.py b/dbm-ui/backend/db_services/mysql/remote_service/mock_data.py index c67b829e58..48264d0343 100644 --- a/dbm-ui/backend/db_services/mysql/remote_service/mock_data.py +++ b/dbm-ui/backend/db_services/mysql/remote_service/mock_data.py @@ -16,6 +16,30 @@ {"cluster_id": 2, "databases": ["db2", "db3"]}, ] -CHECK_CLUSTER_DATABASE_REQUEST_DATA = {"cluster_ids": [1], "db_name": ["test1", "test2"]} +SHOW_TABLES_RESPONSE_DATA = [{"cluster_id": 1, "table_data": {"db1": [], "db2": [], "db3": ["test1"]}}] -CHECK_CLUSTER_DATABASE_RESPONSE_DATA = True +CHECK_CLUSTER_DATABASE_REQUEST_DATA = {"infos": [{"cluster_id": 1, "db_names": ["test1", "test2"]}]} + +CHECK_CLUSTER_DATABASE_RESPONSE_DATA = [ + {"cluster_id": 63, "db_names": ["db1", "db2"], "check_info": {"db1": False, "db2": True}} +] + +FLASHBACK_CHECK_DATA = [ + { + "cluster_id": 63, + "databases": ["kkjj"], + "databases_ignore": [], + "tables": [], + "tables_ignore": [], + "message": "this is a error message", + }, + { + "cluster_id": 63, + "databases": [], + "databases_ignore": [], + "tables": ["iijkk"], + "tables_ignore": [], + "message": "this is a error message", + }, + {"cluster_id": 63, "databases": [], "databases_ignore": [], "tables": [], "tables_ignore": [], "message": ""}, +] diff --git a/dbm-ui/backend/db_services/mysql/remote_service/serializers.py b/dbm-ui/backend/db_services/mysql/remote_service/serializers.py index b73f65239c..b1a0eae218 100644 --- a/dbm-ui/backend/db_services/mysql/remote_service/serializers.py +++ b/dbm-ui/backend/db_services/mysql/remote_service/serializers.py @@ -15,26 +15,56 @@ from backend.db_services.mysql.remote_service.mock_data import ( CHECK_CLUSTER_DATABASE_REQUEST_DATA, CHECK_CLUSTER_DATABASE_RESPONSE_DATA, + FLASHBACK_CHECK_DATA, SHOW_DATABASES_REQUEST_DATA, SHOW_DATABASES_RESPONSE_DATA, + SHOW_TABLES_RESPONSE_DATA, ) +from backend.flow.consts import TenDBBackUpLocation +from backend.ticket.builders.mysql.base import DBTableField class ShowDatabasesRequestSerializer(serializers.Serializer): - cluster_ids = serializers.ListField(help_text=_("集群ID列表"), child=serializers.IntegerField()) + class QueryClusterRoleDatabaseSerializer(serializers.Serializer): + cluster_id = serializers.IntegerField(help_text=_("集群ID列表")) + role = serializers.ChoiceField( + help_text=_("查询的备份角色"), choices=TenDBBackUpLocation.get_choices(), required=False + ) + + # 支持两种模式查询 集群ID列表/集群角色信息列表 + cluster_ids = serializers.ListField(help_text=_("集群ID列表"), child=serializers.IntegerField(), required=False) + cluster_infos = serializers.ListSerializer( + help_text=_("集群信息列表"), child=QueryClusterRoleDatabaseSerializer(), required=False + ) class Meta: swagger_schema_fields = {"example": SHOW_DATABASES_REQUEST_DATA} +class ShowTablesRequestSerializer(serializers.Serializer): + class DbInfoSerializer(serializers.Serializer): + cluster_id = serializers.IntegerField(help_text=_("集群ID列表")) + dbs = serializers.ListField(help_text=_("查询的DB列表"), child=DBTableField(db_field=True)) + + cluster_db_infos = serializers.ListSerializer(help_text=_("集群数据库信息"), child=DbInfoSerializer()) + + +class ShowTablesResponseSerializer(serializers.Serializer): + class Meta: + swagger_schema_fields = {"example": SHOW_TABLES_RESPONSE_DATA} + + class ShowDatabasesResponseSerializer(serializers.Serializer): class Meta: swagger_schema_fields = {"example": SHOW_DATABASES_RESPONSE_DATA} class CheckClusterDatabaseSerializer(serializers.Serializer): - cluster_ids = serializers.ListField(help_text=_("集群ID列表"), child=serializers.IntegerField(help_text=_("集群ID"))) - db_names = serializers.ListField(help_text=_("DB名列表"), child=serializers.CharField(help_text=_("DB名"))) + class CheckInfoSerializer(serializers.Serializer): + cluster_id = serializers.IntegerField(help_text=_("集群ID")) + db_names = serializers.ListField(help_text=_("DB名列表"), child=serializers.CharField(help_text=_("DB名"))) + + infos = serializers.ListSerializer(help_text=_("集群校验信息"), child=CheckInfoSerializer()) class Meta: swagger_schema_fields = {"example": CHECK_CLUSTER_DATABASE_REQUEST_DATA} @@ -43,3 +73,19 @@ class Meta: class CheckClusterDatabaseResponseSerializer(serializers.Serializer): class Meta: swagger_schema_fields = {"example": CHECK_CLUSTER_DATABASE_RESPONSE_DATA} + + +class CheckFlashbackInfoSerializer(serializers.Serializer): + class FlashbackSerializer(serializers.Serializer): + cluster_id = serializers.IntegerField(help_text=_("集群ID")) + databases = serializers.ListField(help_text=_("目标库列表"), child=DBTableField(db_field=True)) + databases_ignore = serializers.ListField(help_text=_("忽略库列表"), child=DBTableField(db_field=True)) + tables = serializers.ListField(help_text=_("目标table列表"), child=DBTableField()) + tables_ignore = serializers.ListField(help_text=_("忽略table列表"), child=DBTableField()) + + infos = serializers.ListSerializer(help_text=_("flashback信息"), child=FlashbackSerializer(), allow_empty=False) + + +class CheckFlashbackInfoResponseSerializer(serializers.Serializer): + class Meta: + swagger_schema_fields = {"example": FLASHBACK_CHECK_DATA} diff --git a/dbm-ui/backend/db_services/mysql/remote_service/views.py b/dbm-ui/backend/db_services/mysql/remote_service/views.py index 5e83264bcf..db51bf726c 100644 --- a/dbm-ui/backend/db_services/mysql/remote_service/views.py +++ b/dbm-ui/backend/db_services/mysql/remote_service/views.py @@ -19,8 +19,12 @@ from backend.db_services.mysql.remote_service.serializers import ( CheckClusterDatabaseResponseSerializer, CheckClusterDatabaseSerializer, + CheckFlashbackInfoResponseSerializer, + CheckFlashbackInfoSerializer, ShowDatabasesRequestSerializer, ShowDatabasesResponseSerializer, + ShowTablesRequestSerializer, + ShowTablesResponseSerializer, ) from backend.iam_app.handlers.drf_perm import DBManageIAMPermission @@ -31,6 +35,16 @@ class RemoteServiceViewSet(viewsets.SystemViewSet): def _get_custom_permissions(self): return [DBManageIAMPermission()] + def _get_cluster_id_and_role(self, validated_data): + cluster_id__role_map = {} + cluster_ids = validated_data.get("cluster_ids") + + if validated_data.get("cluster_infos"): + cluster_ids = [info["cluster_id"] for info in validated_data["cluster_infos"]] + cluster_id__role_map = {info["cluster_id"]: info["role"] for info in validated_data["cluster_infos"]} + + return cluster_ids, cluster_id__role_map + @common_swagger_auto_schema( operation_summary=_("查询集群数据库列表"), request_body=ShowDatabasesRequestSerializer(), @@ -40,8 +54,24 @@ def _get_custom_permissions(self): @action(methods=["POST"], detail=False, serializer_class=ShowDatabasesRequestSerializer) def show_cluster_databases(self, request, bk_biz_id): validated_data = self.params_validate(self.get_serializer_class()) + cluster_ids, cluster_id__role_map = self._get_cluster_id_and_role(validated_data) return Response( - RemoteServiceHandler(bk_biz_id=bk_biz_id).show_databases(cluster_ids=validated_data["cluster_ids"]) + RemoteServiceHandler(bk_biz_id=bk_biz_id).show_databases( + cluster_ids=cluster_ids, cluster_id__role_map=cluster_id__role_map + ), + ) + + @common_swagger_auto_schema( + operation_summary=_("查询集群数据表列表"), + request_body=ShowTablesRequestSerializer(), + tags=[SWAGGER_TAG], + responses={status.HTTP_200_OK: ShowTablesResponseSerializer()}, + ) + @action(methods=["POST"], detail=False, serializer_class=ShowTablesRequestSerializer) + def show_cluster_tables(self, request, bk_biz_id): + validated_data = self.params_validate(self.get_serializer_class()) + return Response( + RemoteServiceHandler(bk_biz_id=bk_biz_id).show_tables(cluster_db_infos=validated_data["cluster_db_infos"]) ) @common_swagger_auto_schema( @@ -54,7 +84,16 @@ def show_cluster_databases(self, request, bk_biz_id): def check_cluster_database(self, request, bk_biz_id): validated_data = self.params_validate(self.get_serializer_class()) return Response( - RemoteServiceHandler(bk_biz_id=bk_biz_id).check_cluster_database( - cluster_ids=validated_data["cluster_ids"], db_names=validated_data["db_names"] - ) + RemoteServiceHandler(bk_biz_id=bk_biz_id).check_cluster_database(check_infos=validated_data["infos"]) ) + + @common_swagger_auto_schema( + operation_summary=_("校验flashback信息是否合法"), + request_body=CheckFlashbackInfoSerializer(), + responses={status.HTTP_200_OK: CheckFlashbackInfoResponseSerializer()}, + tags=[SWAGGER_TAG], + ) + @action(methods=["POST"], detail=False, serializer_class=CheckFlashbackInfoSerializer) + def check_flashback_database(self, request, bk_biz_id): + validated_data = self.params_validate(self.get_serializer_class()) + return Response(RemoteServiceHandler(bk_biz_id=bk_biz_id).check_flashback_database(validated_data["infos"])) diff --git a/dbm-ui/backend/db_services/mysql/resources/spider/query.py b/dbm-ui/backend/db_services/mysql/resources/spider/query.py deleted file mode 100644 index d600320081..0000000000 --- a/dbm-ui/backend/db_services/mysql/resources/spider/query.py +++ /dev/null @@ -1,147 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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 typing import Any, Dict - -from django.db.models import F, Q, QuerySet, Value -from django.utils.translation import ugettext_lazy as _ - -from backend.db_meta.api.cluster.tendbcluster.detail import scan_cluster -from backend.db_meta.enums import InstanceInnerRole, TenDBClusterSpiderRole -from backend.db_meta.enums.cluster_type import ClusterType -from backend.db_meta.models import AppCache -from backend.db_meta.models.cluster import Cluster -from backend.db_meta.models.instance import ProxyInstance, StorageInstance -from backend.db_services.dbbase.resources import query -from backend.db_services.ipchooser.query.resource import ResourceQueryHelper -from backend.db_services.mysql.resources.tendbha.query import ListRetrieveResource as DBHAListRetrieveResource -from backend.ticket.models import ClusterOperateRecord -from backend.utils.time import datetime2str - - -class ListRetrieveResource(DBHAListRetrieveResource): - """查看 mysql dbha 架构的资源""" - - cluster_type = ClusterType.TenDBCluster - - fields = [ - {"name": _("集群名"), "key": "cluster_name"}, - {"name": _("主域名"), "key": "master_domain"}, - {"name": _("从域名"), "key": "slave_domain"}, - {"name": "Spider Master", "key": "spider_master"}, - {"name": "Spider Slave", "key": "spider_slave"}, - {"name": "Remote DB", "key": "remote_db"}, - {"name": "Remote DR", "key": "remote_dr"}, - {"name": _("运维节点"), "key": "spider_mnt"}, - {"name": _("所属db模块"), "key": "db_module_name"}, - {"name": _("创建人"), "key": "creator"}, - {"name": _("创建时间"), "key": "create_at"}, - ] - - @classmethod - def list_clusters(cls, bk_biz_id: int, query_params: Dict, limit: int, offset: int) -> query.ResourceList: - return super().list_clusters(bk_biz_id, query_params, limit, offset) - - @classmethod - def _list( - cls, - cluster_qset: QuerySet, - proxy_inst_qset: QuerySet, - storage_inst_qset: QuerySet, - db_module_names: Dict[int, str], - limit: int, - offset: int, - ) -> query.ResourceList: - return super()._list(cluster_qset, proxy_inst_qset, storage_inst_qset, db_module_names, limit, offset) - - @staticmethod - def _to_cluster_representation( - cluster: Cluster, db_module_names: Dict[int, str], cluster_entry_map: Dict[int, Dict[str, str]] - ) -> Dict[str, Any]: - """将集群对象转为可序列化的 dict 结构""" - spider = { - role: [inst.simple_desc for inst in cluster.proxies if inst.tendbclusterspiderext.spider_role == role] - for role in TenDBClusterSpiderRole.get_values() - } - remote_db = [m.simple_desc for m in cluster.storages if m.instance_inner_role == InstanceInnerRole.MASTER] - remote_dr = [m.simple_desc for m in cluster.storages if m.instance_inner_role == InstanceInnerRole.SLAVE] - - cluster_entry = cluster_entry_map.get(cluster.id, {}) - cloud_info = ResourceQueryHelper.search_cc_cloud(get_cache=True) - - return { - "id": cluster.id, - "phase": cluster.phase, - "status": cluster.status, - "operations": ClusterOperateRecord.objects.get_cluster_operations(cluster.id), - "cluster_name": cluster.name, - "cluster_type": cluster.cluster_type, - "major_version": cluster.major_version, - "region": cluster.region, - "master_domain": cluster_entry.get("master_domain", ""), - "slave_domain": cluster_entry.get("slave_domain", ""), - "bk_biz_id": cluster.bk_biz_id, - "bk_biz_name": AppCache.objects.get(bk_biz_id=cluster.bk_biz_id).bk_biz_name, - "bk_cloud_id": cluster.bk_cloud_id, - "bk_cloud_name": cloud_info[str(cluster.bk_cloud_id)]["bk_cloud_name"], - "spider_master": spider[TenDBClusterSpiderRole.SPIDER_MASTER], - "spider_slave": spider[TenDBClusterSpiderRole.SPIDER_SLAVE], - "spider_mnt": spider[TenDBClusterSpiderRole.SPIDER_MNT], - "remote_db": remote_db, - "remote_dr": remote_dr, - "db_module_name": db_module_names.get(cluster.db_module_id, ""), - "creator": cluster.creator, - "create_at": datetime2str(cluster.create_at), - } - - @staticmethod - def _filter_instance_qs(query_conditions): - fields = [ - "id", - "cluster__id", - "cluster__major_version", - "cluster__cluster_type", - "cluster__db_module_id", - "cluster__name", - "role", - "machine__ip", - "machine__bk_cloud_id", - "inst_port", - "status", - "create_at", - "machine__bk_host_id", - ] - # 获取remote实例的查询集 - remote_insts = StorageInstance.objects.annotate(role=F("instance_role"), inst_port=F("port")).filter( - query_conditions - ) - # 获取spider实例的查询集 - spider_insts = ProxyInstance.objects.annotate( - role=F("tendbclusterspiderext__spider_role"), inst_port=F("port") - ).filter(query_conditions) - # 额外获取spider master混部的中控节点的查询集 - controller_insts = ProxyInstance.objects.annotate( - role=Value("spider_controller"), inst_port=F("admin_port") - ).filter(query_conditions & Q(tendbclusterspiderext__spider_role=TenDBClusterSpiderRole.SPIDER_MASTER.value)) - - # ⚠️这里的union不要用链式union(eg: s1.union(s2).union(s3)),否则django解析sql语句会生成额外的扩号导致mysql语法错误。 - instances = remote_insts.union(spider_insts, controller_insts).values(*fields).order_by("-create_at") - return instances - - @classmethod - def list_instances(cls, bk_biz_id: int, query_params: Dict, limit: int, offset: int) -> query.ResourceList: - return super().list_instances(bk_biz_id, query_params, limit, offset) - - @classmethod - def get_topo_graph(cls, bk_biz_id: int, cluster_id: int) -> dict: - cluster = Cluster.objects.get(bk_biz_id=bk_biz_id, id=cluster_id) - graph = scan_cluster(cluster).to_dict() - return graph diff --git a/dbm-ui/backend/db_services/mysql/resources/spider/views.py b/dbm-ui/backend/db_services/mysql/resources/spider/views.py deleted file mode 100644 index bd3dcc9c5d..0000000000 --- a/dbm-ui/backend/db_services/mysql/resources/spider/views.py +++ /dev/null @@ -1,61 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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.decorators import method_decorator - -from backend.bk_web.swagger import common_swagger_auto_schema -from backend.db_services.dbbase.resources import constants, serializers, viewsets -from backend.db_services.mysql.resources.spider.query import ListRetrieveResource - - -@method_decorator( - name="list", - decorator=common_swagger_auto_schema( - query_serializer=serializers.ListMySQLResourceSLZ(), - tags=[constants.RESOURCE_TAG], - ), -) -@method_decorator( - name="retrieve", - decorator=common_swagger_auto_schema( - tags=[constants.RESOURCE_TAG], - ), -) -@method_decorator( - name="list_instances", - decorator=common_swagger_auto_schema( - query_serializer=serializers.ListInstancesSerializer(), - tags=[constants.RESOURCE_TAG], - ), -) -@method_decorator( - name="retrieve_instance", - decorator=common_swagger_auto_schema( - query_serializer=serializers.RetrieveInstancesSerializer(), - tags=[constants.RESOURCE_TAG], - ), -) -@method_decorator( - name="get_table_fields", - decorator=common_swagger_auto_schema( - tags=[constants.RESOURCE_TAG], - ), -) -@method_decorator( - name="get_topo_graph", - decorator=common_swagger_auto_schema( - tags=[constants.RESOURCE_TAG], - ), -) -class SpiderViewSet(viewsets.ResourceViewSet): - """Spider 架构资源""" - - query_class = ListRetrieveResource - query_serializer_class = serializers.ListMySQLResourceSLZ diff --git a/dbm-ui/backend/db_services/mysql/resources/tendbcluster/query.py b/dbm-ui/backend/db_services/mysql/resources/tendbcluster/query.py new file mode 100644 index 0000000000..df6ba71f8a --- /dev/null +++ b/dbm-ui/backend/db_services/mysql/resources/tendbcluster/query.py @@ -0,0 +1,194 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 typing import Any, Dict, List + +from django.db.models import F, Q, QuerySet, Value +from django.forms import model_to_dict +from django.utils.translation import ugettext_lazy as _ + +from backend.db_meta.api.cluster.tendbcluster.detail import scan_cluster +from backend.db_meta.enums import InstanceInnerRole, InstanceStatus, TenDBClusterSpiderRole +from backend.db_meta.enums.cluster_type import ClusterType +from backend.db_meta.models import AppCache, Machine, Spec +from backend.db_meta.models.cluster import Cluster +from backend.db_meta.models.instance import ProxyInstance, StorageInstance +from backend.db_services.dbbase.resources import query +from backend.db_services.ipchooser.query.resource import ResourceQueryHelper +from backend.db_services.mysql.resources.tendbha.query import ListRetrieveResource as DBHAListRetrieveResource +from backend.ticket.models import ClusterOperateRecord +from backend.utils.time import datetime2str + + +class ListRetrieveResource(DBHAListRetrieveResource): + """查看 mysql dbha 架构的资源""" + + cluster_type = ClusterType.TenDBCluster + + fields = [ + {"name": _("集群名"), "key": "cluster_name"}, + {"name": _("主域名"), "key": "master_domain"}, + {"name": _("从域名"), "key": "slave_domain"}, + {"name": "Spider Master", "key": "spider_master"}, + {"name": "Spider Slave", "key": "spider_slave"}, + {"name": "Remote DB", "key": "remote_db"}, + {"name": "Remote DR", "key": "remote_dr"}, + {"name": _("运维节点"), "key": "spider_mnt"}, + {"name": _("所属db模块"), "key": "db_module_name"}, + {"name": _("创建人"), "key": "creator"}, + {"name": _("创建时间"), "key": "create_at"}, + ] + + @classmethod + def list_clusters(cls, bk_biz_id: int, query_params: Dict, limit: int, offset: int) -> query.ResourceList: + return super().list_clusters(bk_biz_id, query_params, limit, offset) + + @classmethod + def _list( + cls, + cluster_qset: QuerySet, + proxy_inst_qset: QuerySet, + storage_inst_qset: QuerySet, + db_module_names: Dict[int, str], + limit: int, + offset: int, + ) -> query.ResourceList: + return super()._list(cluster_qset, proxy_inst_qset, storage_inst_qset, db_module_names, limit, offset) + + @staticmethod + def _to_cluster_representation( + cluster: Cluster, db_module_names: Dict[int, str], cluster_entry_map: Dict[int, Dict[str, str]] + ) -> Dict[str, Any]: + def get_remote_infos(insts: List[StorageInstance]): + """获取remote信息,并补充分片信息""" + remote_infos = {InstanceInnerRole.MASTER.value: [], InstanceInnerRole.SLAVE.value: []} + for inst in insts: + try: + related = "as_ejector" if inst.instance_inner_role == InstanceInnerRole.MASTER else "as_receiver" + shard_id = getattr(inst, related).first().tendbclusterstorageset.shard_id + except Exception: + # 如果无法找到shard_id,则默认为-1。有可能实例处于restoring状态(比如集群容量变更时) + shard_id = -1 + + remote_infos[inst.instance_inner_role].append({**inst.simple_desc, "shard_id": shard_id}) + + remote_infos[InstanceInnerRole.MASTER.value].sort(key=lambda x: x.get("shard_id", -1)) + remote_infos[InstanceInnerRole.SLAVE.value].sort(key=lambda x: x.get("shard_id", -1)) + return remote_infos[InstanceInnerRole.MASTER.value], remote_infos[InstanceInnerRole.SLAVE.value] + + """将集群对象转为可序列化的 dict 结构""" + spider = { + role: [inst.simple_desc for inst in cluster.proxies if inst.tendbclusterspiderext.spider_role == role] + for role in TenDBClusterSpiderRole.get_values() + } + remote_db, remote_dr = get_remote_infos(cluster.storages) + + machine_list = list(set([inst["bk_host_id"] for inst in [*remote_db, *remote_dr]])) + machine_pair_cnt = len(machine_list) / 2 + + spec_id = Machine.objects.get(bk_host_id=machine_list[0]).spec_id + cluster_spec = Spec.objects.get(spec_id=spec_id) + cluster_entry = cluster_entry_map.get(cluster.id, {}) + cloud_info = ResourceQueryHelper.search_cc_cloud(get_cache=True) + + return { + "id": cluster.id, + "phase": cluster.phase, + "status": cluster.status, + "operations": ClusterOperateRecord.objects.get_cluster_operations(cluster.id), + "cluster_name": cluster.name, + "cluster_type": cluster.cluster_type, + "cluster_spec": model_to_dict(cluster_spec), + "cluster_capacity": cluster_spec.capacity * machine_pair_cnt, + "major_version": cluster.major_version, + "region": cluster.region, + "master_domain": cluster_entry.get("master_domain", ""), + "slave_domain": cluster_entry.get("slave_domain", ""), + "bk_biz_id": cluster.bk_biz_id, + "bk_biz_name": AppCache.objects.get(bk_biz_id=cluster.bk_biz_id).bk_biz_name, + "bk_cloud_id": cluster.bk_cloud_id, + "bk_cloud_name": cloud_info[str(cluster.bk_cloud_id)]["bk_cloud_name"], + "spider_master": spider[TenDBClusterSpiderRole.SPIDER_MASTER], + "spider_slave": spider[TenDBClusterSpiderRole.SPIDER_SLAVE], + "spider_mnt": spider[TenDBClusterSpiderRole.SPIDER_MNT], + # TODO: 待补充当前集群使用容量,需要监控采集的支持 + "cluster_shard_num": len(remote_db), + "remote_shard_num": len(remote_db) / machine_pair_cnt, + "machine_pair_cnt": machine_pair_cnt, + "remote_db": remote_db, + "remote_dr": remote_dr, + "db_module_id": cluster.db_module_id, + "db_module_name": db_module_names.get(cluster.db_module_id, ""), + "creator": cluster.creator, + "create_at": datetime2str(cluster.create_at), + } + + @staticmethod + def _filter_instance_qs(query_conditions, query_params): + fields = [ + "id", + "cluster__id", + "cluster__major_version", + "cluster__cluster_type", + "cluster__db_module_id", + "cluster__name", + "role", + "machine__ip", + "machine__bk_cloud_id", + "inst_port", + "status", + "create_at", + "machine__bk_host_id", + "machine__spec_config", + ] + + # 获取remote实例的查询集 + remote_insts = StorageInstance.objects.annotate(role=F("instance_role"), inst_port=F("port")).filter( + query_conditions + ) + # 获取spider实例的查询集 + spider_insts = ProxyInstance.objects.annotate( + role=F("tendbclusterspiderext__spider_role"), inst_port=F("port") + ).filter(query_conditions) + + # 涉及spider_controller的查询 + if query_params.get("spider_ctl"): + # 额外获取spider master混部的中控节点的查询集 这里有一点: 通过value和annotate来将admin_port覆写port,达到过滤效果 + controller_insts = ( + ProxyInstance.objects.values("admin_port") + .annotate(port=F("admin_port")) + .values(role=Value("spider_ctl"), inst_port=F("admin_port")) + .filter( + query_conditions & Q(tendbclusterspiderext__spider_role=TenDBClusterSpiderRole.SPIDER_MASTER.value) + ) + ) + # ⚠️这里的union不要用链式union(eg: s1.union(s2).union(s3)),否则django解析sql语句会生成额外的扩号导致mysql语法错误。 + instances = remote_insts.union(spider_insts, controller_insts).values(*fields).order_by("-create_at") + else: + instances = remote_insts.union(spider_insts).values(*fields).order_by("-create_at") + + return instances + + @classmethod + def list_instances(cls, bk_biz_id: int, query_params: Dict, limit: int, offset: int) -> query.ResourceList: + return super().list_instances(bk_biz_id, query_params, limit, offset) + + @classmethod + def retrieve_instance(cls, bk_biz_id: int, cluster_id: int, ip: str, port: int) -> dict: + instances = cls.list_instances(bk_biz_id, {"ip": ip, "port": port, "spider_ctl": True}, limit=1, offset=0) + instance = instances.data[0] + return cls._fill_instance_info(instance, cluster_id) + + @classmethod + def get_topo_graph(cls, bk_biz_id: int, cluster_id: int) -> dict: + cluster = Cluster.objects.get(bk_biz_id=bk_biz_id, id=cluster_id) + graph = scan_cluster(cluster).to_dict() + return graph diff --git a/dbm-ui/backend/db_services/mysql/resources/tendbcluster/views.py b/dbm-ui/backend/db_services/mysql/resources/tendbcluster/views.py new file mode 100644 index 0000000000..ac3b89c908 --- /dev/null +++ b/dbm-ui/backend/db_services/mysql/resources/tendbcluster/views.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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.decorators import method_decorator +from rest_framework import status + +from backend.bk_web.swagger import common_swagger_auto_schema +from backend.db_services.dbbase.resources import constants, serializers, viewsets +from backend.db_services.mysql.resources.tendbcluster import yasg_slz +from backend.db_services.mysql.resources.tendbcluster.query import ListRetrieveResource + + +@method_decorator( + name="list", + decorator=common_swagger_auto_schema( + query_serializer=serializers.ListMySQLResourceSLZ(), + responses={status.HTTP_200_OK: yasg_slz.PaginatedResourceSLZ()}, + tags=[constants.RESOURCE_TAG], + ), +) +@method_decorator( + name="retrieve", + decorator=common_swagger_auto_schema( + responses={status.HTTP_200_OK: yasg_slz.ResourceSLZ()}, + tags=[constants.RESOURCE_TAG], + ), +) +@method_decorator( + name="list_instances", + decorator=common_swagger_auto_schema( + query_serializer=serializers.ListInstancesSerializer(), + responses={status.HTTP_200_OK: yasg_slz.PaginatedInstanceResourceSLZ()}, + tags=[constants.RESOURCE_TAG], + ), +) +@method_decorator( + name="retrieve_instance", + decorator=common_swagger_auto_schema( + query_serializer=serializers.RetrieveInstancesSerializer(), + responses={status.HTTP_200_OK: yasg_slz.ResourceInstanceSLZ()}, + tags=[constants.RESOURCE_TAG], + ), +) +@method_decorator( + name="get_table_fields", + decorator=common_swagger_auto_schema( + tags=[constants.RESOURCE_TAG], + ), +) +@method_decorator( + name="get_topo_graph", + decorator=common_swagger_auto_schema( + responses={status.HTTP_200_OK: yasg_slz.ResourceTopoGraphSLZ()}, + tags=[constants.RESOURCE_TAG], + ), +) +class SpiderViewSet(viewsets.ResourceViewSet): + """Spider 架构资源""" + + query_class = ListRetrieveResource + query_serializer_class = serializers.ListMySQLResourceSLZ diff --git a/dbm-ui/backend/db_services/mysql/resources/tendbcluster/yasg_slz.py b/dbm-ui/backend/db_services/mysql/resources/tendbcluster/yasg_slz.py new file mode 100644 index 0000000000..6d3443965c --- /dev/null +++ b/dbm-ui/backend/db_services/mysql/resources/tendbcluster/yasg_slz.py @@ -0,0 +1,284 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 rest_framework import serializers + +from .query import ListRetrieveResource + +REF_NAME = "dbha" + +paginated_resource_example = { + "count": 10, + "next": "http://xxxxx?limit=5&offset=10", + "previous": "http://xxxxx?limit=5&offset=10", + "results": [ + { + "id": 16, + "phase": "online", + "status": "normal", + "operations": [], + "cluster_name": "spidertest", + "cluster_type": "tendbcluster", + "cluster_spec": { + "creator": "admin", + "updater": "admin", + "spec_id": 3, + "spec_name": "test_msql", + "spec_cluster_type": "tendbsingle", + "spec_machine_type": "single", + "cpu": {"max": "12", "min": "12"}, + "mem": {"max": "2", "min": "1"}, + "device_class": [], + "storage_spec": [{"size": 10, "type": "HDD", "mount_point": "/data123"}], + "desc": "qqwrqr", + "instance_num": 1, + "qps": {}, + }, + "cluster_capacity": 10.0, + "major_version": "MySQL-5.7", + "region": "xxx", + "master_domain": "spider.spidertest.db", + "slave_domain": "", + "bk_biz_id": 3, + "bk_biz_name": "DBA", + "bk_cloud_id": 0, + "bk_cloud_name": "xxx", + "spider_master": [ + { + "name": "", + "ip": "127.0.0.1", + "port": 25000, + "instance": "127.0.0.1:25000", + "status": "running", + "phase": "", + "bk_instance_id": 1052, + "bk_host_id": 165, + "bk_cloud_id": 0, + "bk_biz_id": 3, + }, + {"...": "...."}, + ], + "spider_slave": [ + { + "name": "", + "ip": "127.0.0.1", + "port": 20000, + "instance": "127.0.0.1:20000", + "status": "running", + "phase": "online", + "bk_instance_id": 1051, + "bk_host_id": 107, + "bk_cloud_id": 0, + "bk_biz_id": 3, + } + ], + "spider_mnt": [ + { + "name": "", + "ip": "127.0.0.1", + "port": 20000, + "instance": "127.0.0.1:20000", + "status": "running", + "phase": "online", + "bk_instance_id": 1051, + "bk_host_id": 107, + "bk_cloud_id": 0, + "bk_biz_id": 3, + } + ], + "cluster_shard_num": 2, + "remote_shard_num": 2, + "machine_pair_cnt": 1, + "remote_db": [ + { + "name": "", + "ip": "127.0.0.1", + "port": 20001, + "instance": "127.0.0.1:20001", + "status": "running", + "phase": "online", + "bk_instance_id": 1049, + "bk_host_id": 107, + "bk_cloud_id": 0, + "bk_biz_id": 3, + }, + {"...": "...."}, + ], + "remote_dr": [ + { + "name": "", + "ip": "127.0.0.1", + "port": 20001, + "instance": "127.0.0.1:20001", + "status": "running", + "phase": "online", + "bk_instance_id": 1048, + "bk_host_id": 108, + "bk_cloud_id": 0, + "bk_biz_id": 3, + }, + {"...": "...."}, + ], + "db_module_id": 554, + "db_module_name": "", + "creator": "admin", + "create_at": "2023-07-03 19:25:23", + } + ], +} + +paginated_instance_resource_example = { + "count": 18, + "next": "http://127.0.0.1:8001/apis/mysql/bizs/3/spider_resources/list_instances/?limit=10&offset=10", + "previous": "", + "results": [ + { + "id": 9, + "cluster_id": 16, + "cluster_type": "tendbcluster", + "cluster_name": "spidertest", + "version": "MySQL-5.7", + "db_module_id": 554, + "bk_cloud_id": 0, + "bk_cloud_name": "xxx", + "ip": "127.0.0.1", + "port": 26000, + "instance_address": "127.0.0.1:26000", + "bk_host_id": 165, + "role": "spider_ctl", + "master_domain": "spider.spidertest.db", + "slave_domain": "", + "status": "running", + "create_at": "2023-07-03 19:25:23", + } + ], +} + +resource_topo_graph_example = { + "node_id": "spider.spidertest.abc.db", + "nodes": [ + { + "node_id": "127.0.0.1:25000", + "node_type": "spider_master", + "url": "/database/3/tendbcluster-instance/28/127.0.0.1:25000/details", + }, + { + "node_id": "127.0.0.1:25000", + "node_type": "spider_master", + "url": "/database/3/tendbcluster-instance/28/127.0.0.1:25000/details", + }, + {"node_id": "spider.spidertest.abc.db", "node_type": "entry_dns", "url": ""}, + { + "node_id": "127.0.0.1:20001", + "node_type": "remote::remote_master", + "url": "/database/3/tendbcluster-instance/28/127.0.0.1:20001/details", + }, + { + "node_id": "127.0.0.1:20000", + "node_type": "remote::remote_master", + "url": "/database/3/tendbcluster-instance/28/127.0.0.1:20000/details", + }, + { + "node_id": "127.0.0.1:20001", + "node_type": "remote::remote_slave", + "url": "/database/3/tendbcluster-instance/28/127.0.0.1:20001/details", + }, + { + "node_id": "127.0.0.1:20000", + "node_type": "remote::remote_slave", + "url": "/database/3/tendbcluster-instance/28/127.0.0.1:20000/details", + }, + { + "node_id": "127.0.0.1:26000", + "node_type": "controller_node", + "url": "/database/3/tendbcluster-instance/28/127.0.0.1:25000/details", + }, + { + "node_id": "127.0.0.1:26000", + "node_type": "controller_node", + "url": "/database/3/tendbcluster-instance/28/127.0.0.1:25000/details", + }, + ], + "groups": [ + { + "node_id": "spider_master", + "group_name": "Spider Master", + "children_id": ["127.0.0.1:25000", "127.0.0.1:25000"], + }, + {"node_id": "spider_master_entry_bind", "group_name": "xxx", "children_id": ["spider.spidertest.abc.db"]}, + { + "node_id": "remote::remote_master", + "group_name": "RemoteDB", + "children_id": ["127.0.0.1:20001", "127.0.0.1:20000"], + }, + { + "node_id": "remote::remote_slave", + "group_name": "RemoteDR", + "children_id": ["127.0.0.1:20001", "127.0.0.1:20000"], + }, + {"node_id": "controller_group", "group_name": "xxx", "children_id": ["127.0.0.1:26000", "127.0.0.1:26000"]}, + ], + "lines": [ + { + "source": "spider_master_entry_bind", + "source_type": "group", + "target": "spider_master", + "target_type": "group", + "label": "access", + "label_name": "xxx", + }, + { + "source": "spider_master", + "source_type": "group", + "target": "remote::remote_master", + "target_type": "group", + "label": "access", + "label_name": "xxx", + }, + ], + "foreign_relations": {"rep_to": [], "rep_from": [], "access_to": [], "access_from": []}, +} + + +class PaginatedResourceSLZ(serializers.Serializer): + class Meta: + swagger_schema_fields = {"example": paginated_resource_example} + ref_name = f"{REF_NAME}_PaginatedResourceSLZ" + + +class PaginatedInstanceResourceSLZ(serializers.Serializer): + class Meta: + swagger_schema_fields = {"example": paginated_instance_resource_example} + ref_name = f"{REF_NAME}_PaginatedInstanceResourceSLZ" + + +class ResourceFieldSLZ(serializers.Serializer): + class Meta: + swagger_schema_fields = {"example": ListRetrieveResource.get_fields()} + ref_name = f"{REF_NAME}_ResourceFieldSLZ" + + +class ResourceSLZ(serializers.Serializer): + class Meta: + swagger_schema_fields = {"example": paginated_resource_example["results"][0]} + ref_name = f"{REF_NAME}_ResourceSLZ" + + +class ResourceInstanceSLZ(serializers.Serializer): + class Meta: + swagger_schema_fields = {"example": paginated_instance_resource_example["results"][0]} + ref_name = f"{REF_NAME}_ResourceInstanceSLZ" + + +class ResourceTopoGraphSLZ(serializers.Serializer): + class Meta: + swagger_schema_fields = {"example": resource_topo_graph_example} + ref_name = f"{REF_NAME}_ResourceTopoGraphSLZ" diff --git a/dbm-ui/backend/db_services/mysql/resources/tendbha/query.py b/dbm-ui/backend/db_services/mysql/resources/tendbha/query.py index 547d79ac06..ebb1b266d3 100644 --- a/dbm-ui/backend/db_services/mysql/resources/tendbha/query.py +++ b/dbm-ui/backend/db_services/mysql/resources/tendbha/query.py @@ -16,16 +16,16 @@ from backend.constants import IP_PORT_DIVIDER from backend.db_meta.api.cluster.tendbha.detail import scan_cluster -from backend.db_meta.enums import InstanceInnerRole +from backend.db_meta.enums import ClusterStatus, InstanceInnerRole from backend.db_meta.enums.cluster_type import ClusterType from backend.db_meta.models import AppCache from backend.db_meta.models.cluster import Cluster from backend.db_meta.models.cluster_entry import ClusterEntry from backend.db_meta.models.db_module import DBModule from backend.db_meta.models.instance import ProxyInstance, StorageInstance +from backend.db_services.dbbase.instances.handlers import InstanceHandler from backend.db_services.dbbase.resources import query from backend.db_services.ipchooser.query.resource import ResourceQueryHelper -from backend.db_services.mysql.instance.handlers import InstanceHandler from backend.ticket.models import ClusterOperateRecord from backend.utils.time import datetime2str @@ -102,7 +102,7 @@ def list_clusters(cls, bk_biz_id: int, query_params: Dict, limit: int, offset: i return cluster_infos @staticmethod - def _filter_instance_qs(query_conditions): + def _filter_instance_qs(query_conditions, query_params): """获取过滤的queryset""" instances_qs = ( # 此处的实例视图需要同时得到 storage instance 和 proxy instance @@ -123,6 +123,7 @@ def _filter_instance_qs(query_conditions): "status", "create_at", "machine__bk_host_id", + "machine__spec_config", ) .order_by("-create_at") ) @@ -131,6 +132,7 @@ def _filter_instance_qs(query_conditions): @classmethod def list_instances(cls, bk_biz_id: int, query_params: Dict, limit: int, offset: int) -> query.ResourceList: query_conditions = Q(bk_biz_id=bk_biz_id, cluster_type=cls.cluster_type) + if query_params.get("ip"): filter_ip = query_params.get("ip").split(",") query_conditions &= Q(machine__ip__in=filter_ip) @@ -152,7 +154,7 @@ def list_instances(cls, bk_biz_id: int, query_params: Dict, limit: int, offset: slave_domain_query = Q(bind_entry__entry__icontains=query_params["domain"]) query_conditions &= master_domain_query | slave_domain_query - instances_qs = cls._filter_instance_qs(query_conditions) + instances_qs = cls._filter_instance_qs(query_conditions, query_params) instances = instances_qs[offset : limit + offset] cluster_ids = [instance["cluster__id"] for instance in instances] cluster_entry_map = ClusterEntry.get_cluster_entry_map_by_cluster_ids(cluster_ids) @@ -302,4 +304,5 @@ def _to_instance_representation(cls, instance: dict, cluster_entry_map: dict) -> "slave_domain": cluster_entry_map.get(instance["cluster__id"], {}).get("slave_domain", ""), "status": instance["status"], "create_at": datetime2str(instance["create_at"]), + "spec_config": instance["machine__spec_config"], } diff --git a/dbm-ui/backend/db_services/mysql/resources/tendbha/yasg_slz.py b/dbm-ui/backend/db_services/mysql/resources/tendbha/yasg_slz.py index f9646d9d44..f56ed34a7e 100644 --- a/dbm-ui/backend/db_services/mysql/resources/tendbha/yasg_slz.py +++ b/dbm-ui/backend/db_services/mysql/resources/tendbha/yasg_slz.py @@ -106,42 +106,3 @@ class ResourceTopoGraphSLZ(serializers.Serializer): class Meta: swagger_schema_fields = {"example": resource_topo_graph_example} ref_name = f"{REF_NAME}_ResourceTopoGraphSLZ" - - -class CheckInstancesSLZ(serializers.Serializer): - instance_addresses = serializers.ListField( - help_text=_("实例地址列表"), child=serializers.CharField(help_text=_("实例地址(ip:port)"), required=True) - ) - - class Meta: - swagger_schema_fields = {"example": {"instance_addresses": ["127.0.0.1", "127.0.0.1:20000"]}} - - -class CheckInstancesResSLZ(serializers.Serializer): - class Meta: - swagger_schema_fields = { - "example": [ - { - "bk_host_id": 2000059062, - "ip": "127.0.0.1", - "bk_cloud_id": 0, - "port": 10000, - "cluster_id": 1, - "role": "proxy", - "meta": {"scope_type": "biz", "scope_id": "2005000194", "bk_biz_id": 2005000194}, - "host_id": 2000059062, - "ipv6": "", - "cloud_id": 0, - "cloud_vendor": "", - "agent_id": "", - "host_name": "", - "os_name": "1", - "alive": 1, - "cloud_area": {"id": 0, "name": "default area"}, - "biz": {"id": 2005000194, "name": _("测试业务")}, - "bk_mem": None, - "bk_disk": None, - "bk_cpu": None, - } - ] - } diff --git a/dbm-ui/backend/db_services/mysql/resources/tendbsingle/query.py b/dbm-ui/backend/db_services/mysql/resources/tendbsingle/query.py index a0cf3e5b9d..5630152101 100644 --- a/dbm-ui/backend/db_services/mysql/resources/tendbsingle/query.py +++ b/dbm-ui/backend/db_services/mysql/resources/tendbsingle/query.py @@ -17,14 +17,15 @@ from backend.constants import IP_PORT_DIVIDER from backend.db_meta.api.cluster.tendbsingle.detail import scan_cluster +from backend.db_meta.enums import ClusterStatus from backend.db_meta.enums.cluster_type import ClusterType from backend.db_meta.models import AppCache from backend.db_meta.models.cluster import Cluster from backend.db_meta.models.db_module import DBModule from backend.db_meta.models.instance import StorageInstance +from backend.db_services.dbbase.instances.handlers import InstanceHandler from backend.db_services.dbbase.resources import query from backend.db_services.ipchooser.query.resource import ResourceQueryHelper -from backend.db_services.mysql.instance.handlers import InstanceHandler from backend.ticket.models import ClusterOperateRecord from backend.utils.time import datetime2str @@ -90,6 +91,7 @@ def list_clusters(cls, bk_biz_id: int, query_params: Dict, limit: int, offset: i @classmethod def list_instances(cls, bk_biz_id: int, query_params: Dict, limit: int, offset: int) -> query.ResourceList: instance_filters = Q(bk_biz_id=bk_biz_id, cluster_type=cls.cluster_type) + if query_params.get("ip"): instance_filters &= Q(machine__ip=query_params["ip"]) @@ -206,10 +208,11 @@ def _to_instance_representation(cls, instance: StorageInstance) -> Dict[str, Any "bk_cloud_name": cloud_info[str(instance.machine.bk_cloud_id)]["bk_cloud_name"], "ip": instance.machine.ip, "port": instance.port, - "instance_address": f"{instance.machine.ip}{IP_PORT_DIVIDER}{instance.port}", + "instance_address": instance.ip_port, "bk_host_id": instance.machine.bk_host_id, "role": instance.instance_inner_role, "master_domain": instance.bind_entry.first().entry, "status": instance.status, + "spec_config": instance.machine.spec_config, "create_at": datetime2str(instance.create_at), } diff --git a/dbm-ui/backend/db_services/mysql/resources/urls.py b/dbm-ui/backend/db_services/mysql/resources/urls.py index 7772c2ea6a..902625f7c8 100644 --- a/dbm-ui/backend/db_services/mysql/resources/urls.py +++ b/dbm-ui/backend/db_services/mysql/resources/urls.py @@ -11,7 +11,7 @@ from django.urls import path from rest_framework.routers import DefaultRouter -from .spider.views import SpiderViewSet +from .tendbcluster.views import SpiderViewSet from .tendbha.views import DBHAViewSet from .tendbsingle.views import DBSingleViewSet from .views import ListResourceViewSet, ResourceTreeViewSet diff --git a/dbm-ui/backend/db_services/mysql/sql_import/constants.py b/dbm-ui/backend/db_services/mysql/sql_import/constants.py index 06f32768d6..6286f27faa 100644 --- a/dbm-ui/backend/db_services/mysql/sql_import/constants.py +++ b/dbm-ui/backend/db_services/mysql/sql_import/constants.py @@ -13,7 +13,7 @@ BKREPO_SQLFILE_PATH = "mysql/sqlfile" -CACHE_SEMANTIC_TASK_FIELD = "{user}_semantic_check_task" +CACHE_SEMANTIC_TASK_FIELD = "{user}_{cluster_type}_semantic_check_task" CACHE_SEMANTIC_AUTO_COMMIT_FIELD = "{bk_biz_id}_{root_id}_semantic_check_auto_commit" CACHE_SEMANTIC_SKIP_PAUSE_FILED = "{bk_biz_id}_{root_id}_semantic_check_skip_pause" SQL_SEMANTIC_CHECK_DATA_EXPIRE_TIME = 7 * 24 * 60 * 60 diff --git a/dbm-ui/backend/db_services/mysql/sql_import/dataclass.py b/dbm-ui/backend/db_services/mysql/sql_import/dataclass.py index 4b08d030ee..0b2c332cb1 100644 --- a/dbm-ui/backend/db_services/mysql/sql_import/dataclass.py +++ b/dbm-ui/backend/db_services/mysql/sql_import/dataclass.py @@ -23,6 +23,7 @@ class SQLMeta: sql_content: str = None sql_files: List[InMemoryUploadedFile] = None + cluster_type: str = None def to_dict(self): return asdict(self) diff --git a/dbm-ui/backend/db_services/mysql/sql_import/handlers.py b/dbm-ui/backend/db_services/mysql/sql_import/handlers.py index 8b59543bf0..1356de0c10 100644 --- a/dbm-ui/backend/db_services/mysql/sql_import/handlers.py +++ b/dbm-ui/backend/db_services/mysql/sql_import/handlers.py @@ -13,13 +13,13 @@ import tempfile import time import uuid -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List, Optional, Union from django.core.cache import cache from django.core.files.uploadedfile import InMemoryUploadedFile from backend.components.sql_import.client import SQLImportApi -from backend.configuration.constants import PLAT_BIZ_ID +from backend.configuration.constants import PLAT_BIZ_ID, DBType from backend.core.storages.storage import get_storage from backend.db_services.mysql.sql_import.constants import ( BKREPO_SQLFILE_PATH, @@ -27,13 +27,15 @@ CACHE_SEMANTIC_SKIP_PAUSE_FILED, CACHE_SEMANTIC_TASK_FIELD, SQL_SEMANTIC_CHECK_DATA_EXPIRE_TIME, + SQLImportMode, ) -from backend.db_services.mysql.sql_import.dataclass import SemanticOperateMeta, SQLExecuteMeta, SQLMeta from backend.db_services.taskflow.handlers import TaskFlowHandler from backend.flow.consts import StateType from backend.flow.engine.bamboo.engine import BambooEngine from backend.flow.engine.controller.mysql import MySQLController +from backend.flow.engine.controller.spider import SpiderController from backend.flow.models import FlowNode, FlowTree +from backend.flow.plugins.components.collections.mysql.exec_actuator_script import ExecuteDBActuatorScriptComponent from backend.flow.plugins.components.collections.mysql.semantic_check import SemanticCheckComponent from backend.utils.redis import RedisConn @@ -43,14 +45,16 @@ class SQLHandler(object): 封装sql导入相关处理操作 """ - def __init__(self, bk_biz_id: int, context: Dict = None): + def __init__(self, bk_biz_id: int, context: Dict = None, cluster_type: str = ""): """ - :param bk_biz_id: 业务ID - :param context: 上下文数据 + @param bk_biz_id: 业务ID + @param context: 上下文数据 + @param cluster_type: 集群类型 """ self.bk_biz_id = bk_biz_id self.context = context + self.cluster_type = cluster_type def _upload_sql_file( self, sql_content: str = None, sql_file_list: List[InMemoryUploadedFile] = None @@ -87,18 +91,21 @@ def _upload_sql_file( return sql_file_info_list - def grammar_check(self, sql: SQLMeta) -> Optional[Dict]: + def grammar_check(self, sql_content: str = None, sql_files: List[InMemoryUploadedFile] = None) -> Optional[Dict]: """ sql 语法检查 - :param sql: sql元数据 + @param sql_content: sql内容 + @param sql_files: sql文件 """ - sql_file_info_list = self._upload_sql_file(sql.sql_content, sql.sql_files) + sql_file_info_list = self._upload_sql_file(sql_content, sql_files) file_name_list = [os.path.split(sql_file_info["sql_path"])[1] for sql_file_info in sql_file_info_list] dir_name = os.path.split(sql_file_info_list[0]["sql_path"])[0] # 获取检查信息 - check_info = SQLImportApi.grammar_check(params={"path": dir_name, "files": file_name_list}) + check_info = SQLImportApi.grammar_check( + params={"path": dir_name, "files": file_name_list, "cluster_type": self.cluster_type} + ) # 填充sql内容。TODO:如果sql内容过大需要进行压缩吗? for sql_file_info in sql_file_info_list: @@ -110,111 +117,142 @@ def grammar_check(self, sql: SQLMeta) -> Optional[Dict]: return check_info - def semantic_check(self, sql_execute: SQLExecuteMeta) -> Dict: + def semantic_check( + self, + charset: str, + path: str, + cluster_ids: List[int], + execute_sql_files: List[str], + execute_db_infos: List[Dict[str, List]], + highrisk_warnings: Dict, + ticket_type: str, + ticket_mode: Dict, + import_mode: SQLImportMode, + backup: List[Dict], + ) -> Dict: """ sql 模拟执行(sql 语义检查) - :param sql_execute: sql执行元数据 + @param charset: 字符集 + @param path: sql文件路径 + @param cluster_ids: 集群列表 + @param execute_sql_files: 待执行的sql文件名 + @param execute_db_infos: 待执行的db匹配模式 + @param highrisk_warnings: 高危信息 + @param ticket_type: 单据类型 + @param ticket_mode: sql导入单据的触发类型 + @param import_mode: sql文件导入类型 + @param backup: 备份信息(和备份单据一样) """ # 语义检查参数准备 - root_id = f"{datetime.date.today()}{uuid.uuid1().hex[:6]}".replace("-", "") - sql_execute.created_by = self.context["user"] - sql_execute.bk_biz_id = self.bk_biz_id - sql_execute.execute_objects = [] - for sql_file in sql_execute.execute_sql_files: - sql_execute.execute_objects.extend( - [{"sql_file": sql_file, **db_info} for db_info in sql_execute.execute_db_infos] - ) + execute_objects: List[Dict[str, Union[str, List]]] = [] + for sql_file in execute_sql_files: + execute_objects.extend([{"sql_file": sql_file, **db_info} for db_info in execute_db_infos]) # 异步执行语义检查 - MySQLController(root_id=root_id, ticket_data=sql_execute.to_dict()).mysql_sql_semantic_check_scene() + root_id = f"{datetime.date.today()}{uuid.uuid1().hex[:6]}".replace("-", "") + ticket_data = { + "created_by": self.context["user"], + "bk_biz_id": self.bk_biz_id, + "ticket_type": ticket_type, + "charset": charset, + "path": path, + "cluster_ids": cluster_ids, + "execute_objects": execute_objects, + "highrisk_warnings": highrisk_warnings, + "ticket_mode": ticket_mode, + "import_mode": import_mode, + "backup": backup, + } + if self.cluster_type == DBType.MySQL: + MySQLController(root_id=root_id, ticket_data=ticket_data).mysql_sql_semantic_check_scene() + elif self.cluster_type == DBType.TenDBCluster: + SpiderController(root_id=root_id, ticket_data=ticket_data).spider_semantic_check_scene() # 获取语义执行的node id tree = FlowTree.objects.get(root_id=root_id) - node_id = self._get_node_id_by_component(tree, SemanticCheckComponent.code) + code = SemanticCheckComponent.code + node_id = self.get_node_id_by_component(tree.tree, code) - # 缓存用户的语义检查,并删除过期的数据, django的cache不支持redis命令,这里只能使用原生redis客户端进行操作 + # 缓存用户的语义检查,并删除过期的数据。注:django的cache不支持redis命令,这里只能使用原生redis客户端进行操作 now = int(time.time()) - key = CACHE_SEMANTIC_TASK_FIELD.format(user=sql_execute.created_by) - expired_task_ids = RedisConn.zrangebyscore(key, "-inf", now - SQL_SEMANTIC_CHECK_DATA_EXPIRE_TIME) - self.delete_user_semantic_tasks(semantic=SemanticOperateMeta(task_ids=expired_task_ids)) + key = CACHE_SEMANTIC_TASK_FIELD.format(user=self.context["user"], cluster_type=self.cluster_type) RedisConn.zadd(key, {root_id: now}) RedisConn.set(root_id, StateType.CREATED) + expired_task_ids = RedisConn.zrangebyscore(key, "-inf", now - SQL_SEMANTIC_CHECK_DATA_EXPIRE_TIME) + self.delete_user_semantic_tasks(task_ids=expired_task_ids) + return {"root_id": root_id, "node_id": node_id} - def delete_user_semantic_tasks(self, semantic: SemanticOperateMeta) -> None: + def delete_user_semantic_tasks(self, task_ids: List[int]) -> None: """ 删除用户的语义执行任务 - :param semantic: 语义检查相关操作的元数据 + @param task_ids: 待删除的语义任务ID """ - key = CACHE_SEMANTIC_TASK_FIELD.format(user=self.context["user"]) - if semantic.task_ids: - RedisConn.zrem(key, *semantic.task_ids) - RedisConn.delete(*semantic.task_ids) + key = CACHE_SEMANTIC_TASK_FIELD.format(user=self.context["user"], cluster_type=self.cluster_type) + if task_ids: + RedisConn.zrem(key, *task_ids) + RedisConn.delete(*task_ids) - def revoke_semantic_check(self, semantic: SemanticOperateMeta) -> Dict: + def revoke_semantic_check(self, root_id: str) -> Dict: """ 撤销语义检查流程 - :param semantic: 语义检查相关操作的元数据 + @param root_id: 语义检查的任务ID """ - root_id = semantic.root_id revoke_info = TaskFlowHandler(root_id=root_id).revoke_pipeline() return {"result": revoke_info.result, "message": revoke_info.message, "data": revoke_info.data} - def query_semantic_data(self, semantic: SemanticOperateMeta) -> Dict: + def query_semantic_data(self, root_id: str) -> Dict: """ 根据语义执行id查询语义执行的数据 - :param semantic: 语义检查相关操作的元数据 + @param root_id: 语义任务执行ID """ - root_id = semantic.root_id first_act_node_id = FlowNode.objects.filter(root_id=root_id).first().node_id - try: details = BambooEngine(root_id=root_id).get_node_input_data(node_id=first_act_node_id).data["global_data"] except KeyError: return {"sql_files": "", "import_mode": "", "sql_data_ready": False} import_mode = details["import_mode"] + details["execute_sql_files"] = [detail.pop("sql_file") for detail in details["execute_objects"]] + details["execute_db_infos"] = details.pop("execute_objects") return {"semantic_data": details, "import_mode": import_mode, "sql_data_ready": True} - def deploy_user_config(self, semantic: SemanticOperateMeta) -> None: + def deploy_user_config(self, root_id: str, is_auto_commit: bool, is_skip_pause: bool) -> None: """ 更改用户配置(是否自动提交,是否跳过确认) - :param semantic: 语义检查相关操作的元数据 + @param root_id: 语义任务执行ID + @param is_auto_commit: 是否自动提交 + @param is_skip_pause: 是否跳过暂停 """ # auto_commit的配置 - auto_commit_key = CACHE_SEMANTIC_AUTO_COMMIT_FIELD.format(bk_biz_id=self.bk_biz_id, root_id=semantic.root_id) - cache.set(auto_commit_key, semantic.is_auto_commit, SQL_SEMANTIC_CHECK_DATA_EXPIRE_TIME) + auto_commit_key = CACHE_SEMANTIC_AUTO_COMMIT_FIELD.format(bk_biz_id=self.bk_biz_id, root_id=root_id) + cache.set(auto_commit_key, is_auto_commit, SQL_SEMANTIC_CHECK_DATA_EXPIRE_TIME) # skip_pause的配置 - skip_pause_key = CACHE_SEMANTIC_SKIP_PAUSE_FILED.format(bk_biz_id=self.bk_biz_id, root_id=semantic.root_id) - cache.set(skip_pause_key, semantic.is_skip_pause, SQL_SEMANTIC_CHECK_DATA_EXPIRE_TIME) + skip_pause_key = CACHE_SEMANTIC_SKIP_PAUSE_FILED.format(bk_biz_id=self.bk_biz_id, root_id=root_id) + cache.set(skip_pause_key, is_skip_pause, SQL_SEMANTIC_CHECK_DATA_EXPIRE_TIME) - def query_user_config(self, semantic: SemanticOperateMeta) -> Dict: + def query_user_config(self, root_id: str) -> Dict: """ 查询用户配置 - :param semantic: 语义检查相关操作的元数据 + @param root_id: 语义任务执行ID """ - auto_commit_key = CACHE_SEMANTIC_AUTO_COMMIT_FIELD.format(bk_biz_id=self.bk_biz_id, root_id=semantic.root_id) - skip_pause_key = CACHE_SEMANTIC_SKIP_PAUSE_FILED.format(bk_biz_id=self.bk_biz_id, root_id=semantic.root_id) - + auto_commit_key = CACHE_SEMANTIC_AUTO_COMMIT_FIELD.format(bk_biz_id=self.bk_biz_id, root_id=root_id) + skip_pause_key = CACHE_SEMANTIC_SKIP_PAUSE_FILED.format(bk_biz_id=self.bk_biz_id, root_id=root_id) is_auto_commit = cache.get(auto_commit_key) is_skip_pause = cache.get(skip_pause_key) return {"is_auto_commit": is_auto_commit, "is_skip_pause": is_skip_pause} - def get_user_semantic_tasks(self, semantic: SemanticOperateMeta) -> List[Dict]: - """ - 获取用户的语义检查执行信息列表 - :param semantic: 语义检查相关操作的元数据 - """ - - key = CACHE_SEMANTIC_TASK_FIELD.format(user=self.context["user"]) + def _get_user_semantic_tasks(self, cluster_type, code) -> List[Dict]: + # 获取缓存的任务ID + key = CACHE_SEMANTIC_TASK_FIELD.format(user=self.context["user"], cluster_type=cluster_type) task_ids = RedisConn.zrange(key, 0, -1) task_ids__status_map = dict(zip(task_ids, RedisConn.mget(task_ids))) @@ -224,7 +262,7 @@ def get_user_semantic_tasks(self, semantic: SemanticOperateMeta) -> List[Dict]: { "bk_biz_id": tree.bk_biz_id, "root_id": tree.root_id, - "node_id": self._get_node_id_by_component(tree, SemanticCheckComponent.code), + "node_id": self.get_node_id_by_component(tree.tree, code), "created_at": tree.created_at, "status": tree.status, "is_alter": task_ids__status_map[tree.root_id] != tree.status, @@ -240,14 +278,36 @@ def get_user_semantic_tasks(self, semantic: SemanticOperateMeta) -> List[Dict]: return semantic_info_list - def _get_node_id_by_component(self, tree: FlowTree, component_code: str) -> str: + def get_user_semantic_tasks(self) -> List[Dict]: + """ + 获取用户的语义检查执行信息列表 + """ + semantic_info_list: List[Dict] = [] + if not self.cluster_type or self.cluster_type == DBType.MySQL: + semantic_info_list.extend(self._get_user_semantic_tasks(DBType.MySQL, SemanticCheckComponent.code)) + + if not self.cluster_type or self.cluster_type == DBType.TenDBCluster: + semantic_info_list.extend( + self._get_user_semantic_tasks(DBType.TenDBCluster, ExecuteDBActuatorScriptComponent.code) + ) + + return semantic_info_list + + def get_node_id_by_component(self, tree: Dict, component_code: str) -> str: """ 根据component获取node id :param tree: 流程树对象 :param component_code: 组件code名称 """ - activities: Dict = tree.tree["activities"] + activities: Dict = tree["activities"] for node_id, activity in activities.items(): - if activity["component"]["code"] == component_code: + if activity.get("component") and activity["component"]["code"] == component_code: return node_id + + if activity.get("pipeline"): + node_id = self.get_node_id_by_component(activity["pipeline"], component_code) + if node_id: + return node_id + + return "" diff --git a/dbm-ui/backend/db_services/mysql/sql_import/mock_data.py b/dbm-ui/backend/db_services/mysql/sql_import/mock_data.py index 9955cfb838..92acd45ee5 100644 --- a/dbm-ui/backend/db_services/mysql/sql_import/mock_data.py +++ b/dbm-ui/backend/db_services/mysql/sql_import/mock_data.py @@ -47,7 +47,7 @@ SQL_TICKET_AUTO_COMMIT_REQUEST_DATA = {"root_id": 12232133211, "is_auto_commit": True} -USER_SEMANTIC_LIST_REQUEST_DATA = {"user": "admin"} +USER_SEMANTIC_LIST_REQUEST_DATA = {"user": "admin", "cluster_type": "mysql"} USER_SEMANTIC_LIST_RESPONSE_DATA = { "semantic_list": [ {"root_id": 1231613324894, "created_at": "2011/11/11", "status": "RUNNING"}, @@ -62,7 +62,22 @@ REVOKE_SEMANTIC_CHECK_RESPONSE_DATA = {"result": True, "message": None, "data": None} SEMANTIC_SQL_FILES = { - "semantic_data": {"ticket_type": "MYSQL_SEMANTIC_CHECK", "...": "..."}, + "semantic_data": { + "created_by": "admin", + "bk_biz_id": 3, + "ticket_type": "TENDBCLUSTER_SEMANTIC_CHECK", + "charset": "default", + "path": "mysql/sqlfile", + "cluster_ids": [63], + "highrisk_warnings": "", + "ticket_mode": {"mode": "manual", "trigger_time": ""}, + "import_mode": "file", + "backup": [], + "uid": "202308306822cd", + "blueking_language": "en", + "execute_sql_files": ["xxtestxxx.sql"], + "execute_db_infos": [{"dbnames": ["test"], "ignore_dbnames": []}], + }, "import_mode": "file", "sql_data_ready": True, } diff --git a/dbm-ui/backend/db_services/mysql/sql_import/serializers.py b/dbm-ui/backend/db_services/mysql/sql_import/serializers.py index 200b18d2ec..5acf789a7e 100644 --- a/dbm-ui/backend/db_services/mysql/sql_import/serializers.py +++ b/dbm-ui/backend/db_services/mysql/sql_import/serializers.py @@ -13,8 +13,9 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from backend.configuration.constants import DBType from backend.constants import DATETIME_PATTERN -from backend.db_meta.enums import InstanceInnerRole +from backend.db_meta.enums import ClusterType, InstanceInnerRole from backend.db_services.mysql.sql_import import mock_data from backend.db_services.mysql.sql_import.constants import ( BKREPO_SQLFILE_PATH, @@ -43,13 +44,12 @@ def validate(self, attrs): return attrs -class SQLGrammarCheckResultSerializer(serializers.Serializer): - syntax_fails = serializers.ListField(help_text=_("语法错误"), allow_empty=True, allow_null=True) - highrisk_warnings = serializers.ListField(help_text=_("高危警告"), allow_empty=True, allow_null=True) - bancommand_warnings = serializers.ListField(help_text=_("禁止命令"), allow_empty=True, allow_null=True) - - class SQLGrammarCheckResponseSerializer(serializers.Serializer): + class SQLGrammarCheckResultSerializer(serializers.Serializer): + syntax_fails = serializers.ListField(help_text=_("语法错误"), allow_empty=True, allow_null=True) + highrisk_warnings = serializers.ListField(help_text=_("高危警告"), allow_empty=True, allow_null=True) + bancommand_warnings = serializers.ListField(help_text=_("禁止命令"), allow_empty=True, allow_null=True) + sql_file_name = serializers.DictField(help_text=_("语法检查结果"), child=SQLGrammarCheckResultSerializer()) class Meta: @@ -66,7 +66,7 @@ class SQLImportModeSerializer(serializers.Serializer): trigger_time = serializers.CharField(help_text=_("定时任务触发时间"), required=False, allow_blank=True) class SQLImportBackUpSerializer(serializers.Serializer): - backup_on = serializers.ChoiceField(choices=InstanceInnerRole.get_choices(), help_text=_("备份源")) + backup_on = serializers.CharField(help_text=_("备份源"), required=False) db_patterns = serializers.ListField(help_text=_("匹配DB列表"), child=serializers.CharField()) table_patterns = serializers.ListField(help_text=_("匹配Table列表"), child=serializers.CharField()) ignore_dbs = serializers.ListField( @@ -88,6 +88,9 @@ class SQLImportBackUpSerializer(serializers.Serializer): ticket_mode = SQLImportModeSerializer() import_mode = serializers.ChoiceField(help_text=_("sql导入模式"), choices=SQLImportMode.get_choices()) backup = serializers.ListSerializer(help_text=_("备份信息"), child=SQLImportBackUpSerializer(), required=False) + cluster_type = serializers.ChoiceField( + help_text=_("集群类型,默认为mysql"), choices=DBType.get_choices(), required=False, default=DBType.MySQL + ) class Meta: swagger_schema_fields = {"example": mock_data.SQL_SEMANTIC_CHECK_REQUEST_DATA} @@ -99,7 +102,9 @@ def validate(self, attrs): try: datetime.strptime(trigger_time, DATETIME_PATTERN) except Exception as e: # pylint: disable=broad-except - raise serializers.Serializer(_("时间{}格式解析失败: {},请按照{}格式输入时间").format(trigger_time, e, DATETIME_PATTERN)) + raise serializers.ValidationError( + _("时间{}格式解析失败: {},请按照{}格式输入时间").format(trigger_time, e, DATETIME_PATTERN) + ) return attrs @@ -130,8 +135,9 @@ class Meta: class GetUserSemanticListSerializer(serializers.Serializer): - # TODO 暂时不需要用户参数,后面可能会扩展 - # user = serializers.CharField(help_text=_("用户名")) + cluster_type = serializers.ChoiceField( + help_text=_("集群类型mysql/tendbcluster,为空查询所有任务"), choices=DBType.get_choices(), required=False, default="" + ) class Meta: swagger_schema_fields = {"example": mock_data.USER_SEMANTIC_LIST_REQUEST_DATA} @@ -152,6 +158,9 @@ class Meta: class DeleteUserSemanticListSerializer(serializers.Serializer): # user = serializers.CharField(help_text=_("用户名")) task_ids = serializers.ListField(help_text=_("语义执行的root id列表"), child=serializers.CharField()) + cluster_type = serializers.ChoiceField( + help_text=_("集群类型"), choices=DBType.get_choices(), required=False, default="" + ) class Meta: swagger_schema_fields = {"example": mock_data.DELETE_USER_SEMANTIC_LIST_REQUEST_DATA} diff --git a/dbm-ui/backend/db_services/mysql/sql_import/views.py b/dbm-ui/backend/db_services/mysql/sql_import/views.py index 129e3a49b4..7c1234776d 100644 --- a/dbm-ui/backend/db_services/mysql/sql_import/views.py +++ b/dbm-ui/backend/db_services/mysql/sql_import/views.py @@ -17,6 +17,7 @@ from backend.bk_web import viewsets from backend.bk_web.swagger import common_swagger_auto_schema +from backend.configuration.constants import DBType from backend.db_services.mysql.sql_import.dataclass import SemanticOperateMeta, SQLExecuteMeta, SQLMeta from backend.db_services.mysql.sql_import.handlers import SQLHandler from backend.db_services.mysql.sql_import.serializers import ( @@ -58,10 +59,10 @@ def _view_common_handler( :param func: handler的回调函数名称 """ - base_info = {"bk_biz_id": bk_biz_id, "context": {"user": request.user.username}} validated_data = self.params_validate(self.get_serializer_class()) - meta_init_data = meta.from_dict(validated_data) - return Response(getattr(SQLHandler(**base_info), func)(meta_init_data)) + cluster_type = validated_data.pop("cluster_type", None) + base_info = {"bk_biz_id": bk_biz_id, "context": {"user": request.user.username}, "cluster_type": cluster_type} + return Response(getattr(SQLHandler(**base_info), func)(**validated_data)) @common_swagger_auto_schema( operation_summary=_("sql语法检查"), diff --git a/dbm-ui/backend/db_services/partition/constants.py b/dbm-ui/backend/db_services/partition/constants.py index 20c4d481c4..95ef2d6657 100644 --- a/dbm-ui/backend/db_services/partition/constants.py +++ b/dbm-ui/backend/db_services/partition/constants.py @@ -8,8 +8,28 @@ specific language governing permissions and limitations under the License. """ +from blue_krill.data_types.enum import EnumField, StructuredEnum from django.utils.translation import ugettext_lazy as _ SWAGGER_TAG = _("分区管理") PARTITION_NO_EXECUTE_CODE = 51029 # 分区执行无需发起 + +# 查询唯一索引的SQL语句 +QUERY_UNIQUE_FIELDS_SQL = ( + "select distinct table_schema, table_name, index_name, " + "group_concat(distinct column_name order by seq_in_index) as column_list " + "from information_schema.statistics where {table_sts} and {db_sts} and non_unique = 0 " + "group by table_name, index_name;" +) +# 查询所有表的所有字段类型 +QUERY_DATABASE_FIELD_TYPE = ( + "select table_schema, table_name, column_name, column_type " + "from information_schema.columns where {table_sts} and {db_sts}" +) + + +class PartitionTypeEnum(str, StructuredEnum): + INT = EnumField("int", _("整型")) + DATETIME = EnumField("datetime", _("日期类型")) + TIMESTAMP = EnumField("timestamp", _("时间戳类型")) diff --git a/dbm-ui/backend/db_services/partition/exceptions.py b/dbm-ui/backend/db_services/partition/exceptions.py index 679cc398b5..40d91fdfdb 100644 --- a/dbm-ui/backend/db_services/partition/exceptions.py +++ b/dbm-ui/backend/db_services/partition/exceptions.py @@ -16,3 +16,33 @@ class DBPartitionCreateException(AppBaseException): MODULE_CODE = ErrorCode.DB_PARTITION_CODE MESSAGE = _("分区管理创建异常") + + +class DBPartitionInternalServerError(AppBaseException): + MODULE_CODE = 10001 + MESSAGE = _("服务器内部错误") + + +class DBPartitionInvalidFieldException(AppBaseException): + MODULE_CODE = 10002 + MESSAGE = _("不合法的分区字段") + + +class DBPartitionConfigNotExistedException(AppBaseException): + MODULE_CODE = 52022 + MESSAGE = _("分区配置不存在") + + +class DBPartitionNotSupportedClusterTypeException(AppBaseException): + MODULE_CODE = 52024 + MESSAGE = _("分区不支持该集群类型") + + +class DBPartitionNothingToDoException(AppBaseException): + MODULE_CODE = 52029 + MESSAGE = _("没有需要执行的操作") + + +class DBPartitionWrongPartitionNameFormatException(AppBaseException): + MODULE_CODE = 52029 + MESSAGE = _("分区名格式错误") diff --git a/dbm-ui/backend/db_services/partition/handlers.py b/dbm-ui/backend/db_services/partition/handlers.py index 5c18c533e1..d3f19df66f 100644 --- a/dbm-ui/backend/db_services/partition/handlers.py +++ b/dbm-ui/backend/db_services/partition/handlers.py @@ -7,42 +7,237 @@ 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. """ +import json +from collections import defaultdict +from typing import Any, Dict, List, Union +from django.forms import model_to_dict from django.utils.translation import ugettext as _ +from backend.components import DRSApi from backend.components.mysql_partition.client import DBPartitionApi -from backend.db_services.partition.constants import PARTITION_NO_EXECUTE_CODE -from backend.db_services.partition.exceptions import DBPartitionCreateException -from backend.db_services.partition.serializers import PartitionDryRunSerializer +from backend.db_meta.api.cluster.base.handler import ClusterHandler +from backend.db_meta.enums import ClusterType +from backend.db_meta.models import Cluster +from backend.db_services.partition.constants import QUERY_DATABASE_FIELD_TYPE, QUERY_UNIQUE_FIELDS_SQL +from backend.db_services.partition.exceptions import DBPartitionCreateException, DBPartitionInvalidFieldException from backend.exceptions import ApiRequestError, ApiResultError from backend.ticket.constants import TicketType from backend.ticket.models import Ticket +from backend.utils.batch_request import request_multi_thread class PartitionHandler(object): """分区管理视图的处理函数""" + @staticmethod + def format_err_execute_objects(config_data, message): + config_data = config_data or {} + err_execute_object = { + "config_id": config_data.get("id"), + "db_like": config_data.get("dblike"), + "tblike": config_data.get("tblike"), + } + return [{"message": message, "execute_objects": [err_execute_object]}] + + @classmethod + def get_dry_run_data(cls, data): + params, res = data + params = params["params"] if "params" in params else params + config_id = params.get("config_id") or params.get("params", {}).get("config_id", 0) + if res["result"]: + config_data = [{**data, "message": ""} for data in res["data"]] + return {config_id: config_data} + else: + cluster_type, bk_biz_id = params["cluster_type"], params["bk_biz_id"] + query_params = { + "ids": [config_id], + "cluster_type": cluster_type, + "bk_biz_id": bk_biz_id, + "limit": 1, + "offset": 0, + } + config_data = DBPartitionApi.query_conf(query_params) + config_data = config_data["items"][0] if config_data["count"] else None + return {config_id: cls.format_err_execute_objects(config_data, res["message"])} + @classmethod - def create_and_execute_partition(cls, create_data): - # 创建分区 + def create_and_dry_run_partition(cls, create_data: Dict): + """ + 创建预执行分区策略 + @param create_data: 分区策略数据 + """ + + # 创建分区策略 try: - DBPartitionApi.create_conf(params=create_data) + partition = DBPartitionApi.create_conf(params=create_data) except (ApiRequestError, ApiResultError) as e: raise DBPartitionCreateException(_("分区管理创建失败,创建参数:{}, 错误信息: {}").format(create_data, e)) # 判断是否需要执行分区 - partition_dry_run = PartitionDryRunSerializer(data=create_data) - partition_info = DBPartitionApi.dry_run(partition_dry_run.data, raw=True) - if partition_info["code"] == PARTITION_NO_EXECUTE_CODE: - return - - # 执行分区单据 - create_data.update(partition_objects=partition_info["data"]) - Ticket.create_ticket( - ticket_type=TicketType.MYSQL_PARTITION, - creator=create_data["creator"], - bk_biz_id=create_data["bk_biz_id"], - remark=_("创建分区后自动执行的分区单据"), - details=create_data, - auto_execute=True, + partition_ids = partition["config_ids"] + partition_dry_run_params: List[Dict] = [ + {"params": {**create_data, "config_id": partition_id}, "raw": True} for partition_id in partition_ids + ] + results = request_multi_thread( + func=DBPartitionApi.dry_run, + params_list=partition_dry_run_params, + get_data=cls.get_dry_run_data, + in_order=True, ) + + config__id_result: Dict[str, Union[List, str]] = {} + for res in results: + config__id_result.update(res) + + return config__id_result + + @classmethod + def execute_partition( + cls, + user: str, + bk_biz_id: int, + bk_cloud_id: int, + cluster_id: int, + immute_domain: str, + partition_objects: Dict[str, Any], + ): + """ + 执行分区策略 + @param user: 创建者 + @param bk_biz_id: 业务ID + @param bk_cloud_id: 云区域ID + @param cluster_id: 集群ID + @param immute_domain: 集群域名 + @param partition_objects: 分区执行数据 + """ + # 获取分区单据的类型 + cluster = Cluster.objects.get(id=cluster_id) + if cluster.cluster_type == ClusterType.TenDBCluster: + partition_ticket_type = TicketType.TENDBCLUSTER_PARTITION + else: + partition_ticket_type = TicketType.MYSQL_PARTITION + + # 构造分区策略单据数据列表 + partition_data_list: List[Dict] = [ + { + "config_id": config_id, + "cluster_id": cluster_id, + "bk_cloud_id": bk_cloud_id, + "immute_domain": immute_domain, + "partition_objects": partition_object, + } + for config_id, partition_object in partition_objects.items() + ] + + # 循环执行分区单据,这里一个分区策略对应一个单据 + ticket_list: List[Dict] = [] + for partition_data in partition_data_list: + # 创建分区单据 + ticket = Ticket.create_ticket( + ticket_type=partition_ticket_type, + creator=user, + bk_biz_id=bk_biz_id, + remark=_("分区单据执行"), + details={"infos": [partition_data]}, + auto_execute=True, + ) + ticket_list.append(model_to_dict(ticket)) + # 创建分区日志 + partition_log_data = { + "cluster_type": cluster.cluster_type, + "config_id": int(partition_data["config_id"]), + "bk_biz_id": bk_biz_id, + "cluster_id": cluster.id, + "bk_cloud_id": bk_cloud_id, + "ticket_id": ticket.id, + "immute_domain": cluster.immute_domain, + "time_zone": cluster.time_zone, + "ticket_detail": json.dumps(ticket.details), + } + DBPartitionApi.create_log(partition_log_data) + + return ticket_list + + @classmethod + def verify_partition_field( + cls, + bk_biz_id: int, + cluster_id: int, + dblikes: List[str], + tblikes: List[str], + partition_column: str, + partition_column_type: str, + ): + """ + 校验分区字段是否合理 + @param bk_biz_id: 业务ID + @param cluster_id: 集群ID + @param dblikes: 校验库名列表 + @param tblikes: 校验表面列表 + @param partition_column: 分区字段 + @param partition_column_type: 分区字段类型 + """ + + def _verify_valid_index(_index_keys, _field): + # 不属于主键部分 + primary_keys = _index_keys["primary"] + if primary_keys and not (_field in primary_keys): + return False + + # 不属于唯一键交集 + unique_keys_list = _index_keys["unique"] + if unique_keys_list and not (_field in set(unique_keys_list[0]).intersection(*unique_keys_list[1:])): + return False + + return True + + # 获取集群的DRS查询地址,格式化库表过滤条件 + cluster = Cluster.objects.get(id=cluster_id) + address = ClusterHandler.get_exact_handler(bk_biz_id=bk_biz_id, cluster_id=cluster_id).get_remote_address() + + table_sts = "(" + " or ".join([f"table_name = '{table}'" for table in tblikes]) + ")" + db_sts = "(" + " or ".join([f"table_schema like '{db}'" for db in dblikes]) + ")" + unique_fields_sql = QUERY_UNIQUE_FIELDS_SQL.format(table_sts=table_sts, db_sts=db_sts) + fields_type_sql = QUERY_DATABASE_FIELD_TYPE.format(table_sts=table_sts, db_sts=db_sts) + + # 查询涉及的所有库表索引信息和字段类型信息 + rpc_results = DRSApi.rpc( + {"bk_cloud_id": cluster.bk_cloud_id, "addresses": [address], "cmds": [unique_fields_sql, fields_type_sql]} + ) + cmd__data = {res["cmd"]: res["table_data"] for res in rpc_results[0]["cmd_results"]} + index_data, field_type_data = cmd__data[unique_fields_sql], cmd__data[fields_type_sql] + + # 分区策略创建至少要保证能匹配存在的库表 + if not field_type_data: + raise DBPartitionInvalidFieldException(_("【{}】【{}】当前库表模式匹配为空,请检查是否是合法库表").format(dblikes, tblikes)) + + # 对字段索引的要求: + # 1. 如果存在主键,则分区字段必须是主键的一部分 + # 2. 如果存在唯一键,则分区字段必须是所有唯一键的交集 + db_index_keys: Dict[str, Dict[str, Dict]] = defaultdict(lambda: defaultdict(lambda: defaultdict(list))) + for inx in index_data: + index_column_list = inx["column_list"].split(",") + if inx["index_name"] == "PRIMARY": + db_index_keys[inx["table_schema"]][inx["table_name"]]["primary"].extend(index_column_list) + else: + db_index_keys[inx["table_schema"]][inx["table_name"]]["unique"].append(index_column_list) + + for db, table_index_keys in db_index_keys.items(): + for table, index_keys in table_index_keys.items(): + if not _verify_valid_index(index_keys, partition_column): + raise DBPartitionInvalidFieldException( + _("【{}】【{}】分区字段{}不满足属于主键部分或唯一键交集的要求").format(db, table, partition_column) + ) + + # 对字段类型的要求:分区字段对应的原表字段类型相同 + db_fields: Dict[str, Dict[str, Dict]] = defaultdict(lambda: defaultdict(lambda: defaultdict(list))) + for field in field_type_data: + db_fields[field["table_schema"]][field["table_name"]][field["column_name"]] = field["column_type"] + + for db, table_fields in db_fields.items(): + for table, fields in table_fields.items(): + if partition_column not in fields or partition_column_type not in fields[partition_column]: + raise DBPartitionInvalidFieldException( + _("【{}】【{}】分区字段{}与该表对应的字段类型不匹配").format(db, table, partition_column) + ) diff --git a/dbm-ui/backend/db_services/partition/mock.py b/dbm-ui/backend/db_services/partition/mock.py new file mode 100644 index 0000000000..3c615f7c58 --- /dev/null +++ b/dbm-ui/backend/db_services/partition/mock.py @@ -0,0 +1,124 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +PARTITION_LIST_DATA = { + "count": 16, + "results": [ + { + "id": 1, + "bk_biz_id": 3, + "immute_domain": "spider.spidertest.abc.db", + "port": 0, + "bk_cloud_id": 0, + "cluster_id": 28, + "dblike": "kiodb", + "tblike": "kiodb", + "partition_columns": "b", + "partition_column_type": "datetime", + "reserved_partition": 300, + "extra_partition": 15, + "partition_time_interval": 1, + "partition_type": 0, + "expire_time": 300, + "phase": "offline", + "creator": "admin", + "updator": "admin", + "create_time": "2023-07-24T20:32:54+08:00", + "update_time": "2023-07-24T20:32:54+08:00", + "execute_time": "0001-01-01T00:00:00Z", + "ticket_id": 0, + "status": "", + "ticket_status": "", + "check_info": "", + } + ], +} + +PARTITION_DRY_RUN_DATA = { + "41": [ + { + "ip": "127.0.0.1", + "port": 20000, + "shard_name": "SPT0", + "execute_objects": [ + { + "config_id": 41, + "dblike": "test%_0", + "tblike": "test", + "init_partition": [ + {"sql": "xxxx", "need_size": 98304}, + {"sql": "xxxx", "need_size": 98304}, + ], + "add_partition": [], + "drop_partition": [], + } + ], + }, + { + "ip": "127.0.0.2", + "port": 20001, + "shard_name": "SPT1", + "execute_objects": [ + { + "config_id": 41, + "dblike": "test%_1", + "tblike": "test2", + "init_partition": [ + {"sql": "xxxx", "need_size": 98304}, + {"sql": "xxxx", "need_size": 98304}, + ], + "add_partition": [], + "drop_partition": [], + } + ], + }, + ], + "100": "partition configuration does not exist", + "1001": "partition configuration does not exist", + "10002": "partition configuration does not exist", +} + +PARTITION_LOG_DATA = { + "count": 100, + "results": [ + { + "id": 2, + "ticket_id": 0, + "ticket_status": "", + "execute_time": "2023-05-06T17:58:16+08:00", + "check_info": "", + "status": "", + }, + { + "id": 2, + "ticket_id": 0, + "ticket_status": "", + "execute_time": "2023-05-06T15:48:00+08:00", + "check_info": "partition error. create ticket fail: json unmarshal failed, err: invalid character '\u003c'", + "status": "", + }, + { + "id": 2, + "ticket_id": 0, + "ticket_status": "", + "execute_time": "2023-04-14T11:04:34+08:00", + "check_info": "err info", + "status": "", + }, + ], +} + +PARTITION_FIELD_VERIFY_DATA = { + "result": False, + "code": 8710002500, + "data": None, + "message": "【kio123】【kio1】xxxxxx(8710002500)", + "errors": None, +} diff --git a/dbm-ui/backend/db_services/partition/serializers.py b/dbm-ui/backend/db_services/partition/serializers.py index 13a21ea9a9..8124dca870 100644 --- a/dbm-ui/backend/db_services/partition/serializers.py +++ b/dbm-ui/backend/db_services/partition/serializers.py @@ -8,24 +8,42 @@ 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. """ +import re from django.utils.translation import ugettext as _ from rest_framework import serializers from backend.db_meta.enums import ClusterType from backend.db_meta.models import Cluster +from backend.ticket.builders.mysql.mysql_partition import PartitionObjectSerializer + +from ...ticket.builders.mysql.base import DBTableField +from . import mock class PartitionListSerializer(serializers.Serializer): bk_biz_id = serializers.IntegerField(help_text=_("业务ID")) cluster_type = serializers.ChoiceField(help_text=_("集群类型"), choices=ClusterType.get_choices()) - immute_domains = serializers.ListField(help_text=_("集群域名"), child=serializers.CharField(), required=False) - dblikes = serializers.ListField(help_text=_("匹配库"), child=serializers.CharField(), required=False) - tblikes = serializers.ListField(help_text=_("匹配表"), child=serializers.CharField(), required=False) + immute_domains = serializers.CharField(help_text=_("集群域名"), required=False) + dblikes = serializers.CharField(help_text=_("匹配库"), required=False) + tblikes = serializers.CharField(help_text=_("匹配表"), required=False) limit = serializers.IntegerField(required=False, default=10) offset = serializers.IntegerField(required=False, default=0) + def validate(self, attrs): + filter_fields = ["immute_domains", "dblikes", "tblikes"] + for field in filter_fields: + if field in attrs: + attrs[field] = attrs[field].split(",") + + return attrs + + +class PartitionListResponseSerializer(serializers.Serializer): + class Meta: + swagger_schema_fields = {"example": mock.PARTITION_LIST_DATA} + class PartitionDeleteSerializer(serializers.Serializer): bk_biz_id = serializers.IntegerField(help_text=_("业务ID")) @@ -34,31 +52,13 @@ class PartitionDeleteSerializer(serializers.Serializer): class PartitionCreateSerializer(serializers.Serializer): - bk_biz_id = serializers.IntegerField(help_text=_("业务ID")) - bk_cloud_id = serializers.IntegerField(help_text=_("云区域ID")) - cluster_type = serializers.ChoiceField(help_text=_("集群类型"), choices=ClusterType.get_choices()) - immute_domain = serializers.CharField(help_text=_("集群域名")) - port = serializers.SerializerMethodField( - help_text=_("PORT"), - ) cluster_id = serializers.IntegerField(help_text=_("集群ID")) - dblikes = serializers.ListField(help_text=_("匹配库列表(支持通配)"), child=serializers.CharField()) - tblikes = serializers.ListField(help_text=_("匹配表列表(不支持通配)"), child=serializers.CharField()) + dblikes = serializers.ListField(help_text=_("匹配库列表(支持通配)"), child=DBTableField(db_field=True)) + tblikes = serializers.ListField(help_text=_("匹配表列表(不支持通配)"), child=DBTableField()) partition_column = serializers.CharField(help_text=_("分区字段")) partition_column_type = serializers.CharField(help_text=_("分区字段类型")) expire_time = serializers.IntegerField(help_text=_("过期时间")) partition_time_interval = serializers.IntegerField(help_text=_("分区间隔")) - creator = serializers.SerializerMethodField(help_text=_("创建者")) - updator = serializers.SerializerMethodField(help_text=_("更新者")) - - def get_port(self, obj): - return Cluster.objects.get(id=obj["cluster_id"]).get_partition_port() - - def get_creator(self, obj): - return self.context["request"].user.username - - def get_updator(self, obj): - return self.context["request"].user.username def validate(self, attrs): # 表不支持通配 @@ -66,6 +66,23 @@ def validate(self, attrs): if "%" in tb or "*" in tb: raise serializers.ValidationError(_("分区表不支持通配")) + # 校验过期时间>=分区间隔,且为整数倍 + if attrs["expire_time"] and attrs["expire_time"] % attrs["partition_time_interval"]: + raise serializers.ValidationError(_("过期时间大于等于分区间隔,且为分区间隔的整数倍")) + + # 补充集群信息 + cluster = Cluster.objects.get(id=attrs["cluster_id"]) + attrs.update( + bk_biz_id=cluster.bk_biz_id, + bk_cloud_id=cluster.bk_cloud_id, + cluster_type=cluster.cluster_type, + immute_domain=cluster.immute_domain, + port=cluster.get_partition_port(), + time_zone=cluster.time_zone, + creator=self.context["request"].user.username, + updator=self.context["request"].user.username, + ) + return attrs @@ -89,13 +106,50 @@ class PartitionEnableSerializer(PartitionDisableSerializer): class PartitionLogSerializer(serializers.Serializer): cluster_type = serializers.ChoiceField(help_text=_("集群类型"), choices=ClusterType.get_choices()) config_id = serializers.IntegerField(help_text=_("分区策略ID")) + start_time = serializers.CharField(help_text=_("开始时间"), required=False) + end_time = serializers.CharField(help_text=_("结束时间"), required=False) + + limit = serializers.IntegerField(required=False, default=10) + offset = serializers.IntegerField(required=False, default=0) + + +class PartitionLogResponseSerializer(serializers.Serializer): + class Meta: + swagger_schema_fields = {"example": mock.PARTITION_LOG_DATA} class PartitionDryRunSerializer(serializers.Serializer): - cluster_type = serializers.ChoiceField(help_text=_("集群类型"), choices=ClusterType.get_choices()) - bk_biz_id = serializers.IntegerField(help_text=_("业务ID")) config_id = serializers.IntegerField(help_text=_("分区配置ID")) cluster_id = serializers.IntegerField(help_text=_("集群ID")) - immute_domain = serializers.CharField(help_text=_("集群域名")) - port = serializers.IntegerField(help_text=_("分区使用的端口")) - bk_cloud_id = serializers.IntegerField(help_text=_("云区域ID")) + port = serializers.SerializerMethodField(help_text=_("PORT")) + + def get_port(self, obj): + return Cluster.objects.get(id=obj["cluster_id"]).get_partition_port() + + +class PartitionDryRunResponseSerializer(serializers.Serializer): + class Meta: + swagger_schema_fields = {"example": mock.PARTITION_DRY_RUN_DATA} + + +class PartitionRunSerializer(serializers.Serializer): + cluster_id = serializers.IntegerField(help_text=_("集群ID")) + partition_objects = serializers.DictField( + help_text=_("分区执行对象列表"), child=serializers.ListSerializer(child=PartitionObjectSerializer()) + ) + + +class PartitionColumnVerifySerializer(serializers.Serializer): + cluster_id = serializers.IntegerField(help_text=_("云区域ID")) + dblikes = serializers.ListField(help_text=_("匹配库列表(支持通配)"), child=DBTableField(db_field=True)) + tblikes = serializers.ListField(help_text=_("匹配表列表(不支持通配)"), child=DBTableField()) + partition_column = serializers.CharField(help_text=_("分区字段")) + partition_column_type = serializers.CharField(help_text=_("分区字段类型")) + + def validate(self, attrs): + return attrs + + +class PartitionColumnVerifyResponseSerializer(serializers.Serializer): + class Meta: + swagger_schema_fields = {"example": mock.PARTITION_FIELD_VERIFY_DATA} diff --git a/dbm-ui/backend/db_services/partition/views.py b/dbm-ui/backend/db_services/partition/views.py index 175b7deec6..a408716fc4 100644 --- a/dbm-ui/backend/db_services/partition/views.py +++ b/dbm-ui/backend/db_services/partition/views.py @@ -10,6 +10,7 @@ """ from django.utils.translation import ugettext_lazy as _ +from rest_framework import status from rest_framework.decorators import action from rest_framework.response import Response @@ -17,17 +18,27 @@ from backend.bk_web.swagger import common_swagger_auto_schema from backend.components.mysql_partition.client import DBPartitionApi from backend.db_services.partition.serializers import ( + PartitionColumnVerifyResponseSerializer, + PartitionColumnVerifySerializer, PartitionCreateSerializer, PartitionDeleteSerializer, PartitionDisableSerializer, + PartitionDryRunResponseSerializer, PartitionDryRunSerializer, PartitionEnableSerializer, + PartitionListResponseSerializer, PartitionListSerializer, + PartitionLogResponseSerializer, PartitionLogSerializer, + PartitionRunSerializer, PartitionUpdateSerializer, ) from backend.iam_app.handlers.drf_perm import DBManageIAMPermission +from ...db_meta.models import Cluster +from ...ticket.constants import TicketStatus +from ...ticket.models import Ticket +from ...utils.time import remove_timezone from .constants import SWAGGER_TAG from .handlers import PartitionHandler @@ -39,14 +50,41 @@ class DBPartitionViewSet(viewsets.AuditedModelViewSet): def _get_custom_permissions(self): return [DBManageIAMPermission()] + @staticmethod + def _update_log_status(log_list): + # 更新分区日志的状态 + ticket_ids = [info["ticket_id"] for info in log_list if info["ticket_id"]] + ticket_id__ticket_map = {ticket.id: ticket for ticket in Ticket.objects.filter(id__in=ticket_ids)} + for info in log_list: + ticket = ticket_id__ticket_map.get(info["ticket_id"], None) + info["status"] = ticket.status if ticket else (info["status"] or TicketStatus.PENDING) + + return log_list + + @staticmethod + def _format_time_field(infos): + for info in infos: + for time_field in ["create_time", "update_time", "execute_time"]: + if time_field in info: + info[time_field] = remove_timezone(info[time_field]) + + return infos + @common_swagger_auto_schema( operation_summary=_("获取分区策略列表"), query_serializer=PartitionListSerializer(), + responses={status.HTTP_200_OK: PartitionListResponseSerializer()}, tags=[SWAGGER_TAG], ) def list(self, request, *args, **kwargs): - validated_data = self.params_validate(PartitionListSerializer, representation=True) - return Response(DBPartitionApi.query_conf(params=validated_data)) + validated_data = self.params_validate(PartitionListSerializer) + partition_data = DBPartitionApi.query_conf(params=validated_data) + + partition_list = self._update_log_status(partition_data["items"]) + # 去掉时区时间 + partition_list = self._format_time_field(partition_list) + + return Response({"count": partition_data["count"], "results": partition_list}) @common_swagger_auto_schema( operation_summary=_("修改分区策略"), @@ -54,17 +92,19 @@ def list(self, request, *args, **kwargs): tags=[SWAGGER_TAG], ) def update(self, request, *args, **kwargs): - validated_data = self.params_validate(PartitionUpdateSerializer, representation=True) + validated_data = self.params_validate(PartitionUpdateSerializer) + validated_data.update(id=kwargs["pk"]) return Response(DBPartitionApi.update_conf(params=validated_data)) @common_swagger_auto_schema( operation_summary=_("增加分区策略"), request_body=PartitionCreateSerializer(), + responses={status.HTTP_200_OK: PartitionDryRunResponseSerializer()}, tags=[SWAGGER_TAG], ) def create(self, request, *args, **kwargs): - validated_data = self.params_validate(PartitionCreateSerializer, representation=True) - return Response(PartitionHandler.create_and_execute_partition(validated_data)) + validated_data = self.params_validate(PartitionCreateSerializer) + return Response(PartitionHandler.create_and_dry_run_partition(validated_data)) @common_swagger_auto_schema( operation_summary=_("批量删除分区策略"), @@ -98,20 +138,60 @@ def enable(self, request, *args, **kwargs): @common_swagger_auto_schema( operation_summary=_("查询分区策略日志"), - request_body=PartitionLogSerializer(), + query_serializer=PartitionLogSerializer(), + responses={status.HTTP_200_OK: PartitionLogResponseSerializer()}, tags=[SWAGGER_TAG], ) - @action(methods=["POST"], detail=False, serializer_class=PartitionLogSerializer) + @action(methods=["GET"], detail=False, serializer_class=PartitionLogSerializer) def query_log(self, request, *args, **kwargs): validated_data = self.params_validate(PartitionLogSerializer, representation=True) - return Response(DBPartitionApi.query_log(params=validated_data)) + partition_log_data = DBPartitionApi.query_log(params=validated_data) + partition_log_list = self._update_log_status(partition_log_data["items"]) + partition_log_list = self._format_time_field(partition_log_list) + return Response({"count": partition_log_data["count"], "results": partition_log_list}) @common_swagger_auto_schema( operation_summary=_("分区策略前置执行"), request_body=PartitionDryRunSerializer(), + responses={status.HTTP_200_OK: PartitionDryRunResponseSerializer()}, tags=[SWAGGER_TAG], ) @action(methods=["POST"], detail=False, serializer_class=PartitionDryRunSerializer) def dry_run(self, request, *args, **kwargs): validated_data = self.params_validate(PartitionDryRunSerializer, representation=True) - return Response(DBPartitionApi.dry_run(params=validated_data)) + cluster = Cluster.objects.get(id=validated_data["cluster_id"]) + validated_data.update( + immute_domain=cluster.immute_domain, + bk_cloud_id=cluster.bk_cloud_id, + cluster_type=cluster.cluster_type, + bk_biz_id=cluster.bk_biz_id, + ) + dry_run_data = DBPartitionApi.dry_run(params=validated_data, raw=True) + return Response(PartitionHandler.get_dry_run_data((validated_data, dry_run_data))) + + @common_swagger_auto_schema( + operation_summary=_("分区策略执行"), + request_body=PartitionRunSerializer(), + tags=[SWAGGER_TAG], + ) + @action(methods=["POST"], detail=False, serializer_class=PartitionRunSerializer) + def execute_partition(self, request, *args, **kwargs): + validated_data = self.params_validate(PartitionRunSerializer, representation=True) + cluster = Cluster.objects.get(id=validated_data["cluster_id"]) + validated_data.update( + immute_domain=cluster.immute_domain, bk_cloud_id=cluster.bk_cloud_id, bk_biz_id=cluster.bk_biz_id + ) + return Response(PartitionHandler.execute_partition(user=request.user.username, **validated_data)) + + @common_swagger_auto_schema( + operation_summary=_("分区策略字段校验"), + request_body=PartitionColumnVerifySerializer(), + responses={status.HTTP_500_INTERNAL_SERVER_ERROR: PartitionColumnVerifyResponseSerializer()}, + tags=[SWAGGER_TAG], + ) + @action(methods=["POST"], detail=False, serializer_class=PartitionColumnVerifySerializer) + def verify_partition_field(self, request, *args, **kwargs): + validated_data = self.params_validate(PartitionColumnVerifySerializer, representation=True) + cluster = Cluster.objects.get(id=validated_data["cluster_id"]) + validated_data.update(bk_biz_id=cluster.bk_biz_id) + return Response(PartitionHandler.verify_partition_field(**validated_data)) diff --git a/dbm-ui/backend/db_services/plugin/nameservice/clb.py b/dbm-ui/backend/db_services/plugin/nameservice/clb.py index 8c0a0e9af1..d83450df65 100644 --- a/dbm-ui/backend/db_services/plugin/nameservice/clb.py +++ b/dbm-ui/backend/db_services/plugin/nameservice/clb.py @@ -17,26 +17,131 @@ from backend.configuration.models import DBAdministrator from backend.db_meta import api from backend.db_meta.enums import ClusterEntryType -from backend.db_meta.models import Cluster +from backend.db_meta.models import Cluster, ClusterEntry +from backend.env import CLB_DOMAIN +from backend.flow.utils import dns_manage -def create_lb_and_register_target(cluster_id: int, creator: str) -> Dict[str, Any]: - """创建clb并绑定后端主机""" +@transaction.atomic +def tendis_add_clb_domain(immute_domain: str, bk_cloud_id: int, created_by: str): + """ 增加CLB 域名 """ + cluster = Cluster.objects.get(bk_cloud_id=bk_cloud_id, immute_domain=immute_domain) + clb = cluster.clusterentry_set.filter(cluster_entry_type=ClusterEntryType.CLB.value).first() + ClusterEntry.objects.create( + cluster=cluster, + cluster_entry_type=ClusterEntryType.CLBDNS, + entry="clb.{}".format(cluster.immute_domain), + creator=created_by, + forward_to_id=clb.id, + ) + + +@transaction.atomic +def tendis_bind_clb_domain(immute_domain: str, bk_cloud_id: int, created_by: str): + """ 主域名直接指向CLB """ + cluster = Cluster.objects.get(bk_cloud_id=bk_cloud_id, immute_domain=immute_domain) + immute_entry = cluster.clusterentry_set.filter( + cluster_entry_type=ClusterEntryType.DNS.value, entry=cluster.immute_domain + ).first() + clb_entry = cluster.clusterentry_set.filter(cluster_entry_type=ClusterEntryType.CLB.value).first() + immute_entry.forward_to_id = clb_entry.id + immute_entry.creator = created_by + immute_entry.save(update_fields=["forward_to_id", "creator"]) + + +@transaction.atomic +def tendis_unbind_clb_domain(immute_domain: str, bk_cloud_id: int, created_by: str): + """ 主域名解绑CLB """ + cluster = Cluster.objects.get(bk_cloud_id=bk_cloud_id, immute_domain=immute_domain) + immute_entry = cluster.clusterentry_set.filter( + cluster_entry_type=ClusterEntryType.DNS.value, entry=cluster.immute_domain + ).first() + immute_entry.forward_to_id = None + immute_entry.creator = created_by + immute_entry.save(update_fields=["forward_to_id", "creator"]) + + +@transaction.atomic +def delete_clb(domain: str): + """删除db中clb数据""" + + cluster = Cluster.objects.filter(immute_domain=domain).get() + cluster.clusterentry_set.filter(cluster_entry_type=ClusterEntryType.CLB).delete() + + +@transaction.atomic +def delete_clb_dns(domain: str): + """删除db中clbDns""" + + cluster = Cluster.objects.filter(immute_domain=domain).get() + cluster.clusterentry_set.filter(cluster_entry_type=ClusterEntryType.CLBDNS).delete() + + +def tendis_domain_bind_clb_status(immute_domain: str, bk_cloud_id: int) -> bool: + """判断主域名是否绑定了clb ip""" + + cluster = Cluster.objects.get(bk_cloud_id=bk_cloud_id, immute_domain=immute_domain) + immute_entry = cluster.clusterentry_set.filter( + cluster_entry_type=ClusterEntryType.DNS.value, entry=cluster.immute_domain + ).first() + return immute_entry.forward_to_id is not None + + +def get_cluster_info(cluster_id: int) -> Dict[str, Any]: + """获取集群信息""" # 获取集群信息 - result = api.cluster.nosqlcomm.other.get_cluster_detail(cluster_id) + result = api.cluster.nosqlcomm.other.get_cluster_detail(cluster_id=cluster_id) cluster = result[0] - domain = cluster["immute_domain"] + return cluster + + +def get_dns_status_by_domain(bk_biz_id: int, bk_cloud_id: int, domain: str) -> bool: + """判断域名是否存在""" + + result = dns_manage.DnsManage(bk_biz_id=bk_biz_id, bk_cloud_id=bk_cloud_id).get_domain(get_domain_name=domain) + return len(result) > 0 + + +def get_dns_status_by_ip(bk_biz_id: int, bk_cloud_id: int, domain: str, ip: str) -> bool: + """判断ip是否在域名映射中""" + + results = dns_manage.DnsManage(bk_biz_id=bk_biz_id, bk_cloud_id=bk_cloud_id).get_domain(get_domain_name=domain) + for result in results: + if result["ip"] == ip: + return True + return False + + +def response_ok() -> Dict[str, Any]: + """成功返回""" + + return {"code": 0, "message": "ok"} + + +def response_fail(code: int, message: str) -> Dict[str, Any]: + """失败返回""" + + return {"code": code, "message": message} + + +def create_lb_and_register_target(cluster_id: int) -> Dict[str, Any]: + """创建clb并绑定后端主机""" + + # 获取信息 + cluster = get_cluster_info(cluster_id=cluster_id) + immute_domain = cluster["immute_domain"] # 判断clb是否已经存在 - if "clb" in cluster["clusterentry_set"]: - return {"status": 3, "message": "clb of cluster:%s has been existed" % domain} + if ClusterEntryType.CLB.value in cluster["clusterentry_set"]: + message = "clb of cluster:{} has existed".format(immute_domain) + return response_fail(code=3, message=message) region = cluster["region"] ips = cluster["twemproxy_set"] bk_biz_id = cluster["bk_biz_id"] # 通过bk_biz_id获取manager,backupmanager,去除admin - users = DBAdministrator().get_biz_db_type_admins(bk_biz_id, DBType.Redis) + users = DBAdministrator().get_biz_db_type_admins(bk_biz_id=bk_biz_id, db_type=DBType.Redis) users = [user for user in users if user != "admin"] manager = users[0] backupmanager = users[1] if len(users) > 1 else users[0] @@ -45,8 +150,8 @@ def create_lb_and_register_target(cluster_id: int, creator: str) -> Dict[str, An output = NameServiceApi.clb_create_lb_and_register_target( { "region": region, - "loadbalancername": domain, - "listenername": domain, + "loadbalancername": immute_domain, + "listenername": immute_domain, "manager": manager, "backupmanager": backupmanager, "protocol": "TCP", @@ -54,35 +159,124 @@ def create_lb_and_register_target(cluster_id: int, creator: str) -> Dict[str, An }, raw=True, ) + return output + + +def add_clb_info_to_meta(output: Dict[str, Any], cluster_id: int, creator: str) -> Dict[str, Any]: + """clb信息写入meta""" + + # 获取信息 + cluster = get_cluster_info(cluster_id=cluster_id) # 进行判断请求结果,请求结果正确,写入数据库 - if output["status"] == 0: - api.entry.clb.create( - [ - { - "domain": domain, - "clb_ip": output["loadbalancerip"], - "clb_id": output["loadbalancerid"], - "clb_listener_id": output["listenerid"], - "clb_region": region, - } - ], - creator, - ) - return output + if output["code"] == 0 and ClusterEntryType.CLB.value not in cluster["clusterentry_set"]: + clb_ip = output["data"]["loadbalancerip"] + try: + api.entry.clb.create( + [ + { + "domain": cluster["immute_domain"], + "clb_ip": clb_ip, + "clb_id": output["data"]["loadbalancerid"], + "clb_listener_id": output["data"]["listenerid"], + "clb_region": cluster["region"], + } + ], + creator, + ) + except Exception as e: + message = "add clb info to meta fail, error:{}".format(str(e)) + return response_fail(code=3, message=message) + return response_ok() + + +def delete_clb_info_from_meta(output: Dict[str, Any], cluster_id: int) -> Dict[str, Any]: + """在meta中删除clb信息""" + + # 获取信息 + cluster = get_cluster_info(cluster_id=cluster_id) + # 进行判断请求结果,如果为0操作删除db数据 + if output["code"] == 0 and ClusterEntryType.CLB.value in cluster["clusterentry_set"]: + loadbalancerid = cluster["clusterentry_set"]["clb"][0]["clb_id"] + try: + delete_clb(cluster["immute_domain"]) + except Exception as e: + message = "delete clb:{} info in db fail, error:{}".format(loadbalancerid, str(e)) + return response_fail(code=1, message=message) + return response_ok() + + +def add_clb_domain_to_dns(cluster_id: int, creator: str) -> Dict[str, Any]: + """添加clb域名到dns""" + + # 获取信息 + cluster = get_cluster_info(cluster_id=cluster_id) + immute_domain = cluster["immute_domain"] + port = cluster["twemproxy_ports"][0] + bk_cloud_id = cluster["bk_cloud_id"] + bk_biz_id = cluster["bk_biz_id"] + clb_ip = cluster["clusterentry_set"]["clb"][0]["clb_ip"] + # 添加clb域名以及dns + if CLB_DOMAIN: + # 添加clb域名dns + if not get_dns_status_by_domain( + bk_biz_id=bk_biz_id, bk_cloud_id=bk_cloud_id, domain="clb.{}".format(immute_domain) + ): + result = dns_manage.DnsManage(bk_biz_id=bk_biz_id, bk_cloud_id=bk_cloud_id).create_domain( + instance_list=["{}#{}".format(clb_ip, str(port))], add_domain_name="clb.{}".format(immute_domain) + ) + if not result: + return {"code": 3, "message": "add clb domain to dns fail"} + try: + if ClusterEntryType.CLBDNS.value not in cluster["clusterentry_set"]: + tendis_add_clb_domain(immute_domain=immute_domain, bk_cloud_id=bk_cloud_id, created_by=creator) + except Exception as e: + message = "cluster:{} add clb domain fail, error:{}".format(immute_domain, str(e)) + return response_fail(code=3, message=message) + return response_ok() + + +def delete_clb_domain_from_dns(cluster_id: int) -> Dict[str, Any]: + """从dns中删除clb域名""" + + cluster = get_cluster_info(cluster_id=cluster_id) + clb_dns = cluster["clusterentry_set"]["clbDns"] + immute_domain = cluster["immute_domain"] + clb_ip = cluster["clusterentry_set"]["clb"][0]["clb_ip"] + port = cluster["twemproxy_ports"][0] + bk_cloud_id = cluster["bk_cloud_id"] + bk_biz_id = cluster["bk_biz_id"] + # 如果存在clb域名指向clb ip,则删除 + if clb_dns: + # 删除dns:clb域名绑定clb ip + if get_dns_status_by_domain( + bk_biz_id=bk_biz_id, bk_cloud_id=bk_cloud_id, domain="clb.{}".format(immute_domain) + ): + result = dns_manage.DnsManage(bk_biz_id=bk_biz_id, bk_cloud_id=bk_cloud_id).remove_domain_ip( + domain="clb.{}".format(immute_domain), del_instance_list=["{}#{}".format(clb_ip, str(port))] + ) + if not result: + message = "delete clb.{} dns info fail".format(immute_domain) + return response_fail(code=1, message=message) + # 删除元数据clbDns信息 + try: + delete_clb_dns(domain=immute_domain) + except Exception as e: + message = "delete clb domain of cluster:{} fail, error:{}".format(immute_domain, str(e)) + return response_fail(code=1, message=message) + return response_ok() def deregister_target_and_delete_lb(cluster_id: int) -> Dict[str, Any]: """解绑后端主机并删除clb""" # 获取集群信息 - result = api.cluster.nosqlcomm.other.get_cluster_detail(cluster_id) - cluster = result[0] - domain = cluster["immute_domain"] + cluster = get_cluster_info(cluster_id=cluster_id) + immute_domain = cluster["immute_domain"] # 判断clb是否存在 - if "clb" not in cluster["clusterentry_set"]: - return {"status": 3, "message": "clb of cluster:%s is not existed" % domain} + if ClusterEntryType.CLB.value not in cluster["clusterentry_set"]: + return {"code": 3, "message": "clb of cluster:%s does not exist, can not delete clb" % immute_domain} region = cluster["clusterentry_set"]["clb"][0]["clb_region"] loadbalancerid = cluster["clusterentry_set"]["clb"][0]["clb_id"] listenerid = cluster["clusterentry_set"]["clb"][0]["listener_id"] @@ -96,22 +290,97 @@ def deregister_target_and_delete_lb(cluster_id: int) -> Dict[str, Any]: }, raw=True, ) - - # 进行判断请求结果,如果为0操作删除db数据 - if output["status"] == 0: - try: - delete_clb(domain) - except Exception as e: - output["status"] = 1 - output["message"] = "delete clb sucessfully, delete clb:{} info in db fail, error:{}".format( - loadbalancerid, str(e) - ) return output -@transaction.atomic -def delete_clb(domain: str): - """删除db中clb数据""" +def immute_domain_clb_ip(cluster_id: int, creator: str, bind: bool) -> Dict[str, Any]: + """主域名指向clb ip或者解绑""" - cluster = Cluster.objects.filter(immute_domain=domain).get() - cluster.clusterentry_set.filter(cluster_entry_type=ClusterEntryType.CLB).delete() + # 获取集群信息 + cluster = get_cluster_info(cluster_id=cluster_id) + immute_domain = cluster["immute_domain"] + bk_cloud_id = cluster["bk_cloud_id"] + bk_biz_id = cluster["bk_biz_id"] + clb_ip = cluster["clusterentry_set"]["clb"][0]["clb_ip"] + port = cluster["twemproxy_ports"][0] + proxy_ips = cluster["twemproxy_ips_set"] + clb_ip_port = "{}#{}".format(clb_ip, str(port)) + if ClusterEntryType.CLB.value not in cluster["clusterentry_set"]: + message = "clb of cluster:{} does not exist, can not bind or unbind clb ip".format(immute_domain) + return response_fail(code=3, message=message) + if bind: + if not tendis_domain_bind_clb_status(immute_domain=immute_domain, bk_cloud_id=bk_cloud_id): + # 添加dns:主域名指向clb ip + dns_status_result = get_dns_status_by_ip( + bk_biz_id=bk_biz_id, bk_cloud_id=bk_cloud_id, domain=immute_domain, ip=clb_ip + ) + if not dns_status_result: + create_dns_result = dns_manage.DnsManage(bk_biz_id=bk_biz_id, bk_cloud_id=bk_cloud_id).create_domain( + instance_list=[clb_ip_port], add_domain_name=immute_domain + ) + if not create_dns_result: + message = "add immute domain with clb ip to dns fail" + return response_fail(code=3, message=message) + + # 删除老的dns:主域名指向proxy + delete_dns_list = [] + # 判断ip是否在dns中存在 + for ip in proxy_ips: + dns_status_result = get_dns_status_by_ip( + bk_biz_id=bk_biz_id, bk_cloud_id=bk_cloud_id, domain=immute_domain, ip=ip + ) + if dns_status_result: + delete_dns_list.append("{}#{}".format(ip, str(port))) + if delete_dns_list: + dns_remove_status = dns_manage.DnsManage( + bk_biz_id=bk_biz_id, bk_cloud_id=bk_cloud_id + ).remove_domain_ip(domain=immute_domain, del_instance_list=delete_dns_list) + if not dns_remove_status: + message = "delete immute domain with proxy ip from dns fail" + return response_fail(code=3, message=message) + # 修改元数据 + try: + tendis_bind_clb_domain(immute_domain=immute_domain, bk_cloud_id=bk_cloud_id, created_by=creator) + except Exception as e: + message = "change meta data about immute domain bind clb ip fail, error:{}".format(str(e)) + return response_fail(code=3, message=message) + return response_ok() + message = "immute domain has bound clb ip" + return response_fail(code=3, message=message) + # 主域名解绑clb ip + if tendis_domain_bind_clb_status(immute_domain=immute_domain, bk_cloud_id=bk_cloud_id): + # 添加dns:主域名指向proxy + add_dns_list = [] + for ip in proxy_ips: + dns_status_result = get_dns_status_by_ip( + bk_biz_id=bk_biz_id, bk_cloud_id=bk_cloud_id, domain=immute_domain, ip=ip + ) + if not dns_status_result: + add_dns_list.append("{}#{}".format(ip, str(port))) + if add_dns_list: + dns_create_result = dns_manage.DnsManage(bk_biz_id=bk_biz_id, bk_cloud_id=bk_cloud_id).create_domain( + instance_list=add_dns_list, add_domain_name=immute_domain + ) + if not dns_create_result: + message = "add immute domain with proxy ip from dns fail" + return response_fail(code=3, message=message) + + # 删除老的dns:主域名指向clb ip + dns_status_result = get_dns_status_by_ip( + bk_biz_id=bk_biz_id, bk_cloud_id=bk_cloud_id, domain=immute_domain, ip=clb_ip + ) + if dns_status_result: + dns_remove_status = dns_manage.DnsManage(bk_biz_id=bk_biz_id, bk_cloud_id=bk_cloud_id).remove_domain_ip( + immute_domain, [clb_ip_port] + ) + if not dns_remove_status: + message = "delete immute domain with clb ip from dns fail" + return response_fail(code=3, message=message) + # 修改元数据 + try: + tendis_unbind_clb_domain(immute_domain=immute_domain, bk_cloud_id=bk_cloud_id, created_by=creator) + except Exception as e: + message = "change meta data about immute domain bind clb ip fail, error:{}".format(str(e)) + return response_fail(code=3, message=message) + return response_ok() + return response_ok() diff --git a/dbm-ui/backend/db_services/plugin/nameservice/polaris.py b/dbm-ui/backend/db_services/plugin/nameservice/polaris.py index e44bbb252e..cabde8cdce 100644 --- a/dbm-ui/backend/db_services/plugin/nameservice/polaris.py +++ b/dbm-ui/backend/db_services/plugin/nameservice/polaris.py @@ -19,20 +19,38 @@ from backend.db_meta.enums import ClusterEntryType from backend.db_meta.models import Cluster from backend.db_services.cmdb import biz +from backend.db_services.plugin.nameservice.clb import response_fail, response_ok from backend.env import NAMESERVICE_POLARIS_DEPARTMENT -def create_service_alias_bind_targets(cluster_id: int, creator: str) -> Dict[str, Any]: - """创建polaris并绑定后端主机""" +@transaction.atomic +def delete_polaris(domain: str): + """删除db中polaris数据""" + + cluster = Cluster.objects.filter(immute_domain=domain).get() + cluster.clusterentry_set.filter(cluster_entry_type=ClusterEntryType.POLARIS).delete() + + +def get_cluster_info(cluster_id: int) -> Dict[str, Any]: + """获取集群信息""" # 获取集群信息 result = api.cluster.nosqlcomm.other.get_cluster_detail(cluster_id) cluster = result[0] + return cluster + + +def create_service_alias_bind_targets(cluster_id: int) -> Dict[str, Any]: + """创建polaris并绑定后端主机""" + + # 获取集群信息 + cluster = get_cluster_info(cluster_id=cluster_id) domain = cluster["immute_domain"] - # 判断polaris是否已经存在 - if "polaris" in cluster["clusterentry_set"]: - return {"status": 3, "message": "polaris of cluster:%s has been existed" % domain} + # 判断polaris是否已经存在,如果存在则直接返回 + if ClusterEntryType.POLARIS.value in cluster["clusterentry_set"]: + message = "polaris of cluster:{} has been existed".format(domain) + return response_fail(code=3, message=message) name = "polaris." + domain ips = cluster["twemproxy_set"] department = NAMESERVICE_POLARIS_DEPARTMENT @@ -59,36 +77,64 @@ def create_service_alias_bind_targets(cluster_id: int, creator: str) -> Dict[str }, raw=True, ) + return output + +def add_polaris_info_to_meta(output: Dict[str, Any], cluster_id: int, creator: str) -> Dict[str, Any]: + """添加polaris信息到meta""" + + # 获取信息 + cluster = get_cluster_info(cluster_id=cluster_id) # 进行判断请求结果,请求结果正确,写入数据库 - if output["status"] == 0: - api.entry.polaris.create( - [ - { - "domain": domain, - "polaris_name": output["servicename"], - "polaris_token": output["servicetoken"], - "polaris_l5": output["alias"], - "alias_token": output["aliastoken"], - } - ], - creator, - ) - return output + if output["code"] == 0 and ClusterEntryType.POLARIS.value not in cluster["clusterentry_set"]: + try: + api.entry.polaris.create( + [ + { + "domain": cluster["immute_domain"], + "polaris_name": output["data"]["servicename"], + "polaris_token": output["data"]["servicetoken"], + "polaris_l5": output["data"]["alias"], + "alias_token": output["data"]["aliastoken"], + } + ], + creator, + ) + except Exception as e: + message = "add polaris info to meta fail, error:{}".format(str(e)) + return response_fail(code=3, message=message) + return response_ok() + + +def delete_polaris_info_from_meta(output: Dict[str, Any], cluster_id: int) -> Dict[str, Any]: + """在meta中删除polaris信息""" + + # 获取信息 + cluster = get_cluster_info(cluster_id=cluster_id) + # 进行判断请求结果 + if output["code"] == 0 and ClusterEntryType.POLARIS.value in cluster["clusterentry_set"]: + servicename = cluster["clusterentry_set"]["polaris"][0]["polaris_name"] + try: + delete_polaris(cluster["immute_domain"]) + except Exception as e: + message = "delete polaris sucessfully, delete polaris:{} info in db fail, error:{}".format( + servicename, str(e) + ) + return response_fail(code=1, message=message) + return response_ok() def unbind_targets_delete_alias_service(cluster_id: int) -> Dict[str, Any]: """解绑后端主机并删除polaris""" # 获取集群信息 - result = api.cluster.nosqlcomm.other.get_cluster_detail(cluster_id) - cluster = result[0] + cluster = get_cluster_info(cluster_id=cluster_id) domain = cluster["immute_domain"] # 判断polaris是否存在 - if "polaris" not in cluster["clusterentry_set"]: - return {"status": 3, "message": "polaris of cluster:%s is not existed" % domain} - + if ClusterEntryType.POLARIS.value not in cluster["clusterentry_set"]: + message = "polaris of cluster:{} is not existed".format(domain) + return response_fail(code=3, message=message) servicename = cluster["clusterentry_set"]["polaris"][0]["polaris_name"] servicetoken = cluster["clusterentry_set"]["polaris"][0]["polaris_token"] alias = cluster["clusterentry_set"]["polaris"][0]["polaris_l5"] @@ -104,22 +150,4 @@ def unbind_targets_delete_alias_service(cluster_id: int) -> Dict[str, Any]: }, raw=True, ) - - # 进行判断请求结果 - if output["status"] == 0: - try: - delete_polaris(domain) - except Exception as e: - output["status"] = 1 - output["message"] = "delete polaris sucessfully, delete polaris:{} info in db fail, error:{}".format( - servicename, str(e) - ) return output - - -@transaction.atomic -def delete_polaris(domain: str): - """删除db中polaris数据""" - - cluster = Cluster.objects.filter(immute_domain=domain).get() - cluster.clusterentry_set.filter(cluster_entry_type=ClusterEntryType.POLARIS).delete() diff --git a/dbm-ui/backend/db_services/redis/autofix/__init__.py b/dbm-ui/backend/db_services/redis/autofix/__init__.py new file mode 100644 index 0000000000..aa5085c628 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/autofix/__init__.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" diff --git a/dbm-ui/backend/db_services/redis/autofix/apps.py b/dbm-ui/backend/db_services/redis/autofix/apps.py new file mode 100644 index 0000000000..dda04641b2 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/autofix/apps.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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.apps import AppConfig + + +class RedisAutofixConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "backend.db_services.redis.autofix" diff --git a/dbm-ui/backend/db_services/redis/autofix/bill.py b/dbm-ui/backend/db_services/redis/autofix/bill.py new file mode 100644 index 0000000000..a54df84617 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/autofix/bill.py @@ -0,0 +1,136 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import datetime +import json +import logging +from collections import defaultdict +from typing import Any, Dict, List + +from django.db.models import QuerySet +from django.utils.crypto import get_random_string +from django.utils.translation import ugettext_lazy as _ + +from backend.configuration.constants import DBType +from backend.configuration.models.dba import DBAdministrator +from backend.db_meta.enums import InstanceInnerRole, MachineType +from backend.db_meta.models import Cluster +from backend.db_services.dbbase.constants import IpSource +from backend.ticket.builders import BuilderFactory +from backend.ticket.constants import TicketStatus, TicketType +from backend.ticket.flow_manager.manager import TicketFlowManager +from backend.ticket.models import Ticket +from backend.utils.time import datetime2str + +from .enums import AutofixStatus +from .models import RedisAutofixCore + +logger = logging.getLogger("root") + + +def generate_autofix_ticket(fault_clusters: QuerySet): + for cluster in fault_clusters: + cluster_zones = load_cluster_arch_zone(cluster) + fault_machines = json.loads(cluster.fault_machines) + # {"instance_type": swiched_host.instance_type, "ip": swiched_host.ip} + proxy_distrubt, redis_proxies, redis_slaves = cluster_zones["proxy_distrubt"], [], [] + for fault_machine in fault_machines: + fault_ip = fault_machine["ip"] + if fault_machine["instance_type"] in [MachineType.TWEMPROXY.value, MachineType.PREDIXY.value]: + proxy_zone = cluster_zones["proxy_zones"][fault_ip] + proxy_distrubt[proxy_zone["bk_sub_zone_id"]] -= 1 + redis_proxies.append({"ip": fault_ip, "spec_id": proxy_zone["spec_id"]}) + else: + zone_info = cluster_zones["storage_zones"][fault_ip] + redis_slaves.append({"ip": fault_ip, "spec_id": zone_info["spec_id"]}) + + logger.info( + "cluster summary fault {} proxies:{},curr available zone distrubt:{},storages:{}".format( + cluster.immute_domain, len(redis_proxies), proxy_distrubt, len(redis_slaves) + ) + ) + create_ticket(cluster, redis_proxies, redis_slaves) + + +def create_ticket(cluster: RedisAutofixCore, redis_proxies: list, redis_slaves: list): + details = { + "ip_source": IpSource.RESOURCE_POOL.value, + "infos": [ + { + "cluster_id": cluster.cluster_id, + "immute_domain": cluster.immute_domain, + "bk_cloud_id": cluster.bk_cloud_id, + "proxy": redis_proxies, + "redis_slave": redis_slaves, + } + ], + } + logger.info("create ticket for cluster {} , details : {}".format(cluster.immute_domain, details)) + redisDBA = DBAdministrator.objects.get(bk_biz_id=cluster.bk_biz_id, db_type=DBType.Redis.value) + ticket = Ticket.objects.create( + creator=redisDBA.users[0], + bk_biz_id=cluster.bk_biz_id, + ticket_type=TicketType.REDIS_CLUSTER_AUTOFIX.value, + group=DBType.Redis.value, + status=TicketStatus.PENDING.value, + remark=_("自动发起-自愈任务-{cluster.immute_domain}"), + details=details, + is_reviewed=True, + ) + + # 初始化builder类 + builder = BuilderFactory.create_builder(ticket) + builder.patch_ticket_detail() + builder.init_ticket_flows() + + cluster.ticket_id = ticket.id + cluster.status_version = get_random_string(12) + cluster.update_at = datetime2str(datetime.datetime.now()) + cluster.deal_status = AutofixStatus.AF_WFLOW.value + cluster.save(update_fields=["ticket_id", "status_version", "deal_status", "update_at"]) + + TicketFlowManager(ticket=ticket).run_next_flow() + + +def load_cluster_arch_zone(cluster: RedisAutofixCore): + cluster_obj = Cluster.objects.get(bk_biz_id=cluster.bk_biz_id, id=cluster.cluster_id) + # 构造园区分布 bk_sub_zone: bk_sub_zone_id: + proxy_zones, proxy_distrubt = {}, {} + for proxy in cluster_obj.proxyinstance_set.all(): + proxy_zones[proxy.machine.ip] = { + "spec_id": proxy.machine.spec_id, + "proxy_ip": proxy.machine.ip, + "bk_sub_zone": proxy.machine.bk_sub_zone, + "bk_sub_zone_id": proxy.machine.bk_sub_zone_id, + } + if not proxy_distrubt.get(proxy.machine.bk_sub_zone_id): + proxy_distrubt[proxy.machine.bk_sub_zone_id] = 0 + proxy_distrubt[proxy.machine.bk_sub_zone_id] += 1 + + storage_zones = {} + for master in cluster_obj.storageinstance_set.filter(instance_inner_role=InstanceInnerRole.MASTER.value): + slave_obj = master.as_ejector.get().receiver + slave_ip = slave_obj.machine.ip + storage_zones[slave_ip] = { + "slave_ip": slave_ip, + "bk_sub_zone": slave_obj.machine.bk_sub_zone, + "bk_sub_zone_id": slave_obj.machine.bk_sub_zone_id, + "spec_id": slave_obj.machine.spec_id, + "master_ip": master.machine.ip, + "master_zone": master.machine.bk_sub_zone, + "master_zone_id": master.machine.bk_sub_zone_id, + } + + return { + "storage_zones": storage_zones, + "proxy_zones": proxy_zones, + "region": cluster_obj.region, + "proxy_distrubt": proxy_distrubt, + } diff --git a/dbm-ui/backend/db_services/redis/autofix/const.py b/dbm-ui/backend/db_services/redis/autofix/const.py new file mode 100644 index 0000000000..314ad85ebc --- /dev/null +++ b/dbm-ui/backend/db_services/redis/autofix/const.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 dataclasses import dataclass, field + +from django.utils.translation import ugettext_lazy as _ + +# 等待切换成功的机器列表 +REDIS_SWITCH_WAITER = {} +# 等待的最长时间 +SWITCH_MAX_WAIT_SECONDS = 60 * 6 +# 来个默认值吧 +SWITCH_SMALL = 999999 + + +@dataclass() +class RedisSwitchHost: + bk_biz_id: int + cluster_id: int + cluster_type: str + immute_domain: str + instance_type: str + cluster_ports: list + bk_host_id: int + ip: str + switch_ports: list + sw_min_id: int + sw_max_id: int + sw_result: dict + + +@dataclass() +class RedisSwitchCluster: + bk_biz_id: int + cluster_id: int + cluster_type: str + + +@dataclass() +class RedisSwitchWait: + ip: str + entry: int + err: str + counter: int diff --git a/dbm-ui/backend/db_services/redis/autofix/enums.py b/dbm-ui/backend/db_services/redis/autofix/enums.py new file mode 100644 index 0000000000..bef8d57b8b --- /dev/null +++ b/dbm-ui/backend/db_services/redis/autofix/enums.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 dataclasses import dataclass, field + +from blue_krill.data_types.enum import EnumField, StructuredEnum +from django.utils.translation import ugettext_lazy as _ + + +class DBHASwitchResult(str, StructuredEnum): + """切换结果类型枚举""" + + INFO = EnumField("info", _("切换中?")) + FAIL = EnumField("failed", _("切换失败")) + SUCC = EnumField("success", _("切换成功")) + + +class AutofixItem(str, StructuredEnum): + """切换控制""" + + AUTOFIX_ENABLE = EnumField("enable", _("自愈开关")) + DBHA_ID = EnumField("last_id", _("监控到的id")) + IGNORE_APPS = EnumField("ignore_apps", _("忽略自愈的APP列表")) + IGNORE_DOMAINS = EnumField("ignore_domains", _("忽略自愈的集群列表")) + + +class AutofixStatus(str, StructuredEnum): + """自愈状态""" + + AF_INIT = EnumField("initautofix", _("初始化")) + AF_TICKET = EnumField("initticket", _("创建单据")) + AF_SFLOW = EnumField("startflow", _("发起flow流程")) + AF_WFLOW = EnumField("watchflow", _("监控流程完成状态")) + AF_SUCC = EnumField("success", _("自愈成功")) + AF_FAIL = EnumField("fail", _("自愈失败")) diff --git a/dbm-ui/backend/db_services/redis/autofix/migrations/0001_initial.py b/dbm-ui/backend/db_services/redis/autofix/migrations/0001_initial.py new file mode 100644 index 0000000000..f80d2d027c --- /dev/null +++ b/dbm-ui/backend/db_services/redis/autofix/migrations/0001_initial.py @@ -0,0 +1,92 @@ +# Generated by Django 3.2.19 on 2023-08-31 07:38 + +from django.db import migrations, models + +from backend.db_meta.enums import ClusterType +from backend.db_services.redis.autofix.enums import AutofixStatus + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [] + + operations = [ + migrations.CreateModel( + name="RedisAutofixCtl", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("creator", models.CharField(max_length=64, verbose_name="创建人")), + ("create_at", models.DateTimeField(auto_now_add=True, verbose_name="创建时间")), + ("updater", models.CharField(max_length=64, verbose_name="修改人")), + ("update_at", models.DateTimeField(auto_now=True, verbose_name="更新时间")), + ("bk_cloud_id", models.IntegerField(db_index=True, verbose_name="云区域 ID")), + ("bk_biz_id", models.IntegerField(db_index=True, verbose_name="业务 ID")), + ("ctl_name", models.CharField(max_length=64, verbose_name="模块名")), + ("ctl_value", models.CharField(max_length=64, verbose_name="取值范围")), + ], + options={ + "db_table": "tb_tendis_autofix_ctl", + }, + ), + migrations.CreateModel( + name="RedisIgnoreAutofix", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("creator", models.CharField(max_length=64, verbose_name="创建人")), + ("create_at", models.DateTimeField(auto_now_add=True, verbose_name="创建时间")), + ("updater", models.CharField(max_length=64, verbose_name="修改人")), + ("update_at", models.DateTimeField(auto_now=True, verbose_name="更新时间")), + ("bk_cloud_id", models.IntegerField(verbose_name="云区域ID")), + ("bk_biz_id", models.IntegerField(verbose_name="业务ID")), + ("cluster_id", models.IntegerField(verbose_name="集群ID")), + ( + "cluster_type", + models.CharField(choices=ClusterType.get_choices(), max_length=64, verbose_name="集群类型"), + ), + ("immute_domain", models.CharField(max_length=255, verbose_name="集群域名")), + ("instance_type", models.CharField(max_length=64, verbose_name="实例类型")), + ("cluster_ports", models.JSONField(max_length=255, verbose_name="实例列表")), + ("bk_host_id", models.IntegerField(verbose_name="机器ID")), + ("ip", models.GenericIPAddressField(verbose_name="机器IP")), + ("switch_ports", models.JSONField(max_length=255, verbose_name="切换端口")), + ("sw_min_id", models.IntegerField(verbose_name="切换最小值")), + ("sw_max_id", models.IntegerField(verbose_name="切换最大值")), + ("sw_result", models.JSONField(max_length=255, verbose_name="切换结果")), + ("ignore_msg", models.CharField(max_length=64, verbose_name="忽略类型")), + ], + options={ + "db_table": "tb_tendis_autofix_ignore", + }, + ), + migrations.CreateModel( + name="RedisAutofixCore", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("creator", models.CharField(max_length=64, verbose_name="创建人")), + ("create_at", models.DateTimeField(auto_now_add=True, verbose_name="创建时间")), + ("updater", models.CharField(max_length=64, verbose_name="修改人")), + ("update_at", models.DateTimeField(auto_now=True, verbose_name="更新时间")), + ("bk_cloud_id", models.IntegerField(verbose_name="云区域ID")), + ("bk_biz_id", models.IntegerField(verbose_name="业务ID")), + ("cluster_id", models.IntegerField(verbose_name="集群ID")), + ( + "cluster_type", + models.CharField(choices=ClusterType.get_choices(), max_length=64, verbose_name="集群类型"), + ), + ("immute_domain", models.CharField(max_length=255, verbose_name="集群主域名")), + ("fault_machines", models.JSONField(max_length=10000, verbose_name="故障机器")), + ("ticket_id", models.BigIntegerField(default=-1, verbose_name="单据ID")), + ( + "deal_status", + models.CharField(choices=AutofixStatus.get_choices(), max_length=64, verbose_name="自愈状态"), + ), + ("status_version", models.CharField(max_length=64, verbose_name="状态版本")), + ], + options={ + "db_table": "tb_tendis_autofix_core", + "index_together": {("cluster_id",)}, + }, + ), + ] diff --git a/dbm-ui/backend/db_services/redis/autofix/migrations/__init__.py b/dbm-ui/backend/db_services/redis/autofix/migrations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/dbm-ui/backend/db_services/redis/autofix/models.py b/dbm-ui/backend/db_services/redis/autofix/models.py new file mode 100644 index 0000000000..84a5f34f42 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/autofix/models.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +import logging + +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +from backend.bk_web.constants import LEN_LONG, LEN_NORMAL, LEN_XX_LONG +from backend.bk_web.models import AuditedModel +from backend.db_meta.enums import ClusterType + +from .enums import AutofixStatus + + +class RedisAutofixCtl(AuditedModel): + bk_cloud_id = models.IntegerField(verbose_name=_("云区域 ID"), db_index=True) + bk_biz_id = models.IntegerField(verbose_name=_("业务 ID"), db_index=True) + ctl_name = models.CharField(verbose_name=_("模块名"), max_length=LEN_NORMAL) + ctl_value = models.CharField(verbose_name=_("取值范围"), max_length=LEN_NORMAL) + + class Meta: + db_table = "tb_tendis_autofix_ctl" + + +class RedisAutofixCore(AuditedModel): + bk_cloud_id = models.IntegerField(verbose_name=_("云区域ID")) + bk_biz_id = models.IntegerField(verbose_name=_("业务ID")) + cluster_id = models.IntegerField(verbose_name=_("集群ID")) + cluster_type = models.CharField(verbose_name=_("集群类型"), choices=ClusterType.get_choices(), max_length=LEN_NORMAL) + immute_domain = models.CharField(verbose_name=_("集群主域名"), max_length=LEN_LONG) + fault_machines = models.JSONField(verbose_name=_("故障机器"), max_length=LEN_XX_LONG) + ticket_id = models.BigIntegerField(verbose_name=_("单据ID"), default=-1) + deal_status = models.CharField(verbose_name=_("自愈状态"), choices=AutofixStatus.get_choices(), max_length=LEN_NORMAL) + status_version = models.CharField(verbose_name=_("状态版本"), max_length=LEN_NORMAL) + + class Meta: + db_table = "tb_tendis_autofix_core" + index_together = [("cluster_id")] + + +class RedisIgnoreAutofix(AuditedModel): + bk_cloud_id = models.IntegerField(verbose_name=_("云区域ID")) + bk_biz_id = models.IntegerField(verbose_name=_("业务ID")) + cluster_id = models.IntegerField(verbose_name=_("集群ID")) + cluster_type = models.CharField(verbose_name=_("集群类型"), choices=ClusterType.get_choices(), max_length=LEN_NORMAL) + immute_domain = models.CharField(verbose_name=_("集群域名"), max_length=LEN_LONG) + instance_type = models.CharField(verbose_name=_("实例类型"), max_length=LEN_NORMAL) + cluster_ports = models.JSONField(verbose_name=_("实例列表"), max_length=LEN_LONG) + bk_host_id = models.IntegerField(verbose_name=_("机器ID")) + ip = models.GenericIPAddressField(verbose_name=_("机器IP")) + switch_ports = models.JSONField(verbose_name=_("切换端口"), max_length=LEN_LONG) + sw_min_id = models.IntegerField(verbose_name=_("切换最小值")) + sw_max_id = models.IntegerField(verbose_name=_("切换最大值")) + sw_result = models.JSONField(verbose_name=_("切换结果"), max_length=LEN_LONG) + ignore_msg = models.CharField(verbose_name=_("忽略类型"), max_length=LEN_NORMAL) + + class Meta: + db_table = "tb_tendis_autofix_ignore" diff --git a/dbm-ui/backend/db_services/redis/autofix/watcher.py b/dbm-ui/backend/db_services/redis/autofix/watcher.py new file mode 100644 index 0000000000..ece0d131af --- /dev/null +++ b/dbm-ui/backend/db_services/redis/autofix/watcher.py @@ -0,0 +1,239 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import datetime +import json +import logging +from typing import Dict, List + +from django.utils.crypto import get_random_string +from django.utils.translation import ugettext as _ + +from backend.components.hadb.client import HADBApi +from backend.constants import DEFAULT_BK_CLOUD_ID +from backend.db_meta import api +from backend.exceptions import ApiRequestError, ApiResultError +from backend.utils.redis import RedisConn +from backend.utils.time import datetime2timestamp + +from .const import REDIS_SWITCH_WAITER, SWITCH_MAX_WAIT_SECONDS, SWITCH_SMALL, RedisSwitchHost, RedisSwitchWait +from .enums import AutofixItem, AutofixStatus, DBHASwitchResult +from .models import RedisAutofixCore, RedisAutofixCtl, RedisIgnoreAutofix + +logger = logging.getLogger("root") + + +def watcher_get_by_hosts() -> (int, dict): + switch_id = 0 + try: + switch_next = RedisAutofixCtl.objects.filter(ctl_name=AutofixItem.DBHA_ID.value).get() + if switch_next: + switch_id = int(switch_next.ctl_value) + except RedisAutofixCtl.DoesNotExist: + RedisAutofixCtl.objects.create( + bk_cloud_id=0, bk_biz_id=0, ctl_value=0, ctl_name=AutofixItem.DBHA_ID.value + ).save() + + logger.info("watch_dbha_switch_log from id {}".format(switch_id)) + try: + switch_logs = HADBApi.switch_logs(params={"sw_id": switch_id}) + except (ApiResultError, ApiRequestError, Exception) as error: # pylint: disable=broad-except + # 捕获ApiResultError, ApiRequestError和其他未知异常 + logger.warn("meet exception {} when request switch logs".format(error)) + return 0, {} + + switch_hosts, batch_small_id = {}, SWITCH_SMALL + for switch_log in switch_logs: + swith_ip = switch_log["ip"] + switch_id = int(switch_log["sw_id"]) # uid / sw_id + if not switch_hosts.get(swith_ip): + cluster = api.meta.query_cluster_by_hosts([swith_ip]) # return: [{},{}] + if not cluster: + logger.info("will ignore got none cluster info by ip {}".format(swith_ip)) + continue + elif len(cluster) > 1: + logger.info("will ignore got two+ cluster info by ip {} : {}".format(swith_ip, cluster)) + continue + one_cluster = cluster[0] + + switch_hosts[swith_ip] = RedisSwitchHost( + bk_biz_id=one_cluster["bk_biz_id"], + cluster_id=one_cluster["cluster_id"], + immute_domain=one_cluster["cluster"], + cluster_type=one_cluster["cluster_type"], + instance_type=one_cluster["instance_role"], + bk_host_id=one_cluster["bk_host_id"], + cluster_ports=one_cluster["cs_ports"], + ip=swith_ip, + switch_ports=[], + sw_max_id=0, + sw_min_id=SWITCH_SMALL, + sw_result={}, + ) + current_host = switch_hosts[swith_ip] + current_host.switch_ports.append(switch_log["port"]) + current_host.sw_result[switch_log["result"]] = switch_log["port"] + + # 这台机器的Max值 + if switch_id > current_host.sw_max_id: + current_host.sw_max_id = switch_id + # 本轮的small值 + if switch_id < batch_small_id: + batch_small_id = switch_id + # 这台机器的small值 + if switch_id < current_host.sw_min_id: + current_host.sw_min_id = switch_id + logger.info( + "get smallest switchID {} from {} , with hosts : {}".format(batch_small_id, switch_id, switch_hosts.keys()) + ) + return batch_small_id, switch_hosts + + +def get_4_next_watch_ID(batch_small: int, switch_hosts: Dict) -> int: + succ_max_uid, wait_small_uid, ignore_max_uid = batch_small, 0, SWITCH_SMALL + now_timestamp = datetime2timestamp(datetime.datetime.now()) + for swiched_host in switch_hosts.values(): + # 已经全部切换 + if ( + len(swiched_host.cluster_ports) == len(swiched_host.switch_ports) + and len(swiched_host.sw_result) == 1 + and swiched_host.sw_result.get(DBHASwitchResult.SUCC.value) + ): + logger.info( + "machine {} {} all instance swithed success -_- ".format(swiched_host.ip, swiched_host.switch_ports) + ) + if swiched_host.sw_max_id > succ_max_uid: + succ_max_uid = swiched_host.sw_max_id + 1 + continue + # 需要等待切换 + logger.info( + "machine {} {} NOT all instance swithed success ! {}".format( + swiched_host.ip, swiched_host.switch_ports, swiched_host.sw_result + ) + ) + waiter = REDIS_SWITCH_WAITER.get(swiched_host.ip) + if not waiter: + REDIS_SWITCH_WAITER[swiched_host.ip] = RedisSwitchWait( + ip=swiched_host, + err=swiched_host.sw_result, + entry=datetime2timestamp(datetime.datetime.now()), + counter=1, + ) + logger.info( + "machine {} {} NOT all instance swithed , need wait seconds {}".format( + swiched_host.ip, swiched_host.switch_ports, swiched_host.sw_result + ) + ) + if wait_small_uid < swiched_host.sw_min_id: + wait_small_uid = swiched_host.sw_min_id + continue + elif (now_timestamp - waiter.entry) > SWITCH_MAX_WAIT_SECONDS: + if (now_timestamp - waiter.entry) > SWITCH_MAX_WAIT_SECONDS * 6: + waiter.entry = now_timestamp + waiter.counter = 1 + waiter.err = "" + logger.info( + "machine {} {} NOT all instance swithed , need wait seconds.".format( + swiched_host.ip, swiched_host.switch_ports + ) + ) + if wait_small_uid < swiched_host.sw_min_id: + wait_small_uid = swiched_host.sw_min_id + continue + # 等待切换超时 + logger.info( + "machine {} {} NOT all instance swithed , wait timeout entry time : {} {}".format( + swiched_host.ip, swiched_host.switch_ports, waiter.entry, swiched_host.sw_result + ) + ) + # save ignore swithed host + save_ignore_host(swiched_host, "wait_timeout") + if ignore_max_uid > swiched_host.sw_max_id: + ignore_max_uid = swiched_host.sw_max_id + else: + logger.info( + "machine {} {} NOT all instance swithed , continue wait entry time : {} {}".format( + swiched_host.ip, swiched_host.switch_ports, waiter.entry, swiched_host.sw_result + ) + ) + if wait_small_uid < swiched_host.sw_min_id: + wait_small_uid = swiched_host.sw_min_id + waiter.counter = waiter.counter + 1 + + # end for + next_watch_id = succ_max_uid + logger.warn( + "get watch uids, ignore_max_uid:{},wait_small_uid:{},next_watch_id:{},switch_hosts:{},waiter:{}".format( + ignore_max_uid, wait_small_uid, next_watch_id, switch_hosts.keys(), waiter + ) + ) + if ignore_max_uid > succ_max_uid and ignore_max_uid != SWITCH_SMALL: + logger.info("set next watch id from {} ==> {} , it has ignore item ".format(next_watch_id, ignore_max_uid)) + next_watch_id = ignore_max_uid + + if succ_max_uid > wait_small_uid and wait_small_uid != 0: + logger.info("set next watch id from {} ==> {} , it has wait item ".format(next_watch_id, wait_small_uid)) + next_watch_id = wait_small_uid + + return next_watch_id + + +def save_swithed_host_by_cluster(batch_small: int, switch_hosts: Dict): + switched_cluster = {} + for swiched_host in switch_hosts.values(): + if swiched_host.sw_max_id < batch_small: + cluster = swiched_host.immute_domain + if not switched_cluster.get(cluster): + switched_cluster[cluster] = { + "bk_biz_id": swiched_host.bk_biz_id, + "cluster_id": swiched_host.cluster_id, + "cluster_type": swiched_host.cluster_type, + "immute_domain": cluster, + "fault_machines": [], + "deal_status": AutofixStatus.AF_TICKET.value, + "status_version": get_random_string(length=12), + } + switched_cluster[cluster]["fault_machines"].append( + {"instance_type": swiched_host.instance_type, "ip": swiched_host.ip} + ) + + for cluster in switched_cluster.values(): + logger.info( + "autofix cluster {} with hosts {} begin".format(cluster["immute_domain"], cluster["fault_machines"]) + ) + RedisAutofixCore.objects.create( + bk_cloud_id=DEFAULT_BK_CLOUD_ID, + bk_biz_id=cluster["bk_biz_id"], + cluster_id=cluster["cluster_id"], + immute_domain=cluster["immute_domain"], + cluster_type=cluster["cluster_type"], + fault_machines=json.dumps(cluster["fault_machines"]), + deal_status=cluster["deal_status"], + status_version=cluster["status_version"], + ).save() + + +def save_ignore_host(switched_host: RedisSwitchHost, msg): + RedisIgnoreAutofix.objects.create( + bk_cloud_id=DEFAULT_BK_CLOUD_ID, + bk_biz_id=switched_host.bk_biz_id, + cluster_id=switched_host.cluster_id, + immute_domain=switched_host.immute_domain, + cluster_type=switched_host.cluster_type, + cluster_ports=switched_host.cluster_ports, + bk_host_id=switched_host.bk_host_id, + ip=switched_host.ip, + instance_type=switched_host.instance_type, + switch_ports=switched_host.switch_ports, + sw_min_id=switched_host.sw_min_id, + sw_max_id=switched_host.sw_max_id, + sw_result=json.dumps(switched_host.sw_result), + ignore_msg=msg, + ).save() diff --git a/dbm-ui/backend/db_services/redis/instance/__init__.py b/dbm-ui/backend/db_services/redis/instance/__init__.py new file mode 100644 index 0000000000..aa5085c628 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/instance/__init__.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" diff --git a/dbm-ui/backend/db_services/redis/instance/urls.py b/dbm-ui/backend/db_services/redis/instance/urls.py new file mode 100644 index 0000000000..4740f97928 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/instance/urls.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 rest_framework.routers import DefaultRouter + +from backend.db_services.dbbase.instances.views import InstanceViewSet + +router = DefaultRouter(trailing_slash=True) +router.register(r"instance", InstanceViewSet, basename="instance") + +urlpatterns = [] +urlpatterns += router.urls diff --git a/dbm-ui/backend/db_services/redis/redis_dts/__init__.py b/dbm-ui/backend/db_services/redis/redis_dts/__init__.py new file mode 100644 index 0000000000..aa5085c628 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/redis_dts/__init__.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" diff --git a/dbm-ui/backend/db_services/redis_dts/admin.py b/dbm-ui/backend/db_services/redis/redis_dts/admin.py similarity index 100% rename from dbm-ui/backend/db_services/redis_dts/admin.py rename to dbm-ui/backend/db_services/redis/redis_dts/admin.py diff --git a/dbm-ui/backend/db_services/redis/redis_dts/apis.py b/dbm-ui/backend/db_services/redis/redis_dts/apis.py new file mode 100644 index 0000000000..7bc3a7a20b --- /dev/null +++ b/dbm-ui/backend/db_services/redis/redis_dts/apis.py @@ -0,0 +1,548 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging +from datetime import datetime, timedelta, timezone +from typing import Tuple + +from django.db import IntegrityError, transaction +from django.db.models import Case, IntegerField, Q, Value, When +from django.forms.models import model_to_dict + +from backend.components import DRSApi +from backend.db_meta.enums import ClusterType +from backend.db_meta.models import Cluster +from backend.exceptions import ApiError +from backend.utils.time import datetime2str, strptime + +from .constants import DtsOperateType, DtsTaskType +from .enums import DtsCopyType +from .models import ( + TbDtsServerBlacklist, + TbTendisDtsDistributeLock, + TbTendisDTSJob, + TbTendisDtsTask, + dts_task_clean_pwd_and_fmt_time, + dts_task_format_time, +) +from .util import dts_job_cnt_and_status, dts_task_status, is_in_incremental_sync + +logger = logging.getLogger("root") + + +def is_dtsserver_in_blacklist(payload: dict) -> bool: + """判断dts_server是否在黑名单中""" + return TbDtsServerBlacklist.objects.filter(ip=payload.get("ip")).exists() + + +def get_dts_history_jobs(payload: dict) -> dict: + """获取迁移任务列表以及其对应task cnt""" + + where = Q() + if "cluster_name" in payload: + where &= Q(src_cluster__icontains=payload["cluster_name"]) | Q(dst_cluster__icontains=payload["cluster_name"]) + if "start_time" in payload: + start_time = strptime(payload.get("start_time")) + where &= Q(create_time__gte=start_time) + if "end_time" in payload: + end_time = strptime(payload.get("end_time")) + where &= Q(create_time__lte=end_time) + jobs = TbTendisDTSJob.objects.filter(where).order_by("-create_time") + + # 分页 + start_idx = 0 + end_idx = jobs.count() + if "page" in payload and "page_size" in payload and payload.get("page_size") > 0: + page = payload.get("page") + page_size = payload.get("page_size") + start_idx = (page - 1) * page_size + end_idx = page * page_size + if end_idx >= len(jobs): + end_idx = len(jobs) + + resp = [] + for job in jobs[start_idx:end_idx]: + status_ret = dts_job_cnt_and_status(job) + + job_json = model_to_dict(job) + job_json.update(status_ret) + + # fill dst_copy_type with bill type + if job_json["dts_copy_type"] == "": + job_json["dts_copy_type"] = job_json["dts_bill_type"] + + job_json["create_time"] = datetime2str(job.create_time) + job_json["update_time"] = datetime2str(job.update_time) + resp.append(job_json) + + return {"total_cnt": jobs.count(), "jobs": resp} + + +def get_dts_job_detail(payload: dict) -> list: + """获取迁移任务详情""" + bill_id = payload.get("bill_id") + src_cluster = payload.get("src_cluster") + dst_cluster = payload.get("dst_cluster") + + return TbTendisDTSJob.objects.filter( + Q(bill_id=bill_id) & Q(src_cluster=src_cluster) & Q(dst_cluster=dst_cluster) + ).values() + + +def get_dts_job_tasks(payload: dict) -> list: + """获取迁移任务task列表,失败的排在前面""" + bill_id = payload.get("bill_id") + src_cluster = payload.get("src_cluster") + dst_cluster = payload.get("dst_cluster") + tasks = TbTendisDtsTask.objects.filter( + Q(bill_id=bill_id) & Q(src_cluster=src_cluster) & Q(dst_cluster=dst_cluster) + ).order_by( + # order by FIELD(status,-1,1,0,2),src_ip,src_port + Case( + When(status=-1, then=Value(0)), + When(status=1, then=Value(1)), + When(status=0, then=Value(2)), + When(status=2, then=Value(3)), + output_field=IntegerField(), + ), + "src_ip", + "src_port", + ) + resp = [] + for task in tasks: + task_json = model_to_dict(task) + dts_task_clean_pwd_and_fmt_time(task_json, task) + task_json["status"] = dts_task_status(task) + resp.append(task_json) + return resp + + +def task_sync_stop_precheck(task: TbTendisDtsTask) -> Tuple[bool, str]: + """同步完成前置检查""" + + if task.task_type not in [ + DtsTaskType.TENDISSSD_MAKESYNC.value, + DtsTaskType.MAKE_CACHE_SYNC.value, + DtsTaskType.WATCH_CACHE_SYNC.value, + DtsTaskType.TENDISPLUS_SENDINCR.value, + ]: + return False, "{} task_type:{} cannot do sync_top operate".format(task.get_src_redis_addr(), task.task_type) + + if task.status != 1: + return False, "{} status={} is not running status".format(task.get_src_redis_addr(), task.status) + + if task.tendis_binlog_lag > 300: + return False, "{} binlog_lag={} > 300s".format(task.get_src_redis_addr(), task.tendis_binlog_lag) + + current_time = datetime.now(timezone.utc).astimezone() + if task.update_time and (current_time - task.update_time).seconds > 300: + return False, "{} the status has not been updated for more than 5 minutes,last update time:{}".format( + task.get_src_redis_addr(), task.update_time + ) + + return True, "" + + +@transaction.atomic +def dts_job_disconnct_sync(payload: dict): + """dts job断开同步,目前支持 同步完成(syncStopTodo)、强制终止(ForceKillTaskTodo) 两个操作""" + + bill_id = payload.get("bill_id") + src_cluster = payload.get("src_cluster") + dst_cluster = payload.get("dst_cluster") + tasks = TbTendisDtsTask.objects.filter( + Q(bill_id=bill_id) & Q(src_cluster=src_cluster) & Q(dst_cluster=dst_cluster) + ) + + # 判断是否增量同步中 + incremental_sync_running_cnt = 0 + for task in tasks: + if is_in_incremental_sync(task): + incremental_sync_running_cnt += 1 + + # 如果全部 task 都是增量同步状态,则执行 正常断开同步操作 + # 否则 执行强制终止操作 + operate = DtsOperateType.FORCE_KILL_TODO + if incremental_sync_running_cnt == len(tasks): + operate = DtsOperateType.SYNC_STOP_TODO + + for task in tasks: + if task.sync_operate == operate: + continue + task.sync_operate = operate + task.message = operate + "..." + task.update_time = datetime.now(timezone.utc).astimezone() + task.save(update_fields=["sync_operate", "message", "update_time"]) + + return list(tasks.values_list("id", flat=True)) + + +@transaction.atomic +def dts_job_tasks_failed_retry(payload: dict): + """dts tasks重试当前步骤""" + + tasks = TbTendisDtsTask.objects.filter(id__in=payload.get("task_ids")) + for task in tasks: + if task.src_have_list_keys > 0 and task.src_dbtype != ClusterType.TendisRedisInstance.value: + """ + SrcHaveListKeys>0,其实只有在TendisSSD中才会出现,RedisInstance、tendisplus中很难发现是否有list类型的key + 而且当SrcHaveListKeys>0时,代表tendisSSD已经在 tredisump 阶段以后了,有list类型key情况下后续阶段重试是有风险的; + RedisInstance不用管是否有list,因为有同名key存在时,通过restore replace会完全覆盖; + """ + raise Exception("{} include list type keys,cannot retry".format(task.get_src_redis_addr())) + if task.src_dbtype == ClusterType.TendisTendisSSDInstance.value: + task.task_type = DtsTaskType.TENDISSSD_BACKUP.value + elif task.src_dbtype == ClusterType.TendisRedisInstance.value: + task.task_type = DtsTaskType.MAKE_CACHE_SYNC.value + elif task.src_dbtype == ClusterType.TendisTendisplusInsance: + task.task_type = DtsTaskType.TENDISPLUS_MAKESYNC.value + else: + raise Exception("{} src_dbtype:{} not support".format(task.get_src_redis_addr(), task.src_dbtype)) + + task.status = 0 + task.message = "{} waiting for retry...".format(task.task_type) + task.sync_operate = "" + task.retry_times = task.retry_times + 1 + task.update_time = datetime.now() + task.save(update_fields=["task_type", "status", "message", "sync_operate", "retry_times", "update_time"]) + + return list(tasks.values_list("id", flat=True)) + + +def dts_distribute_trylock(payload: dict) -> bool: + """dts 分布式锁,trylock,成功返回True,失败返回False""" + lockkey = payload.get("lockkey") + holder = payload.get("holder") + ttl_sec = payload.get("ttl_sec") + current_time = datetime.now(timezone.utc).astimezone() + expire_time = current_time + timedelta(seconds=ttl_sec) + lock_row = TbTendisDtsDistributeLock( + lock_key=lockkey, holder=holder, creation_time=current_time, lock_expire_time=expire_time + ) + err = None + try: + lock_row.save() + except Exception as e: + err = e + if not err: + return True + + if not (isinstance(err, IntegrityError) and "Duplicate entry" in err.args[1]): + raise err + + # 可重入锁,lock_expire_time>now()代表锁未过期,可以重入,更新过期时间 + # update tb_tendis_dts_distribute_lock set lock_expire_time=? + # where lock_key=? and holder=? and lock_expire_time>now(); + with transaction.atomic(): + updated_rows = TbTendisDtsDistributeLock.objects.filter( + lock_key=lockkey, holder=holder, lock_expire_time__gt=current_time + ).update(lock_expire_time=expire_time) + if updated_rows == 1: + return True + + # 锁存在,且已过期 + # lock_expire_time list: + """获取dts server迁移中的任务 + 对tendiSSD来说,'迁移中' 指处于 'tendisBackup'、'backupfileFetch'、'tendisdump'、'cmdsImporter'中的task, + 不包含处于 status=-1 或 处于 makeSync 状态的task + 对tendisCache来说,'迁移中'指处于 'makeCacheSync'中的task,不包含处于 status=-1 或 处于 watchCacheSync 状态的task + """ + + dts_server = payload.get("dts_server") + db_type = payload.get("db_type") + task_types = payload.get("task_types") + current_time = datetime.now(timezone.utc).astimezone() + thirty_days_ago = current_time - timedelta(days=30) + + where = Q(bk_cloud_id=payload.get("bk_cloud_id")) + if dts_server: + where = where & Q(dts_server=dts_server) + if db_type: + where = where & Q(src_dbtype=db_type) + if task_types: + where = where & Q(task_type__in=task_types) + where = where & Q(update_time__gt=thirty_days_ago) + where = where & Q(status__in=[0, 1]) + + rets = [] + for task in TbTendisDtsTask.objects.filter(where): + json_data = model_to_dict(task) + dts_task_clean_pwd_and_fmt_time(json_data, task) + rets.append(json_data) + return rets + + +def get_dts_server_max_sync_port(payload: dict) -> dict: + """获取DtsServer上syncPort最大的task""" + + dts_server = payload.get("dts_server") + db_type = payload.get("db_type") + task_types = payload.get("task_types") + current_time = datetime.now(timezone.utc).astimezone() + thirty_days_ago = current_time - timedelta(days=30) + + where = Q(bk_cloud_id=payload.get("bk_cloud_id")) + if dts_server: + where = where & Q(dts_server=dts_server) + if db_type: + where = where & Q(src_dbtype=db_type) + if task_types: + where = where & Q(task_type__in=task_types) + where = where & Q(update_time__gt=thirty_days_ago) + where = where & Q(status=1) + + task = TbTendisDtsTask.objects.filter(where).order_by("-syncer_port").first() + if task: + json_data = model_to_dict(task) + dts_task_clean_pwd_and_fmt_time(json_data, task) + return json_data + + return None + + +def get_last_30days_to_exec_tasks(payload: dict) -> list: + """获取最近30天内task_type类型的等待执行的tasks""" + + bk_cloud_id = payload.get("bk_cloud_id") + dts_server = payload.get("dts_server") + task_type = payload.get("task_type") + db_type = payload.get("db_type") + limit = payload.get("limit") + status = payload.get("status") + dts_server = dts_server.strip() + task_type = task_type.strip() + db_type = db_type.strip() + current_time = datetime.now(timezone.utc).astimezone() + thirty_days_ago = current_time - timedelta(days=30) + + where = Q(bk_cloud_id=bk_cloud_id) + if dts_server: + where = where & Q(dts_server=dts_server) + if task_type: + where = where & Q(task_type=task_type) + if db_type: + where = where & Q(src_dbtype=db_type) + if limit <= 0: + limit = 1 + where = where & Q(status=status) + where = where & Q(create_time__gt=thirty_days_ago) + tasks = TbTendisDtsTask.objects.filter(where).order_by("-src_cluster_priority", "create_time")[:limit] + if not tasks: + # logger.warning( + # "get_last_30days_to_exec_tasks empty records" + # ",bk_cloud_id:{},dts_server:{},task_type:{},db_type:{},status:{}".format( + # bk_cloud_id, dts_server, task_type, db_type, status + # ) + # ) + return [] + rets = [] + for task in tasks: + json_data = model_to_dict(task) + dts_task_format_time(json_data, task) + rets.append(json_data) + return rets + + +def get_last_30days_to_schedule_jobs(payload: dict) -> list: + """获取最近30天内的等待调度的jobs + billId、srcCluster、dstCluster唯一确定一个dts_job + 获取的dts_jobs必须满足: + 有一个待调度的task.dataSize < maxDataSize & status=0 & taskType="" & dtsServer="1.1.1.1" + """ + + max_data_size = payload.get("max_data_size") + zone_name = payload.get("zone_name") + db_type = payload.get("db_type") + current_time = datetime.now(timezone.utc).astimezone() + thirty_days_ago = current_time - timedelta(days=30) + + where = Q(bk_cloud_id=payload.get("bk_cloud_id")) + where = where & Q(src_dbsize__lte=max_data_size) + if zone_name: + where = where & Q(src_ip_zonename=zone_name) + if db_type: + where = where & Q(src_dbtype=db_type) + where = where & Q(dts_server="1.1.1.1") & Q(task_type="") & Q(status=0) & Q(create_time__gt=thirty_days_ago) + jobs = TbTendisDtsTask.objects.filter(where).order_by("-src_cluster_priority", "create_time") + if not jobs: + # logger.warning( + # "get_last_30days_to_schedule_jobs empty records," + # "bk_cloud_id={},max_data_size={},zone_name={},db_type={}".format( + # bk_cloud_id, max_data_size, zone_name, db_type + # ) + # ) + return [] + rets = [] + unique_set = set() + for job in jobs: + job_uniq_key = "{}-{}-{}".format(job.bill_id, job.src_cluster, job.dst_cluster) + if job_uniq_key in unique_set: + continue + unique_set.add(job_uniq_key) + json_data = model_to_dict(job) + dts_task_clean_pwd_and_fmt_time(json_data, job) + rets.append(json_data) + return rets + + +def get_job_to_schedule_tasks(payload: dict) -> list: + """获取一个job的所有待调度的tasks""" + # bill_id: int, src_cluster: str, dst_cluster: str + bill_id = payload.get("bill_id") + src_cluster = payload.get("src_cluster") + dst_cluster = payload.get("dst_cluster") + if bill_id == 0 or not src_cluster or not dst_cluster: + raise Exception( + "invalid params,bill_id={},src_cluster={},dst_cluster={} all can't be empty".format( + bill_id, src_cluster, dst_cluster + ) + ) + current_time = datetime.now(timezone.utc).astimezone() + thirty_days_ago = current_time - timedelta(days=30) + + where = Q(bill_id=bill_id) & Q(src_cluster=src_cluster) & Q(dst_cluster=dst_cluster) + where = where & Q(update_time__gt=thirty_days_ago) & Q(dts_server="1.1.1.1") & Q(task_type="") & Q(status=0) + tasks = TbTendisDtsTask.objects.filter(where).order_by("src_weight") + if not tasks: + logger.warning( + "get_job_to_schedule_tasks empty records,bill_id={},src_cluster={},dst_cluster={}".format( + bill_id, src_cluster, dst_cluster + ) + ) + return [] + + rets = [] + for task in tasks: + json_data = model_to_dict(task) + dts_task_clean_pwd_and_fmt_time(json_data, task) + rets.append(json_data) + return rets + + +def get_job_src_ip_running_tasks(payload: dict) -> list: + """获取一个job的所有待调度的tasks""" + bill_id = payload.get("bill_id") + src_cluster = payload.get("src_cluster") + dst_cluster = payload.get("dst_cluster") + src_ip = payload.get("src_ip") + task_types = payload.get("task_types") + current_time = datetime.now(timezone.utc).astimezone() + thirty_days_ago = current_time - timedelta(days=30) + where = Q(bill_id=bill_id) & Q(src_cluster=src_cluster) & Q(dst_cluster=dst_cluster) & Q(src_ip=src_ip) + where = where & Q(update_time__gt=thirty_days_ago) & Q(status__in=(0, 1)) & Q(task_type__in=task_types) + tasks = TbTendisDtsTask.objects.filter(where) + if not tasks: + logger.warning( + "get_job_src_ip_running_tasks empty records,bill_id={},src_cluster={},dst_cluster={},src_ip={}".format( + bill_id, src_cluster, dst_cluster, src_ip + ) + ) + return [] + + rets = [] + for task in tasks: + json_data = model_to_dict(task) + dts_task_clean_pwd_and_fmt_time(json_data, task) + rets.append(json_data) + return rets + + +def get_dts_task_by_id(payload: dict) -> dict: + """根据task_id获取dts_task""" + task_id = payload.get("task_id") + + try: + task = TbTendisDtsTask.objects.get(id=task_id) + except TbTendisDtsTask.DoesNotExist: + logger.warning("dts task not found,task_id={}".format(task_id)) + return None + + json_data = model_to_dict(task) + dts_task_format_time(json_data, task) + return json_data + + +def dts_tasks_updates(paylod: dict): + """批量更新dts_tasks + :param + task_ids: task_id列表 + update_params: 列名和值的对应关系,如 {"status": 1,"message": "test"} + """ + task_ids = paylod.get("task_ids") + col_to_val = paylod.get("col_to_val") + if not task_ids: + raise Exception("invalid params,task_ids can't be empty") + if not col_to_val: + raise Exception("invalid params,update_params can't be empty") + rows_affected = TbTendisDtsTask.objects.filter(id__in=task_ids).update(**col_to_val) + return rows_affected + + +def dts_test_redis_connections(payload: dict): + """ + 测试redis可连接性 + """ + data_copy_type = payload.get("data_copy_type") + infos = payload.get("infos") + cluster: Cluster = None + redis_addr: str = "" + redis_password: str = "" + cluster_id: int = 0 + for info in infos: + if data_copy_type == DtsCopyType.USER_BUILT_TO_DBM.value: + cluster_id = int(info.get("dst_cluster")) + redis_addr = info.get("src_cluster") + redis_password = info.get("src_cluster_password") + elif data_copy_type == DtsCopyType.COPY_TO_OTHER_SYSTEM.value: + cluster_id = int(info.get("src_cluster")) + redis_addr = info.get("dst_cluster") + redis_password = info.get("dst_cluster_password") + else: + raise ApiError( + "invalid data_copy_type:{},valid data_copy_type[{},{}]".format( + data_copy_type, DtsCopyType.USER_BUILT_TO_DBM.value, DtsCopyType.COPY_TO_OTHER_SYSTEM.value + ) + ) + try: + cluster = Cluster.objects.get(id=cluster_id) + DRSApi.redis_rpc( + { + "addresses": [redis_addr], + "db_num": 0, + "password": redis_password, + "command": "ping", + "bk_cloud_id": cluster.bk_cloud_id, + } + ) + except Cluster.DoesNotExist: + logger.error("cluster not found,cluster_id:{}".format(info.get("src_cluster"))) + raise ApiError("cluster not found,cluster_id:{}".format(info.get("src_cluster"))) + except Exception as e: + logger.error("test redis connection failed,redis_addr:{},error:{}".format(redis_addr, e)) + raise ApiError( + "test redis connection failed,redis_addr:{},please check redis and password is ok".format(redis_addr) + ) + return True diff --git a/dbm-ui/backend/db_services/redis/redis_dts/apps.py b/dbm-ui/backend/db_services/redis/redis_dts/apps.py new file mode 100644 index 0000000000..abfc9280fd --- /dev/null +++ b/dbm-ui/backend/db_services/redis/redis_dts/apps.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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.apps import AppConfig + + +class DbDtsConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "backend.db_services.redis.redis_dts" diff --git a/dbm-ui/backend/db_services/redis/redis_dts/constants.py b/dbm-ui/backend/db_services/redis/redis_dts/constants.py new file mode 100644 index 0000000000..e85effd107 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/redis_dts/constants.py @@ -0,0 +1,178 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 blue_krill.data_types.enum import EnumField, StructuredEnum +from django.utils.translation import ugettext_lazy as _ + + +class DtsTaskType(str, StructuredEnum): + """DTS task类型枚举""" + + # tendis ssd相关任务 + TENDISSSD_BACKUP = EnumField("tendisBackup", _("tendis ssd备份任务")) + TENDISSSD_BACKUPFILE_FETCH = EnumField("backupfileFetch", _("tendis ssd备份拉取任务")) + TENDISSSD_TREDISDUMP = EnumField("tendisdump", _("tendis ssd备份解析任务")) + TENDISSSD_CMDSIMPOTER = EnumField("cmdsImpoter", _("tendis ssd数据导入任务")) + TENDISSSD_MAKESYNC = EnumField("makeSync", _("tendis ssd拉起sync任务")) + TENDISSSD_WATCHOLDSYNC = EnumField("WatchOldSync", _("tendis ssd监视sync任务")) + + # redis cache相关任务 + MAKE_CACHE_SYNC = EnumField("makeCacheSync", _("redis cache拉起redis-shake任务")) + WATCH_CACHE_SYNC = EnumField("watchCacheSync", _("tendis ssd监视sync任务")) + + # tendisplus相关任务 + TENDISPLUS_MAKESYNC = EnumField("tendisplusMakeSync", _("tendisplus拉起reids-sync任务")) + # 将全量数据同步 与 增量数据同步分开,原因是 存量数据同步讲占用较多内存,增量不占用内存 + TENDISPLUS_SENDBULK = EnumField("tendisplusSendBulk", _("tendisplus全量数据同步")) + TENDISPLUS_SENDINCR = EnumField("tendisplusSendIncr", _("tendisplus增量数据同步")) + + +class DtsOperateType(str, StructuredEnum): + """DTS task操作类型枚举""" + + SYNC_STOP_TODO = EnumField("SyncStopTodo", _("停止数据同步todo")) + SYNC_STOP_FAIL = EnumField("SyncStopFail", _("停止数据同步失败")) + SYNC_STOP_SUCC = EnumField("SyncStopSucc", _("停止数据同步成功")) + + FORCE_KILL_TODO = EnumField("ForceKillTaskTodo", _("强制暂停任务todo")) + FORCE_KILL_FAIL = EnumField("ForceKillTaskFail", _("强制暂停任务失败")) + FORCE_KILL_SUCC = EnumField("ForceKillTaskSucc", _("强制暂停任务成功")) + + +# DTS在线切换twemproxy前置检查脚本 +DTS_SWITCH_TWEMPROXY_PRECHECK = """ +srcProxyIP={{SRC_PROXY_IP}} +srcProxyPort={{SRC_PROXY_PORT}} +srcAdminPort=$(($srcProxyPort+1000)) +srcProxyPassword="{{SRC_PROXY_PASSWORD}}" + +filterRet=$(ifconfig|grep $srcProxyIP) +if [[ -z $filterRet ]] +then +echo "[ERROR] 'ifconfig' not found $srcProxyIP" +exit -1 +fi + +confFile=$(ps aux|grep "nutcracker.$srcProxyPort.yml"|grep -v grep|grep --only-match -P 'c .*.yml '|awk '{print $2}') +if [[ -z $confFile ]] +then +echo "[ERROR] get twemproxy $srcProxyPort conf file fail" +exit -1 +fi + +filterRet=$(grep -iw 'listen' $confFile|grep "$srcProxyIP:$srcProxyPort") +if [[ -z $filterRet ]] +then +echo "[ERROR] $srcProxyIP twemproxy confFile:$confFile listen not $srcProxyIP:$srcProxyPort" +exit -1 +fi + +filterRet=$(grep -iw 'password' $confFile|grep "$srcProxyPassword") +if [[ -z $filterRet ]] +then +echo "[ERROR] $srcProxyIP twemproxy confFile:$confFile password not $srcProxyPassword" +exit -1 +fi + +filterRet=$(echo 'get nosqlproxy servers'| /home/mysql/dbtools/netcat $srcProxyIP $srcAdminPort) +if [[ -z $filterRet ]] +then +echo "[ERROR] twemproxy[$srcProxyIP:$srcAdminPort] get backend redis fail" +exit -1 +fi + +confDir=$(dirname $confFile) +ret=$(sed -n '{H;s/$/\\\\n/;p;g;}' $confFile > $confDir/dts_proxy_tmp.txt && tr -d '\n' < $confDir/dts_proxy_tmp.txt | cat) +echo "{\\\"data\\\":\\\"${ret}\\\"}" +""" + +# DTS在线切换predixy前置检查脚本 +DTS_SWITCH_PREDIXY_PRECHECK = """ +srcProxyIP={{SRC_PROXY_IP}} +srcProxyPort={{SRC_PROXY_PORT}} +srcAdminPort=$(($srcProxyPort+1000)) +srcProxyPassword="{{SRC_PROXY_PASSWORD}}" + +filterRet=$(ifconfig|grep $srcProxyIP) +if [[ -z $filterRet ]] +then +echo "[ERROR] 'ifconfig' not found $srcProxyIP" +exit -1 +fi + +confFile=$(ps aux|grep "/usr/local/predixy/bin/predixy"|grep -v grep|awk '{print $NF}') +if [[ -z $confFile ]] +then +echo "[ERROR] get predixy $srcProxyPort conf file fail" +exit -1 +fi + +filterRet=$(grep -iw 'Bind' $confFile|grep "$srcProxyIP:$srcProxyPort") +if [[ -z $filterRet ]] +then +echo "[ERROR] $srcProxyIP predixy confFile:$confFile Bind not $srcProxyIP:$srcProxyPort" +exit -1 +fi + +filterRet=$(grep -iw 'Auth' $confFile|grep "$srcProxyPassword") +if [[ -z $filterRet ]] +then +echo "[ERROR] $srcProxyIP predixy confFile:$confFile password not $srcProxyPassword" +exit -1 +fi + +getVal=$(/usr/local/predixy/bin/redis-cli -h $srcProxyIP -p $srcProxyPort -a $srcProxyPassword get a) +shopt -s nocasematch; +if [[ $getVal =~ "ERR" ]] +then +echo "[ERROR] predixy($srcProxyIP:$srcProxyPort) get a failed,err:$getVal" +exit -1 +fi + +python -c "import json; conf_data=open('$confFile').read(); json_data={'data':conf_data}; \ +s1=''+json.dumps(json_data)+'';print(s1)" +""" + +# 添加/etc/hosts +SERVERS_ADD_ETC_HOSTS = """ +lines="{}" + +while read -r line +do + # skip empty line + if [[ "$line" =~ ^[[:space:]]*$ ]]; then + continue + fi + domain=$(cut -d' ' -f2 <<< "$line") + sed -i "/$domain/d" /etc/hosts + if ! grep -qF "$line" /etc/hosts; then + echo "$line" | tee -a /etc/hosts >/dev/null + echo "Added: $line" + else + echo "Skipped: $line" + fi +done <<< "$lines" +""" + +# 删除/etc/hosts +SERVERS_DEL_ETC_HOSTS = """ +lines="{}" + +while read -r line +do + # skip empty line + if [[ "$line" =~ ^[[:space:]]*$ ]]; then + continue + fi + sed -i "/$line/d" /etc/hosts + echo "Deleted: $line" +done <<< "$lines" + +""" diff --git a/dbm-ui/backend/db_services/redis/redis_dts/enums/__init__.py b/dbm-ui/backend/db_services/redis/redis_dts/enums/__init__.py new file mode 100644 index 0000000000..ed0b9344b6 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/redis_dts/enums/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 .type_enums import ( + DtsBillType, + DtsCopyType, + DtsDataCheckFreq, + DtsDataCheckType, + DtsDataRepairMode, + DtsOnlineSwitchType, + DtsSyncDisconnReminderFreq, + DtsSyncDisconnType, + DtsSyncStatus, + DtsWriteMode, + ExecuteMode, + TimeoutVars, +) diff --git a/dbm-ui/backend/db_services/redis/redis_dts/enums/type_enums.py b/dbm-ui/backend/db_services/redis/redis_dts/enums/type_enums.py new file mode 100644 index 0000000000..215e351a95 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/redis_dts/enums/type_enums.py @@ -0,0 +1,139 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 blue_krill.data_types.enum import EnumField, StructuredEnum +from django.utils.translation import gettext_lazy as _ + + +class DtsBillType(str, StructuredEnum): + """ + DTS单据类型 + """ + + REDIS_CLUSTER_SHARD_NUM_UPDATE = EnumField("REDIS_CLUSTER_SHARD_NUM_UPDATE", _("集群节点数变更")) + REDIS_CLUSTER_TYPE_UPDATE = EnumField("REDIS_CLUSTER_TYPE_UPDATE", _("集群类型变更")) + REDIS_CLUSTER_DATA_COPY = EnumField("REDIS_CLUSTER_DATA_COPY", _("集群数据复制")) + REDIS_CLUSTER_ROLLBACK_DATA_COPY = EnumField("REDIS_CLUSTER_ROLLBACK_DATA_COPY", _("集群回档数据回写")) + + +class DtsCopyType(str, StructuredEnum): + """ + DTS数据复制类型(此时 dts_bill_type=REDIS_CLUSTER_DATA_COPY) + """ + + ONE_APP_DIFF_CLUSTER = EnumField("one_app_diff_cluster", _("业务内")) + DIFF_APP_DIFF_CLUSTER = EnumField("diff_app_diff_cluster", _("跨业务")) + COPY_TO_OTHER_SYSTEM = EnumField("copy_to_other_system", _("业务内至第三方")) + USER_BUILT_TO_DBM = EnumField("user_built_to_dbm", _("自建集群至业务内")) + COPY_FROM_ROLLBACK_INSTANCE = EnumField("copy_from_rollback_instance", _("构造实例至业务内")) + + +class DtsWriteMode(str, StructuredEnum): + """ + 写入模式 + """ + + DELETE_AND_WRITE_TO_REDIS = EnumField( + "delete_and_write_to_redis", _("先删除同名redis key, 再执行写入(如:del $key + hset $key)") + ) + KEEP_AND_APPEND_TO_REDIS = EnumField("keep_and_append_to_redis", _("保留同名redis key,追加写入(如hset $key)")) + FLUSHALL_AND_WRITE_TO_REDIS = EnumField("flushall_and_write_to_redis", _("先清空目标集群所有数据,在写入(如flushall + hset $key)")) + + +class ExecuteMode(str, StructuredEnum): + """ + 执行模式 + """ + + AUTO_EXECUTION = EnumField("auto_execution", _("自动执行")) + SCHEDULED_EXECUTION = EnumField("scheduled_execution", _("定时执行")) + + +class DtsOnlineSwitchType(str, StructuredEnum): + """DTS在线切换类型""" + + NO_CONFIRM = EnumField("no_confirm", _("自动切换")) + USER_CONFIRM = EnumField("user_confirm", _("用户确认切换")) + + +class DtsSyncDisconnType(str, StructuredEnum): + """ + 同步断开类型 + """ + + AUTO_DISCONNECT_AFTER_REPLICATION = EnumField("auto_disconnect_after_replication", _("复制完成后自动断开同步关系")) + KEEP_SYNC_WITH_REMINDER = EnumField("keep_sync_with_reminder", _("复制完成后保持同步关系,定时发送断开同步提醒")) + + +class DtsSyncDisconnReminderFreq(str, StructuredEnum): + """ + Dts复制同步提醒频率 + """ + + ONCE_DAILY = EnumField("once_daily", _("每天一次")) + ONCE_WEEKLY = EnumField("once_weekly", _("每周一次")) + + +class DtsDataCheckType(str, StructuredEnum): + """ + 数据校验修复类型 + """ + + DATA_CHECK_AND_REPAIR = EnumField("data_check_and_repair", _("数据校验并修复")) + DATA_CHECK_ONLY = EnumField("data_check_only", _("仅进行数据校验,不进行修复")) + NO_CHECK_NO_REPAIR = EnumField("no_check_no_repair", _("不校验不修复")) + + +class DtsDataCheckFreq(str, StructuredEnum): + """ + Dts数据校验修复频率 + """ + + ONCE_AFTER_REPLICATION = EnumField("once_after_replication", _("复制完成后执行一次")) + ONCE_EVERY_THREE_DAYS = EnumField("once_every_three_days", _("每三天一次")) + ONCE_WEEKLY = EnumField("once_weekly", _("每周一次")) + + +class DtsDataRepairMode(str, StructuredEnum): + """ + Dts数据修复模式 + """ + + AUTO_REPAIR = EnumField("auto_repair", _("自动修复")) + MANUAL_CONFIRM = EnumField("manual_confirm", _("用户确认")) + + +class TimeoutVars(str, StructuredEnum): + """ + 超时时间 + """ + + NEVER = EnumField("never", _("永不超时")) + ONE_HOUR = EnumField("1hour", _("1小时")) + THREE_HOURS = EnumField("3hours", _("3小时")) + SIX_HOURS = EnumField("6hours", _("6小时")) + ONE_DAY = EnumField("1day", _("1天")) + TWO_DAYS = EnumField("2days", _("2天")) + ONE_WEEK = EnumField("1week", _("1周")) + + +class DtsSyncStatus(str, StructuredEnum): + """ + 数据sync 状态 + """ + + IN_FULL_TRANSFER = EnumField("in_full_transfer", _("全量传输中")) + IN_INCREMENTAL_SYNC = EnumField("in_incremental_sync", _("增量同步中")) + FULL_TRANSFER_FAILED = EnumField("full_transfer_failed", _("全量传输失败")) + INCREMENTAL_SYNC_FAILED = EnumField("incremental_sync_failed", _("增量同步失败")) + PENDING_EXECUTION = EnumField("pending_execution", _("待执行")) + TRANSFER_COMPLETED = EnumField("transfer_completed", _("传输已完成")) + TRANSFER_TERMINATED = EnumField("transfer_terminated", _("传输被终止")) + UNKNOWN = EnumField("unknown", _("未知状态")) diff --git a/dbm-ui/backend/db_services/redis_dts/exceptions.py b/dbm-ui/backend/db_services/redis/redis_dts/exceptions.py similarity index 100% rename from dbm-ui/backend/db_services/redis_dts/exceptions.py rename to dbm-ui/backend/db_services/redis/redis_dts/exceptions.py diff --git a/dbm-ui/backend/db_services/redis_dts/migrations/0001_initial.py b/dbm-ui/backend/db_services/redis/redis_dts/migrations/0001_initial.py similarity index 100% rename from dbm-ui/backend/db_services/redis_dts/migrations/0001_initial.py rename to dbm-ui/backend/db_services/redis/redis_dts/migrations/0001_initial.py diff --git a/dbm-ui/backend/db_services/redis/redis_dts/migrations/0002_auto_20230621_1500.py b/dbm-ui/backend/db_services/redis/redis_dts/migrations/0002_auto_20230621_1500.py new file mode 100644 index 0000000000..d0293b312c --- /dev/null +++ b/dbm-ui/backend/db_services/redis/redis_dts/migrations/0002_auto_20230621_1500.py @@ -0,0 +1,37 @@ +# Generated by Django 3.2.19 on 2023-06-21 07:00 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("redis_dts", "0001_initial"), + ] + + operations = [ + migrations.CreateModel( + name="TendisDtsServer", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("bk_cloud_id", models.BigIntegerField(default=0, verbose_name="云区域ID")), + ("ip", models.CharField(default="", max_length=128, verbose_name="DTS_Server IP")), + ("city_id", models.IntegerField(default=0, verbose_name="城市ID")), + ("city_name", models.CharField(default="", max_length=128, verbose_name="城市名")), + ("status", models.IntegerField(default=0, verbose_name="状态")), + ("heartbeat_time", models.DateTimeField(default="1997-01-01 00:00:00", verbose_name="最近心跳时间")), + ("update_time", models.DateTimeField(auto_now=True, verbose_name="更新时间")), + ], + options={ + "db_table": "tb_tendis_dts_server", + }, + ), + migrations.AddIndex( + model_name="tendisdtsserver", + index=models.Index(fields=["update_time"], name="index_update_time"), + ), + migrations.AddConstraint( + model_name="tendisdtsserver", + constraint=models.UniqueConstraint(fields=("bk_cloud_id", "ip"), name="unique_tendis_dts_server"), + ), + ] diff --git a/dbm-ui/backend/db_services/redis/redis_dts/migrations/0002_auto_20230629_1125.py b/dbm-ui/backend/db_services/redis/redis_dts/migrations/0002_auto_20230629_1125.py new file mode 100644 index 0000000000..b4c801ec4f --- /dev/null +++ b/dbm-ui/backend/db_services/redis/redis_dts/migrations/0002_auto_20230629_1125.py @@ -0,0 +1,70 @@ +# Generated by Django 3.2.19 on 2023-06-29 03:25 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("redis_dts", "0001_initial"), + ] + + operations = [ + migrations.RemoveField( + model_name="tbtendisdtsjob", + name="datacheck", + ), + migrations.RemoveField( + model_name="tbtendisdtsjob", + name="datarepair", + ), + migrations.RemoveField( + model_name="tbtendisdtsjob", + name="datarepair_mode", + ), + migrations.AddField( + model_name="tbtendisdtsjob", + name="data_check_repair_execution_frequency", + field=models.CharField(default="", max_length=64, verbose_name="数据校验修复执行频率"), + ), + migrations.AddField( + model_name="tbtendisdtsjob", + name="data_check_repair_type", + field=models.CharField(default="", max_length=64, verbose_name="数据校验修复类型"), + ), + migrations.AddField( + model_name="tbtendisdtsjob", + name="last_data_check_repair_bill_id", + field=models.BigIntegerField(default=0, verbose_name="最近一次数据校验与修复 单据id"), + ), + migrations.AddField( + model_name="tbtendisdtsjob", + name="last_data_check_repair_bill_status", + field=models.IntegerField(default=0, verbose_name="最近一次数据校验与修复 单据状态"), + ), + migrations.AddField( + model_name="tbtendisdtsjob", + name="sync_disconnect_reminder_frequency", + field=models.CharField(default="", max_length=64, verbose_name="同步断开提醒频率"), + ), + migrations.AddField( + model_name="tbtendisdtsjob", + name="sync_disconnect_type", + field=models.CharField(default="", max_length=64, verbose_name="同步断开类型"), + ), + migrations.AddField( + model_name="tbtendisdtsjob", + name="write_mode", + field=models.CharField(default="", max_length=64, verbose_name="写入模式"), + ), + migrations.AddField( + model_name="tbtendisdtstask", + name="write_mode", + field=models.CharField(default="", max_length=64, verbose_name="写入模式"), + ), + migrations.AlterField( + model_name="tbtendisdtsjob", + name="reason", + field=models.BinaryField(default=b"", verbose_name="备注"), + ), + ] diff --git a/dbm-ui/backend/db_services/redis/redis_dts/migrations/0003_auto_20230703_2114.py b/dbm-ui/backend/db_services/redis/redis_dts/migrations/0003_auto_20230703_2114.py new file mode 100644 index 0000000000..095d6d2dd8 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/redis_dts/migrations/0003_auto_20230703_2114.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.19 on 2023-07-03 13:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("redis_dts", "0002_auto_20230629_1125"), + ] + + operations = [ + migrations.AddField( + model_name="tbtendisdtsjob", + name="last_data_check_repair_bill_execute_time", + field=models.DateTimeField(null=True, verbose_name="最近一次数据校验与修复 单据执行时间"), + ), + migrations.AddField( + model_name="tbtendisdtstask", + name="src_twemproxy_hash_tag_enabled", + field=models.IntegerField(default=0, verbose_name="源twemproxy集群是否开启hash_tag"), + ), + ] diff --git a/dbm-ui/backend/db_services/redis/redis_dts/migrations/0004_auto_20230704_1116.py b/dbm-ui/backend/db_services/redis/redis_dts/migrations/0004_auto_20230704_1116.py new file mode 100644 index 0000000000..a58a3f1e9e --- /dev/null +++ b/dbm-ui/backend/db_services/redis/redis_dts/migrations/0004_auto_20230704_1116.py @@ -0,0 +1,32 @@ +# Generated by Django 3.2.19 on 2023-07-04 03:16 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("redis_dts", "0003_auto_20230703_2114"), + ] + + operations = [ + migrations.RenameField( + model_name="tbtendisdtsjob", + old_name="last_data_check_repair_bill_execute_time", + new_name="last_data_check_repair_flow_execute_time", + ), + migrations.RenameField( + model_name="tbtendisdtsjob", + old_name="last_data_check_repair_bill_status", + new_name="last_data_check_repair_flow_status", + ), + migrations.RemoveField( + model_name="tbtendisdtsjob", + name="last_data_check_repair_bill_id", + ), + migrations.AddField( + model_name="tbtendisdtsjob", + name="last_data_check_repair_flow_id", + field=models.CharField(default="", max_length=64, verbose_name="最近一次数据校验与修复 flow id"), + ), + ] diff --git a/dbm-ui/backend/db_services/redis/redis_dts/migrations/0005_tbtendisdtsjob_unique_dts_job_key.py b/dbm-ui/backend/db_services/redis/redis_dts/migrations/0005_tbtendisdtsjob_unique_dts_job_key.py new file mode 100644 index 0000000000..9371e71f15 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/redis_dts/migrations/0005_tbtendisdtsjob_unique_dts_job_key.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2.19 on 2023-07-04 06:50 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("redis_dts", "0004_auto_20230704_1116"), + ] + + operations = [ + migrations.AddConstraint( + model_name="tbtendisdtsjob", + constraint=models.UniqueConstraint( + fields=("bill_id", "src_cluster", "dst_cluster"), name="unique_dts_job_key" + ), + ), + ] diff --git a/dbm-ui/backend/db_services/redis/redis_dts/migrations/0006_tbtendisdtstask_dts_task_unique_key.py b/dbm-ui/backend/db_services/redis/redis_dts/migrations/0006_tbtendisdtstask_dts_task_unique_key.py new file mode 100644 index 0000000000..9390121ad9 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/redis_dts/migrations/0006_tbtendisdtstask_dts_task_unique_key.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2.19 on 2023-07-04 06:59 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("redis_dts", "0005_tbtendisdtsjob_unique_dts_job_key"), + ] + + operations = [ + migrations.AddConstraint( + model_name="tbtendisdtstask", + constraint=models.UniqueConstraint( + fields=("bill_id", "src_cluster", "dst_cluster", "src_ip", "src_port"), name="dts_task_unique_key" + ), + ), + ] diff --git a/dbm-ui/backend/db_services/redis/redis_dts/migrations/0007_merge_20230712_1630.py b/dbm-ui/backend/db_services/redis/redis_dts/migrations/0007_merge_20230712_1630.py new file mode 100644 index 0000000000..a9c9ed65d4 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/redis_dts/migrations/0007_merge_20230712_1630.py @@ -0,0 +1,13 @@ +# Generated by Django 3.2.19 on 2023-07-12 08:30 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("redis_dts", "0002_auto_20230621_1500"), + ("redis_dts", "0006_tbtendisdtstask_dts_task_unique_key"), + ] + + operations = [] diff --git a/dbm-ui/backend/db_services/redis/redis_dts/migrations/0008_auto_20230718_2249.py b/dbm-ui/backend/db_services/redis/redis_dts/migrations/0008_auto_20230718_2249.py new file mode 100644 index 0000000000..dc8bbd85c1 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/redis_dts/migrations/0008_auto_20230718_2249.py @@ -0,0 +1,141 @@ +# Generated by Django 3.2.19 on 2023-07-18 14:49 + +from django.db import migrations, models + +import backend.db_services.redis.redis_dts.enums.type_enums + + +class Migration(migrations.Migration): + + dependencies = [ + ("redis_dts", "0007_merge_20230712_1630"), + ] + + operations = [ + migrations.RemoveField( + model_name="tbtendisdtsjob", + name="last_data_check_repair_flow_status", + ), + migrations.AlterField( + model_name="tbtendisdtsjob", + name="data_check_repair_execution_frequency", + field=models.CharField( + choices=[ + ("once_after_replication", "复制完成后执行一次"), + ("once_every_three_days", "每三天一次"), + ("once_weekly", "每周一次"), + ], + default=backend.db_services.redis.redis_dts.enums.type_enums.DtsDataCheckFreq[ + "ONCE_AFTER_REPLICATION" + ], + max_length=64, + verbose_name="数据校验修复执行频率", + ), + ), + migrations.AlterField( + model_name="tbtendisdtsjob", + name="data_check_repair_type", + field=models.CharField( + choices=[ + ("data_check_and_repair", "数据校验并修复"), + ("data_check_only", "仅进行数据校验,不进行修复"), + ("no_check_no_repair", "不校验不修复"), + ], + default=backend.db_services.redis.redis_dts.enums.type_enums.DtsDataCheckType["DATA_CHECK_AND_REPAIR"], + max_length=64, + verbose_name="数据校验修复类型", + ), + ), + migrations.AlterField( + model_name="tbtendisdtsjob", + name="dts_bill_type", + field=models.CharField( + choices=[ + ("REDIS_CLUSTER_SHARD_NUM_UPDATE", "集群节点数变更"), + ("REDIS_CLUSTER_TYPE_UPDATE", "集群类型变更"), + ("REDIS_CLUSTER_DATA_COPY", "集群数据复制"), + ], + default="", + max_length=64, + verbose_name="DTS单据类型", + ), + ), + migrations.AlterField( + model_name="tbtendisdtsjob", + name="dts_copy_type", + field=models.CharField( + choices=[ + ("one_app_diff_cluster", "同一业务不同集群"), + ("diff_app_diff_cluster", "不同业务不同集群"), + ("copy_from_rollback_temp", "从回滚临时环境复制数据"), + ("copy_to_other_system", "同步到其他系统,如迁移到腾讯云"), + ("user_built_to_dbm", "业务自建迁移到dbm系统"), + ("copy_from_rollback_instance", "从回档实例复制数据"), + ], + default="", + max_length=64, + verbose_name="DTS数据复制类型", + ), + ), + migrations.AlterField( + model_name="tbtendisdtsjob", + name="online_switch_type", + field=models.CharField( + choices=[("auto_switch", "自动切换"), ("user_confirm", "用户确认切换")], + default=backend.db_services.redis.redis_dts.enums.type_enums.DtsOnlineSwitchType["USER_CONFIRM"], + max_length=64, + verbose_name="在线切换类型", + ), + ), + migrations.AlterField( + model_name="tbtendisdtsjob", + name="sync_disconnect_reminder_frequency", + field=models.CharField( + choices=[("once_daily", "每天一次"), ("once_weekly", "每周一次")], + default="", + max_length=64, + verbose_name="同步断开提醒频率", + ), + ), + migrations.AlterField( + model_name="tbtendisdtsjob", + name="sync_disconnect_type", + field=models.CharField( + choices=[ + ("auto_disconnect_after_replication", "复制完成后自动断开同步关系"), + ("keep_sync_with_reminder", "复制完成后保持同步关系,定时发送断开同步提醒"), + ], + default="", + max_length=64, + verbose_name="同步断开类型", + ), + ), + migrations.AlterField( + model_name="tbtendisdtsjob", + name="write_mode", + field=models.CharField( + choices=[ + ("delete_and_write_to_redis", "先删除同名redis key, 再执行写入(如:del $key + hset $key)"), + ("keep_and_append_to_redis", "保留同名redis key,追加写入(如hset $key)"), + ("flushall_and_write_to_redis", "先清空目标集群所有数据,在写入(如flushall + hset $key)"), + ], + default="", + max_length=64, + verbose_name="写入模式", + ), + ), + migrations.AlterField( + model_name="tbtendisdtstask", + name="write_mode", + field=models.CharField( + choices=[ + ("delete_and_write_to_redis", "先删除同名redis key, 再执行写入(如:del $key + hset $key)"), + ("keep_and_append_to_redis", "保留同名redis key,追加写入(如hset $key)"), + ("flushall_and_write_to_redis", "先清空目标集群所有数据,在写入(如flushall + hset $key)"), + ], + default="", + max_length=64, + verbose_name="写入模式", + ), + ), + ] diff --git a/dbm-ui/backend/db_services/redis/redis_dts/migrations/0009_auto_20230720_1723.py b/dbm-ui/backend/db_services/redis/redis_dts/migrations/0009_auto_20230720_1723.py new file mode 100644 index 0000000000..021d2cc911 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/redis_dts/migrations/0009_auto_20230720_1723.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.19 on 2023-07-20 09:23 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("redis_dts", "0008_auto_20230718_2249"), + ] + + operations = [ + migrations.AddField( + model_name="tbtendisdtsjob", + name="dst_cluster_id", + field=models.BigIntegerField(default=0, verbose_name="目的集群id"), + ), + migrations.AddField( + model_name="tbtendisdtsjob", + name="src_cluster_id", + field=models.BigIntegerField(default=0, verbose_name="源集群id"), + ), + ] diff --git a/dbm-ui/backend/db_services/redis/redis_dts/migrations/0010_auto_20230801_1051.py b/dbm-ui/backend/db_services/redis/redis_dts/migrations/0010_auto_20230801_1051.py new file mode 100644 index 0000000000..933f409448 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/redis_dts/migrations/0010_auto_20230801_1051.py @@ -0,0 +1,65 @@ +# Generated by Django 3.2.19 on 2023-08-01 02:51 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("redis_dts", "0009_auto_20230720_1723"), + ] + + operations = [ + migrations.AlterField( + model_name="tbtendisdtsjob", + name="dts_copy_type", + field=models.CharField( + choices=[ + ("one_app_diff_cluster", "业务内"), + ("diff_app_diff_cluster", "跨业务"), + ("copy_from_rollback_temp", "从回滚临时环境复制数据"), + ("copy_to_other_system", "业务内至第三方"), + ("user_built_to_dbm", "自建集群至业务内"), + ("copy_from_rollback_instance", "构造实例至业务内"), + ], + default="", + max_length=64, + verbose_name="DTS数据复制类型", + ), + ), + migrations.AlterField( + model_name="tbtendisdtsjob", + name="key_black_regex", + field=models.TextField(default="", verbose_name="key正则(排除key)"), + ), + migrations.AlterField( + model_name="tbtendisdtsjob", + name="key_white_regex", + field=models.TextField(default="", verbose_name="key正则(包含key)"), + ), + migrations.AlterField( + model_name="tbtendisdtsjob", + name="reason", + field=models.TextField(default="", verbose_name="备注"), + ), + migrations.AlterField( + model_name="tbtendisdtsjob", + name="src_rollback_instances", + field=models.TextField(default="", verbose_name="回滚临时环境实例"), + ), + migrations.AlterField( + model_name="tbtendisdtstask", + name="key_black_regex", + field=models.TextField(default="", verbose_name="排除key(正则)"), + ), + migrations.AlterField( + model_name="tbtendisdtstask", + name="key_white_regex", + field=models.TextField(default="", verbose_name="包含key(正则)"), + ), + migrations.AlterField( + model_name="tbtendisdtstask", + name="message", + field=models.TextField(default="", verbose_name="信息"), + ), + ] diff --git a/dbm-ui/backend/db_services/redis/redis_dts/migrations/0011_tbtendisdtsjob_online_switch_flow_id_squashed_0015_auto_20230817_1059.py b/dbm-ui/backend/db_services/redis/redis_dts/migrations/0011_tbtendisdtsjob_online_switch_flow_id_squashed_0015_auto_20230817_1059.py new file mode 100644 index 0000000000..c0a2a1e1dc --- /dev/null +++ b/dbm-ui/backend/db_services/redis/redis_dts/migrations/0011_tbtendisdtsjob_online_switch_flow_id_squashed_0015_auto_20230817_1059.py @@ -0,0 +1,59 @@ +# Generated by Django 3.2.19 on 2023-08-18 02:08 + +from django.db import migrations, models + +import backend.db_services.redis.redis_dts.enums.type_enums + + +class Migration(migrations.Migration): + + replaces = [ + ("redis_dts", "0011_tbtendisdtsjob_online_switch_flow_id"), + ("redis_dts", "0012_tbtendisdtsjob_dst_cluster_shutdown_flow_id"), + ("redis_dts", "0013_alter_tbtendisdtsjob_online_switch_type"), + ("redis_dts", "0014_tbtendisdtsjob_dst_cluster_close_flow_id"), + ("redis_dts", "0015_auto_20230817_1059"), + ] + + dependencies = [ + ("redis_dts", "0010_auto_20230801_1051"), + ] + + operations = [ + migrations.AddField( + model_name="tbtendisdtsjob", + name="online_switch_flow_id", + field=models.CharField(default="", max_length=64, verbose_name="在线切换 flow id"), + ), + migrations.AddField( + model_name="tbtendisdtsjob", + name="dst_cluster_shutdown_flow_id", + field=models.CharField(default="", max_length=64, verbose_name="目的集群下架 flow id"), + ), + migrations.AlterField( + model_name="tbtendisdtsjob", + name="online_switch_type", + field=models.CharField( + choices=[("no_confirm", "自动切换"), ("user_confirm", "用户确认切换")], + default=backend.db_services.redis.redis_dts.enums.type_enums.DtsOnlineSwitchType["USER_CONFIRM"], + max_length=64, + verbose_name="在线切换类型", + ), + ), + migrations.AddField( + model_name="tbtendisdtsjob", + name="dst_cluster_close_flow_id", + field=models.CharField(default="", max_length=64, verbose_name="目的集群禁用 flow id"), + ), + migrations.RemoveConstraint( + model_name="tbtendisdtstask", + name="dts_task_unique_key", + ), + migrations.AddConstraint( + model_name="tbtendisdtstask", + constraint=models.UniqueConstraint( + fields=("bill_id", "src_cluster", "dst_cluster", "src_ip", "src_port", "src_kvstore_id"), + name="dts_task_unique_key", + ), + ), + ] diff --git a/dbm-ui/backend/db_services/redis/redis_dts/migrations/__init__.py b/dbm-ui/backend/db_services/redis/redis_dts/migrations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/dbm-ui/backend/db_services/redis/redis_dts/models/__init__.py b/dbm-ui/backend/db_services/redis/redis_dts/models/__init__.py new file mode 100644 index 0000000000..f765dbe8e8 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/redis_dts/models/__init__.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 .tb_dts_distribute_lock import TbTendisDtsDistributeLock +from .tb_dts_server import TendisDtsServer +from .tb_dts_server_blacklist import TbDtsServerBlacklist +from .tb_tendis_dts_job import TbTendisDTSJob +from .tb_tendis_dts_task import TbTendisDtsTask, dts_task_clean_pwd_and_fmt_time, dts_task_format_time diff --git a/dbm-ui/backend/db_services/redis_dts/models/tb_dts_distribute_lock.py b/dbm-ui/backend/db_services/redis/redis_dts/models/tb_dts_distribute_lock.py similarity index 100% rename from dbm-ui/backend/db_services/redis_dts/models/tb_dts_distribute_lock.py rename to dbm-ui/backend/db_services/redis/redis_dts/models/tb_dts_distribute_lock.py diff --git a/dbm-ui/backend/db_services/redis/redis_dts/models/tb_dts_server.py b/dbm-ui/backend/db_services/redis/redis_dts/models/tb_dts_server.py new file mode 100644 index 0000000000..1308f47f99 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/redis_dts/models/tb_dts_server.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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.db import models +from django.utils.translation import ugettext_lazy as _ + + +class TendisDtsServer(models.Model): + bk_cloud_id = models.BigIntegerField(default=0, verbose_name=_("云区域ID")) + ip = models.CharField(max_length=128, default="", verbose_name=_("DTS_Server IP")) + city_id = models.IntegerField(default=0, verbose_name=_("城市ID")) + city_name = models.CharField(max_length=128, default="", verbose_name=_("城市名")) + status = models.IntegerField(default=0, verbose_name=_("状态")) + heartbeat_time = models.DateTimeField(default="1997-01-01 00:00:00", verbose_name=_("最近心跳时间")) + update_time = models.DateTimeField(auto_now=True, verbose_name=_("更新时间")) + + class Meta: + db_table = "tb_tendis_dts_server" + indexes = [ + models.Index(fields=["update_time"], name="index_update_time"), + ] + constraints = [models.UniqueConstraint(fields=["bk_cloud_id", "ip"], name="unique_tendis_dts_server")] diff --git a/dbm-ui/backend/db_services/redis_dts/models/tb_dts_server_blacklist.py b/dbm-ui/backend/db_services/redis/redis_dts/models/tb_dts_server_blacklist.py similarity index 100% rename from dbm-ui/backend/db_services/redis_dts/models/tb_dts_server_blacklist.py rename to dbm-ui/backend/db_services/redis/redis_dts/models/tb_dts_server_blacklist.py diff --git a/dbm-ui/backend/db_services/redis/redis_dts/models/tb_tendis_dts_job.py b/dbm-ui/backend/db_services/redis/redis_dts/models/tb_tendis_dts_job.py new file mode 100644 index 0000000000..2c9bae41df --- /dev/null +++ b/dbm-ui/backend/db_services/redis/redis_dts/models/tb_tendis_dts_job.py @@ -0,0 +1,100 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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.db import models +from django.utils.translation import ugettext_lazy as _ + +from backend.db_services.redis.redis_dts.enums import ( + DtsBillType, + DtsCopyType, + DtsDataCheckFreq, + DtsDataCheckType, + DtsOnlineSwitchType, + DtsSyncDisconnReminderFreq, + DtsSyncDisconnType, + DtsWriteMode, +) + + +class TbTendisDTSJob(models.Model): + id = models.BigAutoField(primary_key=True) + bill_id = models.BigIntegerField(default=0, db_index=True, verbose_name=_("单据号")) + app = models.CharField(max_length=64, default="", verbose_name=_("业务bk_biz_id")) + bk_cloud_id = models.BigIntegerField(default=0, verbose_name=_("云区域id")) + user = models.CharField(max_length=64, default="", verbose_name=_("申请人")) + dts_bill_type = models.CharField( + max_length=64, choices=DtsBillType.get_choices(), default="", verbose_name=_("DTS单据类型") + ) + dts_copy_type = models.CharField( + max_length=64, choices=DtsCopyType.get_choices(), default="", verbose_name=_("DTS数据复制类型") + ) + write_mode = models.CharField( + max_length=64, choices=DtsWriteMode.get_choices(), default="", verbose_name=_("写入模式") + ) + online_switch_type = models.CharField( + max_length=64, + choices=DtsOnlineSwitchType.get_choices(), + default=DtsOnlineSwitchType.USER_CONFIRM, + verbose_name=_("在线切换类型"), + ) + sync_disconnect_type = models.CharField( + max_length=64, choices=DtsSyncDisconnType.get_choices(), default="", verbose_name=_("同步断开类型") + ) + sync_disconnect_reminder_frequency = models.CharField( + max_length=64, choices=DtsSyncDisconnReminderFreq.get_choices(), default="", verbose_name=_("同步断开提醒频率") + ) + data_check_repair_type = models.CharField( + max_length=64, + choices=DtsDataCheckType.get_choices(), + default=DtsDataCheckType.DATA_CHECK_AND_REPAIR, + verbose_name=_("数据校验修复类型"), + ) + data_check_repair_execution_frequency = models.CharField( + max_length=64, + choices=DtsDataCheckFreq.get_choices(), + default=DtsDataCheckFreq.ONCE_AFTER_REPLICATION, + verbose_name=_("数据校验修复执行频率"), + ) + # 最近一次数据校验与修复 flow id + last_data_check_repair_flow_id = models.CharField(max_length=64, default="", verbose_name=_("最近一次数据校验与修复 flow id")) + # 最近一次数据校验与修复 单据执行时间 + last_data_check_repair_flow_execute_time = models.DateTimeField(null=True, verbose_name=_("最近一次数据校验与修复 单据执行时间")) + + online_switch_flow_id = models.CharField(max_length=64, default="", verbose_name=_("在线切换 flow id")) + + dst_cluster_close_flow_id = models.CharField(max_length=64, default="", verbose_name=_("目的集群禁用 flow id")) + dst_cluster_shutdown_flow_id = models.CharField(max_length=64, default="", verbose_name=_("目的集群下架 flow id")) + + src_cluster = models.CharField(max_length=128, default="", verbose_name=_("源集群")) + src_cluster_id = models.BigIntegerField(default=0, verbose_name=_("源集群id")) + # 源集群类型,如 PredixyTendisplusCluster、TwemproxyTendisSSDInstance + src_cluster_type = models.CharField(max_length=64, default="", verbose_name=_("源集群类型")) + src_rollback_bill_id = models.BigIntegerField(default=0, verbose_name=_("回滚单据号")) + src_rollback_instances = models.TextField(default="", verbose_name=_("回滚临时环境实例")) + dst_bk_biz_id = models.CharField(max_length=64, default="", verbose_name=_("目标业务id")) + dst_cluster = models.CharField(max_length=128, default="", verbose_name=_("目的集群")) + dst_cluster_id = models.BigIntegerField(default=0, verbose_name=_("目的集群id")) + # 目标集群类型,如 PredixyTendisplusCluster、TwemproxyTendisSSDInstance + dst_cluster_type = models.CharField(max_length=64, default="", verbose_name=_("目标集群类型")) + key_white_regex = models.TextField(default="", verbose_name=_("key正则(包含key)")) + key_black_regex = models.TextField(default="", verbose_name=_("key正则(排除key)")) + # 任务状态,该字段没用了 + status = models.IntegerField(default=0, db_index=True, verbose_name=_("任务状态")) + reason = models.TextField(default="", verbose_name=_("备注")) + create_time = models.DateTimeField(auto_now_add=True, db_index=True, verbose_name=_("创建时间")) + update_time = models.DateTimeField(auto_now=True, verbose_name=_("更新时间")) + + class Meta: + db_table = "tb_tendis_dts_job" + verbose_name = "tb_tendis_dts_job" + verbose_name_plural = verbose_name + constraints = [ + models.UniqueConstraint(fields=["bill_id", "src_cluster", "dst_cluster"], name="unique_dts_job_key") + ] diff --git a/dbm-ui/backend/db_services/redis_dts/models/tb_tendis_dts_task.py b/dbm-ui/backend/db_services/redis/redis_dts/models/tb_tendis_dts_task.py similarity index 87% rename from dbm-ui/backend/db_services/redis_dts/models/tb_tendis_dts_task.py rename to dbm-ui/backend/db_services/redis/redis_dts/models/tb_tendis_dts_task.py index 7c8643219c..be6ffd1700 100644 --- a/dbm-ui/backend/db_services/redis_dts/models/tb_tendis_dts_task.py +++ b/dbm-ui/backend/db_services/redis/redis_dts/models/tb_tendis_dts_task.py @@ -11,6 +11,7 @@ from django.db import models from django.utils.translation import ugettext_lazy as _ +from backend.db_services.redis.redis_dts.enums import DtsWriteMode from backend.utils.time import datetime2str @@ -21,6 +22,9 @@ class TbTendisDtsTask(models.Model): app = models.CharField(max_length=64, default="", verbose_name=_("业务bk_biz_id")) bk_cloud_id = models.BigIntegerField(default=0, verbose_name=_("云区域id")) dts_server = models.CharField(max_length=128, default="", verbose_name=_("执行迁移任务的dts_server")) + write_mode = models.CharField( + max_length=64, choices=DtsWriteMode.get_choices(), default="", verbose_name=_("写入模式") + ) src_cluster = models.CharField(max_length=128, default="", verbose_name=_("源集群")) src_cluster_priority = models.IntegerField(default=0, verbose_name=_("源集群优先级,值越大,优先级越高")) src_ip = models.CharField(max_length=128, default="", verbose_name=_("源slave_ip")) @@ -43,8 +47,9 @@ class TbTendisDtsTask(models.Model): src_new_logcount = models.BigIntegerField(default=0, verbose_name=_("源实例slave-keep-log-count的新值")) is_src_logcount_restored = models.IntegerField(default=0, verbose_name=_("源实例slave-keep-log-count是否恢复")) src_have_list_keys = models.IntegerField(default=0, verbose_name=_("srcRedis是否包含list类型key")) - key_white_regex = models.BinaryField(default=b"", verbose_name=_("包含key(正则)")) - key_black_regex = models.BinaryField(default=b"", verbose_name=_("排除key(正则)")) + src_twemproxy_hash_tag_enabled = models.IntegerField(default=0, verbose_name=_("源twemproxy集群是否开启hash_tag")) + key_white_regex = models.TextField(default="", verbose_name=_("包含key(正则)")) + key_black_regex = models.TextField(default="", verbose_name=_("排除key(正则)")) src_kvstore_id = models.IntegerField(default=0, verbose_name="tendisplus kvstore id") dst_cluster = models.CharField(max_length=128, default="", verbose_name=_("目的集群")) dst_password = models.CharField(max_length=128, default="", verbose_name=_("目的密码base64值")) @@ -63,7 +68,7 @@ class TbTendisDtsTask(models.Model): kill_syncer = models.IntegerField(default=0, verbose_name=_("杀死syncer")) # 任务执行状态,0:未开始 1:执行中 2:完成 -1:发生错误 status = models.IntegerField(default=0, verbose_name=_("任务状态")) - message = models.BinaryField(default=b"", verbose_name=_("信息")) + message = models.TextField(default="", verbose_name=_("信息")) # 迁移过程中被忽略的错误,如key同名不同类型WRONGTYPE Operation ignore_errlist = models.CharField(default="", max_length=512, verbose_name=_("被忽略的错误")) resync_from_time = models.DateTimeField(null=True, blank=True, verbose_name=_("sync从该时间点重新同步")) @@ -79,14 +84,20 @@ class Meta: models.Index(fields=["dts_server", "update_time"], name="idx_jobserver_updatetime"), models.Index(fields=["bill_id", "src_cluster", "dst_cluster"], name="idx_billid_clusters"), ] + constraints = [ + models.UniqueConstraint( + fields=["bill_id", "src_cluster", "dst_cluster", "src_ip", "src_port", "src_kvstore_id"], + name="dts_task_unique_key", + ) + ] def get_src_redis_addr(self) -> str: return "%s:%d" % (self.src_ip, self.src_port) -def dts_task_clean_passwd_and_format_time(json_data: dict, row: TbTendisDtsTask): - json_data["src_password"] = "" - json_data["dst_password"] = "" +def dts_task_clean_pwd_and_fmt_time(json_data: dict, row: TbTendisDtsTask): + del json_data["src_password"] + del json_data["dst_password"] dts_task_format_time(json_data, row) @@ -94,9 +105,3 @@ def dts_task_format_time(json_data: dict, row: TbTendisDtsTask): json_data["resync_from_time"] = datetime2str(row.resync_from_time) if row.resync_from_time else "" json_data["create_time"] = datetime2str(row.create_time) json_data["update_time"] = datetime2str(row.update_time) - - -def dts_task_binary_to_str(json_data: dict, row: TbTendisDtsTask): - json_data["key_white_regex"] = row.key_white_regex.decode("utf-8") if row.key_white_regex else "" - json_data["key_black_regex"] = row.key_black_regex.decode("utf-8") if row.key_black_regex else "" - json_data["message"] = row.message.decode("utf-8") if row.message else "" diff --git a/dbm-ui/backend/db_services/redis/redis_dts/serializers.py b/dbm-ui/backend/db_services/redis/redis_dts/serializers.py new file mode 100644 index 0000000000..d31060bfb0 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/redis_dts/serializers.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 gettext_lazy as _ +from rest_framework import serializers + +from backend.db_services.redis.redis_dts.models import TbTendisDTSJob + + +class TbTendisDTSJobSerializer(serializers.ModelSerializer): + class Meta: + model = TbTendisDTSJob + fields = "__all__" + + +class TendisDtsHistoryJobSLZ(serializers.Serializer): + cluster_name = serializers.CharField(help_text=_("集群名"), required=False, allow_blank=True) + start_time = serializers.CharField(help_text=_("开始时间"), required=False) + end_time = serializers.CharField(help_text=_("结束时间"), required=False) + page = serializers.IntegerField(help_text=_("页码"), required=False, default=1) + page_size = serializers.IntegerField(help_text=_("每页数量"), required=False, default=0) + + +class DtsJobTasksSLZ(serializers.Serializer): + bill_id = serializers.IntegerField(help_text=_("单据ID"), required=True) + src_cluster = serializers.CharField(help_text=_("源集群"), required=True) + dst_cluster = serializers.CharField(help_text=_("目标集群"), required=True) + + +class DtsTaskIDsSLZ(serializers.Serializer): + task_ids = serializers.ListField( + help_text=_("子任务ID列表"), child=serializers.IntegerField(), allow_empty=False, required=True + ) + + +class DtsDataCopyBaseItemSLZ(serializers.Serializer): + src_cluster = serializers.CharField(help_text=_("源集群"), required=True) + src_cluster_password = serializers.CharField(help_text=_("源集群密码"), allow_blank=True) + dst_cluster = serializers.CharField(help_text=_("目标集群"), required=True) + dst_cluster_password = serializers.CharField(help_text=_("目标集群密码"), allow_blank=True) + + +class DtsTestRedisConnectionSLZ(serializers.Serializer): + data_copy_type = serializers.CharField(help_text=_("数据复制类型"), required=True) + infos = serializers.ListField( + help_text=_("复制列表"), child=DtsDataCopyBaseItemSLZ(), allow_empty=False, required=True + ) diff --git a/dbm-ui/backend/db_services/redis/redis_dts/urls.py b/dbm-ui/backend/db_services/redis/redis_dts/urls.py new file mode 100644 index 0000000000..37f0e46928 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/redis_dts/urls.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 rest_framework.routers import DefaultRouter + +from .views import TendisDtsJobViewSet + +routers = DefaultRouter(trailing_slash=True) + +routers.register("dts", TendisDtsJobViewSet, basename="dts") + +urlpatterns = routers.urls diff --git a/dbm-ui/backend/db_services/redis/redis_dts/util.py b/dbm-ui/backend/db_services/redis/redis_dts/util.py new file mode 100644 index 0000000000..267c500b64 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/redis_dts/util.py @@ -0,0 +1,905 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +import ast +import base64 +import logging.config +import re +import traceback +from typing import Dict, List, Tuple + +from django.db.models import Q + +from backend.components import DBConfigApi, DRSApi +from backend.components.dbconfig.constants import FormatType, LevelName +from backend.constants import IP_PORT_DIVIDER +from backend.db_meta.enums import ClusterType, InstanceRole, InstanceStatus +from backend.db_meta.models import Cluster +from backend.db_services.redis.redis_dts.constants import DtsOperateType, DtsTaskType +from backend.db_services.redis.redis_dts.enums import DtsBillType, DtsCopyType, DtsSyncStatus +from backend.db_services.redis.redis_dts.models import TbTendisDTSJob, TbTendisDtsTask +from backend.db_services.redis.rollback.models import TbTendisRollbackTasks +from backend.db_services.redis.util import ( + is_redis_instance_type, + is_tendisplus_instance_type, + is_tendisssd_instance_type, + is_twemproxy_proxy_type, +) +from backend.flow.consts import DEFAULT_TENDISPLUS_KVSTORECOUNT, ConfigTypeEnum +from backend.flow.utils.redis.redis_cluster_nodes import get_masters_with_slots +from backend.flow.utils.redis.redis_context_dataclass import ActKwargs +from backend.flow.utils.redis.redis_proxy_util import decode_twemproxy_backends +from backend.utils.time import datetime2str + +from .models import TendisDtsServer + +logger = logging.getLogger("flow") + + +def get_safe_regex_pattern(key_regex): + """ + 将用户输入的正则表达式转换为正确的正则表达式 + """ + if key_regex == "": + return "" + + if key_regex == "*" or key_regex == ".*" or key_regex.startswith("^.*"): + return ".*" + + final_pattern = "" + patterns = key_regex.split("\n") + + for pattern in patterns: + tmp_pattern = pattern.strip() + if tmp_pattern == "": + continue + + tmp_pattern = tmp_pattern.replace("|", "\\|") + tmp_pattern = tmp_pattern.replace(".", "\\.") + tmp_pattern = tmp_pattern.replace("*", ".*") + + if final_pattern == "": + if tmp_pattern.startswith("^"): + final_pattern = tmp_pattern + else: + final_pattern = "^" + tmp_pattern + else: + if tmp_pattern.startswith("^"): + final_pattern += "|" + tmp_pattern + else: + final_pattern += "|^" + tmp_pattern + + return final_pattern + + +def get_redis_type_by_cluster_type(cluster_type: str) -> str: + """ + 根据集群类型获取redis类型 + """ + if cluster_type in [ + ClusterType.TendisPredixyRedisCluster, + ClusterType.RedisCluster, + ClusterType.TendisTwemproxyRedisInstance, + ClusterType.TendisRedisInstance, + ClusterType.TendisRedisCluster, + ]: + return ClusterType.TendisRedisInstance.value + elif cluster_type in [ + ClusterType.TendisPredixyTendisplusCluster, + ClusterType.TendisTwemproxyTendisplusIns, + ClusterType.TendisTendisplusInsance, + ClusterType.TendisTendisplusCluster, + ]: + return ClusterType.TendisTendisplusInsance.value + elif cluster_type in [ClusterType.TwemproxyTendisSSDInstance, ClusterType.TwemproxyTendisSSDInstance]: + return ClusterType.TendisTendisSSDInstance.value + else: + raise Exception(f"get redis type by cluster type failed, cluster_type: {cluster_type}") + + +def get_cluster_info_by_id( + bk_biz_id: int, + cluster_id: int, +) -> dict: + """ + 根据集群id获取集群信息(域名,id,类型等) + """ + try: + # cluster_id = domain_without_port(cluster_id) + cluster = Cluster.objects.get(bk_biz_id=bk_biz_id, id=cluster_id) + one_master = cluster.storageinstance_set.filter( + instance_role=InstanceRole.REDIS_MASTER.value, status=InstanceStatus.RUNNING + ).first() + proxy_conf = DBConfigApi.query_conf_item( + params={ + "bk_biz_id": str(cluster.bk_biz_id), + "level_name": LevelName.CLUSTER.value, + "level_value": cluster.immute_domain, + "level_info": {"module": str(cluster.db_module_id)}, + "conf_file": cluster.proxy_version, + "conf_type": ConfigTypeEnum.ProxyConf, + "namespace": cluster.cluster_type, + "format": FormatType.MAP, + } + ) + proxy_content = proxy_conf.get("content", {}) + + redis_conf = DBConfigApi.query_conf_item( + params={ + "bk_biz_id": str(cluster.bk_biz_id), + "level_name": LevelName.CLUSTER.value, + "level_value": cluster.immute_domain, + "level_info": {"module": str(cluster.db_module_id)}, + "conf_file": cluster.major_version, + "conf_type": ConfigTypeEnum.DBConf, + "namespace": cluster.cluster_type, + "format": FormatType.MAP, + } + ) + redis_content = redis_conf.get("content", {}) + + # 获取redis的databases + master_addrs = ["{}:{}".format(one_master.machine.ip, one_master.port)] + resp = DRSApi.redis_rpc( + { + "addresses": master_addrs, + "db_num": 0, + "password": redis_content.get("requirepass"), + "command": "confxx get databases", + "bk_cloud_id": cluster.bk_cloud_id, + } + ) + databases = 2 + if len(resp) > 0 and resp[0].get("result", "") != "": + databases = int(resp[0]["result"].split("\n")[1]) + + return { + "cluster_id": cluster.id, + "bk_cloud_id": cluster.bk_cloud_id, + "cluster_domain": cluster.immute_domain, + "cluster_type": cluster.cluster_type, + "cluster_password": proxy_content.get("password"), + "cluster_port": proxy_content.get("port"), + "cluster_version": cluster.major_version, + "cluster_name": cluster.name, + "cluster_city_name": one_master.machine.bk_city.bk_idc_city_name, + "redis_password": redis_content.get("requirepass"), + "redis_databases": databases, + "region": cluster.region, + } + except Exception as e: + traceback.print_exc() + logger.error(f"get cluster info by domain failed {e}, cluster_id: {cluster_id}") + raise Exception(f"get cluster info by domain failed {e}, cluster_id: {cluster_id}") + + +def get_cluster_one_running_master(bk_biz_id: int, cluster_id: int) -> dict: + """ + 获取集群下的slaves实例信息 + """ + try: + cluster = Cluster.objects.get(bk_biz_id=bk_biz_id, id=cluster_id) + one_master_instance = cluster.storageinstance_set.filter( + instance_role=InstanceRole.REDIS_MASTER.value, status=InstanceStatus.RUNNING + ).first() + running_master = { + "ip": one_master_instance.machine.ip, + "port": one_master_instance.port, + "status": one_master_instance.status, + "instance_role": one_master_instance.instance_role, + } + return running_master + except Exception as e: + logger.error(f"get cluster one running master data failed {e}, cluster_id: {cluster_id}") + raise Exception(f"get cluster one running master data failed {e}, cluster_id: {cluster_id}") + + +def get_cluster_segment_info(cluster_id: int) -> Tuple[dict, dict]: + master_segment_info = {} + slave_segment_info = {} + cluster = Cluster.objects.get(id=cluster_id) + for dtl_row in cluster.nosqlstoragesetdtl_set.all(): + master_obj = dtl_row.instance + master_inst = "{}:{}".format(master_obj.machine.ip, master_obj.port) + seg_start = int(dtl_row.seg_range.split("-")[0]) + seg_end = int(dtl_row.seg_range.split("-")[1]) + master_segment_info[master_inst] = {"segment_start": seg_start, "segment_end": seg_end} + if master_obj.as_ejector and master_obj.as_ejector.first(): + slave_obj = master_obj.as_ejector.get().receiver + slave_inst = "{}:{}".format(slave_obj.machine.ip, slave_obj.port) + slave_segment_info[slave_inst] = {"segment_start": seg_start, "segment_end": seg_end} + return master_segment_info, slave_segment_info + + +def get_dbm_cluster_slaves_data(bk_biz_id: int, cluster_id: int) -> Tuple[list, list]: + """ + 获取dbm集群的slaves实例信息 + """ + try: + cluster = Cluster.objects.get(bk_biz_id=bk_biz_id, id=cluster_id) + slave_instances = [] + uniq_hosts = set() + slave_hosts = [] + kvstore: int = 0 + _, slave_segment_info = get_cluster_segment_info(cluster_id) + for slave in cluster.storageinstance_set.filter(instance_role=InstanceRole.REDIS_SLAVE.value): + if is_tendisplus_instance_type(cluster.cluster_type): + kvstore = DEFAULT_TENDISPLUS_KVSTORECOUNT + else: + kvstore = 1 + slave_inst = "{}:{}".format(slave.machine.ip, slave.port) + slave_instances.append( + { + "ip": slave.machine.ip, + "port": slave.port, + "db_type": get_redis_type_by_cluster_type(cluster.cluster_type), + "status": slave.status, + "data_size": 0, + "kvstorecount": kvstore, + "segment_start": slave_segment_info[slave_inst]["segment_start"], + "segment_end": slave_segment_info[slave_inst]["segment_end"], + } + ) + if slave.machine.ip in uniq_hosts: + continue + uniq_hosts.add(slave.machine.ip) + slave_hosts.append( + { + "ip": slave.machine.ip, + "mem_info": {}, + "disk_info": {}, + } + ) + return slave_instances, slave_hosts + except Exception as e: + logger.error(f"get cluster slave data failed {e}, cluster_id: {cluster_id}") + raise Exception(f"get cluster slave data failed {e}, cluster_id: {cluster_id}") + + +def get_rollback_cluster_masters_data(rollback_row: TbTendisRollbackTasks) -> Tuple[list, list]: + """ + 获取数据构造临时集群的masters实例信息 + """ + master_instances = [] + master_hosts = [] + uniq_hosts = set() + kvstore: int = 0 + proxy_backend_decoded = {} + if is_twemproxy_proxy_type(rollback_row.temp_cluster_type): + proxy_ip_port = rollback_row.temp_cluster_proxy.split(IP_PORT_DIVIDER) + proxy_admin_addr = proxy_ip_port[0] + ":" + str(int(proxy_ip_port[1]) + 1000) + resp = DRSApi.twemproxy_rpc( + { + "addresses": [proxy_admin_addr], + "db_num": 0, + "password": "", + "command": "get nosqlproxy servers", + "bk_cloud_id": rollback_row.bk_cloud_id, + } + ) + _, proxy_backend_decoded = decode_twemproxy_backends(resp[0]["result"]) + + for master in rollback_row.temp_instance_range: + if is_tendisplus_instance_type(rollback_row.temp_cluster_type): + kvstore = DEFAULT_TENDISPLUS_KVSTORECOUNT + else: + kvstore = 1 + ip_port = master.split(IP_PORT_DIVIDER) + + segment_start = -1 + segment_end = -1 + if master in proxy_backend_decoded: + segment_start = proxy_backend_decoded.get(master).segment_start + segment_end = proxy_backend_decoded.get(master).segment_end + master_instances.append( + { + "ip": ip_port[0], + "port": int(ip_port[1]), + "db_type": get_redis_type_by_cluster_type(rollback_row.temp_cluster_type), + "status": InstanceStatus.RUNNING, + "data_size": 0, + "kvstorecount": kvstore, + "segment_start": segment_start, + "segment_end": segment_end, + } + ) + if ip_port[0] in uniq_hosts: + continue + uniq_hosts.add(ip_port[0]) + master_hosts.append( + { + "ip": ip_port[0], + "mem_info": {}, + "disk_info": {}, + } + ) + return master_instances, master_hosts + + +def get_user_built_cluster_masters_data( + addr: str, password: str, bk_cloud_id: int, cluster_type: str +) -> Tuple[list, list]: + """ + 获取用户自建集群的masters实例信息 + """ + master_instances = [] + master_hosts = [] + uniq_hosts = set() + if cluster_type == ClusterType.TendisRedisInstance: + ip_port = addr.split(IP_PORT_DIVIDER) + master_instances.append( + { + "ip": ip_port[0], + "port": int(ip_port[1]), + "db_type": get_redis_type_by_cluster_type(cluster_type), + "status": InstanceStatus.RUNNING, + "data_size": 0, + "kvstorecount": 1, + "segment_start": -1, + "segment_end": -1, + } + ) + master_hosts.append( + { + "ip": ip_port[0], + "mem_info": {}, + "disk_info": {}, + } + ) + elif cluster_type == ClusterType.TendisRedisCluster: + resp = DRSApi.redis_rpc( + { + "addresses": [addr], + "db_num": 0, + "password": password, + "command": "cluster nodes", + "bk_cloud_id": bk_cloud_id, + } + ) + masters_with_slots = get_masters_with_slots(resp[0]["result"]) + if len(masters_with_slots) == 0: + logger.error("user built cluster({}) has no master with slots".format(addr)) + raise Exception("user built cluster({}) has no master with slots".format(addr)) + for master in masters_with_slots: + master_instances.append( + { + "ip": master.ip, + "port": master.port, + "db_type": get_redis_type_by_cluster_type(cluster_type), + "status": InstanceStatus.RUNNING, + "data_size": 0, + "kvstorecount": 1, + "segment_start": -1, + "segment_end": -1, + } + ) + if master.ip in uniq_hosts: + continue + uniq_hosts.add(master.ip) + master_hosts.append( + { + "ip": master.ip, + "mem_info": {}, + "disk_info": {}, + } + ) + return master_instances, master_hosts + + +def decode_info_cmd(info_str: str) -> Dict: + info_ret: Dict[str, dict] = {} + info_list: List = info_str.split("\n") + for info_item in info_list: + info_item = info_item.strip() + if info_item.startswith("#"): + continue + if len(info_item) == 0: + continue + tmp_list = info_item.split(IP_PORT_DIVIDER, 1) + if len(tmp_list) < 2: + continue + tmp_list[0] = tmp_list[0].strip() + tmp_list[1] = tmp_list[1].strip() + info_ret[tmp_list[0]] = tmp_list[1] + return info_ret + + +def get_redis_slaves_data_size(cluster_data: dict) -> list: + """ + 获取redis slave的数据大小 + """ + info_cmd: str = "" + if is_redis_instance_type(cluster_data["cluster_type"]): + info_cmd = "info memory" + elif is_tendisplus_instance_type(cluster_data["cluster_type"]): + info_cmd = "info Dataset" + elif is_tendisssd_instance_type(cluster_data["cluster_type"]): + info_cmd = "info" + slave_addrs = [slave["ip"] + IP_PORT_DIVIDER + str(slave["port"]) for slave in cluster_data["slave_instances"]] + resp = DRSApi.redis_rpc( + { + "addresses": slave_addrs, + "db_num": 0, + "password": cluster_data["redis_password"], + "command": info_cmd, + "bk_cloud_id": cluster_data["bk_cloud_id"], + } + ) + info_ret: Dict[str, Dict] = {} + for item in resp: + info_ret[item["address"]] = decode_info_cmd(item["result"]) + new_slave_instances: List = [] + for slave in cluster_data["slave_instances"]: + slave_addr = slave["ip"] + IP_PORT_DIVIDER + str(slave["port"]) + if slave["db_type"] == ClusterType.TendisRedisInstance.value: + slave["data_size"] = int(info_ret[slave_addr]["used_memory"]) + elif slave["db_type"] == ClusterType.TendisTendisplusInsance.value: + slave["data_size"] = int(info_ret[slave_addr]["rocksdb.total-sst-files-size"]) + elif slave["db_type"] == ClusterType.TendisTendisSSDInstance.value: + rockdb_size = 0 + level_head_reg = re.compile(r"^level-\d+$") + level_data_reg = re.compile(r"^bytes=(\d+),num_entries=(\d+),num_deletions=(\d+)") + for k, v in info_ret[slave_addr].items(): + if level_head_reg.match(k): + tmp_list = level_data_reg.findall(v) + if len(tmp_list) != 1: + err = f"redis:{slave_addr} info 'RocksDB Level stats' format not correct,{k}:{v}" + logging.error(err) + raise Exception(err) + size01 = int(tmp_list[0][0]) + rockdb_size += size01 + slave["data_size"] = rockdb_size + new_slave_instances.append(slave) + return new_slave_instances + + +def complete_redis_dts_kwargs_src_data(bk_biz_id: int, dts_copy_type: str, info: dict, act_kwargs: ActKwargs): + """ + 完善redis dts 源集群数据 + """ + src_cluster_data: dict = {} + if dts_copy_type == DtsCopyType.USER_BUILT_TO_DBM: + src_cluster_data["cluster_addr"] = info["src_cluster"] + src_cluster_data["cluster_id"] = 0 + src_cluster_data["cluster_password"] = info["src_cluster_password"] + src_cluster_data["redis_password"] = info["src_cluster_password"] + src_cluster_data["cluster_type"] = info["src_cluster_type"] + + # 无法确定源集群的bk_cloud_id,此时以目的集群的为准 + cluster_info = get_cluster_info_by_id(bk_biz_id, int(info["dst_cluster"])) + src_cluster_data["bk_cloud_id"] = cluster_info["bk_cloud_id"] + src_cluster_data["cluster_city_name"] = cluster_info["cluster_city_name"] + + # 获取源集群的master信息做为迁移源实例 + master_instances, master_hosts = get_user_built_cluster_masters_data( + src_cluster_data["cluster_addr"], + src_cluster_data["cluster_password"], + src_cluster_data["bk_cloud_id"], + src_cluster_data["cluster_type"], + ) + src_cluster_data["one_running_master"] = master_instances[0] + src_cluster_data["slave_instances"] = master_instances + src_cluster_data["slave_hosts"] = master_hosts + src_cluster_data["slave_instances"] = get_redis_slaves_data_size(src_cluster_data) + elif dts_copy_type == DtsCopyType.COPY_FROM_ROLLBACK_INSTANCE: + src_cluster_data["cluster_addr"] = info["src_cluster"] + src_cluster_data["cluster_id"] = 0 + recovery_time = datetime2str(info["recovery_time_point"]) + rollback_row = TbTendisRollbackTasks.objects.get( + temp_cluster_proxy=info["src_cluster"], recovery_time_point=recovery_time, destroyed_status=0 + ) + + proxy_password_bytes = ast.literal_eval(rollback_row.temp_proxy_password) + redis_password_bytes = ast.literal_eval(rollback_row.temp_redis_password) + src_cluster_data["cluster_password"] = base64.b64decode(proxy_password_bytes).decode("utf-8") + src_cluster_data["redis_password"] = base64.b64decode(redis_password_bytes).decode("utf-8") + src_cluster_data["cluster_type"] = rollback_row.prod_cluster_type + + cluster_info = get_cluster_info_by_id(bk_biz_id, int(info["dst_cluster"])) + src_cluster_data["bk_cloud_id"] = cluster_info["bk_cloud_id"] + src_cluster_data["cluster_city_name"] = cluster_info["cluster_city_name"] + + # 获取源集群的master信息做为迁移源实例 + master_instances, master_hosts = get_rollback_cluster_masters_data(rollback_row) + src_cluster_data["one_running_master"] = master_instances[0] + src_cluster_data["slave_instances"] = master_instances + src_cluster_data["slave_hosts"] = master_hosts + src_cluster_data["slave_instances"] = get_redis_slaves_data_size(src_cluster_data) + else: + cluster_info = get_cluster_info_by_id(bk_biz_id, int(info["src_cluster"])) + src_cluster_data["bk_cloud_id"] = cluster_info["bk_cloud_id"] + src_cluster_data["cluster_id"] = cluster_info["cluster_id"] + src_cluster_data["cluster_addr"] = ( + cluster_info["cluster_domain"] + IP_PORT_DIVIDER + str(cluster_info["cluster_port"]) + ) + src_cluster_data["cluster_password"] = cluster_info["cluster_password"] + src_cluster_data["cluster_type"] = cluster_info["cluster_type"] + src_cluster_data["cluster_city_name"] = cluster_info["cluster_city_name"] + src_cluster_data["redis_password"] = cluster_info["redis_password"] + + src_cluster_data["one_running_master"] = get_cluster_one_running_master(bk_biz_id, int(info["src_cluster"])) + src_slave_instances, src_slave_hosts = get_dbm_cluster_slaves_data(bk_biz_id, int(info["src_cluster"])) + src_cluster_data["slave_instances"] = src_slave_instances + src_cluster_data["slave_hosts"] = src_slave_hosts + src_cluster_data["slave_instances"] = get_redis_slaves_data_size(src_cluster_data) + + act_kwargs.cluster["src"] = src_cluster_data + + +def complete_dst_data_without_cluster_id(bk_biz_id: int, dst_cluster_type: str, info: dict, act_kwargs: ActKwargs): + """ + 未知 dst_cluster_id,完善redis dts 目的集群数据 + """ + dst_cluster_data: dict = {} + dst_proxy_instances: list = [] + dst_cluster_data["cluster_id"] = 0 + dst_cluster_data["cluster_addr"] = info["dst_cluster"] + dst_cluster_data["cluster_password"] = info["dst_cluster_password"] + dst_cluster_data["cluster_type"] = dst_cluster_type + # 目的集群的bk_cloud_id,此时以源集群的为准 + dst_cluster_data["bk_cloud_id"] = act_kwargs.cluster["src"]["bk_cloud_id"] + dst_proxy_instances.append( + { + "addr": info["dst_cluster"], + "status": InstanceStatus.RUNNING, + } + ) + dst_cluster_data["proxy_instances"] = dst_proxy_instances + act_kwargs.cluster["dst"] = dst_cluster_data + + +def complete_dst_data_with_cluster_id(bk_biz_id: int, dst_cluster_id: int, act_kwargs: ActKwargs): + """ + 已知 dst_cluster_id,完善redis dts 目的集群数据 + """ + dst_cluster_data: dict = {} + dst_proxy_instances: list = [] + dst_master_instances: list = [] + cluster_info = get_cluster_info_by_id(bk_biz_id, dst_cluster_id) + dst_cluster_data["cluster_id"] = cluster_info["cluster_id"] + dst_cluster_data["bk_cloud_id"] = cluster_info["bk_cloud_id"] + dst_cluster_data["cluster_addr"] = ( + cluster_info["cluster_domain"] + IP_PORT_DIVIDER + str(cluster_info["cluster_port"]) + ) + dst_cluster_data["cluster_password"] = cluster_info["cluster_password"] + dst_cluster_data["cluster_type"] = cluster_info["cluster_type"] + dst_cluster_data["redis_password"] = cluster_info["redis_password"] + + cluster = Cluster.objects.get(id=dst_cluster_id) + dst_cluster_data["major_version"] = cluster.major_version + for proxy in cluster.proxyinstance_set.filter(status=InstanceStatus.RUNNING): + dst_proxy_instances.append( + { + "addr": proxy.machine.ip + IP_PORT_DIVIDER + str(proxy.port), + "status": proxy.status, + } + ) + for master in cluster.storageinstance_set.filter(instance_role=InstanceRole.REDIS_MASTER.value): + dst_master_instances.append( + { + "ip": master.machine.ip, + "port": master.port, + "db_type": get_redis_type_by_cluster_type(cluster.cluster_type), + "status": master.status, + "data_size": 0, + "kvstorecount": 1, + "segment_start": -1, + "segment_end": -1, + } + ) + dst_cluster_data["running_masters"] = dst_master_instances + + dst_cluster_data["proxy_instances"] = dst_proxy_instances + act_kwargs.cluster["dst"] = dst_cluster_data + + +def complete_redis_dts_kwargs_dst_data( + bk_biz_id: int, dts_copy_type: str, dst_cluster_type: str, info: dict, act_kwargs: ActKwargs +): + """ + 完善redis dts 目的集群数据 + """ + if dts_copy_type == DtsCopyType.COPY_TO_OTHER_SYSTEM.value: + complete_dst_data_without_cluster_id(bk_biz_id, ClusterType.TendisRedisInstance.value, info, act_kwargs) + elif dts_copy_type in [ + DtsBillType.REDIS_CLUSTER_SHARD_NUM_UPDATE.value, + DtsBillType.REDIS_CLUSTER_TYPE_UPDATE.value, + ]: + dst_addr_pair = info["dst_cluster"].split(":") + dst_domain = dst_addr_pair[0] + cluster: Cluster = None + try: + cluster = Cluster.objects.get(bk_biz_id=bk_biz_id, immute_domain=dst_domain) + except Cluster.DoesNotExist: + complete_dst_data_without_cluster_id(bk_biz_id, dst_cluster_type, info, act_kwargs) + return + complete_dst_data_with_cluster_id(bk_biz_id, cluster.id, act_kwargs) + else: + complete_dst_data_with_cluster_id(bk_biz_id, int(info["dst_cluster"]), act_kwargs) + + +def get_etc_hosts_lines_and_ips( + bk_biz_id: int, dts_copy_type: str, info: dict, dst_install_params: dict = None +) -> dict: + """ + 获取etc_hosts文件的内容和ip列表 + """ + etc_hosts_lines = "" # 代表需要添加到 /etc/hosts 中的内容 + ip_list = set() # 代表需要添加 /etc/hosts 的ip列表 + if dts_copy_type == DtsCopyType.USER_BUILT_TO_DBM.value: + dst_cluster = Cluster.objects.get(bk_biz_id=bk_biz_id, id=int(info["dst_cluster"])) + idx = 0 + for proxy_inst in dst_cluster.proxyinstance_set.filter(status=InstanceStatus.RUNNING): + if idx == 0: + etc_hosts_lines += "{} {}\n".format(proxy_inst.machine.ip, dst_cluster.immute_domain) + idx = 1 + ip_list.add(proxy_inst.machine.ip) + elif dts_copy_type == DtsCopyType.COPY_TO_OTHER_SYSTEM.value: + src_cluster = Cluster.objects.get(bk_biz_id=bk_biz_id, id=int(info["src_cluster"])) + for proxy_inst in src_cluster.proxyinstance_set.filter(status=InstanceStatus.RUNNING): + etc_hosts_lines += "{} {}\n".format(proxy_inst.machine.ip, src_cluster.immute_domain) + break + for slave_inst in src_cluster.storageinstance_set.filter( + status=InstanceStatus.RUNNING, instance_role=InstanceRole.REDIS_SLAVE.value + ): + ip_list.add(slave_inst.machine.ip) + elif dts_copy_type in [ + DtsBillType.REDIS_CLUSTER_SHARD_NUM_UPDATE.value, + DtsBillType.REDIS_CLUSTER_TYPE_UPDATE.value, + ]: + src_cluster = Cluster.objects.get(bk_biz_id=bk_biz_id, id=int(info["src_cluster"])) + for proxy_inst in src_cluster.proxyinstance_set.filter(status=InstanceStatus.RUNNING): + etc_hosts_lines += "{} {}\n".format(proxy_inst.machine.ip, src_cluster.immute_domain) + break + proxy_ip = dst_install_params["proxy"][0]["ip"] + etc_hosts_lines += "{} {}\n".format(proxy_ip, dst_install_params["cluster_domain"]) + for slave_inst in src_cluster.storageinstance_set.filter( + status=InstanceStatus.RUNNING, instance_role=InstanceRole.REDIS_SLAVE.value + ): + ip_list.add(slave_inst.machine.ip) + elif dts_copy_type == DtsCopyType.COPY_FROM_ROLLBACK_INSTANCE.value: + recovery_time = datetime2str(info["recovery_time_point"]) + rollback_task = TbTendisRollbackTasks.objects.get( + temp_cluster_proxy=info["src_cluster"], recovery_time_point=recovery_time, destroyed_status=0 + ) + for master in rollback_task.temp_instance_range: + ip_port = master.split(IP_PORT_DIVIDER) + ip_list.add(ip_port[0]) + dst_cluster = Cluster.objects.get(bk_biz_id=bk_biz_id, id=int(info["dst_cluster"])) + for proxy_inst in dst_cluster.proxyinstance_set.filter(status=InstanceStatus.RUNNING): + etc_hosts_lines += "{} {}\n".format(proxy_inst.machine.ip, dst_cluster.immute_domain) + break + else: + src_cluster = Cluster.objects.get(bk_biz_id=bk_biz_id, id=int(info["src_cluster"])) + for proxy_inst in src_cluster.proxyinstance_set.filter(status=InstanceStatus.RUNNING): + etc_hosts_lines += "{} {}\n".format(proxy_inst.machine.ip, src_cluster.immute_domain) + break + dst_cluster = Cluster.objects.get(bk_biz_id=bk_biz_id, id=int(info["dst_cluster"])) + for proxy_inst in dst_cluster.proxyinstance_set.filter(status=InstanceStatus.RUNNING): + etc_hosts_lines += "{} {}\n".format(proxy_inst.machine.ip, dst_cluster.immute_domain) + break + for slave_inst in src_cluster.storageinstance_set.filter( + status=InstanceStatus.RUNNING, instance_role=InstanceRole.REDIS_SLAVE.value + ): + ip_list.add(slave_inst.machine.ip) + for row in TendisDtsServer.objects.all(): + ip_list.add(row.ip) + return { + "etc_hosts_lines": etc_hosts_lines, + "ip_list": list(ip_list), + } + + +def is_in_full_transfer(row: TbTendisDtsTask) -> bool: + """ + 判断是否全量传输中 + """ + if row.src_dbtype == ClusterType.TendisTendisSSDInstance: + if ( + row.task_type + in [ + DtsTaskType.TENDISSSD_BACKUP, + DtsTaskType.TENDISSSD_BACKUPFILE_FETCH, + DtsTaskType.TENDISSSD_TREDISDUMP, + DtsTaskType.TENDISSSD_CMDSIMPOTER, + ] + and row.status in [0, 1] + ): + return True + if row.src_dbtype == ClusterType.TendisRedisInstance: + if row.task_type == DtsTaskType.MAKE_CACHE_SYNC and "rdb" in row.message and row.status in [0, 1]: + return True + + if row.src_dbtype == ClusterType.TendisTendisplusInsance: + if row.task_type in [DtsTaskType.TENDISPLUS_MAKESYNC, DtsTaskType.TENDISPLUS_SENDBULK] and row.status in [ + 0, + 1, + ]: + return True + return False + + +def is_full_transfer_failed(row: TbTendisDtsTask) -> bool: + """ + 判断是否全量传输失败 + """ + if row.src_dbtype == ClusterType.TendisTendisSSDInstance: + if ( + row.task_type + in [ + DtsTaskType.TENDISSSD_BACKUP, + DtsTaskType.TENDISSSD_BACKUPFILE_FETCH, + DtsTaskType.TENDISSSD_TREDISDUMP, + DtsTaskType.TENDISSSD_CMDSIMPOTER, + ] + and row.status == -1 + ): + return True + if row.src_dbtype == ClusterType.TendisRedisInstance: + if row.task_type == DtsTaskType.MAKE_CACHE_SYNC and row.status == -1: + return True + + if row.src_dbtype == ClusterType.TendisTendisplusInsance: + if row.task_type in [DtsTaskType.TENDISPLUS_MAKESYNC, DtsTaskType.TENDISPLUS_SENDBULK] and row.status == -1: + return True + return False + + +def is_in_incremental_sync(row: TbTendisDtsTask) -> bool: + """ + 判断是否增量同步中 + """ + if row.src_dbtype == ClusterType.TendisTendisSSDInstance: + if row.task_type in [DtsTaskType.TENDISSSD_MAKESYNC, DtsTaskType.TENDISSSD_WATCHOLDSYNC] and row.status in [ + 0, + 1, + ]: + return True + if row.src_dbtype == ClusterType.TendisRedisInstance: + if row.task_type in [DtsTaskType.MAKE_CACHE_SYNC, DtsTaskType.WATCH_CACHE_SYNC] and row.status in [0, 1]: + return True + + if row.src_dbtype == ClusterType.TendisTendisplusInsance: + if row.task_type in [DtsTaskType.TENDISPLUS_SENDINCR] and row.status in [0, 1]: + return True + return False + + +def is_incremental_sync_failed(row: TbTendisDtsTask) -> bool: + """ + 判断是否增量同步失败 + """ + if row.src_dbtype == ClusterType.TendisTendisSSDInstance: + if row.task_type in [DtsTaskType.TENDISSSD_MAKESYNC, DtsTaskType.TENDISSSD_WATCHOLDSYNC] and row.status == -1: + return True + if row.src_dbtype == ClusterType.TendisRedisInstance: + if row.task_type == DtsTaskType.WATCH_CACHE_SYNC and row.status == -1: + return True + + if row.src_dbtype == ClusterType.TendisTendisplusInsance: + if row.task_type in [DtsTaskType.TENDISPLUS_SENDINCR] and row.status == -1: + return True + return False + + +def is_pending_execution(row: TbTendisDtsTask) -> bool: + """ + 判断是否待执行 + """ + if row.task_type == "" and row.status == 0: + return True + + return False + + +def is_transfer_competed(row: TbTendisDtsTask) -> bool: + """ + 判断是否传输完成 + """ + if row.status == 2: + return True + return False + + +def is_transfer_terminated(row: TbTendisDtsTask) -> bool: + """ + 判断是否传输被终止 + """ + if row.sync_operate == DtsOperateType.FORCE_KILL_SUCC and row.status in [-1, 2]: + return True + return False + + +def dts_task_status(task_row: TbTendisDtsTask) -> str: + """ + 获取task任务状态 + """ + if is_pending_execution(task_row): + return DtsSyncStatus.PENDING_EXECUTION.value + if is_in_full_transfer(task_row): + return DtsSyncStatus.IN_FULL_TRANSFER.value + if is_full_transfer_failed(task_row): + return DtsSyncStatus.FULL_TRANSFER_FAILED.value + if is_in_incremental_sync(task_row): + return DtsSyncStatus.IN_INCREMENTAL_SYNC.value + if is_incremental_sync_failed(task_row): + return DtsSyncStatus.INCREMENTAL_SYNC_FAILED.value + if is_transfer_competed(task_row): + return DtsSyncStatus.TRANSFER_COMPLETED.value + if is_transfer_terminated(task_row): + return DtsSyncStatus.TRANSFER_TERMINATED.value + return DtsSyncStatus.UNKNOWN.value + + +def dts_job_cnt_and_status(job: TbTendisDTSJob) -> dict: + """ + 获取job 任务状态 + """ + ret = {} + pending_exec_cnt = 0 + running_cnt = 0 + failed_cnt = 0 + success_cnt = 0 + transfer_completed_cnt = 0 + transfer_terminated_cnt = 0 + full_transfer_failed_cnt = 0 + incremental_sync_failed_cnt = 0 + full_transfer_running_cnt = 0 + incremental_sync_running_cnt = 0 + tasks = TbTendisDtsTask.objects.filter( + Q(bill_id=job.bill_id) & Q(src_cluster=job.src_cluster) & Q(dst_cluster=job.dst_cluster) + ) + for task in tasks: + if is_pending_execution(task): + pending_exec_cnt += 1 + elif task.task_type == DtsTaskType.TENDISSSD_BACKUP.value and task.status == 0: + pending_exec_cnt += 1 + elif task.task_type == DtsTaskType.TENDISSSD_BACKUP.value and task.status == 1: + running_cnt += 1 + elif task.task_type != DtsTaskType.TENDISSSD_BACKUP.value and (task.status == 0 or task.status == 1): + running_cnt += 1 + elif task.status == -1: + failed_cnt += 1 + elif task.status == 2: + success_cnt += 1 + + if is_transfer_competed(task): + transfer_completed_cnt += 1 + elif is_transfer_terminated(task): + transfer_terminated_cnt += 1 + elif is_full_transfer_failed(task): + full_transfer_failed_cnt += 1 + elif is_incremental_sync_failed(task): + incremental_sync_failed_cnt += 1 + elif is_in_full_transfer(task): + full_transfer_running_cnt += 1 + elif is_in_incremental_sync(task): + incremental_sync_running_cnt += 1 + ret["total_cnt"] = len(tasks) + ret["pending_exec_cnt"] = pending_exec_cnt + ret["running_cnt"] = running_cnt + ret["failed_cnt"] = failed_cnt + ret["success_cnt"] = success_cnt + if pending_exec_cnt == len(tasks): + ret["status"] = DtsSyncStatus.PENDING_EXECUTION.value + elif transfer_completed_cnt == len(tasks): + ret["status"] = DtsSyncStatus.TRANSFER_COMPLETED.value + elif transfer_terminated_cnt == len(tasks): + ret["status"] = DtsSyncStatus.TRANSFER_TERMINATED.value + elif full_transfer_failed_cnt > 0: + ret["status"] = DtsSyncStatus.FULL_TRANSFER_FAILED.value + elif incremental_sync_failed_cnt > 0: + ret["status"] = DtsSyncStatus.INCREMENTAL_SYNC_FAILED.value + elif full_transfer_running_cnt > 0: + ret["status"] = DtsSyncStatus.IN_FULL_TRANSFER.value + elif incremental_sync_running_cnt > 0: + ret["status"] = DtsSyncStatus.IN_INCREMENTAL_SYNC.value + return ret diff --git a/dbm-ui/backend/db_services/redis/redis_dts/views.py b/dbm-ui/backend/db_services/redis/redis_dts/views.py new file mode 100644 index 0000000000..7dd15ace47 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/redis_dts/views.py @@ -0,0 +1,98 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 as _ +from rest_framework import status +from rest_framework.decorators import action +from rest_framework.response import Response + +from backend.bk_web import viewsets +from backend.bk_web.swagger import common_swagger_auto_schema +from backend.iam_app.handlers.drf_perm import ViewBusinessIAMPermission + +from .apis import ( + dts_job_disconnct_sync, + dts_job_tasks_failed_retry, + dts_test_redis_connections, + get_dts_history_jobs, + get_dts_job_tasks, +) +from .serializers import ( + DtsJobTasksSLZ, + DtsTaskIDsSLZ, + DtsTestRedisConnectionSLZ, + TbTendisDTSJobSerializer, + TendisDtsHistoryJobSLZ, +) + +RESOURCE_TAG = "db_services/redis/redis_dts" + + +class TendisDtsJobViewSet(viewsets.AuditedModelViewSet): + serializer_class = TbTendisDTSJobSerializer + + def _get_custom_permissions(self): + return [ViewBusinessIAMPermission()] + + @common_swagger_auto_schema( + operation_summary=_("获取DTS历史任务以及其对应task cnt"), + responses={status.HTTP_200_OK: TendisDtsHistoryJobSLZ}, + tags=[RESOURCE_TAG], + ) + @action(methods=["POST"], detail=False, url_path="history_jobs", serializer_class=TendisDtsHistoryJobSLZ) + def historyjobs(self, request, *args, **kwargs): + slz_data = self.params_validate(self.get_serializer_class()) + slz_data.update(user=request.user.username) + return Response(get_dts_history_jobs(slz_data)) + + @common_swagger_auto_schema( + operation_summary=_("获取迁移任务task列表,失败的排在前面"), + request_body=DtsJobTasksSLZ, + tags=[RESOURCE_TAG], + ) + @action(methods=["POST"], detail=False, serializer_class=DtsJobTasksSLZ, url_path="job_tasks") + def get_dts_job_tasks(self, request, *args, **kwargs): + slz_data = self.params_validate(self.get_serializer_class()) + return Response(get_dts_job_tasks(slz_data)) + + @common_swagger_auto_schema( + operation_summary=_("dts job断开同步"), + request_body=DtsJobTasksSLZ, + tags=[RESOURCE_TAG], + ) + @action(methods=["POST"], detail=False, serializer_class=DtsJobTasksSLZ, url_path="job_disconnect_sync") + def dts_job_disconnect_sync(self, request, *args, **kwargs): + slz = self.get_serializer(data=request.data) + slz.is_valid(raise_exception=True) + return Response(dts_job_disconnct_sync(slz.data)) + + @common_swagger_auto_schema( + operation_summary=_("dts job 批量失败重试"), + request_body=DtsTaskIDsSLZ, + tags=[RESOURCE_TAG], + ) + @action(methods=["POST"], detail=False, serializer_class=DtsTaskIDsSLZ, url_path="job_task_failed_retry") + def dts_job_tasks_failed_retry(self, request, *args, **kwargs): + slz = self.get_serializer(data=request.data) + slz.is_valid(raise_exception=True) + return Response(dts_job_tasks_failed_retry(slz.data)) + + @common_swagger_auto_schema( + operation_summary=_("dts 外部redis连接行测试"), + request_body=DtsTestRedisConnectionSLZ, + tags=[RESOURCE_TAG], + ) + @action( + methods=["POST"], detail=False, serializer_class=DtsTestRedisConnectionSLZ, url_path="test_redis_connection" + ) + def dts_test_redis_connection(self, request, *args, **kwargs): + slz = self.get_serializer(data=request.data) + slz.is_valid(raise_exception=True) + return Response(dts_test_redis_connections(slz.data)) diff --git a/dbm-ui/backend/db_services/redis/redis_dts/yasg_slz.py b/dbm-ui/backend/db_services/redis/redis_dts/yasg_slz.py new file mode 100644 index 0000000000..aa5085c628 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/redis_dts/yasg_slz.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" diff --git a/dbm-ui/backend/db_services/redis/resources/constants.py b/dbm-ui/backend/db_services/redis/resources/constants.py index d24e74e00b..f338ad890e 100644 --- a/dbm-ui/backend/db_services/redis/resources/constants.py +++ b/dbm-ui/backend/db_services/redis/resources/constants.py @@ -9,3 +9,42 @@ specific language governing permissions and limitations under the License. """ RESOURCE_TAG = "db_services/resources/redis" + +SQL_QUERY_STORAGE_INSTANCES = ( + "SELECT m.bk_host_id, m.bk_cloud_id, m.ip, m.spec_config, c.cluster_id, i.instance_role as role, " + "count(port) AS instance_count FROM db_meta_storageinstance i " + "LEFT JOIN db_meta_machine m ON i.machine_id = m.bk_host_id " + "LEFT JOIN db_meta_storageinstance_cluster c ON i.id = c.storageinstance_id " + "{where} " + "GROUP BY ip, role, bk_host_id, bk_cloud_id, cluster_id " + "{having}" +) + +SQL_QUERY_PROXY_INSTANCES = ( + "SELECT m.bk_host_id, m.bk_cloud_id, m.ip, m.spec_config, c.cluster_id, 'proxy' as role, " + "count(port) AS instance_count FROM db_meta_proxyinstance i " + "LEFT JOIN db_meta_machine m ON i.machine_id = m.bk_host_id " + "LEFT JOIN db_meta_proxyinstance_cluster c ON i.id = c.proxyinstance_id " + "{where} " + "GROUP BY ip, role, bk_host_id, bk_cloud_id, cluster_id " + "{having}" +) + +SQL_QUERY_INSTANCES = f"({SQL_QUERY_STORAGE_INSTANCES}) UNION ({SQL_QUERY_PROXY_INSTANCES}) " + " {limit} {offset}" + +SQL_QUERY_MASTER_SLAVE_STATUS = ( + "select mim.ip as ip, " + "count(si.id) as total_slave, " + "count(mi.id) as total_master, " + "sum(case when si.status='running' then 1 else 0 end) as running_slave, " + "sum(case when si.status='unavailable' then 1 else 0 end) as unavailable_slave, " + "sum(case when mi.status='running' then 1 else 0 end) as running_master, " + "sum(case when mi.status='unavailable' then 1 else 0 end) as unavailable_master " + "from db_meta_storageinstancetuple t " + "left join db_meta_storageinstance mi on mi.id = t.ejector " + "left join db_meta_storageinstance si on si.id = t.receiver " + "left join db_meta_machine mim on mim.bk_host_id = mi.machine_id " + "left join db_meta_machine sim on sim.bk_host_id = si.machine_id " + "{where} " + "group by mim.ip" +) diff --git a/dbm-ui/backend/db_services/redis/resources/redis_cluster/query.py b/dbm-ui/backend/db_services/redis/resources/redis_cluster/query.py index abc0bec7b9..6e9db7a541 100644 --- a/dbm-ui/backend/db_services/redis/resources/redis_cluster/query.py +++ b/dbm-ui/backend/db_services/redis/resources/redis_cluster/query.py @@ -11,6 +11,7 @@ from typing import Any, Dict from django.db.models import F, Prefetch, Q, QuerySet +from django.forms import model_to_dict from django.utils.translation import ugettext_lazy as _ from backend import env @@ -19,11 +20,12 @@ from backend.db_meta.api.cluster.tendisssd.handler import TendisSSDClusterHandler from backend.db_meta.enums import InstanceRole from backend.db_meta.enums.cluster_type import ClusterType -from backend.db_meta.models import AppCache, Machine +from backend.db_meta.models import AppCache, Machine, Spec from backend.db_meta.models.cluster import Cluster from backend.db_meta.models.cluster_entry import ClusterEntry from backend.db_meta.models.instance import ProxyInstance, StorageInstance from backend.db_services.dbbase.constants import IP_PORT_DIVIDER +from backend.db_services.dbbase.instances.handlers import InstanceHandler from backend.db_services.dbbase.resources import query from backend.db_services.ipchooser.query.resource import ResourceQueryHelper from backend.ticket.models import ClusterOperateRecord @@ -136,18 +138,32 @@ def list_instances(cls, bk_biz_id: int, query_params: Dict, limit: int, offset: ) instances = query_objs.values( + "id", "cluster__id", + "cluster__major_version", + "cluster__cluster_type", "cluster__name", + "port", "role", "machine__ip", - "port", + "machine__bk_cloud_id", + "machine__bk_host_id", + "machine__spec_config", "status", "create_at", - "machine__bk_host_id", ).order_by("port", "-create_at") paginated_instances = instances[offset : limit + offset] paginated_instances = [cls._to_instance_list(instance) for instance in paginated_instances] + + # 补充实例的额外信息 + if query_params.get("extra"): + instances_extra_info = InstanceHandler(bk_biz_id).check_instances(query_instances=paginated_instances) + address__instance_extra_info = {inst["instance_address"]: inst for inst in instances_extra_info} + for inst in paginated_instances: + extra_info = address__instance_extra_info[inst["instance_address"]] + inst.update(host_info=extra_info["host_info"], related_clusters=extra_info["related_clusters"]) + return query.ResourceList(count=instances.count(), data=paginated_instances) @classmethod @@ -220,12 +236,31 @@ def _to_cluster_list(cluster, cluster_entry_map: Dict[int, Dict[str, str]]) -> D """ cluster_entry = cluster_entry_map.get(cluster.id, {}) cloud_info = ResourceQueryHelper.search_cc_cloud(get_cache=True) + + redis_master = [m.simple_desc for m in cluster.storages if m.instance_role == InstanceRole.REDIS_MASTER] + redis_slave = [m.simple_desc for m in cluster.storages if m.instance_role == InstanceRole.REDIS_SLAVE] + machine_list = list(set([inst["bk_host_id"] for inst in [*redis_master, *redis_slave]])) + machine_pair_cnt = len(machine_list) / 2 + + # 补充集群的规格和容量信息 + spec_id = Machine.objects.get(bk_host_id=machine_list[0]).spec_id + if not spec_id: + # TODO: 暂时兼容手动部署的情况,后续会删除该逻辑 + cluster_spec = cluster_capacity = "" + else: + spec = Spec.objects.get(spec_id=spec_id) + cluster_spec = model_to_dict(spec) + cluster_capacity = spec.capacity * machine_pair_cnt + return { "id": cluster.id, "phase": cluster.phase, "status": cluster.status, "cluster_name": cluster.name, "cluster_alias": cluster.alias, + "cluster_spec": cluster_spec, + # TODO: 待补充当前集群使用容量,需要监控采集的支持 + "cluster_capacity": cluster_capacity, "bk_biz_id": cluster.bk_biz_id, "bk_biz_name": AppCache.objects.get(bk_biz_id=cluster.bk_biz_id).bk_biz_name, "bk_cloud_id": cluster.bk_cloud_id, @@ -235,13 +270,12 @@ def _to_cluster_list(cluster, cluster_entry_map: Dict[int, Dict[str, str]]) -> D "cluster_type": cluster.cluster_type, "cluster_type_name": ClusterType.get_choice_label(cluster.cluster_type), "master_domain": cluster_entry.get("master_domain", ""), + "cluster_entry": list(cluster.clusterentry_set.values("cluster_entry_type", "entry")), "proxy": [m.simple_desc for m in cluster.proxies], - InstanceRole.REDIS_MASTER: [ - m.simple_desc for m in cluster.storages if m.instance_role == InstanceRole.REDIS_MASTER - ], - InstanceRole.REDIS_SLAVE: [ - m.simple_desc for m in cluster.storages if m.instance_role == InstanceRole.REDIS_SLAVE - ], + "redis_master": redis_master, + "redis_slave": redis_slave, + "cluster_shard_num": len(redis_master), + "machine_pair_cnt": machine_pair_cnt, "operations": ClusterOperateRecord.objects.get_cluster_operations(cluster.id), "creator": cluster.creator, "updater": cluster.updater, @@ -267,12 +301,23 @@ def _to_cluster_detail(cls, cluster, cluster_entry_map: Dict[int, Dict[str, str] def _to_instance_list(cls, instance: dict) -> Dict[str, Any]: """实例序列化""" + cloud_info = ResourceQueryHelper.search_cc_cloud(get_cache=True) + bk_cloud_id = instance["machine__bk_cloud_id"] + ip, port = instance["machine__ip"], instance["port"] + return { - "instance_address": f"{instance['machine__ip']}{IP_PORT_DIVIDER}{instance['port']}", - "bk_host_id": instance["machine__bk_host_id"], "cluster_id": instance["cluster__id"], - "cluster__name": instance["cluster__name"], + "cluster_type": instance["cluster__cluster_type"], + "cluster_name": instance["cluster__name"], + "ip": ip, + "bk_cloud_id": bk_cloud_id, + "bk_cloud_name": cloud_info[str(bk_cloud_id)]["bk_cloud_name"], + "bk_host_id": instance["machine__bk_host_id"], + "id": instance["id"], + "port": port, + "instance_address": f"{ip}{IP_PORT_DIVIDER}{port}", "role": instance["role"], + "spec_config": instance["machine__spec_config"], "status": instance["status"], "create_at": datetime2str(instance["create_at"]), } diff --git a/dbm-ui/backend/db_services/redis/rollback/constants.py b/dbm-ui/backend/db_services/redis/rollback/constants.py new file mode 100644 index 0000000000..6abb2136eb --- /dev/null +++ b/dbm-ui/backend/db_services/redis/rollback/constants.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +RESOURCE_TAG = "db_services/redis/rollback" diff --git a/dbm-ui/backend/db_services/redis/rollback/migrations/0001_initial.py b/dbm-ui/backend/db_services/redis/rollback/migrations/0001_initial.py new file mode 100644 index 0000000000..4474282334 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/rollback/migrations/0001_initial.py @@ -0,0 +1,55 @@ +# Generated by Django 3.2.19 on 2023-06-25 03:17 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [] + + operations = [ + migrations.CreateModel( + name="TbTendisRollbackTasks", + fields=[ + ("creator", models.CharField(max_length=64, verbose_name="创建人")), + ("create_at", models.DateTimeField(auto_now_add=True, verbose_name="创建时间")), + ("updater", models.CharField(max_length=64, verbose_name="修改人")), + ("update_at", models.DateTimeField(auto_now=True, verbose_name="更新时间")), + ("id", models.BigAutoField(primary_key=True, serialize=False)), + ("related_rollback_bill_id", models.BigIntegerField(verbose_name=("单据号,关联单据",))), + ("bk_biz_id", models.BigIntegerField(verbose_name=("业务id",))), + ("app", models.CharField(default="", max_length=64, verbose_name=("业务名",))), + ("bk_cloud_id", models.BigIntegerField(default=0, verbose_name=("云区域id",))), + ("prod_cluster_type", models.CharField(default="", max_length=64, verbose_name=("构造源集群类型",))), + ("prod_cluster", models.CharField(default="", max_length=128, verbose_name=("构造的源集群,线上环境cluster",))), + ("prod_instance_range", models.TextField(max_length=10000, verbose_name=("源构造的实例范围",))), + ("temp_cluster_type", models.CharField(default="", max_length=64, verbose_name=("临时集群类型",))), + ("temp_password", models.CharField(default="", max_length=128, verbose_name=("临时集群proxy密码base64值",))), + ("temp_instance_range", models.TextField(max_length=10000, verbose_name=("临时集群构造实例范围",))), + ("temp_cluster_proxy", models.CharField(max_length=100, verbose_name=("构造产物访问入口ip:port",))), + ( + "prod_temp_instance_pairs", + models.TextField(default="", max_length=20000, verbose_name=("构造源实例和临时实例一一对应关系",)), + ), + ("status", models.IntegerField(default=0, verbose_name=("任务状态",))), + ("is_destroyed", models.IntegerField(default=0, verbose_name=("是否已销毁",))), + ("specification", models.CharField(max_length=100, verbose_name=("规格需求",))), + ("host_count", models.IntegerField(verbose_name=("构造的主机数量",))), + ("recovery_time_point", models.DateTimeField(verbose_name=("构造到指定时间",))), + ], + options={ + "verbose_name": "Tendis data construction task", + "db_table": "tb_tendis_rollback_tasks", + }, + ), + migrations.AddIndex( + model_name="tbtendisrollbacktasks", + index=models.Index(fields=["update_at"], name="idx_update_at"), + ), + migrations.AddIndex( + model_name="tbtendisrollbacktasks", + index=models.Index(fields=["prod_cluster"], name="idx_prod_cluster"), + ), + ] diff --git a/dbm-ui/backend/db_services/redis/rollback/migrations/0002_auto_20230625_1123.py b/dbm-ui/backend/db_services/redis/rollback/migrations/0002_auto_20230625_1123.py new file mode 100644 index 0000000000..64e35b7f99 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/rollback/migrations/0002_auto_20230625_1123.py @@ -0,0 +1,98 @@ +# Generated by Django 3.2.19 on 2023-06-25 03:23 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("rollback", "0001_initial"), + ] + + operations = [ + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="app", + field=models.CharField(default="", max_length=64, verbose_name=("业务名",)), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="bk_biz_id", + field=models.BigIntegerField(verbose_name=("业务id",)), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="bk_cloud_id", + field=models.BigIntegerField(default=0, verbose_name=("云区域id",)), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="host_count", + field=models.IntegerField(verbose_name=("构造的主机数量",)), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="is_destroyed", + field=models.IntegerField(default=0, verbose_name=("是否已销毁",)), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="prod_cluster", + field=models.CharField(default="", max_length=128, verbose_name=("构造的源集群,线上环境cluster",)), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="prod_cluster_type", + field=models.CharField(default="", max_length=64, verbose_name=("构造源集群类型",)), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="prod_instance_range", + field=models.TextField(max_length=10000, verbose_name=("源构造的实例范围",)), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="prod_temp_instance_pairs", + field=models.TextField(default="", max_length=20000, verbose_name=("构造源实例和临时实例一一对应关系",)), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="recovery_time_point", + field=models.DateTimeField(verbose_name=("构造到指定时间",)), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="related_rollback_bill_id", + field=models.BigIntegerField(verbose_name=("单据号,关联单据",)), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="specification", + field=models.CharField(max_length=100, verbose_name=("规格需求",)), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="status", + field=models.IntegerField(default=0, verbose_name=("任务状态",)), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="temp_cluster_proxy", + field=models.CharField(max_length=100, verbose_name=("构造产物访问入口ip:port",)), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="temp_cluster_type", + field=models.CharField(default="", max_length=64, verbose_name=("临时集群类型",)), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="temp_instance_range", + field=models.TextField(max_length=10000, verbose_name=("临时集群构造实例范围",)), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="temp_password", + field=models.CharField(default="", max_length=128, verbose_name=("临时集群proxy密码base64值",)), + ), + ] diff --git a/dbm-ui/backend/db_services/redis/rollback/migrations/0003_auto_20230721_1011.py b/dbm-ui/backend/db_services/redis/rollback/migrations/0003_auto_20230721_1011.py new file mode 100644 index 0000000000..96436c64bc --- /dev/null +++ b/dbm-ui/backend/db_services/redis/rollback/migrations/0003_auto_20230721_1011.py @@ -0,0 +1,160 @@ +# Generated by Django 3.2.19 on 2023-07-21 02:11 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("rollback", "0002_auto_20230625_1123"), + ] + + operations = [ + migrations.AddField( + model_name="tbtendisrollbacktasks", + name="prod_cluster_id", + field=models.BigIntegerField(default=0, verbose_name="集群id,cluster.id"), + preserve_default=False, + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="app", + field=models.CharField(default="", max_length=64, verbose_name="业务名"), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="bk_biz_id", + field=models.BigIntegerField(verbose_name="业务id"), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="bk_cloud_id", + field=models.BigIntegerField(default=0, verbose_name="云区域id"), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="host_count", + field=models.IntegerField(verbose_name="构造的主机数量"), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="is_destroyed", + field=models.IntegerField(default=0, verbose_name="是否已销毁"), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="prod_cluster", + field=models.CharField(default="", max_length=128, verbose_name="构造的源集群,线上环境cluster"), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="prod_cluster_type", + field=models.CharField( + choices=[ + ("tendbsingle", "tendbsingle"), + ("tendbha", "tendbha"), + ("tendbcluster", "tendbcluster"), + ("redis", "Redis集群"), + ("PredixyRedisCluster", "Redis集群"), + ("PredixyTendisplusCluster", "Tendisplus存储版集群"), + ("TwemproxyRedisInstance", "TendisCache集群"), + ("TwemproxyTendisSSDInstance", "TendisSSD集群"), + ("TwemproxyTendisplusInstance", "Tendis存储版集群"), + ("RedisInstance", "RedisCache主从版"), + ("TendisSSDInstance", "TendisSSD主从版"), + ("TendisplusInstance", "Tendisplus主从版"), + ("RedisCluster", "RedisCluster集群"), + ("TendisplusCluster", "TendisplusCluster集群"), + ("es", "ES集群"), + ("kafka", "Kafka集群"), + ("hdfs", "Hdfs集群"), + ("influxdb", "Influxdb实例"), + ("pulsar", "Pulsar集群"), + ("MongoReplicaSet", "Mongo副本集"), + ("MongoShardedCluster", "Mongo分片集群"), + ("riak", "Riak集群"), + ], + default="", + max_length=64, + verbose_name="构造源集群类型", + ), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="prod_instance_range", + field=models.TextField(max_length=10000, verbose_name="源构造的实例范围"), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="prod_temp_instance_pairs", + field=models.TextField(default="", max_length=20000, verbose_name="构造源实例和临时实例一一对应关系"), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="recovery_time_point", + field=models.DateTimeField(verbose_name="构造到指定时间"), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="related_rollback_bill_id", + field=models.BigIntegerField(unique=True, verbose_name="单据号,关联单据"), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="specification", + field=models.JSONField(default=dict, verbose_name="规格需求"), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="status", + field=models.IntegerField(default=0, verbose_name="任务状态"), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="temp_cluster_proxy", + field=models.CharField(max_length=100, verbose_name="构造产物访问入口ip:port"), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="temp_cluster_type", + field=models.CharField( + choices=[ + ("tendbsingle", "tendbsingle"), + ("tendbha", "tendbha"), + ("tendbcluster", "tendbcluster"), + ("redis", "Redis集群"), + ("PredixyRedisCluster", "Redis集群"), + ("PredixyTendisplusCluster", "Tendisplus存储版集群"), + ("TwemproxyRedisInstance", "TendisCache集群"), + ("TwemproxyTendisSSDInstance", "TendisSSD集群"), + ("TwemproxyTendisplusInstance", "Tendis存储版集群"), + ("RedisInstance", "RedisCache主从版"), + ("TendisSSDInstance", "TendisSSD主从版"), + ("TendisplusInstance", "Tendisplus主从版"), + ("RedisCluster", "RedisCluster集群"), + ("TendisplusCluster", "TendisplusCluster集群"), + ("es", "ES集群"), + ("kafka", "Kafka集群"), + ("hdfs", "Hdfs集群"), + ("influxdb", "Influxdb实例"), + ("pulsar", "Pulsar集群"), + ("MongoReplicaSet", "Mongo副本集"), + ("MongoShardedCluster", "Mongo分片集群"), + ("riak", "Riak集群"), + ], + default="", + max_length=64, + verbose_name="临时集群类型", + ), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="temp_instance_range", + field=models.TextField(max_length=10000, verbose_name="临时集群构造实例范围"), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="temp_password", + field=models.CharField(default="", max_length=128, verbose_name="临时集群proxy密码base64值"), + ), + ] diff --git a/dbm-ui/backend/db_services/redis/rollback/migrations/0004_auto_20230725_1744.py b/dbm-ui/backend/db_services/redis/rollback/migrations/0004_auto_20230725_1744.py new file mode 100644 index 0000000000..0cc73224ff --- /dev/null +++ b/dbm-ui/backend/db_services/redis/rollback/migrations/0004_auto_20230725_1744.py @@ -0,0 +1,28 @@ +# Generated by Django 3.2.19 on 2023-07-25 09:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("rollback", "0003_auto_20230721_1011"), + ] + + operations = [ + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="prod_instance_range", + field=models.JSONField(default=dict, verbose_name="源构造的实例范围"), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="prod_temp_instance_pairs", + field=models.JSONField(default=dict, verbose_name="构造源实例和临时实例一一对应关系"), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="temp_instance_range", + field=models.JSONField(default=dict, verbose_name="临时集群构造实例范围"), + ), + ] diff --git a/dbm-ui/backend/db_services/redis/rollback/migrations/0005_auto_20230726_1536.py b/dbm-ui/backend/db_services/redis/rollback/migrations/0005_auto_20230726_1536.py new file mode 100644 index 0000000000..c9d6fe8e65 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/rollback/migrations/0005_auto_20230726_1536.py @@ -0,0 +1,22 @@ +# Generated by Django 3.2.19 on 2023-07-26 07:36 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("rollback", "0004_auto_20230725_1744"), + ] + + operations = [ + migrations.RemoveField( + model_name="tbtendisrollbacktasks", + name="is_destroyed", + ), + migrations.AddField( + model_name="tbtendisrollbacktasks", + name="destroyed_status", + field=models.IntegerField(default=0, verbose_name="销毁状态"), + ), + ] diff --git a/dbm-ui/backend/db_services/redis/rollback/migrations/0006_alter_tbtendisrollbacktasks_destroyed_status.py b/dbm-ui/backend/db_services/redis/rollback/migrations/0006_alter_tbtendisrollbacktasks_destroyed_status.py new file mode 100644 index 0000000000..9fe03f7329 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/rollback/migrations/0006_alter_tbtendisrollbacktasks_destroyed_status.py @@ -0,0 +1,24 @@ +# Generated by Django 3.2.19 on 2023-07-26 09:41 + +from django.db import migrations, models + +import backend.db_meta.enums.destroyed_status + + +class Migration(migrations.Migration): + + dependencies = [ + ("rollback", "0005_auto_20230726_1536"), + ] + + operations = [ + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="destroyed_status", + field=models.IntegerField( + choices=[(0, "NOT_DESTROYED"), (1, "DESTROYED"), (2, "DESTROYING")], + default=backend.db_meta.enums.destroyed_status.DestroyedStatus["NOT_DESTROYED"], + verbose_name="销毁状态", + ), + ), + ] diff --git a/dbm-ui/backend/db_services/redis/rollback/migrations/0007_auto_20230801_1634.py b/dbm-ui/backend/db_services/redis/rollback/migrations/0007_auto_20230801_1634.py new file mode 100644 index 0000000000..f6010050c5 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/rollback/migrations/0007_auto_20230801_1634.py @@ -0,0 +1,29 @@ +# Generated by Django 3.2.19 on 2023-08-01 08:34 + +from django.db import migrations, models + +import backend.db_meta.enums.destroyed_status + + +class Migration(migrations.Migration): + + dependencies = [ + ("rollback", "0006_alter_tbtendisrollbacktasks_destroyed_status"), + ] + + operations = [ + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="destroyed_status", + field=models.IntegerField( + choices=[(0, "Not destroyed"), (1, "Destroyed"), (2, "Destroying")], + default=backend.db_meta.enums.destroyed_status.DestroyedStatus["NOT_DESTROYED"], + verbose_name="销毁状态", + ), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="related_rollback_bill_id", + field=models.BigIntegerField(verbose_name="单据号,关联单据"), + ), + ] diff --git a/dbm-ui/backend/db_services/redis/rollback/migrations/0008_auto_20230821_1028.py b/dbm-ui/backend/db_services/redis/rollback/migrations/0008_auto_20230821_1028.py new file mode 100644 index 0000000000..b49c14bd18 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/rollback/migrations/0008_auto_20230821_1028.py @@ -0,0 +1,90 @@ +# Generated by Django 3.2.19 on 2023-08-21 02:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("rollback", "0007_auto_20230801_1634"), + ] + + operations = [ + migrations.AddField( + model_name="tbtendisrollbacktasks", + name="temp_redis_password", + field=models.CharField(default="", max_length=128, verbose_name="临时集群redis密码base64值"), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="prod_cluster_type", + field=models.CharField( + choices=[ + ("tendbsingle", "tendbsingle"), + ("tendbha", "tendbha"), + ("tendbcluster", "tendbcluster"), + ("redis", "Redis集群"), + ("PredixyRedisCluster", "Redis集群"), + ("PredixyTendisplusCluster", "Tendisplus存储版集群"), + ("TwemproxyRedisInstance", "TendisCache集群"), + ("TwemproxyTendisSSDInstance", "TendisSSD集群"), + ("TwemproxyTendisplusInstance", "Tendis存储版集群"), + ("RedisInstance", "RedisCache主从版"), + ("TendisSSDInstance", "TendisSSD主从版"), + ("TendisplusInstance", "Tendisplus主从版"), + ("RedisCluster", "RedisCluster集群"), + ("TendisplusCluster", "TendisplusCluster集群"), + ("TendisplusInstance", "Tendisplus存储版集群 GetTendisType 获取redis类型值"), + ("RedisInstance", "TendisCache集群 GetTendisType 获取redis类型值"), + ("TendisSSDInstance", "TendisSSD集群 GetTendisType 获取redis类型值"), + ("es", "ES集群"), + ("kafka", "Kafka集群"), + ("hdfs", "Hdfs集群"), + ("influxdb", "Influxdb实例"), + ("pulsar", "Pulsar集群"), + ("MongoReplicaSet", "Mongo副本集"), + ("MongoShardedCluster", "Mongo分片集群"), + ("riak", "Riak集群"), + ], + default="", + max_length=64, + verbose_name="构造源集群类型", + ), + ), + migrations.AlterField( + model_name="tbtendisrollbacktasks", + name="temp_cluster_type", + field=models.CharField( + choices=[ + ("tendbsingle", "tendbsingle"), + ("tendbha", "tendbha"), + ("tendbcluster", "tendbcluster"), + ("redis", "Redis集群"), + ("PredixyRedisCluster", "Redis集群"), + ("PredixyTendisplusCluster", "Tendisplus存储版集群"), + ("TwemproxyRedisInstance", "TendisCache集群"), + ("TwemproxyTendisSSDInstance", "TendisSSD集群"), + ("TwemproxyTendisplusInstance", "Tendis存储版集群"), + ("RedisInstance", "RedisCache主从版"), + ("TendisSSDInstance", "TendisSSD主从版"), + ("TendisplusInstance", "Tendisplus主从版"), + ("RedisCluster", "RedisCluster集群"), + ("TendisplusCluster", "TendisplusCluster集群"), + ("TendisplusInstance", "Tendisplus存储版集群 GetTendisType 获取redis类型值"), + ("RedisInstance", "TendisCache集群 GetTendisType 获取redis类型值"), + ("TendisSSDInstance", "TendisSSD集群 GetTendisType 获取redis类型值"), + ("es", "ES集群"), + ("kafka", "Kafka集群"), + ("hdfs", "Hdfs集群"), + ("influxdb", "Influxdb实例"), + ("pulsar", "Pulsar集群"), + ("MongoReplicaSet", "Mongo副本集"), + ("MongoShardedCluster", "Mongo分片集群"), + ("riak", "Riak集群"), + ], + default="", + max_length=64, + verbose_name="临时集群类型", + ), + ), + ] diff --git a/dbm-ui/backend/db_services/redis/rollback/migrations/0009_rename_temp_password_tbtendisrollbacktasks_temp_proxy_password.py b/dbm-ui/backend/db_services/redis/rollback/migrations/0009_rename_temp_password_tbtendisrollbacktasks_temp_proxy_password.py new file mode 100644 index 0000000000..3e344eb8c7 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/rollback/migrations/0009_rename_temp_password_tbtendisrollbacktasks_temp_proxy_password.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.19 on 2023-08-21 02:39 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("rollback", "0008_auto_20230821_1028"), + ] + + operations = [ + migrations.RenameField( + model_name="tbtendisrollbacktasks", + old_name="temp_password", + new_name="temp_proxy_password", + ), + ] diff --git a/dbm-ui/backend/db_services/redis/rollback/migrations/__init__.py b/dbm-ui/backend/db_services/redis/rollback/migrations/__init__.py new file mode 100644 index 0000000000..aa5085c628 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/rollback/migrations/__init__.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" diff --git a/dbm-ui/backend/db_services/redis/rollback/models/__init__.py b/dbm-ui/backend/db_services/redis/rollback/models/__init__.py new file mode 100644 index 0000000000..415690347a --- /dev/null +++ b/dbm-ui/backend/db_services/redis/rollback/models/__init__.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 .tb_tendis_data_construction_task import TbTendisRollbackTasks diff --git a/dbm-ui/backend/db_services/redis/rollback/models/tb_tendis_data_construction_task.py b/dbm-ui/backend/db_services/redis/rollback/models/tb_tendis_data_construction_task.py new file mode 100644 index 0000000000..348c60d8f5 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/rollback/models/tb_tendis_data_construction_task.py @@ -0,0 +1,50 @@ +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +from backend.bk_web.models import AuditedModel +from backend.db_meta.enums import ClusterType, DestroyedStatus + + +class TbTendisRollbackTasks(AuditedModel): + id = models.BigAutoField(primary_key=True) + related_rollback_bill_id = models.BigIntegerField(verbose_name=_("单据号,关联单据")) + bk_biz_id = models.BigIntegerField(verbose_name=_("业务id")) + app = models.CharField(max_length=64, default="", verbose_name=_("业务名")) + bk_cloud_id = models.BigIntegerField(default=0, verbose_name=_("云区域id")) + + prod_cluster_type = models.CharField( + max_length=64, default="", choices=ClusterType.get_choices(), verbose_name=_("构造源集群类型") + ) + prod_cluster = models.CharField(max_length=128, default="", verbose_name=_("构造的源集群,线上环境cluster")) + prod_cluster_id = models.BigIntegerField(verbose_name=_("集群id,cluster.id")) + prod_instance_range = models.JSONField(default=dict, verbose_name=_("源构造的实例范围")) + + temp_cluster_type = models.CharField( + max_length=64, default="", choices=ClusterType.get_choices(), verbose_name=_("临时集群类型") + ) + temp_proxy_password = models.CharField(max_length=128, default="", verbose_name=_("临时集群proxy密码base64值")) + temp_instance_range = models.JSONField(default=dict, verbose_name=_("临时集群构造实例范围")) + temp_cluster_proxy = models.CharField(max_length=100, verbose_name=_("构造产物访问入口ip:port")) + + prod_temp_instance_pairs = models.JSONField(default=dict, verbose_name=_("构造源实例和临时实例一一对应关系")) + # 任务执行状态,0:未开始 1:执行中 2:完成 -1:发生错误 + status = models.IntegerField(default=0, verbose_name=_("任务状态")) + # 构造记录销毁状态,0:未销毁 1:销毁中 2:已销毁 + destroyed_status = models.IntegerField( + choices=DestroyedStatus.get_choices(), + default=DestroyedStatus.NOT_DESTROYED, + verbose_name=_("销毁状态"), + ) + specification = models.JSONField(default=dict, verbose_name=_("规格需求")) + host_count = models.IntegerField(verbose_name=_("构造的主机数量")) + recovery_time_point = models.DateTimeField(verbose_name=_("构造到指定时间")) + temp_redis_password = models.CharField(max_length=128, default="", verbose_name=_("临时集群redis密码base64值")) + + class Meta: + db_table = "tb_tendis_rollback_tasks" + verbose_name = "Tendis data construction task" + + indexes = [ + models.Index(fields=["update_at"], name="idx_update_at"), + models.Index(fields=["prod_cluster"], name="idx_prod_cluster"), + ] diff --git a/dbm-ui/backend/db_services/redis/rollback/serializers.py b/dbm-ui/backend/db_services/redis/rollback/serializers.py new file mode 100644 index 0000000000..eccf3c4b82 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/rollback/serializers.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 rest_framework import serializers + +from backend.bk_web.serializers import AuditedSerializer +from backend.db_services.redis.rollback.models import TbTendisRollbackTasks + + +class RollbackSerializer(AuditedSerializer, serializers.ModelSerializer): + """redis构造实例记录序列化""" + + specification = serializers.JSONField() + prod_instance_range = serializers.JSONField() + temp_instance_range = serializers.JSONField() + prod_temp_instance_pairs = serializers.JSONField() + + class Meta: + model = TbTendisRollbackTasks + exclude = ( + "temp_proxy_password", + "status", + ) diff --git a/dbm-ui/backend/db_services/redis/rollback/urls.py b/dbm-ui/backend/db_services/redis/rollback/urls.py new file mode 100644 index 0000000000..ed0f27290f --- /dev/null +++ b/dbm-ui/backend/db_services/redis/rollback/urls.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 rest_framework.routers import DefaultRouter + +from .views import RollbackViewSet + +router = DefaultRouter(trailing_slash=True) +router.register(r"rollback", RollbackViewSet, basename="rollback") + +urlpatterns = router.urls diff --git a/dbm-ui/backend/db_services/redis/rollback/views.py b/dbm-ui/backend/db_services/redis/rollback/views.py new file mode 100644 index 0000000000..8db3553862 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/rollback/views.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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.decorators import method_decorator +from django.utils.translation import ugettext as _ +from django_filters import rest_framework as filters + +from backend.bk_web.swagger import common_swagger_auto_schema +from backend.bk_web.viewsets import ReadOnlyAuditedModelViewSet +from backend.db_meta.enums import DestroyedStatus + +from . import constants +from .models import TbTendisRollbackTasks +from .serializers import RollbackSerializer + + +class RollbackListFilter(filters.FilterSet): + prod_cluster = filters.CharFilter(field_name="prod_cluster", lookup_expr="icontains", label=_("集群域名")) + related_rollback_bill_id = filters.CharFilter( + field_name="related_rollback_bill_id", lookup_expr="exact", label=_("单据id") + ) + + class Meta: + model = TbTendisRollbackTasks + fields = ["prod_cluster_id", "prod_cluster", "related_rollback_bill_id", "temp_cluster_proxy"] + + +@method_decorator( + name="list", + decorator=common_swagger_auto_schema(tags=[constants.RESOURCE_TAG]), +) +@method_decorator( + name="retrieve", + decorator=common_swagger_auto_schema(tags=[constants.RESOURCE_TAG]), +) +class RollbackViewSet(ReadOnlyAuditedModelViewSet): + """实例构造管理""" + + queryset = TbTendisRollbackTasks.objects.exclude(destroyed_status=DestroyedStatus.DESTROYED).order_by( + "destroyed_status", "-create_at" + ) + serializer_class = RollbackSerializer + filter_class = RollbackListFilter + + def get_queryset(self): + queryset = super().get_queryset() + bk_biz_id = self.kwargs.get("bk_biz_id") + if self.action == "list" and bk_biz_id: + queryset = queryset.filter(bk_biz_id=bk_biz_id) + + return queryset diff --git a/dbm-ui/backend/db_services/redis/toolbox/__init__.py b/dbm-ui/backend/db_services/redis/toolbox/__init__.py new file mode 100644 index 0000000000..aa5085c628 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/toolbox/__init__.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" diff --git a/dbm-ui/backend/db_services/redis/toolbox/handlers.py b/dbm-ui/backend/db_services/redis/toolbox/handlers.py new file mode 100644 index 0000000000..83b760132f --- /dev/null +++ b/dbm-ui/backend/db_services/redis/toolbox/handlers.py @@ -0,0 +1,253 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import itertools +import json +from collections import defaultdict + +from django.db import connection +from django.db.models import Count, F, Q +from django.forms import model_to_dict + +from backend import env +from backend.db_meta.enums import ClusterType, InstanceRole +from backend.db_meta.models import Cluster, ProxyInstance, StorageInstance, StorageInstanceTuple +from backend.db_services.ipchooser.handlers.host_handler import HostHandler +from backend.db_services.ipchooser.query.resource import ResourceQueryHelper +from backend.db_services.redis.resources.constants import SQL_QUERY_INSTANCES, SQL_QUERY_MASTER_SLAVE_STATUS +from backend.ticket.constants import InstanceType +from backend.ticket.models import ClusterOperateRecord +from backend.utils.basic import dictfetchall + + +class ToolboxHandler: + """redis工具箱查询接口封装""" + + def __init__(self, bk_biz_id: int): + self.bk_biz_id = bk_biz_id + + def query_by_ip(self, ips) -> list: + """根据ip查询实例、集群和规格相关信息""" + + ips = list( + itertools.chain( + StorageInstance.filter_by_ips(self.bk_biz_id, ips), + ProxyInstance.filter_by_ips(self.bk_biz_id, ips), + ) + ) + + # 查询主从状态对 + master_slave_map = self.query_master_slave_map( + [i["ip"] for i in ips if i["role"] == InstanceRole.REDIS_MASTER] + ) + + # 主从关系补充 + for item in ips: + item.update(master_slave_map.get(item["ip"], {})) + + return ips + + def query_master_slave_by_ip(self, master_ips) -> list: + """根据master主机查询集群、实例和对应的slave主机""" + + results = [] + for master_ip in master_ips: + masters = StorageInstance.objects.filter( + bk_biz_id=self.bk_biz_id, machine__ip=master_ip, instance_role=InstanceRole.REDIS_MASTER + ) + if not masters.exists(): + continue + + ms_pairs = StorageInstanceTuple.objects.filter(ejector__machine__ip=master_ip) + results.append( + { + "cluster": masters.last().cluster.first().simple_desc, + "master_ip": master_ip, + "slave_ip": ms_pairs.last().receiver.machine.ip, + "instances": map(lambda x: x.simple_desc, masters), + } + ) + + return results + + def query_instance_by_cluster(self, keywords) -> list: + """根据角色和关键字查询集群规格、方案和角色信息 - deprecated""" + + fields = ["id", "machine__ip", "port", "name", "status", "version", "machine__spec_config"] + number_keywords = filter(lambda x: x.isnumeric(), keywords) + clusters = Cluster.objects.filter( + Q(id__in=number_keywords) | Q(name__in=keywords) | Q(immute_domain__in=keywords), bk_biz_id=self.bk_biz_id + ) + + results = [] + for cluster in clusters: + results.append( + { + "cluster": cluster.extra_desc, + InstanceType.PROXY.value: cluster.proxyinstance_set.all().values(*fields), + InstanceType.STORAGE.value: cluster.storageinstance_set.all().values(*fields, "instance_role"), + } + ) + + return results + + def query_master_slave_pairs(self, cluster_id: int) -> list: + """查询主从架构集群的关系对""" + + try: + cluster = Cluster.objects.get(pk=cluster_id, bk_biz_id=self.bk_biz_id) + except Cluster.DoesNotExist: + return [] + + pairs = StorageInstanceTuple.objects.filter(receiver__cluster=cluster, ejector__cluster=cluster).values( + master_id=F("ejector__id"), + master_ip=F("ejector__machine__ip"), + master_port=F("ejector__port"), + slave_id=F("receiver__id"), + slave_ip=F("receiver__machine__ip"), + slave_port=F("receiver__port"), + ) + + # 按照ip合并 + grouped = defaultdict(list) + for pair in pairs: + grouped[(pair["master_ip"], pair["slave_ip"])].append(pair["master_port"]) + + result = [] + for mkey, ports in grouped.items(): + result.append( + { + "master_ip": mkey[0], + "slave_ip": mkey[1], + "ports": ports, + } + ) + + return result + + def query_cluster_list(self): + """"TODO: 切换为集群列表接口,deprecated""" + + clusters = Cluster.objects.filter( + bk_biz_id=self.bk_biz_id, + cluster_type__in=[ + ClusterType.TendisPredixyRedisCluster, + ClusterType.TendisPredixyTendisplusCluster, + ClusterType.TendisTwemproxyRedisInstance, + ClusterType.TwemproxyTendisSSDInstance, + ClusterType.TendisTwemproxyTendisplusIns, + ClusterType.TendisRedisInstance, + ClusterType.TendisTendisSSDInstance, + ClusterType.TendisTendisplusInsance, + ClusterType.TendisRedisCluster, + ClusterType.TendisTendisplusCluster, + ], + ).order_by("-create_at", "name") + + cloud_info = ResourceQueryHelper.search_cc_cloud(get_cache=True, fields=["bk_cloud_id", "bk_cloud_name"]) + + results = [] + for cluster in clusters: + result = { + **model_to_dict(cluster), + "operations": ClusterOperateRecord.objects.get_cluster_operations(cluster.id), + "cloud_info": cloud_info[str(cluster.bk_cloud_id)], + "proxy_count": cluster.proxyinstance_set.count(), + "storage_count": len(set(cluster.storageinstance_set.all().values_list("machine__ip"))), + } + for storage in ( + cluster.storageinstance_set.values("instance_role") + .annotate(cnt=Count("machine__ip", distinct=True)) + .order_by() + ): + result["{}_count".format(storage["instance_role"])] = storage["cnt"] + + results.append(result) + + return results + + @classmethod + def query_master_slave_map(cls, master_ips): + """根据master的ip查询主从状态对""" + + # 取消查询,否则sql报错 + if not master_ips: + return {} + + where_sql = "where mim.ip in ({})".format(",".join(["%s"] * len(master_ips))) + with connection.cursor() as cursor: + cursor.execute(SQL_QUERY_MASTER_SLAVE_STATUS.format(where=where_sql), master_ips) + master_slave_map = {ms["ip"]: ms for ms in dictfetchall(cursor)} + + return master_slave_map + + def query_cluster_ips(self, limit=None, offset=None, cluster_id=None, ip=None, role=None, status=None): + """聚合查询集群下的主机""" + + limit_sql = "" + if limit: + # 强制格式化为int,避免sql注入 + limit_sql = " LIMIT {}".format(int(limit)) + + offset_sql = "" + if offset: + offset_sql = " OFFSET {}".format(int(offset)) + + where_sql = "WHERE i.bk_biz_id = %s " + where_values = [self.bk_biz_id] + + if cluster_id: + where_sql += "AND c.cluster_id = %s " + where_values.append(cluster_id) + + if ip: + where_sql += "AND m.ip LIKE %s " + where_values.append(f"%{ip}%") + + if status: + where_sql += "AND i.status = %s " + where_values.append(status) + + having_sql = "" + if role: + having_sql += "HAVING role = %s " + where_values.append(role) + + # union查询需要两份参数 + where_values = where_values * 2 + + sql = SQL_QUERY_INSTANCES.format(where=where_sql, having=having_sql, limit=limit_sql, offset=offset_sql) + + with connection.cursor() as cursor: + cursor.execute(sql, where_values) + + ips = dictfetchall(cursor) + bk_host_ids = [ip["bk_host_id"] for ip in ips] + + # 查询主机信息 + host_id_info_map = { + host_info["host_id"]: host_info + for host_info in HostHandler.check( + [{"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "scope_type": "biz"}], [], [], bk_host_ids + ) + } + + # 查询主从状态对 + master_slave_map = self.query_master_slave_map( + [i["ip"] for i in ips if i["role"] == InstanceRole.REDIS_MASTER] + ) + + # 补充主机、规格和主从关系信息 + for item in ips: + item["host_info"] = host_id_info_map.get(item["bk_host_id"]) + item["spec_config"] = json.loads(item["spec_config"]) + item.update(master_slave_map.get(item["ip"], {})) + + return ips diff --git a/dbm-ui/backend/db_services/redis/toolbox/mock_data.py b/dbm-ui/backend/db_services/redis/toolbox/mock_data.py new file mode 100644 index 0000000000..aa5085c628 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/toolbox/mock_data.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" diff --git a/dbm-ui/backend/db_services/redis/toolbox/serializers.py b/dbm-ui/backend/db_services/redis/toolbox/serializers.py new file mode 100644 index 0000000000..d5f0b3f754 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/toolbox/serializers.py @@ -0,0 +1,200 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 gettext_lazy as _ +from rest_framework import serializers + +from backend.db_meta.enums import InstanceRole, InstanceStatus + + +class QueryByClusterSerializer(serializers.Serializer): + keywords = serializers.ListSerializer(help_text=_("集群id/name/domain列表"), child=serializers.CharField()) + + class Meta: + swagger_schema_fields = {"example": {"keywords": ["a.b.c", "b.c.d", "1", "2"]}} + + +class QueryByClusterResultSerializer(serializers.Serializer): + cluster = serializers.JSONField() + proxy = serializers.JSONField() + storage = serializers.JSONField() + + class Meta: + swagger_schema_fields = { + "example": [ + { + "cluster": { + "id": 4, + "name": "abc", + "cluster_type": "TwemproxyTendisSSDInstance", + "immute_domain": "abc.dba.db", + "major_version": "TendisSSD-1.3", + "bk_cloud_id": 0, + "region": "", + "proxy_count": 2, + "redis_master_count": 1, + "redis_slave_count": 1, + }, + "proxy": [ + { + "id": 6, + "machine__ip": "127.0.0.3", + "port": 50000, + "name": "", + "status": "running", + "version": "", + "machine__spec_config": { + "id": 1, + "cpu": 1, + "mem": 2, + "name": 2, + "storage_spec": {"size": 500, "type": "ssd", "mount_point": "/data"}, + }, + }, + ], + "storage": [ + { + "id": 53, + "machine__ip": "127.0.0.1", + "port": 30017, + "name": "", + "status": "running", + "version": "", + "machine__spec_config": { + "id": 1, + "cpu": 1, + "mem": 2, + "name": 2, + "storage_spec": {"size": 500, "type": "ssd", "mount_point": "/data"}, + }, + "instance_role": "redis_slave", + }, + { + "id": 18, + "machine__ip": "127.0.0.2", + "port": 30000, + "name": "", + "status": "running", + "version": "", + "machine__spec_config": { + "id": 1, + "cpu": 1, + "mem": 2, + "name": 2, + "storage_spec": {"size": 500, "type": "ssd", "mount_point": "/data"}, + }, + "instance_role": "redis_master", + }, + ], + } + ] + } + + +class QueryByIpSerializer(serializers.Serializer): + ips = serializers.ListField(child=serializers.IPAddressField()) + + class Meta: + swagger_schema_fields = {"example": {"ips": ["127.0.0.1"]}} + + +class QueryByIpResultSerializer(serializers.Serializer): + ip = serializers.IPAddressField() + role = serializers.CharField(max_length=32) + cluster = serializers.JSONField() + spec_config = serializers.JSONField() + + class Meta: + swagger_schema_fields = { + "example": [ + { + "ip": "127.0.0.1", + "role": "redis_master", + "cluster": { + "id": 2, + "name": "online", + "cluster_type": "TwemproxyRedisInstance", + "bk_cloud_id": 0, + "proxy_count": 2, + "redis_master_count": 1, + "redis_slave_count": 1, + "region": "", + }, + "spec_config": { + "id": 1, + "name": 2, + "cpu": 1, + "mem": 2, + "storage_spec": {"mount_point": "/data", "size": 500, "type": "ssd"}, + }, + } + ] + } + + +class QueryMasterSlaveByIpResultSerializer(serializers.Serializer): + class Meta: + swagger_schema_fields = { + "example": [ + { + "cluster": { + "id": 2, + "name": "online", + "cluster_type": "TwemproxyRedisInstance", + "bk_cloud_id": 0, + "region": "", + }, + "master_ip": "127.0.0.1", + "slave_ip": "127.0.0.2", + "instances": [ + { + "name": "", + "ip": "127.0.0.1", + "port": 30003, + "instance": "127.0.0.1:30003", + "status": "running", + "phase": "online", + "bk_instance_id": 7, + "bk_host_id": 11, + "bk_cloud_id": 0, + "bk_biz_id": 3, + } + ], + } + ] + } + + +class QueryByOneClusterSerializer(serializers.Serializer): + cluster_id = serializers.IntegerField(help_text=_("集群id")) + + class Meta: + swagger_schema_fields = {"example": {"cluster_id": 1}} + + +class QueryClusterIpsSerializer(serializers.Serializer): + cluster_id = serializers.IntegerField(help_text=_("集群id"), required=False) + ip = serializers.CharField(max_length=32, required=False) + limit = serializers.IntegerField(help_text=_("limit"), required=False, min_value=1) + offset = serializers.IntegerField(help_text=_("offset"), required=False, min_value=1) + role = serializers.ChoiceField(help_text=_("role"), required=False, choices=InstanceRole.get_choices()) + status = serializers.ChoiceField(help_text=_("status"), required=False, choices=InstanceStatus.get_choices()) + + class Meta: + swagger_schema_fields = { + "example": { + "cluster_id": 1, + "ip": "127.0.0.1", + "limit": 1, + "offset": 2, + "role": "redis_master/redis_slave", + "status": "running", + } + } diff --git a/dbm-ui/backend/db_services/redis/toolbox/urls.py b/dbm-ui/backend/db_services/redis/toolbox/urls.py new file mode 100644 index 0000000000..82e3656e02 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/toolbox/urls.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 rest_framework.routers import DefaultRouter + +from backend.db_services.redis.toolbox.views import ToolboxViewSet + +router = DefaultRouter(trailing_slash=True) +router.register(r"toolbox", ToolboxViewSet, basename="toolbox") + +urlpatterns = [] +urlpatterns += router.urls diff --git a/dbm-ui/backend/db_services/redis/toolbox/views.py b/dbm-ui/backend/db_services/redis/toolbox/views.py new file mode 100644 index 0000000000..73da23c0d4 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/toolbox/views.py @@ -0,0 +1,96 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 as _ +from rest_framework import status +from rest_framework.decorators import action +from rest_framework.response import Response + +from backend.bk_web import viewsets +from backend.bk_web.swagger import common_swagger_auto_schema +from backend.db_services.redis.toolbox.handlers import ToolboxHandler +from backend.db_services.redis.toolbox.serializers import ( + QueryByClusterResultSerializer, + QueryByClusterSerializer, + QueryByIpResultSerializer, + QueryByIpSerializer, + QueryByOneClusterSerializer, + QueryClusterIpsSerializer, + QueryMasterSlaveByIpResultSerializer, +) +from backend.iam_app.handlers.drf_perm import DBManageIAMPermission + +SWAGGER_TAG = "db_services/redis/toolbox" + + +class ToolboxViewSet(viewsets.SystemViewSet): + def _get_custom_permissions(self): + return [DBManageIAMPermission()] + + @common_swagger_auto_schema( + operation_summary=_("根据IP查询集群、角色和规格"), + request_body=QueryByIpSerializer(), + tags=[SWAGGER_TAG], + responses={status.HTTP_200_OK: QueryByIpResultSerializer()}, + ) + @action(methods=["POST"], detail=False, serializer_class=QueryByIpSerializer) + def query_by_ip(self, request, bk_biz_id, **kwargs): + validated_data = self.params_validate(self.get_serializer_class()) + return Response(ToolboxHandler(bk_biz_id).query_by_ip(validated_data["ips"])) + + @common_swagger_auto_schema( + operation_summary=_("根据masterIP查询集群、实例和slave"), + request_body=QueryByIpSerializer(), + tags=[SWAGGER_TAG], + responses={status.HTTP_200_OK: QueryMasterSlaveByIpResultSerializer()}, + ) + @action(methods=["POST"], detail=False, serializer_class=QueryByIpSerializer) + def query_master_slave_by_ip(self, request, bk_biz_id, **kwargs): + validated_data = self.params_validate(self.get_serializer_class()) + return Response(ToolboxHandler(bk_biz_id).query_master_slave_by_ip(validated_data["ips"])) + + @common_swagger_auto_schema( + operation_summary=_("批量过滤获取集群实例信息"), + request_body=QueryByClusterSerializer(), + tags=[SWAGGER_TAG], + responses={status.HTTP_200_OK: QueryByClusterResultSerializer()}, + ) + @action(methods=["POST"], detail=False, serializer_class=QueryByClusterSerializer) + def query_instances_by_cluster(self, request, bk_biz_id): + validated_data = self.params_validate(self.get_serializer_class()) + return Response(ToolboxHandler(bk_biz_id).query_instance_by_cluster(validated_data["keywords"])) + + @common_swagger_auto_schema( + operation_summary=_("查询集群下的主机列表"), + request_body=QueryClusterIpsSerializer(), + tags=[SWAGGER_TAG], + ) + @action(methods=["POST"], detail=False, serializer_class=QueryClusterIpsSerializer, pagination_class=None) + def query_cluster_ips(self, request, bk_biz_id): + validated_data = self.params_validate(self.get_serializer_class()) + return Response(ToolboxHandler(bk_biz_id).query_cluster_ips(**validated_data)) + + @common_swagger_auto_schema( + operation_summary=_("根据cluster_id查询主从关系对"), + request_body=QueryByOneClusterSerializer(), + tags=[SWAGGER_TAG], + ) + @action(methods=["POST"], detail=False, serializer_class=QueryByOneClusterSerializer) + def query_master_slave_pairs(self, request, bk_biz_id, **kwargs): + validated_data = self.params_validate(self.get_serializer_class()) + return Response(ToolboxHandler(bk_biz_id).query_master_slave_pairs(validated_data["cluster_id"])) + + @common_swagger_auto_schema( + operation_summary=_("根据业务ID查询集群列表"), + tags=[SWAGGER_TAG], + ) + @action(methods=["GET"], detail=False, serializer_class=None, pagination_class=None) + def query_cluster_list(self, request, bk_biz_id, **kwargs): + return Response(ToolboxHandler(bk_biz_id).query_cluster_list()) diff --git a/dbm-ui/backend/db_services/redis/urls.py b/dbm-ui/backend/db_services/redis/urls.py index dbbf5f102b..a5f72b2922 100644 --- a/dbm-ui/backend/db_services/redis/urls.py +++ b/dbm-ui/backend/db_services/redis/urls.py @@ -12,4 +12,8 @@ urlpatterns = [ path("bizs//", include("backend.db_services.redis.resources.urls")), + path("bizs//", include("backend.db_services.redis.toolbox.urls")), + path("bizs//", include("backend.db_services.redis.instance.urls")), + path("bizs//", include("backend.db_services.redis.rollback.urls")), + path("bizs//", include("backend.db_services.redis.redis_dts.urls")), ] diff --git a/dbm-ui/backend/db_services/redis/util.py b/dbm-ui/backend/db_services/redis/util.py new file mode 100644 index 0000000000..7d6c9e5876 --- /dev/null +++ b/dbm-ui/backend/db_services/redis/util.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 backend.db_meta.enums import ClusterType + + +def is_redis_instance_type(cluster_type: str) -> bool: + """ + 是否是redis实例类型 + """ + return cluster_type in [ + ClusterType.TendisPredixyRedisCluster, + ClusterType.RedisCluster, + ClusterType.TendisTwemproxyRedisInstance, + ClusterType.TendisRedisInstance, + ClusterType.TendisRedisCluster, + ] + + +def is_tendisplus_instance_type(cluster_type: str) -> bool: + """ + 是否是tendisplus实例集群类型 + """ + return cluster_type in [ + ClusterType.TendisPredixyTendisplusCluster, + ClusterType.TendisTwemproxyTendisplusIns, + ClusterType.TendisTendisplusInsance, + ClusterType.TendisTendisplusCluster, + ] + + +def is_tendisssd_instance_type(cluster_type: str) -> bool: + """ + 是否是tendisssd实例类型 + """ + return cluster_type in [ClusterType.TwemproxyTendisSSDInstance, ClusterType.TendisTendisSSDInstance] + + +def is_twemproxy_proxy_type(cluster_type: str) -> bool: + """ + 是否是twemproxy proxy类型 + """ + return cluster_type in [ + ClusterType.TendisTwemproxyRedisInstance, + ClusterType.TendisTwemproxyTendisplusIns, + ClusterType.TwemproxyTendisSSDInstance, + ] + + +def is_predixy_proxy_type(cluster_type: str) -> bool: + """ + 是否是predixy proxy类型 + """ + return cluster_type in [ + ClusterType.TendisPredixyRedisCluster, + ClusterType.TendisPredixyTendisplusCluster, + ] + + +def is_redis_cluster_protocal(cluster_type: str) -> bool: + """ + 是否是redis_cluster协议 + """ + return cluster_type in [ + ClusterType.TendisPredixyRedisCluster, + ClusterType.TendisPredixyTendisplusCluster, + ClusterType.RedisCluster, + ] diff --git a/dbm-ui/backend/db_services/redis_dts/apis.py b/dbm-ui/backend/db_services/redis_dts/apis.py deleted file mode 100644 index 885fa327de..0000000000 --- a/dbm-ui/backend/db_services/redis_dts/apis.py +++ /dev/null @@ -1,536 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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. -""" -import logging -from datetime import datetime, timedelta, timezone -from typing import Tuple - -from django.db import IntegrityError, connection, transaction -from django.db.models import Case, IntegerField, Q, Value, When -from django.forms.models import model_to_dict -from django.utils.translation import ugettext_lazy as _ - -from backend.db_meta.enums import ClusterType -from backend.utils.time import datetime2str, strptime - -from .constants import DtsOperateType, DtsTaskType -from .exceptions import GetDtsJobTasksException, TaskOperateException -from .models import ( - TbDtsServerBlacklist, - TbTendisDtsDistributeLock, - TbTendisDTSJob, - TbTendisDtsTask, - dts_task_binary_to_str, - dts_task_clean_passwd_and_format_time, - dts_task_format_time, -) - -logger = logging.getLogger("redis_dts") - - -def is_dtsserver_in_blacklist(payload: dict) -> bool: - """判断dts_server是否在黑名单中""" - ip = payload.get("ip") - row = TbDtsServerBlacklist.objects.filter(ip=ip) - if row: - return True - return False - - -def get_dts_history_jobs(payload: dict) -> list: - """获取迁移任务列表以及其对应task cnt""" - local_tz = datetime.now(timezone.utc).astimezone().tzinfo - where = Q() - if "user" in payload: - where &= Q(user=payload.get("user")) - if "start_time" in payload: - # start_time = strptime(payload.get("start_time")).replace(tzinfo=local_tz) - start_time = strptime(payload.get("start_time")) - where &= Q(create_time__gte=start_time) - if "end_time" in payload: - # end_time = strptime(payload.get("end_time")).replace(tzinfo=local_tz) - end_time = strptime(payload.get("end_time")) - # end_time = parse_datetime(payload.get("end_time")) - where &= Q(create_time__lte=end_time) - jobs = TbTendisDTSJob.objects.filter(where).order_by("-create_time") - resp = [] - to_exec_cnt = 0 - running_cnt = 0 - failed_cnt = 0 - success_cnt = 0 - for job in jobs: - tasks = TbTendisDtsTask.objects.filter( - Q(bill_id=job.bill_id) & Q(src_cluster=job.src_cluster) & Q(dst_cluster=job.dst_cluster) - ) - for task in tasks: - if task.task_type == "" and task.status == 0: - to_exec_cnt += 1 - elif task.task_type == DtsTaskType.TENDISSSD_BACKUP.value and task.status == 0: - to_exec_cnt += 1 - elif task.task_type == DtsTaskType.TENDISSSD_BACKUP.value and task.status == 1: - running_cnt += 1 - elif task.task_type != DtsTaskType.TENDISSSD_BACKUP.value and (task.status == 0 or task.status == 1): - running_cnt += 1 - elif task.status == -1: - failed_cnt += 1 - elif task.status == 2: - success_cnt += 1 - job_json = model_to_dict(job) - job_json["total_cnt"] = len(tasks) - job_json["to_exec_cnt"] = to_exec_cnt - job_json["running_cnt"] = running_cnt - job_json["failed_cnt"] = failed_cnt - job_json["success_cnt"] = success_cnt - job_json["create_time"] = datetime2str(job.create_time) - job_json["update_time"] = datetime2str(job.update_time) - resp.append(job_json) - return resp - - -def get_dts_job_detail(payload: dict) -> list: - """获取迁移任务详情""" - bill_id = payload.get("bill_id") - src_cluster = payload.get("src_cluster") - dst_cluster = payload.get("dst_cluster") - jobs = TbTendisDTSJob.objects.filter(Q(bill_id=bill_id) & Q(src_cluster=src_cluster) & Q(dst_cluster=dst_cluster)) - resp = [] - for job in jobs: - job_json = model_to_dict(job) - resp.append(job_json) - return resp - - -def get_dts_job_tasks(payload: dict) -> list: - """获取迁移任务task列表,失败的排在前面""" - bill_id = payload.get("bill_id") - src_cluster = payload.get("src_cluster") - dst_cluster = payload.get("dst_cluster") - tasks = TbTendisDtsTask.objects.filter( - Q(bill_id=bill_id) & Q(src_cluster=src_cluster) & Q(dst_cluster=dst_cluster) - ).order_by( - # order by FIELD(status,-1,1,0,2),src_ip,src_port - Case( - When(status=-1, then=Value(0)), - When(status=1, then=Value(1)), - When(status=0, then=Value(2)), - When(status=2, then=Value(3)), - output_field=IntegerField(), - ), - "src_ip", - "src_port", - ) - resp = [] - for task in tasks: - task_json = model_to_dict(task) - dts_task_clean_passwd_and_format_time(task_json, task) - dts_task_binary_to_str(task_json, task) - resp.append(task_json) - return resp - - -def task_sync_stop_precheck(task: TbTendisDtsTask) -> Tuple[bool, str]: - """同步完成前置检查""" - ok = False - msg = "" - if ( - task.task_type != DtsTaskType.TENDISSSD_MAKESYNC.value - and task.task_type != DtsTaskType.MAKE_CACHE_SYNC.value - and task.task_type != DtsTaskType.WATCH_CACHE_SYNC.value - and task.task_type != DtsTaskType.TENDISPLUS_SENDINCR.value - ): - msg = "{} task_type:{} cannot do sync_top operate".format(task.get_src_redis_addr(), task.task_type) - return ok, msg - if task.status != 1: - msg = "{} status={} is not running status".format(task.get_src_redis_addr(), task.status) - return ok, msg - if task.tendis_binlog_lag > 300: - msg = "{} binlog_lag={} > 300s".format(task.get_src_redis_addr(), task.tendis_binlog_lag) - return ok, msg - current_time = datetime.now(timezone.utc).astimezone() - if task.update_time and (current_time - task.update_time).seconds > 300: - msg = "{} the status has not been updated for more than 5 minutes,last update time:{}".format( - task.get_src_redis_addr(), task.update_time - ) - return ok, msg - ok = True - return ok, msg - - -@transaction.atomic -def dts_tasks_operate(payload: dict): - """dts task操作,目前支持 同步完成(syncStopTodo)、强制终止(ForceKillTaskTodo) 两个操作""" - # taskids: list, operate: str - task_ids = payload.get("task_ids") - operate = payload.get("operate") - if operate != DtsOperateType.SYNC_STOP_TODO.value and operate != DtsOperateType.FORCE_KILL_TODO.value: - raise Exception( - "operate:{} not [{},{}]".format(operate, DtsOperateType.SYNC_STOP_TODO.value, DtsOperateType.value) - ) - if not task_ids: - raise Exception("taskids:{} is empty".format(task_ids)) - tasks = TbTendisDtsTask.objects.filter(id__in=task_ids) - for task in tasks: - if operate == DtsOperateType.SYNC_STOP_TODO.value: - precheck_ret = task_sync_stop_precheck(task) - if not precheck_ret[0]: - logger.warning("dts_tasks_operate fail,err:{}".format(precheck_ret[1])) - raise Exception(precheck_ret[1]) - task.sync_operate = operate - task.message = (operate + "...").encode("utf-8") - task.update_time = datetime.now(timezone.utc).astimezone() - task.save(update_fields=["sync_operate", "message", "update_time"]) - return None - - -@transaction.atomic -def dts_tasks_restart(payload: dict): - """dts tasks重新开始""" - task_ids = payload.get("task_ids") - tasks = TbTendisDtsTask.objects.filter(id__in=task_ids) - for task in tasks: - if task.status != -1: - raise Exception( - "{} not failed(status={} not -1),restart not allowed".format(task.get_src_redis_addr(), task.status) - ) - if task.src_dbtype == ClusterType.TendisTendisSSDInstance.value: - task.task_type = DtsTaskType.TENDISSSD_BACKUP.value - elif task.src_dbtype == ClusterType.TendisRedisInstance.value: - task.task_type = DtsTaskType.MAKE_CACHE_SYNC.value - elif task.src_dbtype == ClusterType.TendisTendisplusInsance: - task.task_type = DtsTaskType.TENDISPLUS_MAKESYNC.value - else: - raise Exception("{} src_dbtype:{} not support".format(task.get_src_redis_addr(), task.src_dbtype)) - task.status = 0 - task.message = "task waiting for restart...".encode("utf-8") - task.sync_operate = "" - task.retry_times = task.retry_times + 1 - task.update_time = datetime.now(timezone.utc).astimezone() - task.save(update_fields=["task_type", "status", "message", "sync_operate", "retry_times", "update_time"]) - - -@transaction.atomic -def dts_tasks_retry(payload: dict): - """dts tasks重试当前步骤""" - taskids = payload.get("taskids") - tasks = TbTendisDtsTask.objects.filter(id__in=taskids) - for task in tasks: - if task.status != -1: - raise Exception( - "{} not failed(status={} not -1),retry not allowed".format(task.get_src_redis_addr(), task.status) - ) - if task.src_have_list_keys > 0 and task.src_dbtype != ClusterType.TendisRedisInstance.value: - """ - SrcHaveListKeys>0,其实只有在TendisSSD中才会出现,RedisInstance、tendisplus中很难发现是否有list类型的key - 而且当SrcHaveListKeys>0时,代表tendisSSD已经在 tredisump 阶段以后了,有list类型key情况下后续阶段重试是有风险的; - RedisInstance不用管是否有list,因为有同名key存在时,通过restore replace会完全覆盖; - """ - raise Exception("{} include list type keys,cannot retry".format(task.get_src_redis_addr())) - if task.task_type == DtsTaskType.TENDISSSD_WATCHOLDSYNC.value: - task.task_type == DtsTaskType.TENDISSSD_MAKESYNC.value - elif task.task_type == DtsTaskType.WATCH_CACHE_SYNC.value: - task.task_type == DtsTaskType.MAKE_CACHE_SYNC.value - elif ( - task.task_type == DtsTaskType.TENDISPLUS_SENDBULK.value - or task.task_type == DtsTaskType.TENDISPLUS_SENDINCR.value - ): - task.task_type == DtsTaskType.TENDISPLUS_MAKESYNC.value - task.status = 0 - task.message = "{} waiting for retry...".format(task.task_type).encode("utf-8") - task.sync_operate = "" - task.retry_times = task.retry_times + 1 - task.update_time = datetime.now() - task.save(update_fields=["task_type", "status", "message", "sync_operate", "retry_times", "update_time"]) - - -def dts_distribute_trylock(payload: dict) -> bool: - """dts 分布式锁,trylock,成功返回True,失败返回False""" - lockkey = payload.get("lockkey") - holder = payload.get("holder") - ttl_sec = payload.get("ttl_sec") - current_time = datetime.now(timezone.utc).astimezone() - expire_time = current_time + timedelta(seconds=ttl_sec) - lock_row = TbTendisDtsDistributeLock( - lock_key=lockkey, holder=holder, creation_time=current_time, lock_expire_time=expire_time - ) - err = None - try: - lock_row.save() - except Exception as e: - err = e - if not err: - return True - - if not (isinstance(err, IntegrityError) and "Duplicate entry" in err.args[1]): - raise err - - # 可重入锁,lock_expire_time>now()代表锁未过期,可以重入,更新过期时间 - # update tb_tendis_dts_distribute_lock set lock_expire_time=? - # where lock_key=? and holder=? and lock_expire_time>now(); - with transaction.atomic(): - updated_rows = TbTendisDtsDistributeLock.objects.filter( - lock_key=lockkey, holder=holder, lock_expire_time__gt=current_time - ).update(lock_expire_time=expire_time) - if updated_rows == 1: - return True - - # 锁存在,且已过期 - # lock_expire_time list: - """获取dts server迁移中的任务 - 对tendiSSD来说,'迁移中' 指处于 'tendisBackup'、'backupfileFetch'、'tendisdump'、'cmdsImporter'中的task, - 不包含处于 status=-1 或 处于 makeSync 状态的task - 对tendisCache来说,'迁移中'指处于 'makeCacheSync'中的task,不包含处于 status=-1 或 处于 watchCacheSync 状态的task - """ - bk_cloud_id = payload.get("bk_cloud_id") - dts_server = payload.get("dts_server") - db_type = payload.get("db_type") - task_types = payload.get("task_types") - current_time = datetime.now(timezone.utc).astimezone() - thirty_days_ago = current_time - timedelta(days=30) - where = Q(bk_cloud_id=bk_cloud_id) - if dts_server: - where = where & Q(dts_server=dts_server) - if db_type: - where = where & Q(src_dbtype=db_type) - if task_types: - where = where & Q(task_type__in=task_types) - where = where & Q(update_time__gt=thirty_days_ago) - where = where & Q(status__in=[0, 1]) - tasks = TbTendisDtsTask.objects.filter(where) - rets = [] - for task in tasks: - json_data = model_to_dict(task) - dts_task_clean_passwd_and_format_time(json_data, task) - dts_task_binary_to_str(json_data, task) - rets.append(json_data) - return rets - - -def get_dts_server_max_sync_port(payload: dict) -> dict: - """获取DtsServer上syncPort最大的task""" - bk_cloud_id = payload.get("bk_cloud_id") - dts_server = payload.get("dts_server") - db_type = payload.get("db_type") - task_types = payload.get("task_types") - current_time = datetime.now(timezone.utc).astimezone() - thirty_days_ago = current_time - timedelta(days=30) - where = Q(bk_cloud_id=bk_cloud_id) - if dts_server: - where = where & Q(dts_server=dts_server) - if db_type: - where = where & Q(src_dbtype=db_type) - if task_types: - where = where & Q(task_type__in=task_types) - where = where & Q(update_time__gt=thirty_days_ago) - where = where & Q(status=1) - task = TbTendisDtsTask.objects.filter(where).order_by("-syncer_port").first() - if task: - json_data = model_to_dict(task) - dts_task_clean_passwd_and_format_time(json_data, task) - dts_task_binary_to_str(json_data, task) - return json_data - return None - - -def get_last_30days_to_exec_tasks(payload: dict) -> list: - """获取最近30天内task_type类型的等待执行的tasks""" - bk_cloud_id = payload.get("bk_cloud_id") - dts_server = payload.get("dts_server") - task_type = payload.get("task_type") - db_type = payload.get("db_type") - limit = payload.get("limit") - status = payload.get("status") - dts_server = dts_server.strip() - task_type = task_type.strip() - db_type = db_type.strip() - current_time = datetime.now(timezone.utc).astimezone() - thirty_days_ago = current_time - timedelta(days=30) - where = Q(bk_cloud_id=bk_cloud_id) - if dts_server: - where = where & Q(dts_server=dts_server) - if task_type: - where = where & Q(task_type=task_type) - if db_type: - where = where & Q(src_dbtype=db_type) - if limit <= 0: - limit = 1 - where = where & Q(status=status) - where = where & Q(create_time__gt=thirty_days_ago) - tasks = TbTendisDtsTask.objects.filter(where).order_by("-src_cluster_priority", "create_time")[:limit] - if not tasks: - logger.warning( - "get_last_30days_to_exec_tasks empty records" - ",bk_cloud_id:{},dts_server:{},task_type:{},db_type:{},status:{}".format( - bk_cloud_id, dts_server, task_type, db_type, status - ) - ) - return [] - rets = [] - for task in tasks: - json_data = model_to_dict(task) - dts_task_format_time(json_data, task) - dts_task_binary_to_str(json_data, task) - rets.append(json_data) - return rets - - -def get_last_30days_to_schedule_jobs(payload: dict) -> list: - """获取最近30天内的等待调度的jobs - billId、srcCluster、dstCluster唯一确定一个dts_job - 获取的dts_jobs必须满足: - 有一个待调度的task.dataSize < maxDataSize & status=0 & taskType="" & dtsServer="1.1.1.1" - """ - bk_cloud_id = payload.get("bk_cloud_id") - max_data_size = payload.get("max_data_size") - zone_name = payload.get("zone_name") - db_type = payload.get("db_type") - current_time = datetime.now(timezone.utc).astimezone() - thirty_days_ago = current_time - timedelta(days=30) - where = Q(bk_cloud_id=bk_cloud_id) - where = where & Q(src_dbsize__lte=max_data_size) - if zone_name: - where = where & Q(src_ip_zonename=zone_name) - if db_type: - where = where & Q(src_dbtype=db_type) - where = where & Q(dts_server="1.1.1.1") & Q(task_type="") & Q(status=0) & Q(create_time__gt=thirty_days_ago) - jobs = TbTendisDtsTask.objects.filter(where).order_by("-src_cluster_priority", "create_time") - if not jobs: - logger.warning( - "get_last_30days_to_schedule_jobs empty records," - "bk_cloud_id={},max_data_size={},zone_name={},db_type={}".format( - bk_cloud_id, max_data_size, zone_name, db_type - ) - ) - return [] - rets = [] - unique_set = set() - for job in jobs: - job_uniq_key = "{}-{}-{}".format(job.bill_id, job.src_cluster, job.dst_cluster) - if job_uniq_key in unique_set: - continue - unique_set.add(job_uniq_key) - json_data = model_to_dict(job) - dts_task_clean_passwd_and_format_time(json_data, job) - dts_task_binary_to_str(json_data, job) - rets.append(json_data) - return rets - - -def get_job_to_schedule_tasks(payload: dict) -> list: - """获取一个job的所有待调度的tasks""" - # bill_id: int, src_cluster: str, dst_cluster: str - bill_id = payload.get("bill_id") - src_cluster = payload.get("src_cluster") - dst_cluster = payload.get("dst_cluster") - if bill_id == 0 or not src_cluster or not dst_cluster: - raise Exception( - "invalid params,bill_id={},src_cluster={},dst_cluster={} all can't be empty".format( - bill_id, src_cluster, dst_cluster - ) - ) - current_time = datetime.now(timezone.utc).astimezone() - thirty_days_ago = current_time - timedelta(days=30) - where = Q(bill_id=bill_id) & Q(src_cluster=src_cluster) & Q(dst_cluster=dst_cluster) - where = where & Q(update_time__gt=thirty_days_ago) & Q(dts_server="1.1.1.1") & Q(task_type="") & Q(status=0) - tasks = TbTendisDtsTask.objects.filter(where).order_by("src_weight") - if not tasks: - logger.warning( - "get_job_to_schedule_tasks empty records,bill_id={},src_cluster={},dst_cluster={}".format( - bill_id, src_cluster, dst_cluster - ) - ) - return [] - rets = [] - for task in tasks: - json_data = model_to_dict(task) - dts_task_clean_passwd_and_format_time(json_data, task) - dts_task_binary_to_str(json_data, task) - rets.append(json_data) - return rets - - -def get_job_src_ip_running_tasks(payload: dict) -> list: - """获取一个job的所有待调度的tasks""" - bill_id = payload.get("bill_id") - src_cluster = payload.get("src_cluster") - dst_cluster = payload.get("dst_cluster") - src_ip = payload.get("src_ip") - task_types = payload.get("task_types") - current_time = datetime.now(timezone.utc).astimezone() - thirty_days_ago = current_time - timedelta(days=30) - where = Q(bill_id=bill_id) & Q(src_cluster=src_cluster) & Q(dst_cluster=dst_cluster) & Q(src_ip=src_ip) - where = where & Q(update_time__gt=thirty_days_ago) & Q(status__in=(0, 1)) & Q(task_type__in=task_types) - tasks = TbTendisDtsTask.objects.filter(where) - if not tasks: - logger.warning( - "get_job_src_ip_running_tasks empty records,bill_id={},src_cluster={},dst_cluster={},src_ip={}".format( - bill_id, src_cluster, dst_cluster, src_ip - ) - ) - return [] - rets = [] - for task in tasks: - json_data = model_to_dict(task) - dts_task_clean_passwd_and_format_time(json_data, task) - dts_task_binary_to_str(json_data, task) - rets.append(json_data) - return rets - - -def get_dts_task_by_id(payload: dict) -> dict: - """根据task_id获取dts_task""" - task_id = payload.get("task_id") - task = TbTendisDtsTask.objects.get(id=task_id) - if not task: - logger.warning("dts task not found,task_id={}".format(task_id)) - return None - json_data = model_to_dict(task) - dts_task_format_time(json_data, task) - dts_task_binary_to_str(json_data, task) - return json_data - - -def dts_tasks_updates(paylod: dict): - """批量更新dts_tasks - :param - task_ids: task_id列表 - update_params: 列名和值的对应关系,如 {"status": 1,"message": "test"} - """ - task_ids = paylod.get("task_ids") - col_to_val = paylod.get("col_to_val") - if not task_ids: - raise Exception("invalid params,task_ids can't be empty") - if not col_to_val: - raise Exception("invalid params,update_params can't be empty") - if "message" in col_to_val: - col_to_val["message"] = col_to_val["message"].encode("utf-8") - if "key_white_regex" in col_to_val: - col_to_val["key_white_regex"] = col_to_val["key_white_regex"].encode("utf-8") - if "key_black_regex" in col_to_val: - col_to_val["key_black_regex"] = col_to_val["key_black_regex"].encode("utf-8") - rows_affected = TbTendisDtsTask.objects.filter(id__in=task_ids).update(**col_to_val) - return rows_affected diff --git a/dbm-ui/backend/db_services/redis_dts/apps.py b/dbm-ui/backend/db_services/redis_dts/apps.py deleted file mode 100644 index 632df22d38..0000000000 --- a/dbm-ui/backend/db_services/redis_dts/apps.py +++ /dev/null @@ -1,16 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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.apps import AppConfig - - -class DbDtsConfig(AppConfig): - default_auto_field = "django.db.models.BigAutoField" - name = "backend.db_services.redis_dts" diff --git a/dbm-ui/backend/db_services/redis_dts/constants.py b/dbm-ui/backend/db_services/redis_dts/constants.py deleted file mode 100644 index f13876e65b..0000000000 --- a/dbm-ui/backend/db_services/redis_dts/constants.py +++ /dev/null @@ -1,169 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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 blue_krill.data_types.enum import EnumField, StructuredEnum -from django.utils.translation import ugettext_lazy as _ - - -class DtsTaskType(str, StructuredEnum): - """DTS task类型枚举""" - - # tendis ssd相关任务 - TENDISSSD_BACKUP = EnumField("tendisBackup", _("tendis ssd备份任务")) - TENDISSSD_BACKUPFILE_FETCH = EnumField("backupfileFetch", _("tendis ssd备份拉取任务")) - TENDISSSD_TREDISDUMP = EnumField("tendisdump", _("tendis ssd备份解析任务")) - TENDISSSD_CMDSIMPOTER = EnumField("cmdsImpoter", _("tendis ssd数据导入任务")) - TENDISSSD_MAKESYNC = EnumField("makeSync", _("tendis ssd拉起sync任务")) - TENDISSSD_WATCHOLDSYNC = EnumField("WatchOldSync", _("tendis ssd监视sync任务")) - - # redis cache相关任务 - MAKE_CACHE_SYNC = EnumField("makeCacheSync", _("redis cache拉起redis-shake任务")) - WATCH_CACHE_SYNC = EnumField("watchCacheSync", _("tendis ssd监视sync任务")) - - # tendisplus相关任务 - TENDISPLUS_MAKESYNC = EnumField("tendisplusMakeSync", _("tendisplus拉起reids-sync任务")) - # 将全量数据同步 与 增量数据同步分开,原因是 存量数据同步讲占用较多内存,增量不占用内存 - TENDISPLUS_SENDBULK = EnumField("tendisplusSendBulk", _("tendisplus全量数据同步")) - TENDISPLUS_SENDINCR = EnumField("tendisplusSendIncr", _("tendisplus增量数据同步")) - - -class DtsOperateType(str, StructuredEnum): - """DTS task操作类型枚举""" - - SYNC_STOP_TODO = EnumField("SyncStopTodo", _("停止数据同步todo")) - SYNC_STOP_FAIL = EnumField("SyncStopTodo", _("停止数据同步失败")) - SYNC_STOP_SUCC = EnumField("SyncStopTodo", _("停止数据同步成功")) - - FORCE_KILL_TODO = EnumField("ForceKillTaskTodo", _("强制暂停任务todo")) - FORCE_KILL_FAIL = EnumField("ForceKillTaskFail", _("强制暂停任务失败")) - FORCE_KILL_SUCC = EnumField("ForceKillTaskSucc", _("强制暂停任务成功")) - - -class DtsBillType(str, StructuredEnum): - """DTS 单据类型""" - - CLUSTER_NODES_NUM_UPDATE = EnumField("cluster_nodes_num_update", _("集群节点数变更")) - CLUSTER_TYPE_UPDATE = EnumField("cluster_type_update", _("集群类型变更")) - CLUSTER_DATA_COPY = EnumField("cluster_data_copy", _("数据复制")) - - -class DtsCopyType(str, StructuredEnum): - """DTS 数据复制 类型""" - - ONE_APP_DIFF_CLUSTER = EnumField("one_app_diff_cluster", _("同一业务不同集群")) - DIFF_APP_DIFF_CLUSTER = EnumField("diff_app_diff_cluster", _("不同业务不同集群")) - COPY_TO_OTHER_SYSTEM = EnumField("copy_to_other_system", _("复制到其他系统")) - COPY_FROM_ROLLBACK_TEMP = EnumField("copy_from_rollback_temp", _("从回档临时环境复制数据")) - USER_BUILT_TO_DBM = EnumField("user_built_to_dbm", _("用户自建redis迁移到DBM")) - - -class DTS_ONLINE_SWITCH_TYPE(str, StructuredEnum): - """DTS在线切换类型""" - - AUTO = EnumField("auto", _("自动切换")) - USER_CONFIRM = EnumField("user_confirm", _("用户确认切换")) - - -class DTS_DATA_REPAIR_TYPE(str, StructuredEnum): - """DTS数据修复类型""" - - AUTO = EnumField("auto", _("自动修复")) - USER_CONFIRM = EnumField("user_confirm", _("用户确认修复")) - - -# DTS在线切换twemproxy前置检查脚本 -DTS_SWITCH_TWEMPROXY_PRECHECK = """ -srcProxyIP={{SRC_PROXY_IP}} -srcProxyPort={{SRC_PROXY_PORT}} -srcAdminPort=$(($srcProxyPort+1000)) -srcProxyPassword="{{SRC_PROXY_PASSWORD}}" - -filterRet=$(ifconfig|grep $srcProxyIP) -if [[ -z $filterRet ]] -then -echo "[ERROR] 'ifconfig' not found $srcProxyIP" -exit -1 -fi - -confFile=$(ps aux|grep "nutcracker.$srcProxyPort.yml"|grep -v grep|grep --only-match -P 'c .*.yml '|awk '{print $2}') -if [[ -z $confFile ]] -then -echo "[ERROR] get twemproxy $srcProxyPort conf file fail" -exit -1 -fi - -filterRet=$(grep -iw 'listen' $confFile|grep "$srcProxyIP:$srcProxyPort") -if [[ -z $filterRet ]] -then -echo "[ERROR] $srcProxyIP twemproxy confFile:$confFile listen not $srcProxyIP:$srcProxyPort" -exit -1 -fi - -filterRet=$(grep -iw 'password' $confFile|grep "$srcProxyPassword") -if [[ -z $filterRet ]] -then -echo "[ERROR] $srcProxyIP twemproxy confFile:$confFile password not $srcProxyPassword" -exit -1 -fi - -filterRet=$(echo 'get nosqlproxy servers'| /home/mysql/dbtools/netcat $srcProxyIP $srcAdminPort) -if [[ -z $filterRet ]] -then -echo "[ERROR] twemproxy[$srcProxyIP:$srcAdminPort] get backend redis fail" -exit -1 -fi - -cat $configFile -""" -# DTS在线切换predixy前置检查脚本 -DTS_SWITCH_PREDIXY_PRECHECK = """ -srcProxyIP={{SRC_PROXY_IP}} -srcProxyPort={{SRC_PROXY_PORT}} -srcAdminPort=$(($srcProxyPort+1000)) -srcProxyPassword="{{SRC_PROXY_PASSWORD}}" - -filterRet=$(ifconfig|grep $srcProxyIP) -if [[ -z $filterRet ]] -then -echo "[ERROR] 'ifconfig' not found $srcProxyIP" -exit -1 -fi - -confFile=$(ps aux|grep "/usr/local/predixy/bin/predixy"|grep -v grep|awk '{print $NF}') -if [[ -z $confFile ]] -then -echo "[ERROR] get predixy $srcProxyPort conf file fail" -exit -1 -fi - -filterRet=$(grep -iw 'Bind' $confFile|grep "$srcProxyIP:$srcProxyPort") -if [[ -z $filterRet ]] -then -echo "[ERROR] $srcProxyIP predixy confFile:$confFile Bind not $srcProxyIP:$srcProxyPort" -exit -1 -fi - -filterRet=$(grep -iw 'Auth' $confFile|grep "$srcProxyPassword") -if [[ -z $filterRet ]] -then -echo "[ERROR] $srcProxyIP predixy confFile:$confFile password not $srcProxyPassword" -exit -1 -fi - -getVal=$(/usr/local/predixy/bin/redis-cli -h $srcProxyIP -p $srcProxyPort -a $srcProxyPassword get a) -shopt -s nocasematch; -if [[ $getVal =~ "ERR" ]] -then -echo "[ERROR] predixy($srcProxyIP:$srcProxyPort) get a failed,err:$getVal" -exit -1 -fi - -cat $confFile -""" diff --git a/dbm-ui/backend/db_services/redis_dts/models/__init__.py b/dbm-ui/backend/db_services/redis_dts/models/__init__.py deleted file mode 100644 index 99093c1786..0000000000 --- a/dbm-ui/backend/db_services/redis_dts/models/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# -*- coding: utf-8 -*- - -from .tb_dts_distribute_lock import TbTendisDtsDistributeLock -from .tb_dts_server_blacklist import TbDtsServerBlacklist -from .tb_tendis_dts_job import TbTendisDTSJob -from .tb_tendis_dts_task import ( - TbTendisDtsTask, - dts_task_binary_to_str, - dts_task_clean_passwd_and_format_time, - dts_task_format_time, -) diff --git a/dbm-ui/backend/db_services/redis_dts/models/tb_tendis_dts_job.py b/dbm-ui/backend/db_services/redis_dts/models/tb_tendis_dts_job.py deleted file mode 100644 index 3cc7f029b9..0000000000 --- a/dbm-ui/backend/db_services/redis_dts/models/tb_tendis_dts_job.py +++ /dev/null @@ -1,63 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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.db import models -from django.utils.translation import ugettext_lazy as _ - - -class TbTendisDTSJob(models.Model): - id = models.BigAutoField(primary_key=True) - bill_id = models.BigIntegerField(default=0, db_index=True, verbose_name=_("单据号")) - app = models.CharField(max_length=64, default="", verbose_name=_("业务bk_biz_id")) - bk_cloud_id = models.BigIntegerField(default=0, verbose_name=_("云区域id")) - user = models.CharField(max_length=64, default="", verbose_name=_("申请人")) - # DTS单据类型, 值包括 - # - cluster_nodes_num_update(集群节点数变更) - # - cluster_type_update(集群类型变更) - # - cluster_data_copy(集群数据复制) - dts_bill_type = models.CharField(max_length=64, default="", verbose_name=_("DTS单据类型")) - # DTS数据复制类型, 值包括 - # - one_app_diff_cluster(同一业务不同集群) - # - diff_app_diff_cluster(不同业务不同集群) - # - copy_from_rollback_temp(从回滚临时环境复制数据) - # - copy_to_other_system(同步到其他系统,如迁移到腾讯云) - # - user_built_to_dbm(业务自建迁移到dbm系统) - dts_copy_type = models.CharField(max_length=64, default="", verbose_name=_("DTS数据复制类型")) - # DTS 在线切换类型,值包括 - # - auto(自动切换) - # - user_confirm(用户确认切换) - online_switch_type = models.CharField(max_length=64, default="", verbose_name=_("在线切换类型")) - datacheck = models.IntegerField(default=0, verbose_name=_("是否数据校验")) - datarepair = models.IntegerField(default=0, verbose_name=_("是否数据修复")) - # DTS 数据修复模式,值包括 - # - auto(自动修复) - # - user_confirm(用户确认修复) - datarepair_mode = models.CharField(max_length=64, default="", verbose_name=_("数据修复模式")) - src_cluster = models.CharField(max_length=128, default="", verbose_name=_("源集群")) - # 源集群类型,如 PredixyTendisplusCluster、TwemproxyTendisSSDInstance - src_cluster_type = models.CharField(max_length=64, default="", verbose_name=_("源集群类型")) - src_rollback_bill_id = models.BigIntegerField(default=0, verbose_name=_("回滚单据号")) - src_rollback_instances = models.BinaryField(max_length=128, default=b"", verbose_name=_("回滚临时环境实例")) - dst_bk_biz_id = models.CharField(max_length=64, default="", verbose_name=_("目标业务id")) - dst_cluster = models.CharField(max_length=128, default="", verbose_name=_("目的集群")) - # 目标集群类型,如 PredixyTendisplusCluster、TwemproxyTendisSSDInstance - dst_cluster_type = models.CharField(max_length=64, default="", verbose_name=_("目标集群类型")) - key_white_regex = models.BinaryField(default=b"", verbose_name=_("key正则(包含key)")) - key_black_regex = models.BinaryField(default=b"", verbose_name=_("key正则(排除key)")) - # 任务状态,该字段没用了 - status = models.IntegerField(default=0, db_index=True, verbose_name=_("任务状态")) - reason = models.BinaryField(default=b"", verbose_name=_("bill备注")) - create_time = models.DateTimeField(auto_now_add=True, db_index=True, verbose_name=_("创建时间")) - update_time = models.DateTimeField(auto_now=True, verbose_name=_("更新时间")) - - class Meta: - db_table = "tb_tendis_dts_job" - verbose_name = "tb_tendis_dts_job" - verbose_name_plural = verbose_name diff --git a/dbm-ui/backend/db_services/redis_dts/serializers.py b/dbm-ui/backend/db_services/redis_dts/serializers.py deleted file mode 100644 index b2685ab739..0000000000 --- a/dbm-ui/backend/db_services/redis_dts/serializers.py +++ /dev/null @@ -1,38 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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 gettext_lazy as _ -from rest_framework import serializers - - -class TendisDtsHistoryJobSLZ(serializers.Serializer): - user = serializers.CharField(help_text=_("创建人"), required=False, allow_blank=True) - start_time = serializers.CharField(help_text=_("开始时间"), required=False) - end_time = serializers.CharField(help_text=_("结束时间"), required=False) - - -class DtsJobTasksSLZ(serializers.Serializer): - bill_id = serializers.IntegerField(help_text=_("任务ID"), required=True) - src_cluster = serializers.CharField(help_text=_("源集群"), required=True) - dst_cluster = serializers.CharField(help_text=_("目标集群"), required=True) - - -class DtsTaskIDsSLZ(serializers.Serializer): - task_ids = serializers.ListField( - help_text=_("子任务ID列表"), child=serializers.IntegerField(), allow_empty=False, required=True - ) - - -class DtsTaskOperateSLZ(serializers.Serializer): - task_ids = serializers.ListField( - help_text=_("子任务ID列表"), child=serializers.IntegerField(), allow_empty=False, required=True - ) - operate = serializers.CharField(help_text=_("操作类型"), required=True) diff --git a/dbm-ui/backend/db_services/redis_dts/urls.py b/dbm-ui/backend/db_services/redis_dts/urls.py deleted file mode 100644 index abb9e34386..0000000000 --- a/dbm-ui/backend/db_services/redis_dts/urls.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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 rest_framework.routers import DefaultRouter - -from .views import TendisDtsJobViewSet - -routers = DefaultRouter(trailing_slash=True) - -routers.register("", TendisDtsJobViewSet, basename="package") - -urlpatterns = routers.urls diff --git a/dbm-ui/backend/db_services/redis_dts/util.py b/dbm-ui/backend/db_services/redis_dts/util.py deleted file mode 100644 index 17ebd3c95a..0000000000 --- a/dbm-ui/backend/db_services/redis_dts/util.py +++ /dev/null @@ -1,101 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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 backend.db_meta.enums import ClusterType - - -def is_redis_instance_type(cluster_type: str) -> bool: - """ - 是否是redis实例类型 - """ - return cluster_type in [ - ClusterType.TendisPredixyRedisCluster, - ClusterType.RedisCluster, - ClusterType.TendisTwemproxyRedisInstance, - ClusterType.TendisRedisInstance, - ClusterType.TendisRedisCluster, - ] - - -def is_tendisplus_instance_type(cluster_type: str) -> bool: - """ - 是否是tendisplus实例集群类型 - """ - return cluster_type in [ - ClusterType.TendisPredixyTendisplusCluster, - ClusterType.TendisTwemproxyTendisplusIns, - ClusterType.TendisTendisplusInsance, - ClusterType.TendisTendisplusCluster, - ] - - -def is_tendisssd_instance_type(cluster_type: str) -> bool: - """ - 是否是tendisssd实例类型 - """ - return cluster_type in [ClusterType.TwemproxyTendisSSDInstance, ClusterType.TendisTendisSSDInstance] - - -def is_twemproxy_proxy_type(cluster_type: str) -> bool: - """ - 是否是twemproxy proxy类型 - """ - return cluster_type in [ - ClusterType.TendisTwemproxyRedisInstance, - ClusterType.TendisTwemproxyTendisplusIns, - ClusterType.TwemproxyTendisSSDInstance, - ] - - -def is_predixy_proxy_type(cluster_type: str) -> bool: - """ - 是否是predixy proxy类型 - """ - return cluster_type in [ - ClusterType.TendisPredixyRedisCluster, - ClusterType.TendisPredixyTendisplusCluster, - ] - - -def get_redis_type_by_cluster_type(cluster_type: str) -> str: - """ - 根据集群类型获取redis类型 - """ - if cluster_type in [ - ClusterType.TendisPredixyRedisCluster, - ClusterType.RedisCluster, - ClusterType.TendisTwemproxyRedisInstance, - ClusterType.TendisRedisInstance, - ClusterType.TendisRedisCluster, - ]: - return ClusterType.TendisRedisInstance.value - elif cluster_type in [ - ClusterType.TendisPredixyTendisplusCluster, - ClusterType.TendisTwemproxyTendisplusIns, - ClusterType.TendisTendisplusInsance, - ClusterType.TendisTendisplusCluster, - ]: - return ClusterType.TendisTendisplusInsance.value - elif cluster_type in [ClusterType.TwemproxyTendisSSDInstance, ClusterType.TwemproxyTendisSSDInstance]: - return ClusterType.TendisTendisSSDInstance.value - else: - raise Exception(f"get redis type by cluster type failed, cluster_type: {cluster_type}") - - -def is_redis_cluster_protocal(cluster_type: str) -> bool: - """ - 是否是redis_cluster协议 - """ - return cluster_type in [ - ClusterType.TendisPredixyRedisCluster, - ClusterType.TendisPredixyTendisplusCluster, - ClusterType.RedisCluster, - ] diff --git a/dbm-ui/backend/db_services/redis_dts/views.py b/dbm-ui/backend/db_services/redis_dts/views.py deleted file mode 100644 index 3f67868035..0000000000 --- a/dbm-ui/backend/db_services/redis_dts/views.py +++ /dev/null @@ -1,84 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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 as _ -from rest_framework import status -from rest_framework.decorators import action -from rest_framework.permissions import AllowAny -from rest_framework.response import Response - -from backend.bk_web import viewsets -from backend.bk_web.swagger import common_swagger_auto_schema -from backend.iam_app.handlers.drf_perm import ViewBusinessIAMPermission - -from .apis import dts_tasks_operate, dts_tasks_restart, dts_tasks_retry, get_dts_history_jobs, get_dts_job_tasks -from .serializers import DtsJobTasksSLZ, DtsTaskIDsSLZ, DtsTaskOperateSLZ, TendisDtsHistoryJobSLZ - -REDIS_DTS_TAG = "db_ts" - - -class TendisDtsJobViewSet(viewsets.AuditedModelViewSet): - def _get_custom_permissions(self): - return [ViewBusinessIAMPermission()] - - @common_swagger_auto_schema( - operation_summary=_("获取DTS历史任务以及其对应task cnt"), - responses={status.HTTP_200_OK: TendisDtsHistoryJobSLZ}, - tags=[REDIS_DTS_TAG], - ) - @action(methods=["POST"], detail=False, url_path="history_jobs", serializer_class=TendisDtsHistoryJobSLZ) - def historyjobs(self, request, *args, **kwargs): - slz = self.get_serializer(data=request.data) - slz.is_valid(raise_exception=True) - return Response(get_dts_history_jobs(slz.data)) - - @common_swagger_auto_schema( - operation_summary=_("获取迁移任务task列表,失败的排在前面"), - request_body=DtsJobTasksSLZ, - tags=[REDIS_DTS_TAG], - ) - @action(methods=["POST"], detail=False, serializer_class=DtsJobTasksSLZ, url_path="job_tasks") - def get_dts_job_tasks(self, request, *args, **kwargs): - slz = self.get_serializer(data=request.data) - slz.is_valid(raise_exception=True) - return Response(get_dts_job_tasks(slz.data)) - - @common_swagger_auto_schema( - operation_summary=_("dts task操作,目前支持 同步完成(syncStopTodo)、强制终止(ForceKillTaskTodo) 两个操作"), - request_body=DtsTaskOperateSLZ, - tags=[REDIS_DTS_TAG], - ) - @action(methods=["POST"], detail=False, serializer_class=DtsTaskOperateSLZ, url_path="tasks_operate") - def dts_tasks_operate(self, request, *args, **kwargs): - slz = self.get_serializer(data=request.data) - slz.is_valid(raise_exception=True) - return Response(dts_tasks_operate(slz.data)) - - @common_swagger_auto_schema( - operation_summary=_("dts tasks重新开始"), - request_body=DtsTaskIDsSLZ, - tags=[REDIS_DTS_TAG], - ) - @action(methods=["POST"], detail=False, serializer_class=DtsTaskIDsSLZ, url_path="tasks_restart") - def dts_tasks_restart(self, request, *args, **kwargs): - slz = self.get_serializer(data=request.data) - slz.is_valid(raise_exception=True) - return Response(dts_tasks_restart(slz.data)) - - @common_swagger_auto_schema( - operation_summary=_("dts tasks重试当前步骤"), - request_body=DtsTaskIDsSLZ, - tags=[REDIS_DTS_TAG], - ) - @action(methods=["POST"], detail=False, serializer_class=DtsTaskIDsSLZ, url_path="tasks_retry") - def dts_tasks_retry(self, request, *args, **kwargs): - slz = self.get_serializer(data=request.data) - slz.is_valid(raise_exception=True) - return Response(dts_tasks_retry(slz.data)) diff --git a/dbm-ui/backend/db_services/taskflow/handlers.py b/dbm-ui/backend/db_services/taskflow/handlers.py index 14b5ee0581..dd196669ae 100644 --- a/dbm-ui/backend/db_services/taskflow/handlers.py +++ b/dbm-ui/backend/db_services/taskflow/handlers.py @@ -17,6 +17,7 @@ from operator import itemgetter from typing import Any, Dict, List, Optional +from bamboo_engine.api import EngineAPIResult from django.utils import timezone from django.utils.translation import gettext as _ @@ -32,7 +33,7 @@ ) from backend.flow.consts import StateType from backend.flow.engine.bamboo.engine import BambooEngine -from backend.flow.models import FlowNode +from backend.flow.models import FlowNode, FlowTree from backend.utils.string import format_json_string from backend.utils.time import calculate_cost_time, datetime2str @@ -45,6 +46,15 @@ def __init__(self, root_id: str): def revoke_pipeline(self): """撤销当前流程""" + + # 如果当前的pipeline未被创建,则直接更新FlowTree的状态为撤销态 + tree = FlowTree.objects.get(root_id=self.root_id) + if tree.status in [StateType.CREATED, StateType.READY]: + tree.status = StateType.REVOKED + tree.save() + return EngineAPIResult(result=True, message=_("pipeline未创建,仅更新FlowTree")) + + # 撤销pipeline bamboo_engine = BambooEngine(root_id=self.root_id) result = bamboo_engine.revoke_pipeline() if not result.result: diff --git a/dbm-ui/backend/db_services/taskflow/task.py b/dbm-ui/backend/db_services/taskflow/task.py index 4e48b7c5a6..adaa758f7c 100644 --- a/dbm-ui/backend/db_services/taskflow/task.py +++ b/dbm-ui/backend/db_services/taskflow/task.py @@ -64,7 +64,7 @@ def send_flow_state(state, _root_id, _node_id, _version_id): try: ticket = Ticket.objects.get(id=flow_node.uid) cluster_ids = get_target_items_from_details(ticket.details, match_keys=["cluster_id", "cluster_ids"]) - Cluster.handle_exclusive_operations(cluster_ids, ticket.ticket_type) + Cluster.handle_exclusive_operations(cluster_ids, ticket.ticket_type, exclude_ticket_ids=[ticket.id]) except ClusterExclusiveOperateException as e: # 互斥下: 手动重试直接报错,自动重试则延迟一定时间后重新执行该任务 flow = Flow.objects.get(flow_obj_id=flow_node.root_id) @@ -84,7 +84,7 @@ def send_flow_state(state, _root_id, _node_id, _version_id): # 进行重试操作 result = BambooEngine(root_id=root_id).retry_node(node_id=node_id) if not result.result: - raise RetryNodeException(",".join(result.exc.args)) + raise RetryNodeException(str(result.exc.args)) service.log_info(_("重试成功")) return result diff --git a/dbm-ui/backend/db_services/taskflow/views/flow.py b/dbm-ui/backend/db_services/taskflow/views/flow.py index 831fd7dacf..db069ff084 100644 --- a/dbm-ui/backend/db_services/taskflow/views/flow.py +++ b/dbm-ui/backend/db_services/taskflow/views/flow.py @@ -13,8 +13,10 @@ from rest_framework.decorators import action from rest_framework.response import Response +from backend import env from backend.bk_web import viewsets from backend.bk_web.swagger import common_swagger_auto_schema +from backend.db_services.dbbase.constants import IpSource from backend.db_services.taskflow.exceptions import RetryNodeException from backend.db_services.taskflow.handlers import TaskFlowHandler from backend.db_services.taskflow.serializers import ( @@ -23,9 +25,12 @@ NodeSerializer, VersionSerializer, ) +from backend.flow.consts import StateType from backend.flow.engine.bamboo.engine import BambooEngine -from backend.flow.models import FlowTree +from backend.flow.models import FlowNode, FlowTree from backend.iam_app.handlers.drf_perm import TaskFlowIAMPermission +from backend.ticket.models import Flow +from backend.utils.basic import get_target_items_from_details SWAGGER_TAG = "taskflow" @@ -51,7 +56,7 @@ def get_queryset(self): return super().get_queryset() # 对root_ids支持批量过滤 - root_ids = self.request.query_params.get("root_id", None) + root_ids = self.request.query_params.get("root_ids", None) if root_ids: self.queryset = self.queryset.filter(root_id__in=root_ids.split(",")) @@ -70,8 +75,26 @@ def list(self, requests, *args, **kwargs): ) def retrieve(self, requests, *args, **kwargs): root_id = kwargs["root_id"] - flow_info = super().retrieve(requests, *args, **kwargs) tree_states = BambooEngine(root_id=root_id).get_pipeline_tree_states() + + flow_info = super().retrieve(requests, *args, **kwargs) + try: + # 从pipeline的第一个节点获取任务的输入数据 + first_act_node_id = FlowNode.objects.filter(root_id=root_id).first().node_id + details = BambooEngine(root_id=root_id).get_node_input_data(node_id=first_act_node_id).data["global_data"] + # 递归遍历字典,获取主机ID + bk_host_ids = get_target_items_from_details( + obj=details, match_keys=["host_id", "bk_host_id", "bk_host_ids"] + ) + bk_biz_id = details["bk_biz_id"] + # 如果当前pipeline整在运行中,并且是从资源池拿取的机器,则bk_biz_id设置为DBA_APP_BK_BIZ_ID + if flow_info.data["status"] != StateType.FINISHED and details.get("ip_source") == IpSource.RESOURCE_POOL: + bk_biz_id = env.DBA_APP_BK_BIZ_ID + except KeyError as e: + # 如果pipeline还未构建,则先忽略 + bk_host_ids, bk_biz_id = [], "" + + flow_info.data.update(bk_host_ids=bk_host_ids, bk_biz_id=bk_biz_id) return Response({"flow_info": flow_info.data, **tree_states}) @common_swagger_auto_schema( diff --git a/dbm-ui/backend/db_services/version/constants.py b/dbm-ui/backend/db_services/version/constants.py index 0606f825c1..35119ceea2 100644 --- a/dbm-ui/backend/db_services/version/constants.py +++ b/dbm-ui/backend/db_services/version/constants.py @@ -25,6 +25,13 @@ class MySQLVersion(str, StructuredEnum): MySQL80 = EnumField("MySQL-8.0", _("MySQL-8.0")) +class SpiderVersion(str, StructuredEnum): + """Spider的版本枚举""" + + Spider1 = EnumField("Spider-1", _("Spider-1")) + Spider3 = EnumField("Spider-3", _("Spider-3")) + + class RedisVersion(str, StructuredEnum): """Redis-Cache数据库版本枚举""" diff --git a/dbm-ui/backend/db_services/version/utils.py b/dbm-ui/backend/db_services/version/utils.py new file mode 100644 index 0000000000..b01089fb33 --- /dev/null +++ b/dbm-ui/backend/db_services/version/utils.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 backend.db_meta.enums import ClusterType +from backend.db_package.constants import PackageType +from backend.db_package.models import Package +from backend.db_services.version import constants + + +def query_versions_by_key(query_key): + """集群类型->集群版本""" + + if query_key in [ClusterType.TenDBSingle, ClusterType.TenDBHA, ClusterType.TenDBCluster, PackageType.MySQL]: + versions = constants.MySQLVersion.get_values() + elif query_key in [PackageType.Spider]: + versions = constants.SpiderVersion.get_values() + elif query_key in [ + PackageType.TendisPlus, + ClusterType.TendisPredixyTendisplusCluster, + ClusterType.TendisTwemproxyTendisplusIns, + ClusterType.TendisTendisplusInsance, + ClusterType.TendisTendisplusCluster, + ]: + versions = constants.TendisPlusVersion.get_values() + elif query_key in [ + PackageType.Proxy, + PackageType.DBActuator, + PackageType.RedisTools, + PackageType.DbMon, + PackageType.MySQLRotateBinlog, + PackageType.MySQLToolKit, + PackageType.DbBackup, + PackageType.MySQLChecksum, + PackageType.MySQLMonitor, + PackageType.MySQLCrond, + PackageType.RedisDts, + ]: + versions = [constants.LATEST] + elif query_key in [ + PackageType.Twemproxy, + ]: + versions = constants.TwemproxyVersion.get_values() + elif query_key in [ + PackageType.Predixy, + ]: + versions = constants.PredixyVersion.get_values() + + elif query_key in [ + PackageType.Redis, + ClusterType.TendisPredixyRedisCluster, + ClusterType.TendisTwemproxyRedisInstance, + ClusterType.TendisRedisInstance, + ClusterType.TendisRedisCluster, + ]: + versions = constants.RedisVersion.get_values() + elif query_key in [ + PackageType.TendisSsd, + ClusterType.TwemproxyTendisSSDInstance, + ]: + versions = constants.TendisSsdVersion.get_values() + else: + versions = list(Package.objects.filter(pkg_type=query_key).values_list("version", flat=True)) + + if not versions: + # 当没有版本时,默认给个 latest 版本 + versions = [constants.LATEST] + + return versions diff --git a/dbm-ui/backend/db_services/version/views.py b/dbm-ui/backend/db_services/version/views.py index 2e67d5b467..49fcde0126 100644 --- a/dbm-ui/backend/db_services/version/views.py +++ b/dbm-ui/backend/db_services/version/views.py @@ -15,14 +15,23 @@ from backend.bk_web import viewsets from backend.bk_web.swagger import common_swagger_auto_schema from backend.db_meta.enums import ClusterType -from backend.db_package.constants import PackageType -from backend.db_package.models import Package -from backend.db_services.version import constants, serializers +from backend.db_services.version import serializers +from backend.db_services.version.utils import query_versions_by_key SWAGGER_TAG = "version" class VersionViewSet(viewsets.SystemViewSet): + @common_swagger_auto_schema( + operation_summary=_("查询所有数据库的版本列表"), + tags=[SWAGGER_TAG], + ) + @action(methods=["GET"], detail=False, serializer_class=None, pagination_class=None) + def cluster_type_to_versions(self, requests, *args, **kwargs): + return Response( + {cluster_type: query_versions_by_key(cluster_type) for cluster_type in ClusterType.get_values()} + ) + @common_swagger_auto_schema( operation_summary=_("查询数据库版本列表"), query_serializer=serializers.ListVersionSerializer(), @@ -33,56 +42,5 @@ def list_versions(self, requests, *args, **kwargs): """版本列表""" validated_data = self.params_validate(self.get_serializer_class()) query_key = validated_data["query_key"] - - if query_key in [ClusterType.TenDBSingle, ClusterType.TenDBHA, ClusterType.TenDBCluster, PackageType.MySQL]: - versions = constants.MySQLVersion.get_values() - elif query_key in [ - PackageType.TendisPlus, - ClusterType.TendisPredixyTendisplusCluster, - ClusterType.TendisTwemproxyTendisplusIns, - ClusterType.TendisTendisplusInsance, - ClusterType.TendisTendisplusCluster, - ]: - versions = constants.TendisPlusVersion.get_values() - elif query_key in [ - PackageType.Proxy, - PackageType.DBActuator, - PackageType.RedisTools, - PackageType.DbMon, - PackageType.MySQLRotateBinlog, - PackageType.MySQLToolKit, - PackageType.DbBackup, - PackageType.MySQLChecksum, - PackageType.MySQLMonitor, - PackageType.MySQLCrond, - ]: - versions = [constants.LATEST] - elif query_key in [ - PackageType.Twemproxy, - ]: - versions = constants.TwemproxyVersion.get_values() - elif query_key in [ - PackageType.Predixy, - ]: - versions = constants.PredixyVersion.get_values() - - elif query_key in [ - PackageType.Redis, - ClusterType.TendisPredixyRedisCluster, - ClusterType.TendisTwemproxyRedisInstance, - ClusterType.TendisRedisInstance, - ClusterType.TendisRedisCluster, - ]: - versions = constants.RedisVersion.get_values() - elif query_key in [ - PackageType.TendisSsd, - ClusterType.TwemproxyTendisSSDInstance, - ]: - versions = constants.TendisSsdVersion.get_values() - else: - versions = list(Package.objects.filter(pkg_type=query_key).values_list("version", flat=True)) - if not versions: - # 当没有版本时,默认给个 latest 版本 - # TODO 后续考虑废除 latest,所有二进制都给带上版本号,以便排查问题 - versions = [constants.LATEST] + versions = query_versions_by_key(query_key) return Response(versions) diff --git a/dbm-ui/backend/dbm_init/apigw/definition.yaml b/dbm-ui/backend/dbm_init/apigw/definition.yaml new file mode 100644 index 0000000000..2ccd9ce7ee --- /dev/null +++ b/dbm-ui/backend/dbm_init/apigw/definition.yaml @@ -0,0 +1,35 @@ +release: + version: {{ settings.BK_APIGW_STATIC_VERSION }} + comment: "auto release by bk-dbm" + +apigateway: + description: {{ settings.APP_CODE }}-apigw + is_public: true + maintainers: + {% for member in settings.BK_APIGW_MANAGER_MAINTAINERS %} + - "{{ member }}" + {% endfor %} + +stage: + name: {{ settings.BK_APIGW_STAGE_NAME }} + proxy_http: + timeout: 120 + upstreams: + loadbalance: roundrobin + hosts: + - host: {{ settings.BK_SAAS_HOST }} + weight: 100 + rate_limit: + enabled: false + rate: + tokens: 5000 + period: 60 + +# 主动授权,网关主动给应用,添加访问网关所有资源的权限 +grant_permissions: + {% for bk_app_code in settings.BK_APIGW_GRANT_APPS %} + - bk_app_code: "{{ bk_app_code }}" + {% endfor %} + +resource_docs: + archivefile: {{ settings.BK_APIGW_RESOURCE_DOCS_ARCHIVE_FILE }} diff --git a/dbm-ui/backend/dbm_init/apigw/resources.yaml b/dbm-ui/backend/dbm_init/apigw/resources.yaml new file mode 100644 index 0000000000..28f420bf71 --- /dev/null +++ b/dbm-ui/backend/dbm_init/apigw/resources.yaml @@ -0,0 +1,85 @@ +swagger: '2.0' +basePath: / +info: + version: '0.1' + title: API Gateway Resources + description: '' +schemes: +- http +paths: + /mysql/bizs/{bk_biz_id}/cluster/query_clusters/: + post: + operationId: query_clusters + description: '' + tags: + - Spider + - Cluster + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + type: HTTP + method: post + path: /apis/mysql/bizs/{bk_biz_id}/cluster/query_clusters/ + matchSubpath: false + timeout: 0 + upstreams: {} + transformHeaders: {} + authConfig: + userVerifiedRequired: false + disabledStages: [] + descriptionEn: + /mysql/bizs/{bk_biz_id}/spider_resources/: + get: + operationId: list_spider_resource + description: 查询spider集群列表 + tags: + - Spider + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + type: HTTP + method: get + path: /apis/mysql/bizs/{bk_biz_id}/spider_resources/ + matchSubpath: false + timeout: 0 + upstreams: {} + transformHeaders: {} + authConfig: + userVerifiedRequired: true + disabledStages: [] + descriptionEn: + /tickets/: + post: + operationId: create_ticket + description: 创建单据 + tags: + - Ticket + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + type: HTTP + method: post + path: /apis/tickets/ + matchSubpath: false + timeout: 0 + upstreams: {} + transformHeaders: {} + authConfig: + userVerifiedRequired: false + disabledStages: [] + descriptionEn: diff --git a/dbm-ui/backend/dbm_init/json_files/bklog/dbm_dbactuator.json b/dbm-ui/backend/dbm_init/json_files/bklog/dbm_dbactuator.json index afbff94924..404d74dd4f 100644 --- a/dbm-ui/backend/dbm_init/json_files/bklog/dbm_dbactuator.json +++ b/dbm-ui/backend/dbm_init/json_files/bklog/dbm_dbactuator.json @@ -24,9 +24,7 @@ "target_object_type": "HOST", "target_node_type": "TOPO", "target_nodes": [ - { - "bk_inst_id": {{bk_inst_id}}, - "bk_obj_id": "biz" - } + {% for node in target_nodes %}{{ node }}{% if not loop.last %}, {% endif %} + {% endfor %} ] } \ No newline at end of file diff --git a/dbm-ui/backend/dbm_init/json_files/bklog/dbm_redis_record.json b/dbm-ui/backend/dbm_init/json_files/bklog/dbm_redis_record.json index d5de53286c..3bfc2fbf25 100644 --- a/dbm-ui/backend/dbm_init/json_files/bklog/dbm_redis_record.json +++ b/dbm-ui/backend/dbm_init/json_files/bklog/dbm_redis_record.json @@ -24,11 +24,7 @@ "target_object_type": "HOST", "target_node_type": "TOPO", "target_nodes": [ - {% for bk_set_id in bk_set_ids %} - { - "bk_inst_id": {{ bk_set_id }}, - "bk_obj_id": "set" - }{% if not loop.last %}, {% endif %} + {% for node in target_nodes %}{{ node }}{% if not loop.last %}, {% endif %} {% endfor %} ] } \ No newline at end of file diff --git a/dbm-ui/backend/dbm_init/json_files/bklog/mysql_backup_result.json b/dbm-ui/backend/dbm_init/json_files/bklog/mysql_backup_result.json index dbe1c5ff9c..9e980c46f6 100644 --- a/dbm-ui/backend/dbm_init/json_files/bklog/mysql_backup_result.json +++ b/dbm-ui/backend/dbm_init/json_files/bklog/mysql_backup_result.json @@ -24,11 +24,7 @@ "target_object_type": "HOST", "target_node_type": "TOPO", "target_nodes": [ - {% for bk_set_id in bk_set_ids %} - { - "bk_inst_id": {{ bk_set_id }}, - "bk_obj_id": "set" - }{% if not loop.last %}, {% endif %} + {% for node in target_nodes %}{{ node }}{% if not loop.last %}, {% endif %} {% endfor %} ] } \ No newline at end of file diff --git a/dbm-ui/backend/dbm_init/json_files/bklog/mysql_binlog_result.json b/dbm-ui/backend/dbm_init/json_files/bklog/mysql_binlog_result.json index ef1fbc5d40..e9480b0e5f 100644 --- a/dbm-ui/backend/dbm_init/json_files/bklog/mysql_binlog_result.json +++ b/dbm-ui/backend/dbm_init/json_files/bklog/mysql_binlog_result.json @@ -263,11 +263,7 @@ "target_object_type": "HOST", "target_node_type": "TOPO", "target_nodes": [ - {% for bk_set_id in bk_set_ids %} - { - "bk_inst_id": {{ bk_set_id }}, - "bk_obj_id": "set" - }{% if not loop.last %}, {% endif %} + {% for node in target_nodes %}{{ node }}{% if not loop.last %}, {% endif %} {% endfor %} ] } \ No newline at end of file diff --git a/dbm-ui/backend/dbm_init/json_files/bklog/mysql_checksum_result.json b/dbm-ui/backend/dbm_init/json_files/bklog/mysql_checksum_result.json index a7ed05a495..93ec23ce03 100644 --- a/dbm-ui/backend/dbm_init/json_files/bklog/mysql_checksum_result.json +++ b/dbm-ui/backend/dbm_init/json_files/bklog/mysql_checksum_result.json @@ -24,11 +24,7 @@ "target_object_type": "HOST", "target_node_type": "TOPO", "target_nodes": [ - {% for bk_set_id in bk_set_ids %} - { - "bk_inst_id": {{ bk_set_id }}, - "bk_obj_id": "set" - }{% if not loop.last %}, {% endif %} + {% for node in target_nodes %}{{ node }}{% if not loop.last %}, {% endif %} {% endfor %} ] } \ No newline at end of file diff --git a/dbm-ui/backend/dbm_init/json_files/bklog/mysql_slowlog.json b/dbm-ui/backend/dbm_init/json_files/bklog/mysql_slowlog.json index b098b485e9..288082a7b9 100644 --- a/dbm-ui/backend/dbm_init/json_files/bklog/mysql_slowlog.json +++ b/dbm-ui/backend/dbm_init/json_files/bklog/mysql_slowlog.json @@ -204,11 +204,7 @@ "target_object_type": "HOST", "target_node_type": "TOPO", "target_nodes": [ - {% for bk_set_id in bk_set_ids %} - { - "bk_inst_id": {{ bk_set_id }}, - "bk_obj_id": "set" - }{% if not loop.last %}, {% endif %} + {% for node in target_nodes %}{{ node }}{% if not loop.last %}, {% endif %} {% endfor %} ] } diff --git a/dbm-ui/backend/dbm_init/json_files/bklog/redis_slowlog.json b/dbm-ui/backend/dbm_init/json_files/bklog/redis_slowlog.json index fb1ff40fb1..05e5c237b3 100644 --- a/dbm-ui/backend/dbm_init/json_files/bklog/redis_slowlog.json +++ b/dbm-ui/backend/dbm_init/json_files/bklog/redis_slowlog.json @@ -18,14 +18,10 @@ "data_encoding": "UTF-8", "retention": 7, "es_shards": 1, - "target_object_type": "HOST", + "target_object_type": "SERVICE", "target_node_type": "TOPO", "target_nodes": [ - {% for bk_set_id in bk_set_ids %} - { - "bk_inst_id": {{ bk_set_id }}, - "bk_obj_id": "set" - }{% if not loop.last %}, {% endif %} + {% for node in target_nodes %}{{ node }}{% if not loop.last %}, {% endif %} {% endfor %} ] } diff --git a/dbm-ui/backend/dbm_init/json_files/format.py b/dbm-ui/backend/dbm_init/json_files/format.py index c423ebdc4a..8a6bf73105 100644 --- a/dbm-ui/backend/dbm_init/json_files/format.py +++ b/dbm-ui/backend/dbm_init/json_files/format.py @@ -8,12 +8,14 @@ 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. """ +import json from typing import Any, Union from urllib.parse import urljoin from jinja2 import Environment from backend import env +from backend.configuration.constants import DBType from backend.db_meta.models import AppMonitorTopo @@ -23,6 +25,26 @@ class JsonConfigFormat: 格式函数的命名规则: format_{file_name} """ + @classmethod + def get_db_set_ctx(cls, db_type: str): + return { + "bk_biz_id": env.DBA_APP_BK_BIZ_ID, + "target_nodes": list( + set( + [ + json.dumps( + { + "bk_biz_id": bk_set["bk_biz_id"], + "bk_inst_id": bk_set["bk_set_id"], + "bk_obj_id": "set", + } + ) + for bk_set in AppMonitorTopo.get_set_by_dbtype(db_type) + ] + ) + ), + } + @classmethod def format(cls, content: str, format_func_name: str) -> Union[str, Any]: jinja_env = Environment() @@ -31,26 +53,26 @@ def format(cls, content: str, format_func_name: str) -> Union[str, Any]: @classmethod def format_dbm_dbactuator(cls): - return {"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "bk_inst_id": env.DBA_APP_BK_BIZ_ID} + target_nodes = [json.dumps({"bk_obj_id": "biz", "bk_inst_id": env.DBA_APP_BK_BIZ_ID})] + target_nodes.extend( + [ + json.dumps({"bk_obj_id": "set", "bk_inst_id": topo.bk_set_id, "bk_biz_id": topo.bk_biz_id}) + for topo in AppMonitorTopo.objects.exclude(bk_biz_id=env.DBA_APP_BK_BIZ_ID) + ] + ) + + return {"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "target_nodes": list(set(target_nodes))} @classmethod def format_mysql(cls): - return { - "bk_biz_id": env.DBA_APP_BK_BIZ_ID, - "bk_set_ids": list({bk_set["bk_set_id"] for bk_set in AppMonitorTopo.get_set_by_dbtype("mysql")}), - } + return cls.get_db_set_ctx(DBType.MySQL.value) @classmethod def format_mysql_slowlog(cls): - return { - "bk_biz_id": env.DBA_APP_BK_BIZ_ID, - "bk_set_ids": list({bk_set["bk_set_id"] for bk_set in AppMonitorTopo.get_set_by_dbtype("mysql")}), - "slow_query_parse_url": urljoin(env.SLOW_QUERY_PARSER_DOMAIN, "mysql/"), - } + ctx = cls.get_db_set_ctx(DBType.MySQL.value) + ctx["slow_query_parse_url"] = urljoin(env.SLOW_QUERY_PARSER_DOMAIN, "mysql/") + return ctx @classmethod def format_redis(cls): - return { - "bk_biz_id": env.DBA_APP_BK_BIZ_ID, - "bk_set_ids": list({bk_set["bk_set_id"] for bk_set in AppMonitorTopo.get_set_by_dbtype("redis")}), - } + return cls.get_db_set_ctx(DBType.Redis.value) diff --git a/dbm-ui/backend/dbm_init/management/commands/cloud_component.py b/dbm-ui/backend/dbm_init/management/commands/cloud_component.py index f942b1fc2b..8fd66b62da 100644 --- a/dbm-ui/backend/dbm_init/management/commands/cloud_component.py +++ b/dbm-ui/backend/dbm_init/management/commands/cloud_component.py @@ -9,12 +9,11 @@ specific language governing permissions and limitations under the License. """ import logging -import os from django.core.management.base import BaseCommand from django.utils.translation import ugettext as _ -from backend.ticket.views import TicketViewSet +from backend.ticket.handler import TicketHandler logger = logging.getLogger("root") @@ -33,6 +32,6 @@ def handle(self, *args, **options): ips = options["ips"].split(",") try: - TicketViewSet.fast_create_cloud_component_method(bk_biz_id, bk_cloud_id, ips) + TicketHandler.fast_create_cloud_component_method(bk_biz_id, bk_cloud_id, ips) except Exception as e: # pylint: disable=broad-except logger.error(_("云区域组件初始化失败,错误信息:{}").format(e)) diff --git a/dbm-ui/backend/dbm_init/management/commands/sync_saas_apigw.py b/dbm-ui/backend/dbm_init/management/commands/sync_saas_apigw.py new file mode 100644 index 0000000000..0cccf04b4e --- /dev/null +++ b/dbm-ui/backend/dbm_init/management/commands/sync_saas_apigw.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 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. +""" + +import logging + +from django.core.management import call_command +from django.core.management.base import BaseCommand + +from backend import env + +logger = logging.getLogger("root") + + +class Command(BaseCommand): + def handle(self, *args, **kwargs): + + definition_file_path = "backend/dbm_init/apigw/definition.yaml" + resources_file_path = "backend/dbm_init/apigw/resources.yaml" + + # 同步网关基本信息 + logger.info("call sync_apigw_config with definition: %s" % definition_file_path) + call_command("sync_apigw_config", file=definition_file_path) + + # 同步网关环境信息 + logger.info("call sync_apigw_stage with definition: %s" % definition_file_path) + call_command("sync_apigw_stage", file=definition_file_path) + + # 为应用主动授权 + logger.info("call grant_apigw_permissions with definition: %s" % definition_file_path) + call_command("grant_apigw_permissions", file=definition_file_path) + + # 同步网关资源 + logger.info("call sync_apigw_resources with resources: %s" % resources_file_path) + call_command("sync_apigw_resources", file=resources_file_path) + + # 同步资源文档 + if env.BK_APIGW_RESOURCE_DOCS_ARCHIVE_FILE: + logger.info("call sync_resource_docs_by_archive with definition: %s" % definition_file_path) + call_command("sync_resource_docs_by_archive", file=definition_file_path) + + # 获取网关公钥 + logger.info("call fetch_apigw_public_key") + call_command("fetch_apigw_public_key") diff --git a/dbm-ui/backend/dbm_init/services.py b/dbm-ui/backend/dbm_init/services.py index 3b6287099e..c8d2d2bd4d 100644 --- a/dbm-ui/backend/dbm_init/services.py +++ b/dbm-ui/backend/dbm_init/services.py @@ -21,14 +21,15 @@ from backend import env from backend.components import BKLogApi, BKMonitorV3Api, CCApi, ItsmApi from backend.components.constants import SSL_KEY -from backend.configuration.constants import BKM_DBM_REPORT, DBM_REPORT_INITIAL_VALUE, DBM_SSL, DBType -from backend.configuration.models.system import SystemSettings, SystemSettingsEnum +from backend.configuration.constants import DBM_REPORT_INITIAL_VALUE, SystemSettingsEnum +from backend.configuration.models.system import SystemSettings from backend.core.storages.constants import FileCredentialType, StorageType from backend.core.storages.file_source import BkJobFileSourceManager from backend.core.storages.storage import get_storage from backend.db_meta.models import AppMonitorTopo from backend.db_monitor.constants import TPLS_ALARM_DIR, TPLS_COLLECT_DIR from backend.db_monitor.models import AlertRule, CollectInstance, CollectTemplate, NoticeGroup, RuleTemplate +from backend.db_services.ipchooser.constants import DB_MANAGE_SET, DIRTY_MODULE, RESOURCE_MODULE from backend.dbm_init.constants import CC_APP_ABBR_ATTR, CC_HOST_DBM_ATTR from backend.dbm_init.json_files.format import JsonConfigFormat from backend.exceptions import ApiError, ApiRequestError, ApiResultError @@ -185,6 +186,38 @@ def auto_create_bkcc_service() -> bool: logger.info("init cc topo for monitor discover.") AppMonitorTopo.init_topo() + # 初始化db的管理集群和相关模块 + if not SystemSettings.get_setting_value(key=SystemSettingsEnum.MANAGE_TOPO.value): + # 创建管理集群 + manage_set = CCApi.create_set( + { + "bk_biz_id": env.DBA_APP_BK_BIZ_ID, + "data": {"bk_parent_id": env.DBA_APP_BK_BIZ_ID, "bk_set_name": DB_MANAGE_SET}, + } + ) + # 创建资源池模块和污点池模块 + manage_modules = [RESOURCE_MODULE, DIRTY_MODULE] + module_name__module_info = {} + for module in manage_modules: + module_info = CCApi.create_module( + { + "bk_biz_id": env.DBA_APP_BK_BIZ_ID, + "bk_set_id": manage_set["bk_set_id"], + "data": {"bk_parent_id": manage_set["bk_set_id"], "bk_module_name": module}, + } + ) + module_name__module_info[module] = module_info + # 插入管理集群的配置 + SystemSettings.insert_setting_value( + key=SystemSettingsEnum.MANAGE_TOPO.value, + value_type="dict", + value={ + "set_id": manage_set["bk_set_id"], + "resource_module_id": module_name__module_info[RESOURCE_MODULE]["bk_module_id"], + "dirty_module_id": module_name__module_info[DIRTY_MODULE]["bk_module_id"], + }, + ) + # 初始化主机自定义属性,用于system数据拷贝 logger.info("init cc biz custom field <%s> for monitor's dbm_system copy.", CC_HOST_DBM_ATTR) model_attrs = CCApi.search_object_attribute( @@ -257,7 +290,7 @@ def auto_create_bkcc_service() -> bool: def init_alarm_strategy(): """初始化告警策略""" - bkm_dbm_report = SystemSettings.get_setting_value(key=BKM_DBM_REPORT) + bkm_dbm_report = SystemSettings.get_setting_value(key=SystemSettingsEnum.BKM_DBM_REPORT.value) now = datetime.datetime.now() updated_alarms = 0 @@ -266,10 +299,10 @@ def init_alarm_strategy(): # 未来考虑将模板放到db管理 # rules = RuleTemplate.objects.filter(is_enabled=True, bk_biz_id=0) # for rule in rules: - alarm_tpls = os.path.join(TPLS_ALARM_DIR, "*.tpl64") + alarm_tpls = os.path.join(TPLS_ALARM_DIR, "*.json") for alarm_tpl in glob.glob(alarm_tpls): - with open(alarm_tpl, "rb") as f: - template_dict = json.loads(base64.b64decode(f.read())) + with open(alarm_tpl, "r") as f: + template_dict = json.loads(f.read()) rule = RuleTemplate(**template_dict) alert_params = rule.details @@ -366,10 +399,10 @@ def init_collect_strategy(): # templates = CollectTemplate.objects.filter(bk_biz_id=0) # for template in templates: - collect_tpls = os.path.join(TPLS_COLLECT_DIR, "*.tpl64") + collect_tpls = os.path.join(TPLS_COLLECT_DIR, "*.json") for collect_tpl in glob.glob(collect_tpls): - with open(collect_tpl, "rb") as f: - template_dict = json.loads(base64.b64decode(f.read())) + with open(collect_tpl, "r") as f: + template_dict = json.loads(f.read()) template = CollectTemplate(**template_dict) collect_params = template.details @@ -406,8 +439,8 @@ def init_collect_strategy(): collect_params["plugin_id"] = template.plugin_id collect_params["target_nodes"] = [ - {"bk_inst_id": bk_set_id, "bk_obj_id": "set", "bk_biz_id": env.DBA_APP_BK_BIZ_ID} - for bk_set_id in AppMonitorTopo.get_set_by_plugin_id(plugin_id=template.plugin_id) + {"bk_inst_id": bk_set_id, "bk_obj_id": "set", "bk_biz_id": bk_biz_id} + for bk_set_id, bk_biz_id in AppMonitorTopo.get_set_by_plugin_id(plugin_id=template.plugin_id) ] res = BKMonitorV3Api.save_collect_config(collect_params, use_admin=True) @@ -532,7 +565,7 @@ def init_custom_metric_and_event(): "creator": "system", "updater": "system", }, - key=BKM_DBM_REPORT, + key=SystemSettingsEnum.BKM_DBM_REPORT.value, ) @staticmethod @@ -592,7 +625,7 @@ def auto_create_ssl_service() -> bool: dbm_ssl[ssl_file_name] = str(ssl_file.read().decode()) # 入库到系统配置,提供给组件接口使用 - SystemSettings.insert_setting_value(key=DBM_SSL, value=dbm_ssl) + SystemSettings.insert_setting_value(key=SystemSettingsEnum.DBM_SSL.value, value=dbm_ssl) logger.info("auto_create_ssl_service success") return True diff --git a/dbm-ui/backend/env/__init__.py b/dbm-ui/backend/env/__init__.py index 98094ecaad..b40e218c47 100644 --- a/dbm-ui/backend/env/__init__.py +++ b/dbm-ui/backend/env/__init__.py @@ -54,6 +54,12 @@ IAM_APP_URL = get_type_env(key="IAM_APP_URL", _type=str, default="https://iam.example.com") BK_IAM_RESOURCE_API_HOST = get_type_env(key="BK_IAM_RESOURCE_API_HOST", _type=str, default="https://bkdbm.example.com") +# APIGW 相关配置 +BK_APIGW_STATIC_VERSION = get_type_env(key="BK_APIGW_STATIC_VERSION", _type=str, default="1.0.0") +BK_APIGW_MANAGER_MAINTAINERS = get_type_env(key="BK_APIGW_MANAGER_MAINTAINERS", _type=list, default=["admin"]) +BK_APIGW_STAGE_NAME = get_type_env(key="BK_APIGW_STAGE_NAME", _type=str, default="test") +BK_APIGW_GRANT_APPS = get_type_env(key="BK_APIGW_GRANT_APPS", _type=list, default=[]) +BK_APIGW_RESOURCE_DOCS_ARCHIVE_FILE = get_type_env(key="BK_APIGW_RESOURCE_DOCS_ARCHIVE_FILE", _type=str) ENVIRONMENT = get_type_env(key="BKPAAS_ENVIRONMENT", default="dev", _type=str) @@ -87,6 +93,8 @@ # 名字服务北极星部门字段 NAMESERVICE_POLARIS_DEPARTMENT = get_type_env(key="NAMESERVICE_POLARIS_DEPARTMENT", _type=str, default="") +# 名字服务添加clb域名 +CLB_DOMAIN = get_type_env(key="CLB_DOMAIN", _type=bool, default=False) # 标准运维SA 空闲检查任务模版ID SA_CHECK_TEMPLATE_ID = get_type_env(key="SA_CHECK_TEMPLATE_ID", _type=int) @@ -129,3 +137,14 @@ # 版本号 APP_VERSION = get_type_env(key="APP_VERSION", _type=str, default="") CHART_VERSION = get_type_env(key="CHART_VERSION", _type=str, default="") + +# 资源池伪造开关 +FAKE_RESOURCE_APPLY_ENABLE = get_type_env(key="FAKE_RESOURCE_APPLY_ENABLE", _type=bool, default=False) +# 资源池是否支持亲和性(暂不支持) +RESOURCE_SUPPORT_AFFINITY = get_type_env(key="RESOURCE_SUPPORT_AFFINITY", _type=bool, default=False) + +# 备份系统是否开启 +BACKUP_SYSTEM_ENABLED = get_type_env(key="BACKUP_SYSTEM_ENABLED", _type=bool, default=False) + +# Agent版本: 1.0/2.0 +GSE_AGENT_VERSION = get_type_env(key="GSE_AGENT_VERSION", _type=str, default="1.0") diff --git a/dbm-ui/backend/env/apigw_domains.py b/dbm-ui/backend/env/apigw_domains.py index b5470c375f..2d565c48ae 100644 --- a/dbm-ui/backend/env/apigw_domains.py +++ b/dbm-ui/backend/env/apigw_domains.py @@ -28,7 +28,7 @@ DRS_APIGW_DOMAIN = get_type_env(key="DRS_APIGW_DOMAIN", _type=str) SQL_IMPORT_APIGW_DOMAIN = get_type_env(key="SQL_IMPORT_APIGW_DOMAIN", _type=str) BKMONITORV3_APIGW_DOMAIN = get_type_env(key="BKMONITORV3_APIGW_DOMAIN", _type=str) -MYSQL_SIMULATION_DONAIN = get_type_env(key="MYSQL_SIMULATION_DONAIN", _type=str) +MYSQL_SIMULATION_DOMAIN = get_type_env(key="MYSQL_SIMULATION_DOMAIN", _type=str) NAMESERVICE_APIGW_DOMAIN = get_type_env(key="NAMESERVICE_APIGW_DOMAIN", _type=str) HADB_APIGW_DOMAIN = get_type_env(key="HADB_APIGW_DOMAIN", _type=str) DBRESOURCE_APIGW_DOMAIN = get_type_env(key="DBRESOURCE_APIGW_DOMAIN", _type=str) @@ -37,3 +37,5 @@ key="SLOW_QUERY_PARSER_DOMAIN", _type=str, default="http://bk-dbm-slow-query-parser-service" ) DBHA_APIGW_DOMAIN_LIST = get_type_env(key="DBHA_APIGW_DOMAIN_LIST", _type=list, default=[]) +BACKUP_DOWNLOAD_USER = get_type_env(key="BACKUP_DOWNLOAD_USER", _type=str, default="root") +CELERY_SERVICE_APIGW_DOMAIN = get_type_env(key="CELERY_SERVICE_APIGW_DOMAIN", _type=str) diff --git a/dbm-ui/backend/exceptions.py b/dbm-ui/backend/exceptions.py index a52c9713b8..ee1214fc8d 100644 --- a/dbm-ui/backend/exceptions.py +++ b/dbm-ui/backend/exceptions.py @@ -40,6 +40,8 @@ class ErrorCode(object): DB_PARTITION_CODE = "11" RESOURCE_POOL_CODE = "12" REDIS_DTS_CODE = "13" + DB_REMOTE_SERVICE_CODE = "14" + DB_MONITOR_CODE = "15" class AppBaseException(Exception): diff --git a/dbm-ui/backend/flow/consts.py b/dbm-ui/backend/flow/consts.py index 7a9dc8950d..ee984b5951 100644 --- a/dbm-ui/backend/flow/consts.py +++ b/dbm-ui/backend/flow/consts.py @@ -37,11 +37,17 @@ # 切换时, 允许多少秒丢失 DEFAULT_LAST_IO_SECOND_AGO = 100 +# 默认Riak端口 +DEFAULT_RIAK_PORT = 8087 + # tendisplus默认kvstorecount DEFAULT_TENDISPLUS_KVSTORECOUNT = 10 # 定义每个TenDB-Cluster集群最大spider-master/mnt角色的节点数量(暂定) MAX_SPIDER_MASTER_COUNT = 37 +# 定义每个TenDB-Cluster集群最小spider-master/slave角色的节点数量(暂定) +MIN_SPIDER_MASTER_COUNT = 2 +MIN_SPIDER_SLAVE_COUNT = 1 # 定义每个TenDB-Cluster集群中每个node的内置账号名称 TDBCTL_USER = "tdbctl" @@ -71,6 +77,7 @@ "command", "dbha:agent:", "info", + "PSYNC", "twemproxy_mon", ] @@ -102,6 +109,9 @@ # ES默认部署的实例数 ES_DEFAULT_INSTANCE_NUM = 1 +# MySQL 系统内置账号列表 +MYSQL_SYS_USER = ["system user", "event_scheduler"] + class StateType(str, StructuredEnum): CREATED = EnumField("CREATED", _("创建态")) @@ -117,6 +127,17 @@ class StateType(str, StructuredEnum): FAILED_STATES = [StateType.FAILED.value, StateType.REVOKED.value] SUCCEED_STATES = [StateType.FINISHED] +# 备份系统文件TAG +BACKUP_TAG = ( + "REDIS_BINLOG,INCREMENT_BACKUP,REDIS_FULL,MYSQL_FULL_BACKUP," + "MSSQL_FULL_BACKUP,BINLOG,OSDATA,MONGO_INCR_BACKUP,LOG,ORACLE,OTHER" +) +# 备份系统默认用户 +BACKUP_DEFAULT_OS_USER = "mysql" + +# TBinlogDumper 默认安装端口 +TBINLOGDUMPER_PORT = 27000 + class NameSpaceEnum(str, StructuredEnum): Common = EnumField("common", _("共用参数")) @@ -132,6 +153,7 @@ class NameSpaceEnum(str, StructuredEnum): Pulsar = EnumField("pulsar", _("Pulsar")) Influxdb = EnumField("influxdb", _("Influxdb")) TenDBCluster = EnumField("tendbcluster", _("tendbcluster")) + Riak = EnumField("riak", _("Riak")) class ConfigTypeEnum(str, StructuredEnum): @@ -202,6 +224,9 @@ class MediumEnum(str, StructuredEnum): Spider = EnumField("spider", _("spider节点名称")) tdbCtl = EnumField("tdbctl", _("spider中控节点名称")) Riak = EnumField("riak", _("riak")) + RiakMonitor = EnumField("riak-monitor", _("riak-monitor")) + RedisDts = EnumField("redis-dts", _("redis-dts")) + TBinlogDumper = EnumField("tbinlogdumper", _("tbinlogdumper实例")) class CloudServiceName(str, StructuredEnum): @@ -209,6 +234,7 @@ class CloudServiceName(str, StructuredEnum): DNS = EnumField("dns", _("dns服务")) DRS = EnumField("drs", _("drs服务")) DBHA = EnumField("dbha", _("dbha服务")) + RedisDTS = EnumField("redis_dts", _("redis 数据传输服务")) class CloudServiceConfFileEnum(str, StructuredEnum): @@ -224,7 +250,6 @@ class CloudDBHATypeEnum(str, StructuredEnum): MySQLMonitor = EnumField("mysql-monitor", _("mysql-monitor")) -CLOUD_SERVICE_SET_NAME = "cloud.service.set" CLOUD_SSL_PATH = "cloud/ssl" CLOUD_NGINX_DBM_DEFAULT_PORT = 80 CLOUD_NGINX_MANAGE_DEFAULT_HOST = 8080 @@ -253,6 +278,7 @@ class DBActuatorTypeEnum(str, StructuredEnum): Proxy = EnumField("proxy", _("proxy")) Redis = EnumField("redis", _("redis")) Tendis = EnumField("tendis", _("tendis")) + TendisSSD = EnumField("tendisssd", _("tendisssd")) Twemproxy = EnumField("twemproxy", _("twemproxy")) Predixy = EnumField("predixy", _("predixy")) Es = EnumField("es", _("es")) @@ -265,6 +291,7 @@ class DBActuatorTypeEnum(str, StructuredEnum): Spider = EnumField("spider", _("spider")) SpiderCtl = EnumField("spiderctl", _("spiderctl")) Riak = EnumField("riak", _("riak")) + TBinlogDumper = EnumField("tbinlogdumper", _("tbinlogdumper")) class DBActuatorActionEnum(str, StructuredEnum): @@ -279,7 +306,6 @@ class DBActuatorActionEnum(str, StructuredEnum): UnInstall = EnumField("uninstall", _("uninstall")) DeployDbbackup = EnumField("deploy-dbbackup", _("deploy-dbbackup")) InstallMonitor = EnumField("install-monitor", _("install-monitor")) - DeployRotate = EnumField("deploy-rotate", _("deploy-rotate")) SenmanticDumpSchema = EnumField("semantic-dumpschema", _("semantic-dumpschema")) ImportSQLFile = EnumField("import-sqlfile", _("import-sqlfile")) CloneClientGrant = EnumField("clone-client-grant", _("clone-client-grant")) @@ -309,6 +335,14 @@ class DBActuatorActionEnum(str, StructuredEnum): RestartSpider = EnumField("restart-spider", _("restart-spider")) AddSlaveClusterRouting = EnumField("add-slave-cluster-routing", _("添加spider-slave集群的相关路由信息")) MySQLBackupDemand = EnumField("backup-demand", _("mysql备份请求")) + TenDBClusterBackendSwitch = EnumField("cluster-backend-switch", _("TenDBCluster集群做后端切换")) + TenDBClusterMigrateCutOver = EnumField("cluster-backend-migrate-cutover", _("TenDBCluster集群做后端的成对迁移")) + DumpSchema = EnumField("dumpschema", _("为TBinlogDumper实例导出导入源表结构")) + MysqlOpenAreaDumpSchema = EnumField("open_area_dumpschema", _("Mysql开区导出库表结构")) + MysqlOpenAreaImportSchema = EnumField("open_area_importschema", _("Mysql开区导入库表结构")) + MysqlOpenAreaDumpData = EnumField("open_area_dumpdata", _("Mysql开区导出库表数据")) + MysqlOpenAreaImportData = EnumField("open_area_importdata", _("Mysql开区导入库表数据")) + EnableTokudb = EnumField("enable-tokudb-engine", _("MySQL实例安装tokudb引擎")) class RedisActuatorActionEnum(str, StructuredEnum): @@ -330,8 +364,17 @@ class RedisActuatorActionEnum(str, StructuredEnum): KillConn = EnumField("kill_conn", _("kill_conn")) SyncParam = EnumField("param_sync", _("param_sync")) CheckSync = EnumField("sync_check", _("sync_check")) + SwitchBackends = EnumField("switch", _("switch")) + ClusterForget = EnumField("cluster_forget", _("cluster_forget")) + DR_RESTORE = EnumField("dr_restore", _("dr_restore")) + CheckProxysMd5 = EnumField("check_backends", _("check_backends")) DTS_DATACHECK = EnumField("dts_datacheck", _("dts_datacheck")) - DTS_DATAREPAIRE = EnumField("dts_datarepaire", _("dts_datarepaire")) + DTS_DATAREPAIR = EnumField("dts_datarepair", _("dts_datarepair")) + DTS_ONLINE_SWITCH = EnumField("dts_online_switch", _("dts_online_switch")) + ADD_DTS_SERVER = EnumField("add_dts_server", _("add_dts_server")) + REMOVE_DTS_SERVER = EnumField("remove_dts_server", _("remove_dts_server")) + DATA_STRUCTURE = EnumField("data_structure", _("data_structure")) + CLUSTER_MEET_CHECK = EnumField("clustermeet_checkfinish", _("clustermeet_checkfinish")) class EsActuatorActionEnum(str, StructuredEnum): @@ -419,14 +462,22 @@ class PulsarActuatorActionEnum(str, StructuredEnum): class RiakActuatorActionEnum(str, StructuredEnum): SysinitRiak = EnumField("sysinit-riak", _("sysinit-riak")) Deploy = EnumField("deploy", _("deploy")) + GetConfig = EnumField("get-config", _("get-config")) JoinCluster = EnumField("join-cluster", _("join-cluster")) CommitClusterChange = EnumField("commit-cluster-change", _("commit-cluster-change")) InitBucketType = EnumField("init-bucket-type", _("init-bucket-type")) RemoveNode = EnumField("remove-node", _("remove-node")) + Transfer = EnumField("transfer", _("transfer")) + CheckConnections = EnumField("check-connections", _("check-connections")) InstallMonitor = EnumField("install-monitor", _("install-monitor")) DeployRiakCrond = EnumField("deploy-riak-crond", _("deploy-riak-crond")) ClearCrontab = EnumField("clear-crontab", _("clear-crontab")) UnInstall = EnumField("uninstall", _("uninstall")) + Start = EnumField("start", _("start")) + Stop = EnumField("stop", _("stop")) + DeployMonitor = EnumField("deploy-monitor", _("deploy-monitor")) + StartMonitor = EnumField("start-monitor", _("start-monitor")) + StopMonitor = EnumField("stop-monitor", _("stop-monitor")) class RiakModuleId(int, StructuredEnum): @@ -547,6 +598,7 @@ class DBRoleEnum(str, StructuredEnum): class LevelInfoEnum(str, StructuredEnum): TendataModuleDefault = EnumField("0", _("TendataModuleDefault")) + RiakModuleDefault = EnumField("0", _("RiakModuleDefault")) class ESRoleEnum(str, StructuredEnum): @@ -644,10 +696,15 @@ class InstanceFuncAliasEnum(str, StructuredEnum): MYSQL_FUNC_ALIAS = EnumField("mysql", _("Mysql的进程名称")) MYSQL_PROXY_FUNC_ALIAS = EnumField("mysql-proxy", _("Mysql-proxy进程名称")) + REDIS_FUNC_ALIAS = EnumField("redis", _("Redis的进程名称")) + MONGODB_FUNC_ALIAS = EnumField("mongodb", _("MONGODB的进程名称")) ES_FUNC_ALIAS = EnumField("java", _("ES的进程名称")) + KAFKA_FUNC_ALIAS = EnumField("java", _("KAFKA的进程名称")) HDFS_NAME_NODE_FUNC_ALIAS = EnumField("java", _("HDFS-NameNode的进程名称")) HDFS_DATA_NODE_FUNC_ALIAS = EnumField("java", _("HDFS-DataNode的进程名称")) PULSAR_FUNC_ALIAS = EnumField("java", _("Pulsar的进程名称")) + INFLUXDB_FUNC_ALIAS = EnumField("telegraf", _("InfluxDB 的进程名称")) + RIAK_FUNC_ALIAS = EnumField("riak", _("Riak的进程名称")) class RollbackType(str, StructuredEnum): @@ -675,7 +732,13 @@ class SyncType(str, StructuredEnum): SYNC_MS = EnumField("ms", _("ms")) SYNC_MMS = EnumField("mms", _("mms")) - SYNC_SMS = EnumField("sms", _("sms")) + SYNC_SMS = EnumField("msms", _("msms")) + + +class SwitchType(str, StructuredEnum): + "切换时是否需要,用户确认" + SWITCH_WITH_CONFIRM = "user_confirm" + SWITCH_WITHOUT_CONFIRM = "no_confirm" class RedisSlotSep(str, StructuredEnum): @@ -742,6 +805,11 @@ class RedisClusterState(str, StructuredEnum): FAIL = EnumField("fail", _("redis cluster state fail,not all slots are covered.通过 cluster info 命令获取")) +class KafkaRoleEnum(str, StructuredEnum): + ZOOKEEPER = EnumField("zookeeper", _("zookeeper")) + BROKER = EnumField("broker", _("broker")) + + class PrivRole(str, StructuredEnum): """ 定义授权实例角色 @@ -750,3 +818,32 @@ class PrivRole(str, StructuredEnum): SPIDER = EnumField("spider", _("spider")) TDBCTL = EnumField("tdbctl", _("tdbctl")) MYSQL = EnumField("mysql", _("mysql")) + + +class MysqlChangeMasterType(str, StructuredEnum): + MASTERSTATUS = EnumField("MasterStatus", _("from show master status")) + BACKUPFILE = EnumField("BackFile", _("from backup file")) + + +class TenDBBackUpLocation(str, StructuredEnum): + """ + TendbCluster的库表备份位置 + """ + + REMOTE = EnumField("remote", _("REMOTE")) + SPIDER_MNT = EnumField("spider_mnt", _("SPIDER_MNT")) + + +class AccessType(str, StructuredEnum): + CLB = EnumField("clb", _("clb")) + POLARIS = EnumField("polaris", _("北极星")) + DNS = EnumField("dns", _("域名")) + + +class TBinlogDumperAddType(str, StructuredEnum): + """ + TBinlogDumper添加类型 + """ + + FULL_SYNC = EnumField("full_sync", _("全量同步")) + INCR_SYNC = EnumField("incr_sync", _("增量同步")) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/common/get_file_list.py b/dbm-ui/backend/flow/engine/bamboo/scene/common/get_file_list.py index 4c581d6302..ad277c827f 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/common/get_file_list.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/common/get_file_list.py @@ -53,11 +53,13 @@ def get_mysql_surrounding_apps_package(): checksum_pkg = Package.get_latest_package(version=MediumEnum.Latest, pkg_type=MediumEnum.MySQLChecksum) rotate_binlog = Package.get_latest_package(version=MediumEnum.Latest, pkg_type=MediumEnum.MySQLRotateBinlog) mysql_monitor_pkg = Package.get_latest_package(version=MediumEnum.Latest, pkg_type=MediumEnum.MySQLMonitor) + mysql_crond_pkg = Package.get_latest_package(version=MediumEnum.Latest, pkg_type=MediumEnum.MySQLCrond) return [ f"{env.BKREPO_PROJECT}/{env.BKREPO_BUCKET}/{db_backup_pkg.path}", f"{env.BKREPO_PROJECT}/{env.BKREPO_BUCKET}/{checksum_pkg.path}", f"{env.BKREPO_PROJECT}/{env.BKREPO_BUCKET}/{rotate_binlog.path}", f"{env.BKREPO_PROJECT}/{env.BKREPO_BUCKET}/{mysql_monitor_pkg.path}", + f"{env.BKREPO_PROJECT}/{env.BKREPO_BUCKET}/{mysql_crond_pkg.path}", ] def mysql_install_package(self, db_version: str) -> list: @@ -111,20 +113,20 @@ def mysql_proxy_install_package(self) -> list: f"{env.BKREPO_PROJECT}/{env.BKREPO_BUCKET}/{mysql_monitor_pkg.path}", ] - def riak_install_package(self) -> list: + def riak_install_package(self, db_version: str) -> list: """ riak安装需要的安装包列表 """ - riak_pkg = Package.get_latest_package(version="2.2.1", pkg_type=MediumEnum.Riak, db_type=DBType.Riak) - # riak_crond_pkg = Package.get_latest_package(version=MediumEnum.Latest, - # pkg_type=MediumEnum.RiakCrond, db_type=DBType.Riak) - # riak_monitor_pkg = Package.get_latest_package(version=MediumEnum.Latest, - # pkg_type=MediumEnum.RiakMonitor, db_type=DBType.Riak) + riak_pkg = Package.get_latest_package(version=db_version, pkg_type=MediumEnum.Riak, db_type=DBType.Riak) + mysql_crond_pkg = Package.get_latest_package(version=MediumEnum.Latest, pkg_type=MediumEnum.MySQLCrond) + riak_monitor_pkg = Package.get_latest_package( + version=MediumEnum.Latest, pkg_type=MediumEnum.RiakMonitor, db_type=DBType.Riak.value + ) return [ f"{env.BKREPO_PROJECT}/{env.BKREPO_BUCKET}/{self.actuator_pkg.path}", f"{env.BKREPO_PROJECT}/{env.BKREPO_BUCKET}/{riak_pkg.path}", - # f"{env.BKREPO_PROJECT}/{env.BKREPO_BUCKET}/{riak_crond_pkg.path}", - # f"{env.BKREPO_PROJECT}/{env.BKREPO_BUCKET}/{riak_monitor_pkg.path}", + f"{env.BKREPO_PROJECT}/{env.BKREPO_BUCKET}/{mysql_crond_pkg.path}", + f"{env.BKREPO_PROJECT}/{env.BKREPO_BUCKET}/{riak_monitor_pkg.path}", ] def redis_cluster_apply_proxy(self, cluster_type) -> list: @@ -174,6 +176,12 @@ def redis_cluster_apply_backend(self, db_version: str) -> list: f"{env.BKREPO_PROJECT}/{env.BKREPO_BUCKET}/{bkdbmon_pkg.path}", ] + def redis_actuator_backend(self) -> list: + """ + 下发redis actuator + """ + return [f"{env.BKREPO_PROJECT}/{env.BKREPO_BUCKET}/{self.actuator_pkg.path}"] + def redis_dbmon(self) -> list: """ 安装 或者重装 dbmon @@ -330,6 +338,26 @@ def redis_base(self) -> list: f"{env.BKREPO_PROJECT}/{env.BKREPO_BUCKET}/{bkdbmon_pkg.path}", ] + def redis_add_dts_server(self) -> list: + """ + redis add dts_server + """ + redis_dts_pkg = Package.get_latest_package( + version=MediumEnum.Latest, pkg_type=MediumEnum.RedisDts, db_type=DBType.Redis + ) + return [ + f"{env.BKREPO_PROJECT}/{env.BKREPO_BUCKET}/{self.actuator_pkg.path}", + f"{env.BKREPO_PROJECT}/{env.BKREPO_BUCKET}/{redis_dts_pkg.path}", + ] + + def redis_remove_dts_server(self) -> list: + """ + redis remove dts_server + """ + return [ + f"{env.BKREPO_PROJECT}/{env.BKREPO_BUCKET}/{self.actuator_pkg.path}", + ] + def hdfs_apply(self, db_version: str) -> list: # 部署hdfs集群需要的pkg列表 hdfs_pkg = Package.get_latest_package(version=db_version, pkg_type=MediumEnum.Hdfs, db_type=DBType.Hdfs) @@ -443,14 +471,10 @@ def spider_slave_install_package(self, spider_version: str) -> list: mysql_crond_pkg = Package.get_latest_package( version=MediumEnum.Latest, pkg_type=MediumEnum.MySQLCrond, db_type=DBType.MySQL ) - mysql_monitor_pkg = Package.get_latest_package( - version=MediumEnum.Latest, pkg_type=MediumEnum.MySQLMonitor, db_type=DBType.MySQL - ) return [ f"{env.BKREPO_PROJECT}/{env.BKREPO_BUCKET}/{self.actuator_pkg.path}", f"{env.BKREPO_PROJECT}/{env.BKREPO_BUCKET}/{spider_slave_pkg.path}", f"{env.BKREPO_PROJECT}/{env.BKREPO_BUCKET}/{mysql_crond_pkg.path}", - f"{env.BKREPO_PROJECT}/{env.BKREPO_BUCKET}/{mysql_monitor_pkg.path}", ] @staticmethod @@ -466,3 +490,15 @@ def get_spider_apps_package(): f"{env.BKREPO_PROJECT}/{env.BKREPO_BUCKET}/{db_backup_pkg.path}", f"{env.BKREPO_PROJECT}/{env.BKREPO_BUCKET}/{mysql_monitor_pkg.path}", ] + + def get_tbinlogdumper_package(self): + """ + 获取tbinlogdumper安装的 + """ + tbinlogdumper_pkg = Package.get_latest_package( + version=MediumEnum.Latest, pkg_type=MediumEnum.TBinlogDumper, db_type=DBType.MySQL + ) + return [ + f"{env.BKREPO_PROJECT}/{env.BKREPO_BUCKET}/{tbinlogdumper_pkg.path}", + f"{env.BKREPO_PROJECT}/{env.BKREPO_BUCKET}/{self.actuator_pkg.path}", + ] diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/common/machine_os_init.py b/dbm-ui/backend/flow/engine/bamboo/scene/common/machine_os_init.py index 71acabead8..6267b7be3b 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/common/machine_os_init.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/common/machine_os_init.py @@ -15,10 +15,13 @@ from backend import env from backend.components.dbresource.client import DBResourceApi +from backend.configuration.constants import SystemSettingsEnum +from backend.configuration.models import SystemSettings from backend.flow.engine.bamboo.scene.common.builder import Builder from backend.flow.plugins.components.collections.common.external_service import ExternalServiceComponent from backend.flow.plugins.components.collections.common.sa_idle_check import CheckMachineIdleComponent from backend.flow.plugins.components.collections.common.sa_init import SaInitComponent +from backend.flow.plugins.components.collections.common.transfer_host_service import TransferHostServiceComponent class ImportResourceInitStepFlow(object): @@ -71,4 +74,17 @@ def machine_init_flow(self): }, ) + # 转移模块到资源池空闲机 + p.add_act( + act_name=_("主机转移至资源池空闲模块"), + act_component_code=TransferHostServiceComponent.code, + kwargs={ + "bk_biz_id": env.DBA_APP_BK_BIZ_ID, + "bk_module_ids": [ + SystemSettings.get_setting_value(key=SystemSettingsEnum.MANAGE_TOPO.value)["resource_module_id"] + ], + "bk_host_ids": [host["host_id"] for host in ip_list], + }, + ) + p.run_pipeline() diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/es/es_flow.py b/dbm-ui/backend/flow/engine/bamboo/scene/es/es_flow.py index 24e14f0959..a430d056cb 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/es/es_flow.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/es/es_flow.py @@ -40,7 +40,8 @@ def __init__(self, root_id: str, data: Optional[Dict]): self.uid = data.get("uid") self.bk_biz_id = data.get("bk_biz_id") self.nodes = data.get("nodes") - + # 仅 IP来源为资源池时,会有传值 + self.resource_spec = data.get("resource_spec") if self.ticket_type == TicketType.ES_APPLY: self.cluster_id = -1 self.cluster_name = data.get("cluster_name") @@ -113,6 +114,7 @@ def get_flow_base_data(self) -> dict: "created_by": self.created_by, "domain": self.domain, "es_config": self.es_config, + "resource_spec": self.resource_spec, } return flow_data diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/kafka/kafka_scale_up_flow.py b/dbm-ui/backend/flow/engine/bamboo/scene/kafka/kafka_scale_up_flow.py index f785d2fbac..8c55b65d39 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/kafka/kafka_scale_up_flow.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/kafka/kafka_scale_up_flow.py @@ -78,6 +78,7 @@ def __init__(self, root_id: str, data: Optional[Dict]): self.data["factor"] = int(kafka_config["factor"]) self.data["adminUser"] = kafka_config["adminUser"] self.data["adminPassword"] = kafka_config["adminPassword"] + self.data["no_security"] = int(kafka_config["no_security"]) def __get_node_ips_by_role(self, role: str) -> list: if role not in self.data["nodes"]: diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/mysql/common/common_sub_flow.py b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/common/common_sub_flow.py index 787198fd5d..b82b770872 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/mysql/common/common_sub_flow.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/common/common_sub_flow.py @@ -13,14 +13,27 @@ from django.utils.translation import gettext as _ +from backend.components import DBConfigApi +from backend.components.dbconfig.constants import FormatType, LevelName from backend.configuration.constants import DBType from backend.db_meta.enums import ClusterType -from backend.flow.consts import DBA_ROOT_USER, DBA_SYSTEM_USER +from backend.db_meta.models import Cluster +from backend.flow.consts import AUTH_ADDRESS_DIVIDER, DBA_ROOT_USER, DBA_SYSTEM_USER from backend.flow.engine.bamboo.scene.common.builder import SubBuilder from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList +from backend.flow.plugins.components.collections.common.download_backup_client import DownloadBackupClientComponent +from backend.flow.plugins.components.collections.mysql.check_client_connections import CheckClientConnComponent +from backend.flow.plugins.components.collections.mysql.clone_user import CloneUserComponent from backend.flow.plugins.components.collections.mysql.exec_actuator_script import ExecuteDBActuatorScriptComponent from backend.flow.plugins.components.collections.mysql.trans_flies import TransFileComponent -from backend.flow.utils.mysql.mysql_act_dataclass import DownloadMediaKwargs, ExecActuatorKwargs +from backend.flow.plugins.components.collections.mysql.verify_checksum import VerifyChecksumComponent +from backend.flow.utils.common_act_dataclass import DownloadBackupClientKwargs +from backend.flow.utils.mysql.mysql_act_dataclass import ( + CheckClientConnKwargs, + DownloadMediaKwargs, + ExecActuatorKwargs, + VerifyChecksumKwargs, +) from backend.flow.utils.mysql.mysql_act_playload import MysqlActPayload """ @@ -258,6 +271,23 @@ def build_surrounding_apps_sub_flow( } ) + if is_init: + # 如果非重建模式(上架阶段), 不需要重建安装backup-client工具,默认情况下部署时 proxy+mysql机器都会安装 + acts_list.append( + { + "act_name": _("安装backup-client工具"), + "act_component_code": DownloadBackupClientComponent.code, + "kwargs": asdict( + DownloadBackupClientKwargs( + bk_cloud_id=bk_cloud_id, + download_host_list=list( + filter(None, list(set(master_ip_list + slave_ip_list + proxy_ip_list))) + ), + ) + ), + } + ) + sub_pipeline.add_parallel_acts(acts_list=acts_list) return sub_pipeline.build_sub_process(sub_name=_("安装MySql周边程序")) @@ -268,7 +298,8 @@ def build_repl_by_manual_input_sub_flow( parent_global_data: dict, master_ip: str, slave_ip: str, - mysql_port: int, + master_port: int, + slave_port: int, sub_flow_name: str = None, ): """ @@ -279,14 +310,10 @@ def build_repl_by_manual_input_sub_flow( @param parent_global_data: 子流程的上层全局只读上下文 @param master_ip:主节点ip @param slave_ip: 从节点ip - @param mysql_port: mysql port,系统默认主从都一致 + @param master_port: master port, + @param slave_port: slave port, @param sub_flow_name: 子流程名称 """ - cluster = { - "new_slave_ip": slave_ip, - "new_master_ip": master_ip, - "mysql_port": mysql_port, - } # write_payload_var_name: pos位点信息存储上下文的变量位置 write_payload_var_name = "master_ip_sync_info" @@ -301,7 +328,7 @@ def build_repl_by_manual_input_sub_flow( bk_cloud_id=bk_cloud_id, exec_ip=master_ip, get_mysql_payload_func=MysqlActPayload.get_grant_mysql_repl_user_payload.__name__, - cluster=cluster, + cluster={"new_slave_ip": slave_ip, "mysql_port": master_port}, run_as_system_user=DBA_SYSTEM_USER, ) ), @@ -316,9 +343,175 @@ def build_repl_by_manual_input_sub_flow( bk_cloud_id=bk_cloud_id, exec_ip=slave_ip, get_mysql_payload_func=MysqlActPayload.get_change_master_payload.__name__, - cluster=cluster, + cluster={"new_master_ip": master_ip, "master_port": master_port, "slave_port": slave_port}, run_as_system_user=DBA_SYSTEM_USER, ) ), ) return sub_pipeline.build_sub_process(sub_name=_("建立主从同步[{}]".format(sub_flow_name))) + + +def install_mysql_in_cluster_sub_flow( + uid: str, root_id: str, cluster: Cluster, new_mysql_list: list, install_ports: list +): + """ + 设计基于某个cluster,以及计算好的实例安装端口列表,对新机器安装mysql实例的公共子流 + 子流程并不是提供给部署类单据的,目标是提供tendb_ha/tendb_cluster扩容类单据 + @param uid: 流程uid + @param root_id: flow流程的root_id + @param cluster: 关联的cluster对象 + @param new_mysql_list: 新机器列表,每个元素是ip + @param install_ports: 每台机器按照的实例端口列表 + """ + + # 目前先根据cluster对应,请求bk-config服务去获取对应的 + # todo 后续可能继续优化这块逻辑,mysql的版本号通过记录的小版本信息来获取? + data = DBConfigApi.query_conf_item( + { + "bk_biz_id": str(cluster.bk_biz_id), + "level_name": LevelName.MODULE, + "level_value": str(cluster.db_module_id), + "conf_file": "deploy_info", + "conf_type": "deploy", + "namespace": cluster.cluster_type, + "format": FormatType.MAP, + } + )["content"] + + parent_global_data = { + "uid": uid, + "charset": data["charset"], + "db_version": data["db_version"], + "mysql_ports": install_ports, + "bk_biz_id": cluster.bk_biz_id, + "clusters": [], + } + for port in install_ports: + parent_global_data["clusters"].append({"mysql_port": port, "master": cluster.immute_domain}) + sub_pipeline = SubBuilder(root_id=root_id, data=parent_global_data) + + # 拼接执行原子任务活动节点需要的通用的私有参数结构体, 减少代码重复率,但引用时注意内部参数值传递的问题 + exec_act_kwargs = ExecActuatorKwargs( + bk_cloud_id=cluster.bk_cloud_id, + cluster_type=cluster.cluster_type, + ) + + # 阶段1 并行分发安装文件 + sub_pipeline.add_parallel_acts( + acts_list=[ + { + "act_name": _("下发MySQL介质包"), + "act_component_code": TransFileComponent.code, + "kwargs": asdict( + DownloadMediaKwargs( + bk_cloud_id=cluster.bk_cloud_id, + exec_ip=new_mysql_list, + file_list=GetFileList(db_type=DBType.MySQL).mysql_install_package( + db_version=data["db_version"] + ), + ) + ), + } + ] + ) + + # 阶段2 批量初始化所有机器,安装crond进程 + exec_act_kwargs.exec_ip = new_mysql_list + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_sys_init_payload.__name__ + sub_pipeline.add_act( + act_name=_("初始化机器 {}".format(exec_act_kwargs.exec_ip)), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(exec_act_kwargs), + ) + + acts_list = [] + for mysql_ip in new_mysql_list: + exec_act_kwargs.exec_ip = mysql_ip + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_deploy_mysql_crond_payload.__name__ + acts_list.append( + { + "act_name": _("部署mysql-crond {}".format(exec_act_kwargs.exec_ip)), + "act_component_code": ExecuteDBActuatorScriptComponent.code, + "kwargs": asdict(exec_act_kwargs), + } + ) + sub_pipeline.add_parallel_acts(acts_list=acts_list) + + # 阶段3 并发安装mysql实例(一个活动节点部署多实例) + acts_list = [] + for mysql_ip in new_mysql_list: + exec_act_kwargs.exec_ip = mysql_ip + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_install_mysql_payload.__name__ + acts_list.append( + { + "act_name": _("安装MySQL实例 {}".format(exec_act_kwargs.exec_ip)), + "act_component_code": ExecuteDBActuatorScriptComponent.code, + "kwargs": asdict(exec_act_kwargs), + } + ) + sub_pipeline.add_parallel_acts(acts_list=acts_list) + + return sub_pipeline.build_sub_process(sub_name=_("安装mysql实例flow")) + + +def check_sub_flow( + uid: str, + root_id: str, + cluster: Cluster, + is_check_client_conn: bool = False, + check_client_conn_inst: list = None, + is_verify_checksum: bool = False, + verify_checksum_tuples: list = None, +): + """ + 设计预检测的公共子流程,主要服务于切换类的流程,做前置检查,方便管控 + @param uid: 流程单据的uid + @param root_id: flow流程的root_id + @param cluster: 关联的cluster对象 + @param is_check_client_conn: 是否做客户端连接检测 + @param check_client_conn_inst: 如果做客户端连接检测,则传入待检测的实例列表,["ip:port"...] + @param is_verify_checksum: 是否做验证checksum结果 + @param verify_checksum_tuples: 如果验证checksum,则传入待检测的实例列表,每个元素[{"master":"ip:port", "slave":"ip:port"}..] + """ + + if is_check_client_conn and not check_client_conn_inst: + raise Exception(_("构建子流程失败,联系系统管理员, check_client_conn_inst is null")) + if is_verify_checksum and not verify_checksum_tuples: + raise Exception(_("构建子流程失败,联系系统管理员, verify_checksum_tuples is null")) + + act_list = [] + if is_check_client_conn: + act_list.append( + { + "act_name": _("检测客户端连接情况"), + "act_component_code": CheckClientConnComponent.code, + "kwargs": asdict( + CheckClientConnKwargs( + bk_cloud_id=cluster.bk_cloud_id, + check_instances=check_client_conn_inst, + ) + ), + } + ) + + if is_verify_checksum: + act_list.append( + { + "act_name": _("检测checksum结果"), + "act_component_code": VerifyChecksumComponent.code, + "kwargs": asdict( + VerifyChecksumKwargs( + bk_cloud_id=cluster.bk_cloud_id, + checksum_instance_tuples=verify_checksum_tuples, + ) + ), + } + ) + + if not act_list: + return None + + sub_pipeline = SubBuilder(root_id=root_id, data={"uid": uid}) + sub_pipeline.add_parallel_acts(acts_list=act_list) + + return sub_pipeline.build_sub_process(sub_name=_("[{}]预检测".format(cluster.name))) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/mysql/common/exceptions.py b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/common/exceptions.py index e2cc0ef28d..d5dde8cb11 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/mysql/common/exceptions.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/common/exceptions.py @@ -21,3 +21,15 @@ class NormalTenDBFlowException(TenDBFlowBaseException): ERROR_CODE = "001" MESSAGE = _("通用异常") MESSAGE_TPL = _("{message}") + + +class TenDBGetBackupInfoFailedException(TenDBFlowBaseException): + ERROR_CODE = "006" + MESSAGE = _("获取备份失败") + MESSAGE_TPL = _("{message}") + + +class TenDBGetBinlogFailedException(TenDBFlowBaseException): + ERROR_CODE = "007" + MESSAGE = _("获取binlog失败") + MESSAGE_TPL = _("{message}") diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_clone_rules.py b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_clone_rules.py index c9aca45642..a364ab6364 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_clone_rules.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_clone_rules.py @@ -22,7 +22,6 @@ class MySQLCloneRules(object): """ mysql权限克隆的流程抽象类 - todo 后续需要兼容跨云管理 """ def __init__(self, root_id: str, data: Optional[Dict]): diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_flashback_flow.py b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_flashback_flow.py index 5f026531c6..1e9056f125 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_flashback_flow.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_flashback_flow.py @@ -16,13 +16,19 @@ from django.utils.translation import ugettext as _ from backend.configuration.constants import DBType +from backend.flow.consts import TruncateDataTypeEnum from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList from backend.flow.plugins.components.collections.mysql.exec_actuator_script import ExecuteDBActuatorScriptComponent +from backend.flow.plugins.components.collections.mysql.filter_database_table_by_flashback_input import ( + FilterDatabaseTableFromFlashbackInputComponent, +) +from backend.flow.plugins.components.collections.mysql.general_check_db_in_using import GeneralCheckDBInUsingComponent from backend.flow.plugins.components.collections.mysql.trans_flies import TransFileComponent from backend.flow.utils.mysql.common.mysql_cluster_info import get_cluster_info -from backend.flow.utils.mysql.mysql_act_dataclass import DownloadMediaKwargs, ExecActuatorKwargs +from backend.flow.utils.mysql.mysql_act_dataclass import BKCloudIdKwargs, DownloadMediaKwargs, ExecActuatorKwargs from backend.flow.utils.mysql.mysql_act_playload import MysqlActPayload +from backend.flow.utils.mysql.mysql_context_dataclass import MySQLFlashBackContext logger = logging.getLogger("flow") @@ -48,12 +54,34 @@ def mysql_flashback_flow(self): mysql_restore_slave_pipeline = Builder(root_id=self.root_id, data=copy.deepcopy(self.data)) sub_pipeline_list = [] for info in self.data["infos"]: - ticket_data = copy.deepcopy(self.data) - sub_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(ticket_data)) one_cluster = get_cluster_info(info["cluster_id"]) one_cluster.update(info) one_cluster["work_dir"] = f"/data/dbbak/{self.root_id}" + ticket_data = { + **copy.deepcopy(self.data), + "uid": self.data["uid"], + "created_by": self.data["created_by"], + "bk_biz_id": self.data["bk_biz_id"], + "ip": one_cluster["master_ip"], + "port": one_cluster["master_port"], + "truncate_data_type": TruncateDataTypeEnum.TRUNCATE_TABLE.value, # 不是真的要删表, 只是复用打开检查必须 + } + sub_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(ticket_data)) + + sub_pipeline.add_act( + act_name=_("获取回档库表"), + act_component_code=FilterDatabaseTableFromFlashbackInputComponent.code, + kwargs=asdict(BKCloudIdKwargs(bk_cloud_id=one_cluster["bk_cloud_id"])), + ) + + if not self.data["force"]: + sub_pipeline.add_act( + act_name=_("检查库表是否在用"), + act_component_code=GeneralCheckDBInUsingComponent.code, + kwargs=asdict(BKCloudIdKwargs(bk_cloud_id=one_cluster["bk_cloud_id"])), + ) + sub_pipeline.add_act( act_name=_("下发db-actor到集群主节点{}").format(one_cluster["master_ip"]), act_component_code=TransFileComponent.code, @@ -81,4 +109,4 @@ def mysql_flashback_flow(self): sub_pipeline_list.append(sub_pipeline.build_sub_process(sub_name=_("flash开始恢复数据"))) mysql_restore_slave_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipeline_list) - mysql_restore_slave_pipeline.run_pipeline() + mysql_restore_slave_pipeline.run_pipeline(init_trans_data_class=MySQLFlashBackContext()) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_ha_db_table_backup.py b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_ha_db_table_backup.py index f7bf2e667e..679c53f95c 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_ha_db_table_backup.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_ha_db_table_backup.py @@ -110,6 +110,7 @@ def backup_flow(self): "backup_type": "logical", "backup_gsd": ["schema", "data"], "custom_backup_dir": "backupDatabaseTable", + "role": instance_obj.instance_role, }, ) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_ha_destroy_flow.py b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_ha_destroy_flow.py index cf9f7cb38a..7adb627ce5 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_ha_destroy_flow.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_ha_destroy_flow.py @@ -15,6 +15,7 @@ from django.utils.translation import ugettext as _ from backend.configuration.constants import DBType +from backend.db_meta.exceptions import ClusterNotExistException from backend.db_meta.models import Cluster, ProxyInstance, StorageInstance from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList @@ -49,12 +50,17 @@ def __init__(self, root_id: str, data: Optional[Dict]): self.data = data @staticmethod - def __get_ha_cluster_info(cluster_id: int) -> dict: + def __get_ha_cluster_info(cluster_id: int, bk_biz_id: int) -> dict: """ 根据cluster_id 获取到集群相关信息 @param cluster_id: 需要下架的集群id + @param bk_biz_id: 需要下架集群对饮bk_biz_id """ - cluster = Cluster.objects.get(id=cluster_id) + try: + cluster = Cluster.objects.get(id=cluster_id, bk_biz_id=bk_biz_id) + except Cluster.DoesNotExist: + raise ClusterNotExistException(cluster_id=cluster_id, bk_biz_id=bk_biz_id, message=_("集群不存在")) + backend_info = StorageInstance.objects.filter(cluster=cluster).all() proxy_info = ProxyInstance.objects.filter(cluster=cluster).all() return { @@ -78,7 +84,7 @@ def destroy_mysql_ha_flow(self): for cluster_id in self.data["cluster_ids"]: # 获取集群的实例信息 - cluster = self.__get_ha_cluster_info(cluster_id=cluster_id) + cluster = self.__get_ha_cluster_info(cluster_id=cluster_id, bk_biz_id=int(self.data["bk_biz_id"])) sub_pipeline = SubBuilder(root_id=self.root_id, data=self.data) @@ -118,20 +124,6 @@ def destroy_mysql_ha_flow(self): ), ) - # 阶段2 清理周边配置,包括事件监控 - # acts_list = [] - # for ip in cluster["backend_ip_list"] + cluster["proxy_ip_list"]: - # exec_act_kwargs.exec_ip = ip - # exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_clear_surrounding_config_payload.__name__ - # acts_list.append( - # { - # "act_name": _("清理实例周边配置"), - # "act_component_code": ExecuteDBActuatorScriptComponent.code, - # "kwargs": asdict(exec_act_kwargs), - # } - # ) - # sub_pipeline.add_parallel_acts(acts_list=acts_list) - # 阶段3 卸载相关db组件 acts_list = [] for proxy_ip in cluster["proxy_ip_list"]: diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_ha_full_backup_flow.py b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_ha_full_backup_flow.py index 428a4b2b08..b00c2b546e 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_ha_full_backup_flow.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_ha_full_backup_flow.py @@ -95,6 +95,7 @@ def full_backup_flow(self): "backup_type": self.data["infos"]["backup_type"], "backup_id": uuid.uuid1(), "backup_gsd": ["schema", "data"], + "role": slave_obj.instance_role, }, ) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_ha_standardize_flow.py b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_ha_standardize_flow.py new file mode 100644 index 0000000000..4d9a72dde8 --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_ha_standardize_flow.py @@ -0,0 +1,281 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +import copy +import logging +from collections import defaultdict +from dataclasses import asdict +from typing import Dict, List, Optional + +from django.utils.translation import ugettext as _ + +from backend.configuration.constants import DBType +from backend.db_meta.models import Cluster +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder, SubProcess +from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList +from backend.flow.plugins.components.collections.mysql.cluster_standardize_trans_module import ( + ClusterStandardizeTransModuleComponent, +) +from backend.flow.plugins.components.collections.mysql.exec_actuator_script import ExecuteDBActuatorScriptComponent +from backend.flow.plugins.components.collections.mysql.trans_flies import TransFileComponent +from backend.flow.utils.mysql.mysql_act_dataclass import DownloadMediaKwargs, ExecActuatorKwargs +from backend.flow.utils.mysql.mysql_act_playload import MysqlActPayload + +logger = logging.getLogger("flow") + + +class MySQLHAStandardizeFlow(object): + def __init__(self, root_id: str, data: Optional[Dict]): + self.root_id = root_id + self.data = data + + def standardize(self): + """ + self.data = { + "uid": "20230830", + "created_by": "xxx", + "bk_biz_id": "11", + "ticket_type": "MYSQL_HA_STANDARDIZE", + "infos": { + "cluster_ids": [1, 2, 3], + } + } + """ + cluster_objects = Cluster.objects.filter(pk__in=self.data["infos"]["cluster_ids"]) + if len(cluster_objects) != len(self.data["infos"]["cluster_ids"]): + pass # ToDo + + standardize_pipe = Builder(root_id=self.root_id, data=self.data) + standardize_pipe.add_parallel_sub_pipeline( + sub_flow_list=[ + self._build_trans_module_sub(clusters=cluster_objects), + self._build_proxy_sub(clusters=cluster_objects), + self._build_storage_sub(clusters=cluster_objects), + ] + ) + logger.info(_("构建TenDBHA集群标准化流程成功")) + standardize_pipe.run_pipeline() + + def _build_trans_module_sub(self, clusters: List[Cluster]) -> SubProcess: + pipes = [] + for cluster in clusters: + cluster_pipe = SubBuilder( + root_id=self.root_id, data={**copy.deepcopy(self.data), "cluster_id": cluster.id} + ) + cluster_pipe.add_act( + act_name=_(""), act_component_code=ClusterStandardizeTransModuleComponent.code, kwargs={} + ) + + pipes.append(cluster_pipe.build_sub_process(sub_name=_("{} CC 模块标准化".format(cluster.immute_domain)))) + + p = SubBuilder(root_id=self.root_id, data=self.data) + p.add_parallel_sub_pipeline(sub_flow_list=pipes) + return p.build_sub_process(sub_name=_("CC标准化")) + + def _build_proxy_sub(self, clusters: List[Cluster]) -> SubProcess: + ip_cluster_map = defaultdict(list) + for cluster in clusters: + for ins in cluster.proxyinstance_set.all(): + # 集群的 N 个接入层实例肯定在不同的 N 台机器上 + # 一个实例肯定只是一个集群的接入层 + # 所以这个列表不会有重复值 + ip_cluster_map[ins.machine.ip].append(cluster) + + pipes = [] + for ip, relate_clusters in ip_cluster_map.items(): + bk_cloud_id = relate_clusters[0].bk_cloud_id + cluster_type = relate_clusters[0].cluster_type + pipe = SubBuilder(root_id=self.root_id, data=self.data) + + pipe.add_act( + act_name=_("下发MySQL周边程序介质"), + act_component_code=TransFileComponent.code, + kwargs=asdict( + DownloadMediaKwargs( + bk_cloud_id=bk_cloud_id, + exec_ip=ip, + file_list=GetFileList(db_type=DBType.MySQL).get_mysql_surrounding_apps_package(), + ) + ), + ) + + pipe.add_act( + act_name=_("下发actuator介质"), + act_component_code=TransFileComponent.code, + kwargs=asdict( + DownloadMediaKwargs( + bk_cloud_id=bk_cloud_id, + exec_ip=ip, + file_list=GetFileList(db_type=DBType.MySQL).get_db_actuator_package(), + ) + ), + ) + + pipe.add_act( + act_name=_("部署mysql-crond"), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict( + ExecActuatorKwargs( + exec_ip=ip, + bk_cloud_id=bk_cloud_id, + get_mysql_payload_func=MysqlActPayload.get_deploy_mysql_crond_payload.__name__, + cluster_type=cluster_type, + ) + ), + ) + + pipe.add_act( + act_name=_("部署监控程序"), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict( + ExecActuatorKwargs( + exec_ip=ip, + bk_cloud_id=bk_cloud_id, + get_mysql_payload_func=MysqlActPayload.get_deploy_mysql_monitor_payload.__name__, + cluster_type=cluster_type, + ) + ), + ) + + pipes.append( + pipe.build_sub_process( + sub_name=_("{} 部署dba工具".format("\n".join([ele.immute_domain for ele in relate_clusters]))) + ) + ) + + p = SubBuilder(root_id=self.root_id, data=self.data) + p.add_parallel_sub_pipeline(sub_flow_list=pipes) + + return p.build_sub_process(sub_name=_("接入层标准化")) + + def _build_storage_sub(self, clusters: List[Cluster]) -> SubProcess: + ip_cluster_map = defaultdict(list) + for cluster in clusters: + for ins in cluster.storageinstance_set.all(): + ip_cluster_map[ins.machine.ip].append(cluster) + + pipes = [] + for ip, relate_clusters in ip_cluster_map.items(): + bk_cloud_id = relate_clusters[0].bk_cloud_id + cluster_type = relate_clusters[0].cluster_type + pipe = SubBuilder(root_id=self.root_id, data=self.data) + + pipe.add_act( + act_name=_("下发MySQL周边程序介质"), + act_component_code=TransFileComponent.code, + kwargs=asdict( + DownloadMediaKwargs( + bk_cloud_id=bk_cloud_id, + exec_ip=ip, + file_list=GetFileList(db_type=DBType.MySQL).get_mysql_surrounding_apps_package(), + ) + ), + ) + + pipe.add_act( + act_name=_("下发actuator介质"), + act_component_code=TransFileComponent.code, + kwargs=asdict( + DownloadMediaKwargs( + bk_cloud_id=bk_cloud_id, + exec_ip=ip, + file_list=GetFileList(db_type=DBType.MySQL).get_db_actuator_package(), + ) + ), + ) + + pipe.add_act( + act_name=_("部署mysql-crond"), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict( + ExecActuatorKwargs( + exec_ip=ip, + bk_cloud_id=bk_cloud_id, + get_mysql_payload_func=MysqlActPayload.get_deploy_mysql_crond_payload.__name__, + cluster_type=cluster_type, + ) + ), + ) + + pipe.add_act( + act_name=_("部署监控程序"), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict( + ExecActuatorKwargs( + exec_ip=ip, + bk_cloud_id=bk_cloud_id, + get_mysql_payload_func=MysqlActPayload.get_deploy_mysql_monitor_payload.__name__, + cluster_type=cluster_type, + ) + ), + ) + + pipe.add_act( + act_name=_("部署备份程序"), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict( + ExecActuatorKwargs( + exec_ip=ip, + bk_cloud_id=bk_cloud_id, + get_mysql_payload_func=MysqlActPayload.get_install_db_backup_payload.__name__, + cluster_type=cluster_type, + ) + ), + ) + + pipe.add_act( + act_name=_("部署rotate binlog"), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict( + ExecActuatorKwargs( + exec_ip=ip, + bk_cloud_id=bk_cloud_id, + get_mysql_payload_func=MysqlActPayload.get_install_mysql_rotatebinlog_payload.__name__, + cluster_type=cluster_type, + ) + ), + ) + + pipe.add_act( + act_name=_("部署数据校验程序"), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict( + ExecActuatorKwargs( + exec_ip=ip, + bk_cloud_id=bk_cloud_id, + get_mysql_payload_func=MysqlActPayload.get_install_mysql_checksum_payload.__name__, + cluster_type=cluster_type, + ) + ), + ) + + pipe.add_act( + act_name=_("部署DBA工具箱"), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict( + ExecActuatorKwargs( + bk_cloud_id=bk_cloud_id, + exec_ip=ip, + get_mysql_payload_func=MysqlActPayload.get_install_dba_toolkit_payload.__name__, + cluster_type=cluster_type, + ) + ), + ) + + pipes.append( + pipe.build_sub_process( + sub_name=_("{} 部署dba工具".format("\n".join([ele.immute_domain for ele in relate_clusters]))) + ) + ) + + p = SubBuilder(root_id=self.root_id, data=self.data) + p.add_parallel_sub_pipeline(sub_flow_list=pipes) + return p.build_sub_process(sub_name=_("存储层标准化")) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_master_slave_switch.py b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_master_slave_switch.py index afaf7d9ea5..1b247690f3 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_master_slave_switch.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_master_slave_switch.py @@ -84,7 +84,7 @@ def get_cluster_info(cluster_id: int, new_master_ip: str, old_master_ip: str) -> if not new_master.is_stand_by: # 传来的待新主实例,is_stand_by为False,则中断流程,报异常 raise NormalTenDBFlowException( - message=_("the is_stand_by of new-master-instance [{}] is False ".format(new_master.ip_port())) + message=_("the is_stand_by of new-master-instance [{}] is False ".format(new_master.ip_port)) ) other_slave_info = ( diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_open_area_flow.py b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_open_area_flow.py new file mode 100644 index 0000000000..af7c7a99a0 --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_open_area_flow.py @@ -0,0 +1,349 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import copy +from dataclasses import asdict +from typing import Dict, Optional + +from django.utils.translation import ugettext as _ + +from backend.configuration.constants import DBType +from backend.core.consts import BK_PKG_INSTALL_PATH +from backend.db_meta.enums import ClusterType, InstanceInnerRole, TenDBClusterSpiderRole +from backend.db_meta.exceptions import ClusterNotExistException, DBMetaException +from backend.db_meta.models import Cluster +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList +from backend.flow.plugins.components.collections.mysql.exec_actuator_script import ExecuteDBActuatorScriptComponent +from backend.flow.plugins.components.collections.mysql.trans_flies import TransFileComponent +from backend.flow.utils.mysql.mysql_act_dataclass import DownloadMediaKwargs, ExecActuatorKwargs +from backend.flow.utils.mysql.mysql_act_playload import MysqlActPayload + + +class MysqlOpenAreaFlow(object): + def __init__(self, root_id: str, data: Optional[Dict]): + """ + tenDBHA导出导入库表结构、数据都在主db上进行 + tenDBCluster在中控主节点上导出导入库表结构、在spider节点上导出导入数据 + @param root_id : 任务流程定义的root_id + @param data : 单据传递参数 + """ + self.root_id = root_id + self.data = data + self.data["uid"] = self.data.get("uid") or self.root_id + self.uid = self.data["uid"] + + self.work_dir = f"{BK_PKG_INSTALL_PATH}/mysql_open_area" + self.schema_tar_file_name = f"{self.root_id}_schema.tar.gz" + self.schema_md5sum_file_name = f"{self.root_id}_schema.md5sum" + self.data_tar_file_name = f"{self.root_id}_data.tar.gz" + self.data_md5sum_file_name = f"{self.root_id}_data.md5sum" + + def __get_cluster_info(self, cluster_id: int, bk_biz_id: int, data_flag=False) -> dict: + """ + 获取集群基本信息 source与target共用 + @param cluster_id: + @param bk_biz_id: + @param data_flag: tenDBClusterspider节点上导出和导入数据,因此需要的是spider节点的信息 + @return: + """ + try: + # get查询时,结果只能有一个 查到多个结果会报错 + cluster = Cluster.objects.get(id=cluster_id, bk_biz_id=bk_biz_id) + except Cluster.DoesNotExist: + raise ClusterNotExistException(cluster_id=cluster_id, bk_biz_id=bk_biz_id, message=_("集群不存在")) + # 不同集群类型,下发的ip角色不一样 + # tenDBHA下发主db tenDBCluster库表下发中控主节点 数据下发spider节点 + if cluster.cluster_type == ClusterType.TenDBCluster.value: + if data_flag: + ip_port = ( + cluster.proxyinstance_set.filter( + tendbclusterspiderext__spider_role=TenDBClusterSpiderRole.SPIDER_MASTER + ) + .first() + .ip_port + ) + else: + ip_port = cluster.tendbcluster_ctl_primary_address() + elif cluster.cluster_type == ClusterType.TenDBHA.value: + ip_port = cluster.storageinstance_set.get(instance_inner_role=InstanceInnerRole.MASTER).ip_port + else: + raise DBMetaException(message=_("集群实例类型不适用于开区")) + + return { + "cluster_id": cluster.id, + "bk_cloud_id": cluster.bk_cloud_id, + "cluster_type": cluster.cluster_type, + "ip": ip_port.split(":")[0], + "port": int(ip_port.split(":")[1]), + "root_id": self.root_id, + } + + def __get_source_cluster(self, data_flag=False) -> dict: + """ + 获取源实例的库表结构,可指定表 + 区分tenDBHA与tenDBCluster + @return: + """ + source_cluster = self.__get_cluster_info( + cluster_id=self.data["source_cluster"], bk_biz_id=self.data["bk_biz_id"], data_flag=data_flag + ) + source_cluster["is_upload_bkrepo"] = self.__is_upload_bkrepo( + source_cluster=self.data["source_cluster"], target_clusters=self.data["target_clusters"] + ) + # 表列表只有在导出的时候才需要 导入的时候只需要知道新旧库名 + # 多个目标集群开区,但规则用的是一套相同的,因此取第一个获取库表开区规则 + if data_flag: + source_cluster["open_area_param"] = [ + {"schema": exec_obj["source_db"], "tables": exec_obj["data_tblist"]} + for exec_obj in self.data["target_clusters"][0]["execute_objects"] + if len(exec_obj["data_tblist"]) > 0 + ] + else: + source_cluster["open_area_param"] = [ + {"schema": exec_obj["source_db"], "tables": exec_obj["schema_tblist"]} + for exec_obj in self.data["target_clusters"][0]["execute_objects"] + ] + + return source_cluster + + def __get_target_cluster(self, data_flag=False) -> list: + """ + 获取目标集群相关信息 + 目标集群执行导入库表结构操作,需要知道原库表名称和新库表名称 + @return: + """ + target_clusters = [] + for tc in self.data["target_clusters"]: + target_cluster = self.__get_cluster_info( + cluster_id=tc["target_cluster"], bk_biz_id=self.data["bk_biz_id"], data_flag=data_flag + ) + if data_flag: + target_cluster["open_area_param"] = [ + {"schema": exec_obj["source_db"], "newdb": exec_obj["target_db"]} + for exec_obj in tc["execute_objects"] + if len(exec_obj["data_tblist"]) > 0 + ] + else: + target_cluster["open_area_param"] = [ + {"schema": exec_obj["source_db"], "newdb": exec_obj["target_db"]} + for exec_obj in tc["execute_objects"] + ] + + target_clusters.append(target_cluster) + + return target_clusters + + def __is_upload_bkrepo(self, source_cluster: int, target_clusters: list) -> bool: + """ + 本地开区,不用上传制品库 + 集群维度判断 + tendbcluster集群 库表结构在中控 数据在spider节点 但属于同一集群 + 有上传必然有下发 是否下发文件也用这个来判断 + @param source_cluster: + @param target_clusters: + @return: + """ + for tc in target_clusters: + if int(source_cluster) != int(tc["target_cluster"]): + # 只要存在跟源集群不一样的 就要上传制品库 + return True + + return False + + def __get_exec_ip_list(self, source_cluster: dict, target_clusters: list) -> list: + """ + 过滤需要下发act的IP + @param source_cluster: + @param target_clusters: + @return: + """ + exec_ip_list = [] + exec_ip_list.append(source_cluster["ip"]) + for tc in target_clusters: + if tc["ip"] not in exec_ip_list: + exec_ip_list.append() + + return exec_ip_list + + def __get_data_flag(self) -> bool: + """ + 判断是否需要迁移数据 + @return: + """ + execute_objects = self.data["target_clusters"][0]["execute_objects"] + for exe_obj in execute_objects: + if len(exe_obj["data_tblist"]) > 0: + return True + + return False + + def mysql_open_area_flow(self): + source_cluster_schema = self.__get_source_cluster(data_flag=False) + target_clusters_schema = self.__get_target_cluster(data_flag=False) + # 提取要下发act的机器ip(过滤重复) + exec_ip_list = self.__get_exec_ip_list(source_cluster_schema, target_clusters_schema) + + pipeline = Builder(root_id=self.root_id, data=self.data) + + pipeline.add_act( + act_name=_("下发db-actuator介质"), + act_component_code=TransFileComponent.code, + kwargs=asdict( + DownloadMediaKwargs( + bk_cloud_id=source_cluster_schema["bk_cloud_id"], + exec_ip=exec_ip_list, + file_list=GetFileList(db_type=DBType.MySQL).get_db_actuator_package(), + ) + ), + ) + + pipeline.add_act( + act_name=_("从源实例获取开区所需库表结构"), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict( + ExecActuatorKwargs( + bk_cloud_id=source_cluster_schema["bk_cloud_id"], + cluster_type=source_cluster_schema["cluster_type"], + cluster=source_cluster_schema, + exec_ip=source_cluster_schema["ip"], + get_mysql_payload_func=MysqlActPayload.get_open_area_dump_schema_payload.__name__, + ) + ), + ) + + # 本地开区,没有上传制品库,也不用下发 + if source_cluster_schema["is_upload_bkrepo"]: + # 目标集群下发库表文件,源集群不用下发 + exec_ip_list.remove(source_cluster_schema["ip"]) + pipeline.add_act( + act_name=_("下发开区库表文件"), + act_component_code=TransFileComponent.code, + kwargs=asdict( + DownloadMediaKwargs( + bk_cloud_id=0, + exec_ip=exec_ip_list, + file_target_path=self.work_dir, + file_list=GetFileList(db_type=DBType.MySQL).mysql_import_sqlfile( + path="mysql/sqlfile", filelist=[self.schema_tar_file_name, self.schema_md5sum_file_name] + ), + ) + ), + ) + + sub_pipelines = [] + for target_cluster in target_clusters_schema: + sub_pipeline = SubBuilder(root_id=self.root_id, data=self.data) + sub_pipeline.add_act( + act_name=_("向目标实例导入库表结构"), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict( + ExecActuatorKwargs( + bk_cloud_id=target_cluster["bk_cloud_id"], + cluster_type=target_cluster["cluster_type"], + cluster=target_cluster, + exec_ip=target_cluster["ip"], + get_mysql_payload_func=MysqlActPayload.get_open_area_import_schema_payload.__name__, + ) + ), + ) + + sub_pipelines.append(sub_pipeline.build_sub_process(sub_name=_("目标集群开区导入表结构流程"))) + pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) + + # 判断是否需要进行数据的迁移 + data_flag = self.__get_data_flag() + if data_flag: + pipeline.add_sub_pipeline(sub_flow=self.open_area_data_flow()) + + pipeline.run_pipeline() + + def open_area_data_flow(self): + """ + 用于构建导入导出数据的子流程 + 库表信息和导入导出表结构不一样 主要是指定表不一样 + 对于数据来说,空列表表示不导出数据,只有指定的时候才进行操作 + 另外,对于tenDBCluster集群来说,导出导入数据在spider节点上进行,需要单独再下发一次act + @return: + """ + # 获取导入导出数据集群参数 + source_cluster_data = self.__get_source_cluster(data_flag=True) + target_clusters_data = self.__get_target_cluster(data_flag=True) + # 获取之后操作的所有ip,过滤重复值 + exec_ip_list = self.__get_exec_ip_list(source_cluster_data, target_clusters_data) + + sub_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(self.data)) + + # tenDBCluster集群需要给spider下发act + if source_cluster_data["cluster_type"] == ClusterType.TenDBCluster.value: + sub_pipeline.add_act( + act_name=_("下发db-actuator介质"), + act_component_code=TransFileComponent.code, + kwargs=asdict( + DownloadMediaKwargs( + bk_cloud_id=source_cluster_data["bk_cloud_id"], + exec_ip=exec_ip_list, + file_list=GetFileList(db_type=DBType.MySQL).get_db_actuator_package(), + ) + ), + ) + sub_pipeline.add_act( + act_name=_("从源实例获取开区所需库表数据"), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict( + ExecActuatorKwargs( + bk_cloud_id=source_cluster_data["bk_cloud_id"], + cluster_type=source_cluster_data["cluster_type"], + cluster=source_cluster_data, + exec_ip=source_cluster_data["ip"], + get_mysql_payload_func=MysqlActPayload.get_open_area_dump_data_payload.__name__, + ) + ), + ) + + # 本地开区,没有上传制品库,也不用下发 + if source_cluster_data["is_upload_bkrepo"]: + # 目标集群下发库表文件,源集群不用下发 + exec_ip_list.remove(source_cluster_data["ip"]) + sub_pipeline.add_act( + act_name=_("下发开区库表数据文件"), + act_component_code=TransFileComponent.code, + kwargs=asdict( + DownloadMediaKwargs( + bk_cloud_id=0, + exec_ip=exec_ip_list, + file_target_path=self.work_dir, + file_list=GetFileList(db_type=DBType.MySQL).mysql_import_sqlfile( + path="mysql/sqlfile", filelist=[self.data_tar_file_name, self.data_md5sum_file_name] + ), + ) + ), + ) + + acts_list = [] + for target_cluster in target_clusters_data: + acts_list.append( + { + "act_name": _("向目标实例导入库表数据"), + "act_component_code": ExecuteDBActuatorScriptComponent.code, + "kwargs": asdict( + ExecActuatorKwargs( + bk_cloud_id=target_cluster["bk_cloud_id"], + cluster_type=target_cluster["cluster_type"], + cluster=target_cluster, + exec_ip=target_cluster["ip"], + get_mysql_payload_func=MysqlActPayload.get_open_area_import_data_payload.__name__, + ) + ), + } + ) + sub_pipeline.add_parallel_acts(acts_list=acts_list) + + return sub_pipeline.build_sub_process(sub_name=_("开区数据迁移流程")) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_partition.py b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_partition.py index 55f7d8fc24..94756d22f4 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_partition.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_partition.py @@ -10,10 +10,10 @@ from backend.configuration.constants import DBType from backend.core.consts import BK_PKG_INSTALL_PATH -from backend.db_meta.enums import ClusterType, InstanceRole +from backend.db_meta.enums import InstanceRole from backend.db_meta.exceptions import ClusterNotExistException, MasterInstanceNotExistException from backend.db_meta.models import Cluster, StorageInstance -from backend.flow.consts import DBA_SYSTEM_USER +from backend.flow.consts import DBA_ROOT_USER from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList from backend.flow.plugins.components.collections.mysql.exec_actuator_script import ExecuteDBActuatorScriptComponent @@ -136,7 +136,7 @@ def mysql_partition_flow(self): ExecActuatorKwargs( exec_ip=ip, bk_cloud_id=bk_cloud_id, - run_as_system_user=DBA_SYSTEM_USER, + run_as_system_user=DBA_ROOT_USER, get_mysql_payload_func=MysqlActPayload.get_partition_payload.__name__, cluster=cluster, ) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_rollback_data_flow.py b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_rollback_data_flow.py index 445b757f64..af7bc7786e 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_rollback_data_flow.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_rollback_data_flow.py @@ -9,56 +9,36 @@ specific language governing permissions and limitations under the License. """ import copy -import datetime import logging.config -from dataclasses import asdict from typing import Dict, Optional from django.utils.translation import ugettext as _ -from backend.configuration.constants import DBType -from backend.db_services.mysql.fixpoint_rollback.handlers import FixPointRollbackHandler from backend.flow.consts import RollbackType from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder -from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList -from backend.flow.plugins.components.collections.common.pause import PauseComponent -from backend.flow.plugins.components.collections.mysql.clear_machine import MySQLClearMachineComponent -from backend.flow.plugins.components.collections.mysql.exec_actuator_script import ExecuteDBActuatorScriptComponent -from backend.flow.plugins.components.collections.mysql.mysql_db_meta import MySQLDBMetaComponent -from backend.flow.plugins.components.collections.mysql.mysql_download_backupfile import ( - MySQLDownloadBackupfileComponent, -) -from backend.flow.plugins.components.collections.mysql.rollback_local_trans_flies import ( - RollBackLocalTransFileComponent, +from backend.flow.engine.bamboo.scene.mysql.common.exceptions import NormalTenDBFlowException +from backend.flow.engine.bamboo.scene.mysql.mysql_rollback_data_sub_flow import ( + install_instance_sub_flow, + rollback_local_and_backupid, + rollback_local_and_time, + rollback_remote_and_backupid, + rollback_remote_and_time, + uninstall_instance_sub_flow, ) -from backend.flow.plugins.components.collections.mysql.rollback_trans_flies import RollBackTransFileComponent -from backend.flow.plugins.components.collections.mysql.trans_flies import TransFileComponent +from backend.flow.plugins.components.collections.common.pause import PauseComponent from backend.flow.utils.mysql.common.mysql_cluster_info import ( get_cluster_info, get_cluster_ports, get_version_and_charset, ) -from backend.flow.utils.mysql.mysql_act_dataclass import ( - DBMetaOPKwargs, - DownloadBackupFileKwargs, - DownloadMediaKwargs, - ExecActuatorKwargs, - RollBackTransFileKwargs, -) -from backend.flow.utils.mysql.mysql_act_playload import MysqlActPayload from backend.flow.utils.mysql.mysql_context_dataclass import ClusterInfoContext -from backend.flow.utils.mysql.mysql_db_meta import MySQLDBMeta -from backend.utils import time logger = logging.getLogger("flow") class MySQLRollbackDataFlow(object): """ - mysql 重建slave流程 - 元数据修改: - 1 mysql_restore_slave_add_instance - 2 mysql_rollback_remove_instance + mysql 定点回档 """ def __init__(self, root_id: str, data: Optional[Dict]): @@ -77,26 +57,29 @@ def rollback_data_flow(self): sub_pipeline_list = [] for info in self.data["infos"]: # 根据ip级别安装mysql实例 - ticket_data = copy.deepcopy(self.data) cluster_ports = get_cluster_ports([info["cluster_id"]]) info.update(cluster_ports) - one_cluster = get_cluster_info(info["cluster_id"]) charset, db_version = get_version_and_charset( self.data["bk_biz_id"], db_module_id=info["db_module_id"], cluster_type=info["cluster_type"], ) - info["new_slave_ip"] = info["rollback_ip"] + + ticket_data = copy.deepcopy(self.data) ticket_data["clusters"] = info["clusters"] ticket_data["mysql_ports"] = info["cluster_ports"] ticket_data["charset"] = charset ticket_data["db_version"] = db_version + # 用于兼容 restore slave payload + info["new_slave_ip"] = info["rollback_ip"] + info["db_version"] = db_version sub_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(ticket_data)) sub_pipeline.add_sub_pipeline( - sub_flow=self.install_instance_sub_flow(ticket_data=ticket_data, cluster_info=info) + sub_flow=install_instance_sub_flow(ticket_data=ticket_data, cluster_info=info) ) + one_cluster = get_cluster_info(info["cluster_id"]) one_cluster["rollback_ip"] = info["rollback_ip"] one_cluster["databases"] = info["databases"] one_cluster["tables"] = info["tables"] @@ -105,318 +88,58 @@ def rollback_data_flow(self): one_cluster["backend_port"] = one_cluster["master_port"] one_cluster["charset"] = charset one_cluster["change_master"] = False - one_cluster["diretory"] = f"/data/dbbak/{self.root_id}" - - one_cluster["file_target_path"] = one_cluster["diretory"] + directory = "/data/dbbak/{}/{}".format(self.root_id, one_cluster["master_port"]) + one_cluster["file_target_path"] = directory one_cluster["skip_local_exists"] = True - one_cluster["name_regex"] = f"^.+{one_cluster['master_port']}\\.\\d+(\\..*)*$" + one_cluster["name_regex"] = "^.+{}\\.\\d+(\\..*)*$".format(one_cluster["master_port"]) one_cluster["rollback_time"] = info["rollback_time"] - # ??? - one_cluster["new_master_ip"] = one_cluster["master_ip"] - one_cluster["new_slave_ip"] = info["new_slave_ip"] + # one_cluster["new_master_ip"] = one_cluster["master_ip"] + # one_cluster["new_slave_ip"] = info["rollback_ip"] + one_cluster["rollback_ip"] = info["rollback_ip"] + one_cluster["rollback_port"] = one_cluster["master_port"] + one_cluster["rollback_time"] = info["rollback_time"] + one_cluster["backupinfo"] = info["backupinfo"] + one_cluster["rollback_type"] = info["rollback_type"] - # 拼接执行原子任务活动节点需要的通用的私有参数结构体, 减少代码重复率,但引用时注意内部参数值传递的问题 - exec_act_kwargs = ExecActuatorKwargs( - bk_cloud_id=one_cluster["bk_cloud_id"], - cluster_type=one_cluster["cluster_type"], - cluster=one_cluster, - ) - # === 本地备份文件+时间点恢复 === + # 本地备份+时间 if info["rollback_type"] == RollbackType.LOCAL_AND_TIME: - # rollback_time = time.strptime(info["rollback_time"], "%Y-%m-%d %H:%M:%S") - one_cluster["rollback_time"] = info["rollback_time"] - sub_pipeline.add_act( - act_name=_("下发db_actuator介质"), - act_component_code=TransFileComponent.code, - kwargs=asdict( - DownloadMediaKwargs( - bk_cloud_id=one_cluster["bk_cloud_id"], - exec_ip=[one_cluster["master_ip"], one_cluster["old_slave_ip"]], - file_list=GetFileList(db_type=DBType.MySQL).get_db_actuator_package(), - ) - ), - ) - - exec_act_kwargs.exec_ip = one_cluster["master_ip"] - exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_find_local_backup_payload.__name__ - sub_pipeline.add_act( - act_name=_("定点恢复之获取MASTER节点备份介质{}").format(exec_act_kwargs.exec_ip), - act_component_code=ExecuteDBActuatorScriptComponent.code, - kwargs=asdict(exec_act_kwargs), - write_payload_var="master_backup_file", - ) - - exec_act_kwargs.exec_ip = one_cluster["old_slave_ip"] - exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_find_local_backup_payload.__name__ - sub_pipeline.add_act( - act_name=_("定点恢复之获取SLAVE节点备份介质{}").format(exec_act_kwargs.exec_ip), - act_component_code=ExecuteDBActuatorScriptComponent.code, - kwargs=asdict(exec_act_kwargs), - write_payload_var="slave_backup_file", - ) - - sub_pipeline.add_act( - act_name=_("判断备份文件来源,并传输备份文件到新定点恢复节点{}").format(info["rollback_ip"]), - act_component_code=RollBackTransFileComponent.code, - kwargs=asdict( - RollBackTransFileKwargs( - bk_cloud_id=one_cluster["bk_cloud_id"], - file_list=[], - file_target_path=one_cluster["file_target_path"], - source_ip_list=[], - exec_ip=info["rollback_ip"], - cluster=one_cluster, - ) - ), - ) - - exec_act_kwargs.exec_ip = info["rollback_ip"] - exec_act_kwargs.get_mysql_payload_func = ( - MysqlActPayload.get_rollback_local_data_restore_payload.__name__ - ) - sub_pipeline.add_act( - act_name=_("定点恢复之恢复数据{}").format(exec_act_kwargs.exec_ip), - act_component_code=ExecuteDBActuatorScriptComponent.code, - kwargs=asdict(exec_act_kwargs), - write_payload_var="change_master_info", - ) - # todo 需添加binlog恢复流程 - - # === 以下为远程备份+时间/备份ID === - backupinfo = info["backupinfo"] - if info["rollback_type"] == RollbackType.REMOTE_AND_TIME.value: - rollback_time = time.strptime(info["rollback_time"], "%Y-%m-%d %H:%M:%S") - rollback_handler = FixPointRollbackHandler(one_cluster["cluster_id"]) - # 查询接口 - backupinfo = rollback_handler.query_latest_backup_log(rollback_time) - one_cluster["master_ip"] = backupinfo["mysql_host"] - one_cluster["backup_time"] = backupinfo["backup_begin_time"] - one_cluster["total_backupinfo"] = copy.deepcopy(backupinfo) - rollback_handler.query_backup_log_from_bklog() - - if ( - info["rollback_type"] == RollbackType.REMOTE_AND_TIME.value - or info["rollback_type"] == RollbackType.REMOTE_AND_BACKUPID.value - ): - task_files = [{"file_name": i} for i in backupinfo["file_list"]] - one_cluster["task_files"] = task_files - # 区分 - one_cluster["master_ip"] = backupinfo["mysql_host"] - one_cluster["backup_time"] = backupinfo["backup_begin_time"] - - # 用于下载 - # 用于日志恢复、日志恢复前后增加半个小时来获取恢复binlog - backup_time = time.strptime(backupinfo["backup_begin_time"], "%Y-%m-%d %H:%M:%S") - one_cluster["begin_time"] = (backup_time - datetime.timedelta(days=1)).strftime("%Y-%m-%d %H:%M:%S") - one_cluster["end_time"] = (backup_time + datetime.timedelta(days=1)).strftime("%Y-%m-%d %H:%M:%S") - one_cluster["total_backupinfo"] = copy.deepcopy(backupinfo) - - exec_act_kwargs.cluster = one_cluster - task_ids = [i["task_id"] for i in backupinfo["file_list_details"]] - download_kwargs = DownloadBackupFileKwargs( - bk_cloud_id=one_cluster["bk_cloud_id"], - task_ids=task_ids, - dest_ip=info["rollback_ip"], - login_user="mysql", - desc_dir=one_cluster["file_target_path"], - reason="mysql rollback data", - ) - sub_pipeline.add_act( - act_name=_("下载定点恢复的全库备份介质到{}").format(info["rollback_ip"]), - act_component_code=MySQLDownloadBackupfileComponent.code, - kwargs=asdict(download_kwargs), + sub_pipeline.add_sub_pipeline( + sub_flow=rollback_local_and_time( + root_id=self.root_id, ticket_data=copy.deepcopy(self.data), cluster_info=one_cluster + ) ) - exec_act_kwargs.exec_ip = info["rollback_ip"] - exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_rollback_data_restore_payload.__name__ - sub_pipeline.add_act( - act_name=_("定点恢复之恢复数据{}").format(exec_act_kwargs.exec_ip), - act_component_code=ExecuteDBActuatorScriptComponent.code, - kwargs=asdict(exec_act_kwargs), - write_payload_var="change_master_info", - ) - # 远程备份+时间需要binlog前滚 - if info["rollback_type"] == RollbackType.REMOTE_AND_TIME.value: - rollback_time = time.strptime(info["rollback_time"], "%Y-%m-%d %H:%M:%S") - one_cluster["binlog_begin_time"] = (backup_time - datetime.timedelta(minutes=30)).strftime( - "%Y-%m-%d %H:%M:%S" + # 远程备份+时间 + elif info["rollback_type"] == RollbackType.REMOTE_AND_TIME.value: + sub_pipeline.add_sub_pipeline( + sub_flow=rollback_remote_and_time( + root_id=self.root_id, ticket_data=copy.deepcopy(self.data), cluster_info=one_cluster ) - one_cluster["binlog_end_time"] = (rollback_time + datetime.timedelta(minutes=30)).strftime( - "%Y-%m-%d %H:%M:%S" + ) + # 远程备份+备份ID + elif info["rollback_type"] == RollbackType.REMOTE_AND_BACKUPID.value: + sub_pipeline.add_sub_pipeline( + sub_flow=rollback_remote_and_backupid( + root_id=self.root_id, ticket_data=copy.deepcopy(self.data), cluster_info=one_cluster ) - exec_act_kwargs.exec_ip = info["rollback_ip"] - - # todo 需添加binlog恢复流程 - # exec_act_kwargs.get_mysql_payload_func = ( - # MysqlActPayload.get_rollback_data_download_binlog_payload.__name__ - # ) - # sub_pipeline.add_act( - # act_name=_("下载定点恢复的binlog文件到{}").format(exec_act_kwargs.exec_ip), - # act_component_code=ExecuteDBActuatorScriptComponent.code, - # kwargs=asdict(exec_act_kwargs), - # write_payload_var="binlog_files", - # ) - # exec_act_kwargs.exec_ip = info["rollback_ip"] - # exec_act_kwargs.get_mysql_payload_func = ( - # MysqlActPayload.get_rollback_data_recover_binlog_payload.__name__ - # ) - # sub_pipeline.add_act( - # act_name=_("定点恢复之前滚binlog{}").format(exec_act_kwargs.exec_ip), - # act_component_code=ExecuteDBActuatorScriptComponent.code, - # kwargs=asdict(exec_act_kwargs), - # ) - - # === 本地备份+备份ID == - elif info["rollback_type"] == RollbackType.LOCAL_AND_BACKUPID: - backupinfo = info["backupinfo"] - backupinfo["backup_begin_time"] = backupinfo["backup_time"] - backupinfo["mysql_host"] = backupinfo["inst_host"] - backupinfo["mysql_port"] = backupinfo["inst_port"] - # one_cluster["master_ip"] = backupinfo["inst_host"] - one_cluster["total_backupinfo"] = copy.deepcopy(backupinfo) - - exec_act_kwargs.exec_ip = info["rollback_ip"] - exec_act_kwargs.cluster = one_cluster - - sub_pipeline.add_act( - act_name=_("传输文件{}").format(info["rollback_ip"]), - act_component_code=RollBackLocalTransFileComponent.code, - kwargs=asdict( - RollBackTransFileKwargs( - bk_cloud_id=one_cluster["bk_cloud_id"], - file_list=[], - file_target_path=one_cluster["file_target_path"], - source_ip_list=[], - exec_ip=info["rollback_ip"], - cluster=one_cluster, - ) - ), ) - exec_act_kwargs.exec_ip = info["rollback_ip"] - exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_rollback_data_restore_payload.__name__ - sub_pipeline.add_act( - act_name=_("定点恢复之恢复数据{}").format(exec_act_kwargs.exec_ip), - act_component_code=ExecuteDBActuatorScriptComponent.code, - kwargs=asdict(exec_act_kwargs), - write_payload_var="change_master_info", + # 本地备份+备份ID + elif info["rollback_type"] == RollbackType.LOCAL_AND_BACKUPID: + sub_pipeline.add_sub_pipeline( + sub_flow=rollback_local_and_backupid( + root_id=self.root_id, ticket_data=copy.deepcopy(self.data), cluster_info=one_cluster + ) ) + else: + raise NormalTenDBFlowException(message=_("rollback_type不存在")) # 设置暂停。接下来卸载数据库的流程 sub_pipeline.add_act(act_name=_("人工确认"), act_component_code=PauseComponent.code, kwargs={}) sub_pipeline.add_sub_pipeline( - sub_flow=self.uninstall_instance_sub_flow(ticket_data=ticket_data, cluster_info=one_cluster) + sub_flow=uninstall_instance_sub_flow(ticket_data=ticket_data, cluster_info=one_cluster) ) sub_pipeline_list.append(sub_pipeline.build_sub_process(sub_name=_("定点恢复"))) mysql_restore_slave_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipeline_list) mysql_restore_slave_pipeline.run_pipeline(init_trans_data_class=ClusterInfoContext()) - - # 实例安装子流程 - def install_instance_sub_flow(self, ticket_data: dict, cluster_info: dict): - - install_sub_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(ticket_data)) - - # 拼接执行原子任务活动节点需要的通用的私有参数结构体, 减少代码重复率,但引用时注意内部参数值传递的问题 - exec_act_kwargs = ExecActuatorKwargs( - exec_ip=cluster_info["rollback_ip"], - cluster_type=cluster_info["cluster_type"], - bk_cloud_id=int(cluster_info["bk_cloud_id"]), - ) - - install_sub_pipeline.add_act( - act_name=_("下发MySQL介质"), - act_component_code=TransFileComponent.code, - kwargs=asdict( - DownloadMediaKwargs( - bk_cloud_id=int(cluster_info["bk_cloud_id"]), - exec_ip=cluster_info["rollback_ip"], - file_list=GetFileList(db_type=DBType.MySQL).mysql_install_package( - db_version=ticket_data["db_version"] - ), - ) - ), - ) - - exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_sys_init_payload.__name__ - install_sub_pipeline.add_act( - act_name=_("初始化机器"), - act_component_code=ExecuteDBActuatorScriptComponent.code, - kwargs=asdict(exec_act_kwargs), - ) - - exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_deploy_mysql_crond_payload.__name__ - install_sub_pipeline.add_act( - act_name=_("部署mysql-crond"), - act_component_code=ExecuteDBActuatorScriptComponent.code, - kwargs=asdict(exec_act_kwargs), - ) - - exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_install_mysql_payload.__name__ - install_sub_pipeline.add_act( - act_name=_("安装MySQL实例"), - act_component_code=ExecuteDBActuatorScriptComponent.code, - kwargs=asdict(exec_act_kwargs), - ) - - # 安装完毕实例,写入元数据 - install_sub_pipeline.add_act( - act_name=_("写入初始化实例的db_meta元信息"), - act_component_code=MySQLDBMetaComponent.code, - kwargs=asdict( - DBMetaOPKwargs( - db_meta_class_func=MySQLDBMeta.mysql_restore_slave_add_instance.__name__, - cluster=cluster_info, - is_update_trans_data=True, - ) - ), - ) - - exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_install_restore_backup_payload.__name__ - install_sub_pipeline.add_act( - act_name=_("安装备份程序"), - act_component_code=ExecuteDBActuatorScriptComponent.code, - kwargs=asdict(exec_act_kwargs), - ) - return install_sub_pipeline.build_sub_process(sub_name=_("安装实例flow")) - - # 实例卸载子流程 - def uninstall_instance_sub_flow(self, ticket_data: dict, cluster_info: dict): - sub_ticket_data = copy.deepcopy(ticket_data) - sub_ticket_data["force"] = True - uninstall_sub_pipeline = SubBuilder(root_id=self.root_id, data=sub_ticket_data) - - # 拼接执行原子任务活动节点需要的通用的私有参数结构体, 减少代码重复率,但引用时注意内部参数值传递的问题 - exec_act_kwargs = ExecActuatorKwargs( - exec_ip=cluster_info["rollback_ip"], - bk_cloud_id=int(cluster_info["bk_cloud_id"]), - cluster=cluster_info, - ) - - exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_uninstall_mysql_payload.__name__ - uninstall_sub_pipeline.add_act( - act_name=_("卸载rollback实例{}").format(exec_act_kwargs.exec_ip), - act_component_code=ExecuteDBActuatorScriptComponent.code, - kwargs=asdict(exec_act_kwargs), - ) - # 删除rollback_ip对应的集群实例 - uninstall_sub_pipeline.add_act( - act_name=_("卸载rollback实例完毕,修改元数据"), - act_component_code=MySQLDBMetaComponent.code, - kwargs=asdict( - DBMetaOPKwargs( - db_meta_class_func=MySQLDBMeta.mysql_rollback_remove_instance.__name__, - cluster=cluster_info, - is_update_trans_data=True, - ) - ), - ) - - exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_clear_machine_crontab.__name__ - uninstall_sub_pipeline.add_act( - act_name=_("清理机器配置{}").format(exec_act_kwargs.exec_ip), - act_component_code=MySQLClearMachineComponent.code, - kwargs=asdict(exec_act_kwargs), - ) - - return uninstall_sub_pipeline.build_sub_process(sub_name=_("清理机器flow")) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_rollback_data_sub_flow.py b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_rollback_data_sub_flow.py new file mode 100644 index 0000000000..4ee855064a --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_rollback_data_sub_flow.py @@ -0,0 +1,437 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import copy +import logging.config +from dataclasses import asdict + +from django.utils.translation import ugettext as _ + +from backend.configuration.constants import DBType +from backend.db_services.mysql.fixpoint_rollback.handlers import FixPointRollbackHandler +from backend.flow.engine.bamboo.scene.common.builder import SubBuilder +from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList +from backend.flow.engine.bamboo.scene.mysql.common.exceptions import TenDBGetBackupInfoFailedException +from backend.flow.plugins.components.collections.mysql.clear_machine import MySQLClearMachineComponent +from backend.flow.plugins.components.collections.mysql.exec_actuator_script import ExecuteDBActuatorScriptComponent +from backend.flow.plugins.components.collections.mysql.mysql_db_meta import MySQLDBMetaComponent +from backend.flow.plugins.components.collections.mysql.mysql_download_backupfile import ( + MySQLDownloadBackupfileComponent, +) +from backend.flow.plugins.components.collections.mysql.mysql_rollback_data_download_binlog import ( + MySQLRollbackDownloadBinlog, +) +from backend.flow.plugins.components.collections.mysql.rollback_local_trans_flies import ( + RollBackLocalTransFileComponent, +) +from backend.flow.plugins.components.collections.mysql.rollback_trans_flies import RollBackTransFileComponent +from backend.flow.plugins.components.collections.mysql.trans_flies import TransFileComponent +from backend.flow.utils.mysql.mysql_act_dataclass import ( + DBMetaOPKwargs, + DownloadBackupFileKwargs, + DownloadMediaKwargs, + ExecActuatorKwargs, + RollBackTransFileKwargs, +) +from backend.flow.utils.mysql.mysql_act_playload import MysqlActPayload +from backend.flow.utils.mysql.mysql_db_meta import MySQLDBMeta +from backend.utils import time + +logger = logging.getLogger("flow") + + +def install_instance_sub_flow(root_id: str, ticket_data: dict, cluster_info: dict): + """ + mysql 定点回档安装实例 + @param root_id: flow 流程root_id + @param ticket_data: 关联单据 ticket对象 + @param cluster_info: 关联的cluster对象 + """ + install_sub_pipeline = SubBuilder(root_id=root_id, data=copy.deepcopy(ticket_data)) + + # 拼接执行原子任务活动节点需要的通用的私有参数结构体, 减少代码重复率,但引用时注意内部参数值传递的问题 + exec_act_kwargs = ExecActuatorKwargs( + exec_ip=cluster_info["rollback_ip"], + cluster_type=cluster_info["cluster_type"], + bk_cloud_id=int(cluster_info["bk_cloud_id"]), + ) + + install_sub_pipeline.add_act( + act_name=_("下发MySQL介质"), + act_component_code=TransFileComponent.code, + kwargs=asdict( + DownloadMediaKwargs( + bk_cloud_id=int(cluster_info["bk_cloud_id"]), + exec_ip=cluster_info["rollback_ip"], + file_list=GetFileList(db_type=DBType.MySQL).mysql_install_package( + db_version=cluster_info["db_version"] + ), + ) + ), + ) + + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_sys_init_payload.__name__ + install_sub_pipeline.add_act( + act_name=_("初始化机器"), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(exec_act_kwargs), + ) + + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_deploy_mysql_crond_payload.__name__ + install_sub_pipeline.add_act( + act_name=_("部署mysql-crond"), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(exec_act_kwargs), + ) + + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_install_mysql_payload.__name__ + install_sub_pipeline.add_act( + act_name=_("安装MySQL实例"), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(exec_act_kwargs), + ) + + # 安装完毕实例,写入元数据 + install_sub_pipeline.add_act( + act_name=_("写入初始化实例的db_meta元信息"), + act_component_code=MySQLDBMetaComponent.code, + kwargs=asdict( + DBMetaOPKwargs( + db_meta_class_func=MySQLDBMeta.mysql_restore_slave_add_instance.__name__, + cluster=cluster_info, + is_update_trans_data=True, + ) + ), + ) + + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_install_restore_backup_payload.__name__ + install_sub_pipeline.add_act( + act_name=_("安装备份程序"), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(exec_act_kwargs), + ) + return install_sub_pipeline.build_sub_process(sub_name=_("安装实例flow")) + + +def uninstall_instance_sub_flow(root_id: str, ticket_data: dict, cluster_info: dict): + """ + mysql 定点回档卸载实例 + @param root_id: flow 流程root_id + @param ticket_data: 关联单据 ticket对象 + @param cluster_info: 关联的cluster对象 + """ + sub_ticket_data = copy.deepcopy(ticket_data) + sub_ticket_data["force"] = True + uninstall_sub_pipeline = SubBuilder(root_id=root_id, data=sub_ticket_data) + + # 拼接执行原子任务活动节点需要的通用的私有参数结构体, 减少代码重复率,但引用时注意内部参数值传递的问题 + exec_act_kwargs = ExecActuatorKwargs( + exec_ip=cluster_info["rollback_ip"], + bk_cloud_id=int(cluster_info["bk_cloud_id"]), + cluster=cluster_info, + ) + + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_uninstall_mysql_payload.__name__ + uninstall_sub_pipeline.add_act( + act_name=_("卸载rollback实例{}").format(exec_act_kwargs.exec_ip), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(exec_act_kwargs), + ) + # 删除rollback_ip对应的集群实例 + uninstall_sub_pipeline.add_act( + act_name=_("卸载rollback实例完毕,修改元数据"), + act_component_code=MySQLDBMetaComponent.code, + kwargs=asdict( + DBMetaOPKwargs( + db_meta_class_func=MySQLDBMeta.mysql_rollback_remove_instance.__name__, + cluster=cluster_info, + is_update_trans_data=True, + ) + ), + ) + + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_clear_machine_crontab.__name__ + uninstall_sub_pipeline.add_act( + act_name=_("清理机器配置{}").format(exec_act_kwargs.exec_ip), + act_component_code=MySQLClearMachineComponent.code, + kwargs=asdict(exec_act_kwargs), + ) + + return uninstall_sub_pipeline.build_sub_process(sub_name=_("清理机器flow")) + + +def rollback_local_and_time(root_id: str, ticket_data: dict, cluster_info: dict): + """ + mysql 定点回档类型 本地备份+指定时间 + @param root_id: flow 流程root_id + @param ticket_data: 关联单据 ticket对象 + @param cluster_info: 关联的cluster对象 + """ + sub_pipeline = SubBuilder(root_id=root_id, data=copy.deepcopy(ticket_data)) + sub_pipeline.add_act( + act_name=_("下发db_actuator介质"), + act_component_code=TransFileComponent.code, + kwargs=asdict( + DownloadMediaKwargs( + bk_cloud_id=cluster_info["bk_cloud_id"], + exec_ip=[cluster_info["master_ip"], cluster_info["old_slave_ip"]], + file_list=GetFileList(db_type=DBType.MySQL).get_db_actuator_package(), + ) + ), + ) + exec_act_kwargs = ExecActuatorKwargs( + bk_cloud_id=cluster_info["bk_cloud_id"], + cluster_type=cluster_info["cluster_type"], + cluster=cluster_info, + ) + exec_act_kwargs.exec_ip = cluster_info["master_ip"] + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_find_local_backup_payload.__name__ + sub_pipeline.add_act( + act_name=_("定点恢复之获取MASTER节点备份介质{}").format(exec_act_kwargs.exec_ip), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(exec_act_kwargs), + write_payload_var="master_backup_file", + ) + + exec_act_kwargs.exec_ip = cluster_info["old_slave_ip"] + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_find_local_backup_payload.__name__ + sub_pipeline.add_act( + act_name=_("定点恢复之获取SLAVE节点备份介质{}").format(exec_act_kwargs.exec_ip), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(exec_act_kwargs), + write_payload_var="slave_backup_file", + ) + + sub_pipeline.add_act( + act_name=_("判断备份文件来源,并传输备份文件到新定点恢复节点{}").format(cluster_info["rollback_ip"]), + act_component_code=RollBackTransFileComponent.code, + kwargs=asdict( + RollBackTransFileKwargs( + bk_cloud_id=cluster_info["bk_cloud_id"], + file_list=[], + file_target_path=cluster_info["file_target_path"], + source_ip_list=[], + exec_ip=cluster_info["rollback_ip"], + cluster=cluster_info, + ) + ), + ) + + exec_act_kwargs.exec_ip = cluster_info["rollback_ip"] + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_rollback_data_restore_payload.__name__ + sub_pipeline.add_act( + act_name=_("定点恢复之恢复数据{}").format(exec_act_kwargs.exec_ip), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(exec_act_kwargs), + write_payload_var="change_master_info", + ) + + # backup_time 在活动节点里。到flow下载binlog + download_kwargs = DownloadBackupFileKwargs( + bk_cloud_id=cluster_info["bk_cloud_id"], + task_ids=[], + dest_ip=cluster_info["rollback_ip"], + desc_dir=cluster_info["file_target_path"], + reason="spider node rollback binlog", + cluster=cluster_info, + ) + sub_pipeline.add_act( + act_name=_("下载定点恢复的binlog到{}").format(cluster_info["rollback_ip"]), + act_component_code=MySQLRollbackDownloadBinlog.code, + kwargs=asdict(download_kwargs), + ) + + exec_act_kwargs.exec_ip = cluster_info["rollback_ip"] + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.tendb_recover_binlog_payload.__name__ + sub_pipeline.add_act( + act_name=_("定点恢复之前滚binlog{}".format(exec_act_kwargs.exec_ip)), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(exec_act_kwargs), + ) + return sub_pipeline.build_sub_process(sub_name=_("{}定点回滚数据 LOCAL_AND_TIME ".format(cluster_info["rollback_ip"]))) + + +def rollback_remote_and_time(root_id: str, ticket_data: dict, cluster_info: dict): + """ + mysql 定点回档类型 远程备份文件+指定时间 + @param root_id: flow 流程root_id + @param ticket_data: 关联单据 ticket对象 + @param cluster_info: 关联的cluster对象 + """ + sub_pipeline = SubBuilder(root_id=root_id, data=copy.deepcopy(ticket_data)) + rollback_time = time.strptime(cluster_info["rollback_time"], "%Y-%m-%d %H:%M:%S") + rollback_handler = FixPointRollbackHandler(cluster_info["cluster_id"]) + # 查询接口 + backupinfo = rollback_handler.query_latest_backup_log(rollback_time) + cluster_info["backupinfo"] = copy.deepcopy(backupinfo) + cluster_info["backup_time"] = backupinfo["backup_time"] + + task_files = [{"file_name": i} for i in backupinfo["file_list"]] + cluster_info["task_files"] = task_files + cluster_info["backup_time"] = backupinfo["backup_begin_time"] + + exec_act_kwargs = ExecActuatorKwargs( + bk_cloud_id=cluster_info["bk_cloud_id"], + cluster_type=cluster_info["cluster_type"], + cluster=cluster_info, + ) + + exec_act_kwargs.cluster = cluster_info + task_ids = [i["task_id"] for i in backupinfo["file_list_details"]] + download_kwargs = DownloadBackupFileKwargs( + bk_cloud_id=cluster_info["bk_cloud_id"], + task_ids=task_ids, + dest_ip=cluster_info["rollback_ip"], + desc_dir=cluster_info["file_target_path"], + reason="mysql rollback data", + ) + sub_pipeline.add_act( + act_name=_("下载定点恢复的全库备份介质到{}").format(cluster_info["rollback_ip"]), + act_component_code=MySQLDownloadBackupfileComponent.code, + kwargs=asdict(download_kwargs), + ) + + exec_act_kwargs.exec_ip = cluster_info["rollback_ip"] + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_rollback_data_restore_payload.__name__ + sub_pipeline.add_act( + act_name=_("定点恢复之恢复数据{}").format(exec_act_kwargs.exec_ip), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(exec_act_kwargs), + write_payload_var="change_master_info", + ) + backup_time = time.strptime(backupinfo["backup_time"], "%Y-%m-%d %H:%M:%S") + rollback_time = time.strptime(cluster_info["rollback_time"], "%Y-%m-%d %H:%M:%S") + rollback_handler = FixPointRollbackHandler(cluster_info["cluster_id"]) + backup_binlog = rollback_handler.query_binlog_from_bklog( + start_time=backup_time, + end_time=rollback_time, + minute_range=30, + host_ip=cluster_info["master_ip"], + port=cluster_info["master_port"], + ) + if backup_binlog is None: + raise TenDBGetBackupInfoFailedException(message=_("获取实例 {} 的备份信息失败".format(cluster_info["master_ip"]))) + + task_ids = [i["task_id"] for i in backup_binlog["file_list_details"]] + binlog_files = [i["file_name"] for i in backup_binlog["file_list_details"]] + cluster_info["binlog_files"] = ",".join(binlog_files) + download_kwargs = DownloadBackupFileKwargs( + bk_cloud_id=cluster_info["bk_cloud_id"], + task_ids=task_ids, + dest_ip=cluster_info["rollback_ip"], + desc_dir=cluster_info["file_target_path"], + reason="spider node rollback binlog", + ) + sub_pipeline.add_act( + act_name=_("下载定点恢复的binlog到{}").format(cluster_info["rollback_ip"]), + act_component_code=MySQLDownloadBackupfileComponent.code, + kwargs=asdict(download_kwargs), + ) + exec_act_kwargs.exec_ip = cluster_info["rollback_ip"] + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.tendb_recover_binlog_payload.__name__ + sub_pipeline.add_act( + act_name=_("定点恢复之前滚binlog{}".format(exec_act_kwargs.exec_ip)), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(exec_act_kwargs), + ) + return sub_pipeline.build_sub_process(sub_name=_("{}定点回滚数据 REMOTE_AND_TIME ".format(cluster_info["rollback_ip"]))) + + +def rollback_remote_and_backupid(root_id: str, ticket_data: dict, cluster_info: dict): + """ + mysql 定点回档类型 远程备份+指定备份文件 + @param root_id: flow 流程root_id + @param ticket_data: 关联单据 ticket对象 + @param cluster_info: 关联的cluster对象 + """ + sub_pipeline = SubBuilder(root_id=root_id, data=copy.deepcopy(ticket_data)) + backupinfo = cluster_info["backupinfo"] + task_files = [{"file_name": i} for i in backupinfo["file_list"]] + cluster_info["task_files"] = task_files + cluster_info["backup_time"] = backupinfo["backup_begin_time"] + + exec_act_kwargs = ExecActuatorKwargs( + bk_cloud_id=cluster_info["bk_cloud_id"], + cluster_type=cluster_info["cluster_type"], + cluster=cluster_info, + ) + task_ids = [i["task_id"] for i in backupinfo["file_list_details"]] + download_kwargs = DownloadBackupFileKwargs( + bk_cloud_id=cluster_info["bk_cloud_id"], + task_ids=task_ids, + dest_ip=cluster_info["rollback_ip"], + desc_dir=cluster_info["file_target_path"], + reason="mysql rollback data", + ) + sub_pipeline.add_act( + act_name=_("下载定点恢复的全库备份介质到{}").format(cluster_info["rollback_ip"]), + act_component_code=MySQLDownloadBackupfileComponent.code, + kwargs=asdict(download_kwargs), + ) + + exec_act_kwargs.exec_ip = cluster_info["rollback_ip"] + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_rollback_data_restore_payload.__name__ + sub_pipeline.add_act( + act_name=_("定点恢复之恢复数据{}").format(exec_act_kwargs.exec_ip), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(exec_act_kwargs), + write_payload_var="change_master_info", + ) + return sub_pipeline.build_sub_process( + sub_name=_("{}定点回滚数据 REMOTE_AND_BACKUPID ".format(cluster_info["rollback_ip"])) + ) + + +def rollback_local_and_backupid(root_id: str, ticket_data: dict, cluster_info: dict): + """ + mysql 定点回档类型 本地备份+指定备份文件 + @param root_id: flow 流程root_id + @param ticket_data: 关联单据 ticket对象 + @param cluster_info: 关联的cluster对象 + """ + sub_pipeline = SubBuilder(root_id=root_id, data=copy.deepcopy(ticket_data)) + exec_act_kwargs = ExecActuatorKwargs( + bk_cloud_id=cluster_info["bk_cloud_id"], + cluster_type=cluster_info["cluster_type"], + cluster=cluster_info, + ) + + backupinfo = cluster_info["backupinfo"] + backupinfo["backup_begin_time"] = backupinfo["backup_time"] + + exec_act_kwargs.exec_ip = cluster_info["rollback_ip"] + exec_act_kwargs.cluster = cluster_info + task_ids = [i["task_id"] for i in backupinfo["file_list_details"]] + sub_pipeline.add_act( + act_name=_("传输文件{}").format(cluster_info["rollback_ip"]), + act_component_code=RollBackLocalTransFileComponent.code, + kwargs=asdict( + RollBackTransFileKwargs( + bk_cloud_id=cluster_info["bk_cloud_id"], + file_list=task_ids, + file_target_path=cluster_info["file_target_path"], + source_ip_list=[], + exec_ip=cluster_info["rollback_ip"], + cluster=cluster_info, + ) + ), + ) + + exec_act_kwargs.exec_ip = cluster_info["rollback_ip"] + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_rollback_data_restore_payload.__name__ + sub_pipeline.add_act( + act_name=_("定点恢复之恢复数据{}").format(exec_act_kwargs.exec_ip), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(exec_act_kwargs), + write_payload_var="change_master_info", + ) + return sub_pipeline.build_sub_process( + sub_name=_("{}定点回滚数据 LOCAL_AND_BACKUPID ".format(cluster_info["rollback_ip"])) + ) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_single_destroy_flow.py b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_single_destroy_flow.py index b602429788..b3df652ee0 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_single_destroy_flow.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_single_destroy_flow.py @@ -16,6 +16,7 @@ from backend.configuration.constants import DBType from backend.db_meta.enums import ClusterType +from backend.db_meta.exceptions import ClusterNotExistException from backend.db_meta.models import Cluster, StorageInstance from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList @@ -51,12 +52,17 @@ def __init__(self, root_id: str, data: Optional[Dict]): self.data = data @staticmethod - def __get_single_cluster_info(cluster_id: int) -> dict: + def __get_single_cluster_info(cluster_id: int, bk_biz_id: int) -> dict: """ 根据cluster_id 获取到单节点集群实例信息,单节点只有一个实例 @param cluster_id: 需要下架的集群id + @param bk_biz_id: 需要下架集群的对应的业务id """ - cluster = Cluster.objects.get(id=cluster_id) + try: + cluster = Cluster.objects.get(id=cluster_id, bk_biz_id=bk_biz_id) + except Cluster.DoesNotExist: + raise ClusterNotExistException(cluster_id=cluster_id, bk_biz_id=bk_biz_id, message=_("集群不存在")) + backend_info = StorageInstance.objects.get(cluster=cluster) return { "id": cluster_id, @@ -77,7 +83,7 @@ def destroy_mysql_single_flow(self): for cluster_id in self.data["cluster_ids"]: # 获取集群的实例信息 - cluster = self.__get_single_cluster_info(cluster_id=cluster_id) + cluster = self.__get_single_cluster_info(cluster_id=cluster_id, bk_biz_id=int(self.data["bk_biz_id"])) sub_pipeline = SubBuilder(root_id=self.root_id, data=self.data) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_truncate_flow.py b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_truncate_flow.py index 7f368dc39d..de572d6fa4 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_truncate_flow.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_truncate_flow.py @@ -73,7 +73,7 @@ def truncate_flow(self): "created_by": "xxx", "bk_biz_id": "152", "ticket_type": "[MYSQL_HA_TRUNCATE_DATA, MYSQL_SINGLE_TRUNCATE_DATA]", - "truncate_data_infos": [ + "infos": [ { "cluster_id": int, "db_patterns": ["db1%", "db2%"], diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/mysql/pt_table_sync.py b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/pt_table_sync.py index c0d0e8f336..60a9102c59 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/mysql/pt_table_sync.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/pt_table_sync.py @@ -61,6 +61,36 @@ def exec_pt_table_sync_flow(self): sync_account, sync_pwd = self.__get_sync_account() sub_pipelines = [] + # 这里优化场景,计算不一致的存储对的slave-ip,然后去重,避免阻塞异常 + target_ips = {} + for info in self.data["infos"]: + cluster = Cluster.objects.get(id=info["cluster_id"]) + for slave in info["slaves"]: + if not slave["is_consistent"]: + if cluster.bk_cloud_id not in target_ips: + target_ips[cluster.bk_cloud_id] = [] + target_ips[cluster.bk_cloud_id].append(slave["ip"]) + + acts_list = [] + for k, v in target_ips.items(): + acts_list.append( + { + "act_name": _("下发db-actuator介质"), + "act_component_code": TransFileComponent.code, + "kwargs": asdict( + DownloadMediaKwargs( + bk_cloud_id=k, + exec_ip=v, + file_list=GetFileList(db_type=DBType.MySQL).get_db_actuator_package(), + ) + ), + } + ) + if not acts_list: + raise Exception(_("修复单据找不到可修复的存储对")) + + table_sync_pipeline.add_parallel_acts(acts_list=acts_list) + for info in self.data["infos"]: # 拼接子流程需要全局参数 sub_flow_context = copy.deepcopy(self.data) @@ -84,18 +114,6 @@ def exec_pt_table_sync_flow(self): slave_sync_sub_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(slave_sync_global_data)) - slave_sync_sub_pipeline.add_act( - act_name=_("下发db-actuator介质"), - act_component_code=TransFileComponent.code, - kwargs=asdict( - DownloadMediaKwargs( - bk_cloud_id=cluster.bk_cloud_id, - exec_ip=slave["ip"], - file_list=GetFileList(db_type=DBType.MySQL).get_db_actuator_package(), - ) - ), - ) - slave_sync_sub_pipeline.add_act( act_name=_("执行数据修复"), act_component_code=PtTableSyncComponent.code, @@ -117,5 +135,8 @@ def exec_pt_table_sync_flow(self): sub_pipeline.add_parallel_sub_pipeline(sub_flow_list=cluster_sync_sub_list) sub_pipelines.append(sub_pipeline.build_sub_process(sub_name=_("{}数据修复").format(cluster.name))) + if not sub_pipelines: + raise Exception(_("修复单据找不到可修复的集群")) + table_sync_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) table_sync_pipeline.run_pipeline() diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/name_service/name_service.py b/dbm-ui/backend/flow/engine/bamboo/scene/name_service/name_service.py index 97fdbb6ab7..07decaa484 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/name_service/name_service.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/name_service/name_service.py @@ -9,15 +9,14 @@ specific language governing permissions and limitations under the License. """ import logging.config +from dataclasses import asdict from typing import Dict, Optional from django.utils.translation import ugettext as _ from backend.flow.engine.bamboo.scene.common.builder import Builder -from backend.flow.plugins.components.collections.name_service.name_service import ( - ExecNameServiceCreateComponent, - ExecNameServiceDeleteComponent, -) +from backend.flow.plugins.components.collections.name_service.name_service import ExecNameServiceOperationComponent +from backend.flow.utils.name_service.name_service_dataclass import ActKwargs, TransDataKwargs logger = logging.getLogger("flow") @@ -33,8 +32,12 @@ def __init__(self, root_id: str, data: Optional[Dict]): """ self.root_id = root_id self.data = data + self.kwargs = ActKwargs() + self.kwargs.set_trans_data_dataclass = TransDataKwargs.__name__ + self.kwargs.cluster_id = self.data["cluster_id"] + self.kwargs.creator = self.data["created_by"] - def name_service_clb_create_flow(self): + def clb_create_flow(self): """ clb create流程 """ @@ -42,17 +45,28 @@ def name_service_clb_create_flow(self): # 创建流程实例 pipeline = Builder(root_id=self.root_id, data=self.data) - # 定义私有变量 - kwargs = self.data - kwargs["name_service_type"] = "clb" - - # 添加活动节点 - pipeline.add_act(act_name=_("创建clb"), act_component_code=ExecNameServiceCreateComponent.code, kwargs=kwargs) + # 添加创建clb活动节点 + self.kwargs.name_service_operation_type = "create_clb" + pipeline.add_act( + act_name=_("创建clb"), act_component_code=ExecNameServiceOperationComponent.code, kwargs=asdict(self.kwargs) + ) + self.kwargs.name_service_operation_type = "add_clb_info_to_meta" + pipeline.add_act( + act_name=_("clb信息写入meta"), + act_component_code=ExecNameServiceOperationComponent.code, + kwargs=asdict(self.kwargs), + ) + self.kwargs.name_service_operation_type = "add_clb_domain_to_dns" + pipeline.add_act( + act_name=_("clb域名添加到dns,clb域名信息写入meta"), + act_component_code=ExecNameServiceOperationComponent.code, + kwargs=asdict(self.kwargs), + ) # 运行流程 pipeline.run_pipeline() - def name_service_clb_delete_flow(self): + def clb_delete_flow(self): """ clb delete流程 """ @@ -60,17 +74,34 @@ def name_service_clb_delete_flow(self): # 创建流程实例 pipeline = Builder(root_id=self.root_id, data=self.data) - # 定义私有变量 - kwargs = self.data - kwargs["name_service_type"] = "clb" - # 添加活动节点 - pipeline.add_act(act_name=_("删除clb"), act_component_code=ExecNameServiceDeleteComponent.code, kwargs=kwargs) + self.kwargs.name_service_operation_type = "domain_unbind_clb_ip" + pipeline.add_act( + act_name=_("主域名解绑clb ip"), + act_component_code=ExecNameServiceOperationComponent.code, + kwargs=asdict(self.kwargs), + ) + self.kwargs.name_service_operation_type = "delete_clb_domain_from_dns" + pipeline.add_act( + act_name=_("dns删除clb域名,从meta删除clb域名信息"), + act_component_code=ExecNameServiceOperationComponent.code, + kwargs=asdict(self.kwargs), + ) + self.kwargs.name_service_operation_type = "delete_clb" + pipeline.add_act( + act_name=_("删除clb"), act_component_code=ExecNameServiceOperationComponent.code, kwargs=asdict(self.kwargs) + ) + self.kwargs.name_service_operation_type = "delete_clb_info_from_meta" + pipeline.add_act( + act_name=_("从meta删除clb信息"), + act_component_code=ExecNameServiceOperationComponent.code, + kwargs=asdict(self.kwargs), + ) # 运行流程 pipeline.run_pipeline() - def name_service_polaris_create_flow(self): + def polaris_create_flow(self): """ polaris create流程 """ @@ -78,19 +109,24 @@ def name_service_polaris_create_flow(self): # 创建流程实例 pipeline = Builder(root_id=self.root_id, data=self.data) - # 定义私有变量 - kwargs = self.data - kwargs["name_service_type"] = "polaris" - # 添加活动节点 + self.kwargs.name_service_operation_type = "create_polaris" pipeline.add_act( - act_name=_("创建polaris"), act_component_code=ExecNameServiceCreateComponent.code, kwargs=kwargs + act_name=_("创建polaris"), + act_component_code=ExecNameServiceOperationComponent.code, + kwargs=asdict(self.kwargs), + ) + self.kwargs.name_service_operation_type = "add_polaris_info_to_meta" + pipeline.add_act( + act_name=_("polaris信息写入meta"), + act_component_code=ExecNameServiceOperationComponent.code, + kwargs=asdict(self.kwargs), ) # 运行流程 pipeline.run_pipeline() - def name_service_polaris_delete_flow(self): + def polaris_delete_flow(self): """ polaris delete流程 """ @@ -98,13 +134,56 @@ def name_service_polaris_delete_flow(self): # 创建流程实例 pipeline = Builder(root_id=self.root_id, data=self.data) - # 定义私有变量 - kwargs = self.data - kwargs["name_service_type"] = "polaris" + # 添加活动节点 + self.kwargs.name_service_operation_type = "delete_polaris" + pipeline.add_act( + act_name=_("删除polaris"), + act_component_code=ExecNameServiceOperationComponent.code, + kwargs=asdict(self.kwargs), + ) + self.kwargs.name_service_operation_type = "delete_polaris_info_from_meta" + pipeline.add_act( + act_name=_("从meta删除polaris信息"), + act_component_code=ExecNameServiceOperationComponent.code, + kwargs=asdict(self.kwargs), + ) + + # 运行流程 + pipeline.run_pipeline() + + def immute_domain_bind_clb_ip(self): + """ + 主域名绑定clb ip流程 + """ + + # 创建流程实例 + pipeline = Builder(root_id=self.root_id, data=self.data) + + # 添加活动节点 + self.kwargs.name_service_operation_type = "domain_bind_clb_ip" + pipeline.add_act( + act_name=_("主域名绑定clb ip"), + act_component_code=ExecNameServiceOperationComponent.code, + kwargs=asdict(self.kwargs), + ) + + # 运行流程 + pipeline.run_pipeline() + + def immute_domain_unbind_clb_ip(self): + """ + 主域名绑定clb ip流程 + """ + + # 创建流程实例 + pipeline = Builder(root_id=self.root_id, data=self.data) # 添加活动节点 + self.kwargs.name_service_operation_type = "domain_unbind_clb_ip" pipeline.add_act( - act_name=_("删除polaris"), act_component_code=ExecNameServiceDeleteComponent.code, kwargs=kwargs + act_name=_("主域名解绑clb ip"), + act_component_code=ExecNameServiceOperationComponent.code, + kwargs=asdict(self.kwargs), ) # 运行流程 diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/__init__.py b/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/__init__.py index 48f02b15b6..d970df5ea6 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/__init__.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/__init__.py @@ -9,7 +9,11 @@ specific language governing permissions and limitations under the License. """ +from .access_manager import AccessManagerAtomJob from .proxy_install import ProxyBatchInstallAtomJob +from .proxy_uninstall import ProxyUnInstallAtomJob +from .redis_cluster_master_rep import RedisClusterMasterReplaceJob +from .redis_cluster_slave_rep import RedisClusterSlaveReplaceJob from .redis_dbmon import RedisDbmonAtomJob from .redis_install import RedisBatchInstallAtomJob from .redis_makesync import RedisMakeSyncAtomJob diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/access_manager.py b/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/access_manager.py new file mode 100644 index 0000000000..c0ec933748 --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/access_manager.py @@ -0,0 +1,206 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging.config +from dataclasses import asdict +from typing import Dict + +from django.utils.translation import ugettext as _ + +from backend.db_meta.models import ClusterEntry +from backend.flow.consts import AccessType, DnsOpType +from backend.flow.engine.bamboo.scene.common.builder import SubBuilder +from backend.flow.plugins.components.collections.common.clb_manage import RedisClbManageComponent +from backend.flow.plugins.components.collections.common.polaris_manage import RedisPolarisManageComponent +from backend.flow.plugins.components.collections.redis.dns_manage import RedisDnsManageComponent +from backend.flow.utils.redis.redis_context_dataclass import ActKwargs, ClbKwargs, DnsKwargs, PolarisKwargs + +logger = logging.getLogger("flow") + + +def DNSManagerAtomJob(root_id, ticket_data, act_kwargs: ActKwargs, param: Dict) -> SubBuilder: + """ + 原生DNS域名管理 + """ + dns_sub_pipeline = SubBuilder(root_id=root_id, data=ticket_data) + # 添加域名 + if param["op_type"] == DnsOpType.CREATE: + dns_kwargs = DnsKwargs( + dns_op_type=DnsOpType.CREATE, + add_domain_name=param["entry"], + dns_op_exec_port=param["port"], + ) + act_kwargs.exec_ip = param["add_ips"] + dns_sub_pipeline.add_act( + act_name=_("添加域名映射"), + act_component_code=RedisDnsManageComponent.code, + kwargs={**asdict(act_kwargs), **asdict(dns_kwargs)}, + ) + # 清理域名 + if param["op_type"] == DnsOpType.RECYCLE_RECORD: + dns_kwargs = DnsKwargs( + dns_op_type=DnsOpType.RECYCLE_RECORD, + dns_op_exec_port=param["port"], + ) + act_kwargs.exec_ip = param["del_ips"] + dns_sub_pipeline.add_act( + act_name=_("删除域名映射"), + act_component_code=RedisDnsManageComponent.code, + kwargs={**asdict(act_kwargs), **asdict(dns_kwargs)}, + ) + + if param["op_type"] == DnsOpType.CLUSTER_DELETE: + dns_kwargs = DnsKwargs(dns_op_type=DnsOpType.CLUSTER_DELETE, delete_cluster_id=param["cluster_id"]) + dns_sub_pipeline.add_act( + act_name=_("删除域名"), + act_component_code=RedisDnsManageComponent.code, + kwargs={**asdict(act_kwargs), **asdict(dns_kwargs)}, + ) + + return dns_sub_pipeline.build_sub_process(sub_name=_("域名变更子流程")) + + +def CLBManagerAtomJob(root_id, ticket_data, act_kwargs: ActKwargs, param: Dict) -> SubBuilder: + """ + CLB 指向管理 + """ + clb_sub_pipeline = SubBuilder(root_id=root_id, data=ticket_data) + # clb添加rs + if param["op_type"] == DnsOpType.CREATE: + dns_kwargs = ClbKwargs( + clb_op_type=DnsOpType.CREATE, + clb_ip=param["entry"], + clb_op_exec_port=param["port"], + ) + act_kwargs.exec_ip = param["add_ips"] + clb_sub_pipeline.add_act( + act_name=_("clb添加rs"), + act_component_code=RedisClbManageComponent.code, + kwargs={**asdict(act_kwargs), **asdict(dns_kwargs)}, + ) + + # clb删除rs + if param["op_type"] == DnsOpType.RECYCLE_RECORD: + dns_kwargs = ClbKwargs( + clb_op_type=DnsOpType.RECYCLE_RECORD, + clb_ip=param["entry"], + clb_op_exec_port=param["port"], + ) + act_kwargs.exec_ip = param["del_ips"] + clb_sub_pipeline.add_act( + act_name=_("clb剔除rs"), + act_component_code=RedisClbManageComponent.code, + kwargs={**asdict(act_kwargs), **asdict(dns_kwargs)}, + ) + + if param["op_type"] == DnsOpType.CLUSTER_DELETE: + dns_kwargs = ClbKwargs( + clb_op_type=DnsOpType.CLUSTER_DELETE, + clb_ip=param["entry"], + ) + clb_sub_pipeline.add_act( + act_name=_("删除clb"), + act_component_code=RedisClbManageComponent.code, + kwargs={**asdict(act_kwargs), **asdict(dns_kwargs)}, + ) + + return clb_sub_pipeline.build_sub_process(sub_name=_("CLB变更子流程")) + + +def PolarisManagerAtomJob(root_id, ticket_data, act_kwargs: ActKwargs, param: Dict) -> SubBuilder: + """ " + 北极星 指向管理 + """ + polaris_sub_pipeline = SubBuilder(root_id=root_id, data=ticket_data) + # clb添加rs + if param["op_type"] == DnsOpType.CREATE: + dns_kwargs = PolarisKwargs( + polaris_op_type=DnsOpType.CREATE, + servicename=param["entry"], + polaris_op_exec_port=param["port"], + ) + act_kwargs.exec_ip = param["add_ips"] + polaris_sub_pipeline.add_act( + act_name=_("polaris添加rs"), + act_component_code=RedisPolarisManageComponent.code, + kwargs={**asdict(act_kwargs), **asdict(dns_kwargs)}, + ) + + # clb删除rs + if param["op_type"] == DnsOpType.RECYCLE_RECORD: + dns_kwargs = PolarisKwargs( + polaris_op_type=DnsOpType.RECYCLE_RECORD, + servicename=param["entry"], + polaris_op_exec_port=param["port"], + ) + act_kwargs.exec_ip = param["del_ips"] + polaris_sub_pipeline.add_act( + act_name=_("polaris剔除rs"), + act_component_code=RedisPolarisManageComponent.code, + kwargs={**asdict(act_kwargs), **asdict(dns_kwargs)}, + ) + + if param["op_type"] == DnsOpType.CLUSTER_DELETE: + dns_kwargs = PolarisKwargs( + polaris_op_type=DnsOpType.CLUSTER_DELETE, + servicename=param["entry"], + ) + polaris_sub_pipeline.add_act( + act_name=_("删除polaris"), + act_component_code=RedisPolarisManageComponent.code, + kwargs={**asdict(act_kwargs), **asdict(dns_kwargs)}, + ) + + return polaris_sub_pipeline.build_sub_process(sub_name=_("北极星变更子流程")) + + +def generic_manager(cluster_entry_type, root_id, ticket_data, act_kwargs: ActKwargs, param: Dict) -> SubBuilder: + if cluster_entry_type == AccessType.DNS: + return DNSManagerAtomJob(root_id, ticket_data, act_kwargs, param) + if cluster_entry_type == AccessType.CLB: + return CLBManagerAtomJob(root_id, ticket_data, act_kwargs, param) + if cluster_entry_type == AccessType.POLARIS: + return PolarisManagerAtomJob(root_id, ticket_data, act_kwargs, param) + + +def AccessManagerAtomJob(root_id, ticket_data, act_kwargs: ActKwargs, param: Dict) -> SubBuilder: + """ + 封装接入层管理原子任务。 + 主要包含类型:dns、clb、北极星、CLB域名 (clb.xxxx.x.xx.x.db)、 主域名直接指向CLB + dns: forward_id == null 原生dns; forward_id != null 跳转处理forward_id + 主要操作:增删改查,不包含创建 + 要求:根据域名的类型,同步更新操作相关的组件 + + Args: + param (Dict): { + "cluster_id", 必传 + "op_type": "", 必传 + "port": 30000, + "add_ips": [], + "del_ips": [], + } + """ + # 1. 根据cluster_id从db_meta_clusterentry表中查询出所有记录。看下这个集群都有些啥接入组件 + # 2. 然后根据使用的接入组件,进行对应的操作 + # op_type in [DnsOpType.CREATE、DnsOpType.RECYCLE_RECORD、DnsOpType.CLUSTER_DELETE] + + cluster_id = param["cluster_id"] + cluster_enterys = ClusterEntry.objects.filter(cluster__id=cluster_id).values() + sub_builder_list = [] + for ce in cluster_enterys: + if ce["forward_to_id"]: + # 有forward_to_id, 这这条记录不需要操作,只需要操作forward_to_id对应的记录 + continue + param["entry"] = ce["entry"] + sub_builder_list.append(generic_manager(ce["cluster_entry_type"], root_id, ticket_data, act_kwargs, param)) + + sub_pipeline = SubBuilder(root_id=root_id, data=ticket_data) + sub_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_builder_list) + return sub_pipeline.build_sub_process(sub_name=_("dns/clb 接入层子任务")) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/proxy_install.py b/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/proxy_install.py index 30a29a3810..3fb519d9f1 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/proxy_install.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/proxy_install.py @@ -20,9 +20,11 @@ from backend.db_meta.models import AppCache from backend.flow.engine.bamboo.scene.common.builder import SubBuilder from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList +from backend.flow.plugins.components.collections.common.download_backup_client import DownloadBackupClientComponent from backend.flow.plugins.components.collections.redis.exec_actuator_script import ExecuteDBActuatorScriptComponent from backend.flow.plugins.components.collections.redis.redis_db_meta import RedisDBMetaComponent from backend.flow.plugins.components.collections.redis.trans_flies import TransFileComponent +from backend.flow.utils.common_act_dataclass import DownloadBackupClientKwargs from backend.flow.utils.redis.redis_act_playload import RedisActPayload from backend.flow.utils.redis.redis_context_dataclass import ActKwargs from backend.flow.utils.redis.redis_db_meta import RedisDBMeta @@ -52,6 +54,9 @@ def ProxyBatchInstallAtomJob(root_id, ticket_data, act_kwargs: ActKwargs, param: "redis_pwd":"", "proxy_pwd":"", "servers":"", + + "spec_id":0, + "spec_config": "", } """ app = AppCache.get_app_attr(act_kwargs.cluster["bk_biz_id"], "db_app_abbr") @@ -77,6 +82,19 @@ def ProxyBatchInstallAtomJob(root_id, ticket_data, act_kwargs: ActKwargs, param: kwargs=asdict(act_kwargs), ) + sub_pipeline.add_act( + act_name=_("Proxy-002-{}-安装backup-client工具").format(exec_ip), + act_component_code=DownloadBackupClientComponent.code, + kwargs=asdict( + DownloadBackupClientKwargs(bk_cloud_id=act_kwargs.cluster["bk_cloud_id"], download_host_list=[exec_ip]), + ), + ) + + # proxy扩容是,config从参数传过来 + if ticket_data["ticket_type"] == TicketType.PROXY_SCALE_UP.value: + act_kwargs.cluster["conf_configs"] = param["conf_configs"] + act_kwargs.cluster["dbconfig"] = param["conf_configs"] + # 安装proxy实例 if act_kwargs.cluster["cluster_type"] == ClusterType.TendisPredixyTendisplusCluster.value: act_kwargs.cluster["ip"] = exec_ip @@ -104,6 +122,8 @@ def ProxyBatchInstallAtomJob(root_id, ticket_data, act_kwargs: ActKwargs, param: # 写入元数据 act_kwargs.cluster["new_proxy_ips"] = [exec_ip] + act_kwargs.cluster["spec_id"] = param["spec_id"] + act_kwargs.cluster["spec_config"] = param["spec_config"] act_kwargs.cluster["meta_func_name"] = RedisDBMeta.proxy_install.__name__ sub_pipeline.add_act( act_name=_("Proxy-004-{}-写入元数据").format(exec_ip), @@ -114,13 +134,14 @@ def ProxyBatchInstallAtomJob(root_id, ticket_data, act_kwargs: ActKwargs, param: # 部署bkdbmon act_kwargs.cluster["servers"] = [ { + "app": app, + "app_name": app_name, "bk_biz_id": str(act_kwargs.cluster["bk_biz_id"]), "bk_cloud_id": int(act_kwargs.cluster["bk_cloud_id"]), + "server_ip": exec_ip, "server_ports": [param["proxy_port"]], "meta_role": act_kwargs.cluster["machine_type"], "cluster_domain": act_kwargs.cluster["immute_domain"], - "app": app, - "app_name": app_name, "cluster_name": act_kwargs.cluster["cluster_name"], "cluster_type": act_kwargs.cluster["cluster_type"], } diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/proxy_uninstall.py b/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/proxy_uninstall.py new file mode 100644 index 0000000000..be444d5c4d --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/proxy_uninstall.py @@ -0,0 +1,123 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging.config +from dataclasses import asdict +from typing import Dict + +from django.utils.translation import ugettext as _ + +from backend.configuration.constants import DBType +from backend.db_meta.enums.cluster_type import ClusterType +from backend.db_meta.enums.machine_type import MachineType +from backend.db_meta.models import AppCache +from backend.flow.consts import DBActuatorTypeEnum, RedisActuatorActionEnum +from backend.flow.engine.bamboo.scene.common.builder import SubBuilder +from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList +from backend.flow.plugins.components.collections.redis.exec_actuator_script import ExecuteDBActuatorScriptComponent +from backend.flow.plugins.components.collections.redis.redis_db_meta import RedisDBMetaComponent +from backend.flow.plugins.components.collections.redis.trans_flies import TransFileComponent +from backend.flow.utils.redis.redis_act_playload import RedisActPayload +from backend.flow.utils.redis.redis_context_dataclass import ActKwargs +from backend.flow.utils.redis.redis_db_meta import RedisDBMeta +from backend.ticket.constants import TicketType + +cluster_apply_ticket = [TicketType.REDIS_SINGLE_APPLY.value, TicketType.REDIS_CLUSTER_APPLY.value] + +logger = logging.getLogger("flow") + + +def ProxyUnInstallAtomJob(root_id, ticket_data, act_kwargs: ActKwargs, param: Dict) -> SubBuilder: + """ + ### SubBuilder: Proxy卸载原子任务 + act_kwargs.cluster = { + "bk_biz_id": "", + "immute_domain":"", + "cluster_name":"", + "bk_cloud_id":"", + "cluster_type":"", + } + + Args: + param (Dict): { + "ip":"1.1.1.x", + "proxy_port": 30000, + } + """ + app = AppCache.get_app_attr(act_kwargs.cluster["bk_biz_id"], "db_app_abbr") + app_name = AppCache.get_app_attr(act_kwargs.cluster["bk_biz_id"], "bk_biz_name") + immute_domain = act_kwargs.cluster["immute_domain"] + + sub_pipeline = SubBuilder(root_id=root_id, data=ticket_data) + exec_ip = param["ip"] + if act_kwargs.cluster["cluster_type"] == ClusterType.TendisPredixyTendisplusCluster.value: + act_kwargs.cluster["machine_type"] = MachineType.PREDIXY.value + else: + act_kwargs.cluster["machine_type"] = MachineType.TWEMPROXY.value + + # 下发介质包 + trans_files = GetFileList(db_type=DBType.Redis) + act_kwargs.file_list = trans_files.redis_cluster_apply_proxy(act_kwargs.cluster["cluster_type"]) + act_kwargs.exec_ip = exec_ip + sub_pipeline.add_act( + act_name=_("Proxy-001-{}-下发介质包").format(exec_ip), + act_component_code=TransFileComponent.code, + kwargs=asdict(act_kwargs), + ) + + # 部署bkdbmon + act_kwargs.cluster["servers"] = [ + { + "bk_biz_id": str(act_kwargs.cluster["bk_biz_id"]), + "bk_cloud_id": int(act_kwargs.cluster["bk_cloud_id"]), + "server_ip": exec_ip, + "server_ports": [], + "meta_role": act_kwargs.cluster["machine_type"], + "cluster_domain": act_kwargs.cluster["immute_domain"], + "app": app, + "app_name": app_name, + "cluster_name": act_kwargs.cluster["cluster_name"], + "cluster_type": act_kwargs.cluster["cluster_type"], + } + ] + act_kwargs.get_redis_payload_func = RedisActPayload.bkdbmon_install.__name__ + sub_pipeline.add_act( + act_name=_("Proxy-002-{}-卸载监控").format(exec_ip), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(act_kwargs), + ) + + act_kwargs.exec_ip = exec_ip + act_kwargs.cluster = { + "operate": DBActuatorTypeEnum.Proxy.value + "_" + RedisActuatorActionEnum.Shutdown.value, + "port": param["proxy_port"], + "ip": exec_ip, + } + act_kwargs.get_redis_payload_func = RedisActPayload.proxy_operate_payload.__name__ + sub_pipeline.add_act( + act_name=_("Proxy-003-{}-卸载实例").format(exec_ip), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(act_kwargs), + ) + + # 删除元数据 + act_kwargs.cluster = { + "proxy_ips": [exec_ip], + "proxy_port": param["proxy_port"], + "meta_func_name": RedisDBMeta.proxy_uninstall.__name__, + "domain_name": immute_domain, + } + sub_pipeline.add_act( + act_name=_("Proxy-004-{}-删除元数据").format(exec_ip), + act_component_code=RedisDBMetaComponent.code, + kwargs=asdict(act_kwargs), + ) + + return sub_pipeline.build_sub_process(sub_name=_("Proxy-{}-卸载原子任务").format(exec_ip)) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/redis_cluster_master_rep.py b/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/redis_cluster_master_rep.py new file mode 100644 index 0000000000..a02028c6f8 --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/redis_cluster_master_rep.py @@ -0,0 +1,315 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging.config +from collections import defaultdict +from copy import deepcopy +from dataclasses import asdict +from typing import Dict, List, Optional + +from django.utils.translation import ugettext as _ + +from backend.constants import IP_PORT_DIVIDER +from backend.db_meta.enums import ClusterType, InstanceRole +from backend.flow.consts import ( + DEFAULT_LAST_IO_SECOND_AGO, + DEFAULT_MASTER_DIFF_TIME, + DEFAULT_REDIS_START_PORT, + SyncType, +) +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.plugins.components.collections.redis.redis_db_meta import RedisDBMetaComponent +from backend.flow.utils.redis.redis_context_dataclass import ActKwargs, CommonContext +from backend.flow.utils.redis.redis_db_meta import RedisDBMeta + +from .redis_cluster_slave_rep import RedisClusterSlaveReplaceJob +from .redis_install import RedisBatchInstallAtomJob +from .redis_makesync import RedisMakeSyncAtomJob +from .redis_shutdown import RedisBatchShutdownAtomJob +from .redis_switch import RedisClusterSwitchAtomJob + +logger = logging.getLogger("flow") + + +def RedisClusterMasterReplaceJob(root_id, ticket_data, sub_kwargs: ActKwargs, master_replace_info: Dict) -> SubBuilder: + if sub_kwargs.cluster["cluster_type"] in [ + ClusterType.TwemproxyTendisSSDInstance, + ClusterType.TendisTwemproxyRedisInstance, + ]: + return TwemproxyClusterMasterReplaceJob(root_id, ticket_data, sub_kwargs, master_replace_info) + + elif sub_kwargs.cluster["cluster_type"] == ClusterType.TendisPredixyTendisplusCluster: + return TendisClusterMasterReplaceJob(root_id, ticket_data, sub_kwargs, master_replace_info) + + else: + raise Exception( + "unsupported cluster type {}:{}".format( + sub_kwargs.cluster["immute_domain"], sub_kwargs.cluster["cluster_type"] + ) + ) + + +def TwemproxyClusterMasterReplaceJob( + root_id, ticket_data, sub_kwargs: ActKwargs, master_replace_info: Dict +) -> SubBuilder: + """### 适用于 集群中Master 机房裁撤/迁移替换场景 (成对替换) + 步骤: 获取变更锁--> 新实例部署--> + 建Sync关系--> 检测同步状态 + Kill Dead链接--> 下架旧实例 + + master_replace_detail:[{ + {"ip": "1.1.1.c","spec_id": 17, + "target": { + "master": {"bk_cloud_id": 0,"bk_host_id": 195,"status": 1,"ip": "2.2.2.b"}, + "slave": {"bk_cloud_id": 0,"bk_host_id": 187,"status": 1,"ip": "3.3.3.x"}} + }] + """ + act_kwargs = deepcopy(sub_kwargs) + redis_pipeline = SubBuilder(root_id=root_id, data=ticket_data) + # ## 部署实例 ############################################################################# + sub_pipelines = [] + master_replace_detail = master_replace_info["redis_master"] + for replace_info in master_replace_detail: + old_master = replace_info["ip"] + new_host_master = replace_info["target"]["master"]["ip"] + new_host_slave = replace_info["target"]["slave"]["ip"] + # 安装Master + params = { + "ip": new_host_master, + "ports": [], + "meta_role": InstanceRole.REDIS_MASTER.value, + "start_port": DEFAULT_REDIS_START_PORT, + "instance_numb": len(act_kwargs.cluster["master_ports"][old_master]), + "spec_id": master_replace_info["master_spec"].get("id", 0), + "spec_config": master_replace_info["master_spec"], + } + sub_pipelines.append(RedisBatchInstallAtomJob(root_id, ticket_data, act_kwargs, params)) + + # 安装slave + params["ip"] = new_host_slave + params["meta_role"] = InstanceRole.REDIS_SLAVE.value + params["spec_id"] = master_replace_info["slave_spec"].get("id", 0) + params["spec_config"] = master_replace_info["slave_spec"] + sub_pipelines.append(RedisBatchInstallAtomJob(root_id, ticket_data, act_kwargs, params)) + redis_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) + # ### 部署实例 ####################################################################### 完毕 ### + + sync_relations, new_slave_ports = [], [] # 按照机器对组合 + # #### 建同步关系 ############################################################################# + sub_pipelines, sync_kwargs = [], deepcopy(act_kwargs) + for replace_info in master_replace_detail: + old_master = replace_info["ip"] + new_host_master = replace_info["target"]["master"]["ip"] + new_host_slave = replace_info["target"]["slave"]["ip"] + sync_params = { + "sync_type": act_kwargs.cluster["sync_type"], + "origin_1": old_master, + "origin_2": act_kwargs.cluster["master_slave_map"][old_master], + "sync_dst1": new_host_master, + "sync_dst2": new_host_slave, + "ins_link": [], + } + new_ins_port = DEFAULT_REDIS_START_PORT + master_ports = act_kwargs.cluster["master_ports"][old_master] + master_ports.sort() + for old_master_port in master_ports: + old_master_ins = "{}{}{}".format(old_master, IP_PORT_DIVIDER, old_master_port) + old_slave_ins = act_kwargs.cluster["ins_pair_map"][old_master_ins] + new_slave_ports.append(new_ins_port) + sync_params["ins_link"].append( + { + "origin_1": old_master_port, + "origin_2": old_slave_ins.split(IP_PORT_DIVIDER)[1], + "sync_dst1": new_ins_port, + "sync_dst2": new_ins_port, + } + ) + new_ins_port = new_ins_port + 1 + sync_relations.append(sync_params) + sub_builder = RedisMakeSyncAtomJob(root_id, ticket_data, sync_kwargs, sync_params) + sub_pipelines.append(sub_builder) + redis_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) + # ### 建同步关系 ####################################################################### 完毕 ### + + # 执行切换 ##################################################################################### + act_kwargs.cluster["switch_condition"] = { + "is_check_sync": True, # 不强制切换 + "slave_master_diff_time": DEFAULT_MASTER_DIFF_TIME, + "last_io_second_ago": DEFAULT_LAST_IO_SECOND_AGO, + "can_write_before_switch": True, + "sync_type": act_kwargs.cluster["sync_type"], + } + sub_pipeline = RedisClusterSwitchAtomJob(root_id, ticket_data, act_kwargs, sync_relations) + redis_pipeline.add_sub_pipeline(sub_flow=sub_pipeline) + # #### 执行切换 ############################################################################### + + # #### 下架旧实例 ############################################################################# + sub_pipelines, shutdown_kwargs = [], deepcopy(act_kwargs) + for replace_info in master_replace_detail: + old_master = replace_info["ip"] + params = { + "ip": old_master, + "ports": act_kwargs.cluster["master_ports"][old_master], + } + sub_builder = RedisBatchShutdownAtomJob(root_id, ticket_data, shutdown_kwargs, params) + sub_pipelines.append(sub_builder) + # 旧的slave 实例也要一起下架 + old_slave = act_kwargs.cluster["master_slave_map"][old_master] + params = { + "ip": old_slave, + "ports": act_kwargs.cluster["slave_ports"][old_slave], + } + sub_builder = RedisBatchShutdownAtomJob(root_id, ticket_data, shutdown_kwargs, params) + sub_pipelines.append(sub_builder) + redis_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) + # #### 下架旧实例 ###################################################################### 完毕 ### + + return redis_pipeline.build_sub_process(sub_name=_("主从替换-{}").format(act_kwargs.cluster["cluster_type"])) + + +def TendisClusterMasterReplaceJob( + root_id, ticket_data, sub_kwargs: ActKwargs, master_replace_detail: Dict +) -> SubBuilder: + """master_replace_detail:[{ + {"ip": "1.1.1.c","spec_id": 17, + "target": { + "master": {"bk_cloud_id": 0,"bk_host_id": 195,"status": 1,"ip": "2.2.2.b"}, + "slave": {"bk_cloud_id": 0,"bk_host_id": 187,"status": 1,"ip": "3.3.3.x"}} + }] + """ + act_kwargs = deepcopy(sub_kwargs) + redis_pipeline = SubBuilder(root_id=root_id, data=ticket_data) + # #### 替换掉旧Slave ####################################################################### + sub_pipeline = RedisClusterSlaveReplaceJob( + root_id, + ticket_data, + act_kwargs, + { + "redis_slave": [ + { + "ip": act_kwargs.cluster["master_slave_map"][item["ip"]], + "spec_id": item["spec_id"], + "target": item.get("target", {}).get("master", {}), + } + for item in master_replace_detail["redis_master"] + ], + "slave_spec": master_replace_detail.get("master_spec", {}), + }, + ) + redis_pipeline.add_sub_pipeline(sub_pipeline) + # #### 替换掉旧Slave ####################################################################### + + sync_relations, new_master_ports, new_master_old_master_map = [], {}, {} + # 执行切换 ##################################################################################### + for replace_info in master_replace_detail["redis_master"]: + old_master = replace_info["ip"] + new_host_master = replace_info["target"]["master"]["ip"] + new_host_slave = replace_info["target"]["slave"]["ip"] + new_master_ports[new_host_master] = [] + sync_params = { + "sync_type": SyncType.SYNC_MS.value, + "origin_1": old_master, + "origin_2": act_kwargs.cluster["master_slave_map"][old_master], + "sync_dst1": new_host_master, + "sync_dst2": new_host_slave, + "ins_link": [], + } + new_ins_port = DEFAULT_REDIS_START_PORT + master_ports = act_kwargs.cluster["master_ports"][old_master] + master_ports.sort() + for old_master_port in master_ports: + new_master_ports[new_host_master].append(int(new_ins_port)) + new_master_old_master_map["{}{}{}".format(old_master, IP_PORT_DIVIDER, old_master_port)] = "{}{}{}".format( + new_host_master, IP_PORT_DIVIDER, new_ins_port + ) + sync_params["ins_link"].append( + { + "origin_1": int(old_master_port), + "sync_dst1": int(new_ins_port), + "sync_dst2": int(new_ins_port), + } + ) + new_ins_port = new_ins_port + 1 + sync_relations.append(sync_params) + act_kwargs.cluster["switch_condition"] = { + "is_check_sync": True, # 不强制切换 + "slave_master_diff_time": DEFAULT_MASTER_DIFF_TIME, + "last_io_second_ago": DEFAULT_LAST_IO_SECOND_AGO, + "can_write_before_switch": True, + "sync_type": SyncType.SYNC_MS.value, + } + redis_pipeline.add_sub_pipeline( + sub_flow=RedisClusterSwitchAtomJob(root_id, ticket_data, act_kwargs, sync_relations) + ) + # #### 执行切换 ############################################################################### + + # #### 元数据角色互换 ####################################################################### + act_kwargs.cluster["role_swap_ins"] = [] + for swap_info in sync_relations: + act_kwargs.cluster["role_swap_ins"].extend( + [ + { + "new_ejector_ip": swap_info["sync_dst1"], + "new_ejector_port": port_link["sync_dst1"], + "new_receiver_ip": swap_info["origin_1"], + "new_receiver_port": port_link["origin_1"], + } + for port_link in swap_info["ins_link"] + ] + ) + act_kwargs.cluster["meta_func_name"] = RedisDBMeta.redis_role_swap_4_scene.__name__ + redis_pipeline.add_act( + act_name=_("元数据角色互换-{}").format(old_master), + act_component_code=RedisDBMetaComponent.code, + kwargs=asdict(act_kwargs), + ) + # #### 元数据角色互换 ############################################################################### + + # #### 重新加载集群元数据 ####################################################################### + logger.info( + "before switch | slave ports : {}, slave's master : {}, slave's master addr : {} ".format( + act_kwargs.cluster["slave_ports"], + act_kwargs.cluster["slave_master_map"], + act_kwargs.cluster["slave_ins_map"], + ) + ) + for replace_info in master_replace_detail["redis_master"]: + old_master = replace_info["ip"] + new_host_master = replace_info["target"]["master"]["ip"] + act_kwargs.cluster["slave_ports"][old_master] = act_kwargs.cluster["master_ports"][old_master] + act_kwargs.cluster["slave_master_map"][old_master] = new_host_master + # add new-master --> old-master + act_kwargs.cluster["slave_ins_map"].update(new_master_old_master_map) + logger.info( + "after switch | slave ports : {}, slave's master : {}, slave's master addr : {} ".format( + act_kwargs.cluster["slave_ports"], + act_kwargs.cluster["slave_master_map"], + act_kwargs.cluster["slave_ins_map"], + ) + ) + # #### 重新加载集群元数据 ########################################################################### + + # #### 替换掉旧Master ####################################################################### + sub_pipeline = RedisClusterSlaveReplaceJob( + root_id, + ticket_data, + act_kwargs, + { + "redis_slave": [ + {"ip": item["ip"], "spec_id": item["spec_id"], "target": item.get("target", {}).get("slave", {})} + for item in master_replace_detail["redis_master"] + ], + "slave_spec": master_replace_detail.get("slave_spec", {}), + }, + ) + redis_pipeline.add_sub_pipeline(sub_pipeline) + # #### 替换掉旧Master ####################################################################### + + return redis_pipeline.build_sub_process(sub_name=_("主从替换-{}").format(act_kwargs.cluster["cluster_type"])) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/redis_cluster_slave_rep.py b/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/redis_cluster_slave_rep.py new file mode 100644 index 0000000000..aecaa7a9b7 --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/redis_cluster_slave_rep.py @@ -0,0 +1,134 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging.config +from collections import defaultdict +from copy import deepcopy +from dataclasses import asdict +from typing import Dict, Optional + +from django.utils.translation import ugettext as _ + +from backend.configuration.constants import DBType +from backend.constants import IP_PORT_DIVIDER +from backend.db_meta.enums import InstanceRole +from backend.db_meta.models import Cluster +from backend.flow.consts import DEFAULT_REDIS_START_PORT, SyncType +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList +from backend.flow.plugins.components.collections.redis.get_redis_payload import GetRedisActPayloadComponent +from backend.flow.plugins.components.collections.redis.redis_db_meta import RedisDBMetaComponent +from backend.flow.utils.redis.redis_context_dataclass import ActKwargs, CommonContext +from backend.flow.utils.redis.redis_db_meta import RedisDBMeta + +from .redis_install import RedisBatchInstallAtomJob +from .redis_makesync import RedisMakeSyncAtomJob +from .redis_shutdown import RedisBatchShutdownAtomJob + +logger = logging.getLogger("flow") + + +def RedisClusterSlaveReplaceJob(root_id, ticket_data, sub_kwargs: ActKwargs, slave_replace_info: Dict) -> SubBuilder: + """适用于 集群中Slave 机房裁撤/迁移替换场景 + 步骤: 获取变更锁--> 新实例部署--> + 重建热备--> 检测同步状态--> + Kill Dead链接--> 下架旧实例 + """ + act_kwargs = deepcopy(sub_kwargs) + redis_pipeline = SubBuilder(root_id=root_id, data=ticket_data) + # ### 部署实例 ############################################################################### + sub_pipelines = [] + slave_replace_detail = slave_replace_info["redis_slave"] + for replace_link in slave_replace_detail: + # {"ip": "1.1.1.a","spec_id": 17,"target": {"bk_cloud_id": 0,"bk_host_id": 216,"status": 1,"ip": "2.2.2.b"}} + old_slave = replace_link["ip"] + new_slave = replace_link["target"]["ip"] + params = { + "ip": new_slave, + "meta_role": InstanceRole.REDIS_SLAVE.value, + "start_port": DEFAULT_REDIS_START_PORT, + "ports": act_kwargs.cluster["slave_ports"][old_slave], + "instance_numb": len(act_kwargs.cluster["slave_ports"][old_slave]), + "spec_id": slave_replace_info["slave_spec"].get("id", 0), + "spec_config": slave_replace_info["slave_spec"], + } + sub_builder = RedisBatchInstallAtomJob(root_id, ticket_data, act_kwargs, params) + sub_pipelines.append(sub_builder) + redis_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) + # ### 部署实例 ######################################################################## 完毕 ### + + # #### 建同步关系 ############################################################################## + sub_pipelines = [] + for replace_link in slave_replace_detail: + # "Old": {"ip": "2.2.a.4", "bk_cloud_id": 0, "bk_host_id": 123}, + old_slave = replace_link["ip"] + new_slave = replace_link["target"]["ip"] + install_params = { + "sync_type": SyncType.SYNC_MS, + "origin_1": act_kwargs.cluster["slave_master_map"][old_slave], + "origin_2": old_slave, + "sync_dst1": new_slave, + "ins_link": [], + } + for slave_port in act_kwargs.cluster["slave_ports"][old_slave]: + old_ins = "{}{}{}".format(old_slave, IP_PORT_DIVIDER, slave_port) + master_port = act_kwargs.cluster["slave_ins_map"].get(old_ins).split(IP_PORT_DIVIDER)[1] + install_params["ins_link"].append({"origin_1": master_port, "sync_dst1": slave_port}) + sub_builder = RedisMakeSyncAtomJob(root_id, ticket_data, act_kwargs, install_params) + sub_pipelines.append(sub_builder) + redis_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) + # #### 建同步关系 ##################################################################### 完毕 #### + + # 新节点加入集群 ################################################################################ + act_kwargs.cluster["meta_func_name"] = RedisDBMeta.redis_redo_slaves.__name__ + act_kwargs.cluster["old_slaves"] = [] + act_kwargs.cluster["created_by"] = ticket_data["created_by"] + act_kwargs.cluster["tendiss"] = [] + for replace_link in slave_replace_detail: + # "Old": {"ip": "2.2.a.4", "bk_cloud_id": 0, "bk_host_id": 123}, + old_slave = replace_link["ip"] + new_slave = replace_link["target"]["ip"] + act_kwargs.cluster["old_slaves"].append( + {"ip": old_slave, "ports": act_kwargs.cluster["slave_ports"][old_slave]} + ) + for slave_port in act_kwargs.cluster["slave_ports"][old_slave]: + old_ins = "{}{}{}".format(old_slave, IP_PORT_DIVIDER, slave_port) + master = act_kwargs.cluster["slave_ins_map"].get(old_ins) + act_kwargs.cluster["tendiss"].append( + { + "ejector": { + "ip": master.split(IP_PORT_DIVIDER)[0], + "port": int(master.split(IP_PORT_DIVIDER)[1]), + }, + "receiver": {"ip": new_slave, "port": int(slave_port)}, + } + ) + redis_pipeline.add_act( + act_name=_("Redis-新节点加入集群"), act_component_code=RedisDBMetaComponent.code, kwargs=asdict(act_kwargs) + ) + # #### 新节点加入集群 ################################################################# 完毕 ### + + # #### 下架旧实例 ############################################################################ + sub_pipelines = [] + for replace_link in slave_replace_detail: + # "Old": {"ip": "2.2.a.4", "bk_cloud_id": 0, "bk_host_id": 123}, + old_slave = replace_link["ip"] + new_slave = replace_link["target"]["ip"] + params = { + "ignore_ips": act_kwargs.cluster["slave_master_map"][old_slave], + "ip": old_slave, + "ports": act_kwargs.cluster["slave_ports"][old_slave], + } + sub_builder = RedisBatchShutdownAtomJob(root_id, ticket_data, act_kwargs, params) + sub_pipelines.append(sub_builder) + redis_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) + # #### 下架旧实例 ###################################################################### 完毕 ### + + return redis_pipeline.build_sub_process(sub_name=_("Slave替换-{}").format(act_kwargs.cluster["cluster_type"])) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/redis_dbmon.py b/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/redis_dbmon.py index ea7780c09d..9046bf3e64 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/redis_dbmon.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/redis_dbmon.py @@ -66,8 +66,10 @@ def RedisDbmonAtomJob(root_id, ticket_data, act_kwargs: ActKwargs, param: Dict) "app_name": app_name, "bk_biz_id": str(ticket_data["bk_biz_id"]), "bk_cloud_id": int(ticket_data["bk_cloud_id"]), + "server_ip": exec_ip, "server_ports": param["ports"], "meta_role": param["meta_role"], + "cluster_name": param["cluster_name"], "cluster_type": param["cluster_type"], "cluster_domain": param["immute_domain"], } diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/redis_dts.py b/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/redis_dts.py new file mode 100644 index 0000000000..632f500cff --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/redis_dts.py @@ -0,0 +1,155 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import copy +from dataclasses import asdict + +from django.utils.translation import ugettext as _ + +from backend.configuration.constants import DBType +from backend.db_services.redis.redis_dts.enums import DtsCopyType +from backend.flow.consts import RedisBackupEnum, WriteContextOpType +from backend.flow.engine.bamboo.scene.common.builder import SubBuilder +from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList +from backend.flow.plugins.components.collections.redis.exec_actuator_script import ExecuteDBActuatorScriptComponent +from backend.flow.plugins.components.collections.redis.exec_shell_script import ExecuteShellScriptComponent +from backend.flow.plugins.components.collections.redis.get_redis_payload import GetRedisActPayloadComponent +from backend.flow.plugins.components.collections.redis.redis_dts import ( + RedisDtsExecuteComponent, + RedisDtsPrecheckComponent, +) +from backend.flow.plugins.components.collections.redis.trans_flies import TransFileComponent +from backend.flow.utils.redis.redis_act_playload import RedisActPayload +from backend.flow.utils.redis.redis_context_dataclass import ActKwargs +from backend.flow.utils.redis.redis_util import domain_without_port + + +def redis_dts_data_copy_atom_job(root_id, ticket_data, act_kwargs: ActKwargs) -> SubBuilder: + sub_pipeline = SubBuilder(root_id=root_id, data=ticket_data) + sub_pipeline.add_act( + act_name=_("初始化配置"), act_component_code=GetRedisActPayloadComponent.code, kwargs=asdict(act_kwargs) + ) + if ticket_data["dts_copy_type"] != DtsCopyType.USER_BUILT_TO_DBM.value: + for host in act_kwargs.cluster["src"]["slave_hosts"]: + # 获取slave磁盘信息 + act_kwargs.exec_ip = host["ip"] + act_kwargs.write_op = WriteContextOpType.APPEND.value + act_kwargs.cluster[ + "shell_command" + ] = """ + d=`df -k $REDIS_BACKUP_DIR | grep -iv Filesystem` + echo "{\\\"data\\\":\\\"${d}\\\"}" + """ + # acts_list.append( + # { + # "act_name": _("获取磁盘使用情况: {}").format(host["ip"]), + # "act_component_code": ExecuteShellScriptComponent.code, + # "kwargs": asdict(act_kwargs), + # "write_payload_var": "disk_used", + # } + # ) + sub_pipeline.add_act( + act_name=_("{}磁盘信息 ").format(host["ip"]), + act_component_code=ExecuteShellScriptComponent.code, + kwargs=asdict(act_kwargs), + write_payload_var="disk_used", + ) + # sub_pipeline.add_parallel_acts(acts_list=acts_list) + + sub_pipeline.add_act( + act_name=_("redis dts前置检查,{}->{}").format( + act_kwargs.cluster["src"]["cluster_addr"], act_kwargs.cluster["dst"]["cluster_addr"] + ), + act_component_code=RedisDtsPrecheckComponent.code, + kwargs=asdict(act_kwargs), + ) + sub_pipeline.add_act( + act_name=_("redis dts发起任务并等待至增量同步阶段"), + act_component_code=RedisDtsExecuteComponent.code, + kwargs=asdict(act_kwargs), + ) + return sub_pipeline.build_sub_process(sub_name=_("redis dts任务发起并等待同步完成")) + + +def generate_dst_cluster_backup_flush_info(act_kwargs: ActKwargs) -> dict: + dst_running_masters = act_kwargs.cluster["dst"]["running_masters"] + ret = { + "backup_type": RedisBackupEnum.NORMAL_BACKUP.value, + "domain_name": domain_without_port(act_kwargs.cluster["dst"]["cluster_addr"]), + "cluster_type": act_kwargs.cluster["dst"]["cluster_type"], + "force": True, + "requirepass": act_kwargs.cluster["dst"]["redis_password"], + "db_list": [0], + "flushall": True, + "db_version": act_kwargs.cluster["dst"]["major_version"], + } + for master in dst_running_masters: + if master["ip"] in ret: + ret[master["ip"]].append(master["port"]) + else: + ret[master["ip"]] = [master["port"]] + return ret + + +def redis_dst_cluster_backup_and_flush(root_id, ticket_data, act_kwargs: ActKwargs) -> SubBuilder: + dst_master_ips = set() + for dst_master in act_kwargs.cluster["dst"]["running_masters"]: + dst_master_ips.add(dst_master["ip"]) + + # 保存原始cluster信息 + cluster_bak = copy.deepcopy(act_kwargs.cluster) + + # 替换成 flush 和 backup 的 cluster 信息 + backup_flush_cluster = generate_dst_cluster_backup_flush_info(act_kwargs) + act_kwargs.cluster = backup_flush_cluster + + trans_files = GetFileList(db_type=DBType.Redis) + act_kwargs.file_list = trans_files.redis_base() + act_kwargs.exec_ip = list(dst_master_ips) + + sub_pipeline = SubBuilder(root_id=root_id, data=ticket_data) + sub_pipeline.add_act( + act_name=_("初始化配置"), act_component_code=GetRedisActPayloadComponent.code, kwargs=asdict(act_kwargs) + ) + sub_pipeline.add_act(act_name=_("下发介质包"), act_component_code=TransFileComponent.code, kwargs=asdict(act_kwargs)) + + acts_list = [] + for dst_master_ip in dst_master_ips: + act_kwargs.exec_ip = dst_master_ip + act_kwargs.get_redis_payload_func = RedisActPayload.redis_cluster_backup_payload.__name__ + acts_list.append( + { + "act_name": _("redis备份: {}").format(dst_master_ip), + "act_component_code": ExecuteDBActuatorScriptComponent.code, + "kwargs": asdict(act_kwargs), + "write_payload_var": "tendis_backup_info", + } + ) + if acts_list: + sub_pipeline.add_parallel_acts(acts_list=acts_list) + + acts_list = [] + for dst_master_ip in dst_master_ips: + act_kwargs.exec_ip = dst_master_ip + act_kwargs.get_redis_payload_func = RedisActPayload.redis_flush_data_payload.__name__ + acts_list.append( + { + "act_name": _("redis 清档: {}").format(dst_master_ip), + "act_component_code": ExecuteDBActuatorScriptComponent.code, + "kwargs": asdict(act_kwargs), + } + ) + if acts_list: + sub_pipeline.add_parallel_acts(acts_list=acts_list) + + # 恢复原始cluster信息 + act_kwargs.cluster = cluster_bak + + return sub_pipeline.build_sub_process(sub_name=_("redis 备份后清档")) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/redis_install.py b/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/redis_install.py index b150927a9a..c676c53f3c 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/redis_install.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/redis_install.py @@ -9,6 +9,7 @@ specific language governing permissions and limitations under the License. """ import logging.config +from copy import deepcopy from dataclasses import asdict from typing import Dict @@ -19,20 +20,27 @@ from backend.db_meta.models import AppCache from backend.flow.engine.bamboo.scene.common.builder import SubBuilder from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList +from backend.flow.plugins.components.collections.common.download_backup_client import DownloadBackupClientComponent from backend.flow.plugins.components.collections.redis.exec_actuator_script import ExecuteDBActuatorScriptComponent from backend.flow.plugins.components.collections.redis.redis_db_meta import RedisDBMetaComponent from backend.flow.plugins.components.collections.redis.trans_flies import TransFileComponent +from backend.flow.utils.common_act_dataclass import DownloadBackupClientKwargs from backend.flow.utils.redis.redis_act_playload import RedisActPayload from backend.flow.utils.redis.redis_context_dataclass import ActKwargs from backend.flow.utils.redis.redis_db_meta import RedisDBMeta from backend.ticket.constants import TicketType -cluster_apply_ticket = [TicketType.REDIS_SINGLE_APPLY.value, TicketType.REDIS_CLUSTER_APPLY.value] +cluster_apply_ticket = [ + TicketType.REDIS_SINGLE_APPLY.value, + TicketType.REDIS_CLUSTER_APPLY.value, + TicketType.REDIS_CLUSTER_SHARD_NUM_UPDATE.value, + TicketType.REDIS_CLUSTER_TYPE_UPDATE.value, +] logger = logging.getLogger("flow") -def RedisBatchInstallAtomJob(root_id, ticket_data, act_kwargs: ActKwargs, param: Dict) -> SubBuilder: +def RedisBatchInstallAtomJob(root_id, ticket_data, sub_kwargs: ActKwargs, param: Dict) -> SubBuilder: """ ### SubBuilder: Redis安装原籽任务 #### 备注: 主从创建的时候, 不创建主从关系(包含元数据 以及真实的同步状态) @@ -44,8 +52,13 @@ def RedisBatchInstallAtomJob(root_id, ticket_data, act_kwargs: ActKwargs, param: "meta_role":"redis_slave", "start_port":30000, "instance_numb":12, + + // 资源池新增 2023-06 + "spec_id": 0, + "spec_config": "xx", } """ + act_kwargs = deepcopy(sub_kwargs) app = AppCache.get_app_attr(act_kwargs.cluster["bk_biz_id"], "db_app_abbr") app_name = AppCache.get_app_attr(act_kwargs.cluster["bk_biz_id"], "bk_biz_name") @@ -60,7 +73,7 @@ def RedisBatchInstallAtomJob(root_id, ticket_data, act_kwargs: ActKwargs, param: act_kwargs.file_list = trans_files.redis_cluster_apply_backend(act_kwargs.cluster["db_version"]) act_kwargs.exec_ip = exec_ip sub_pipeline.add_act( - act_name=_("Redis-001-{}-下发介质包").format(exec_ip), + act_name=_("Redis-{}-下发介质包").format(exec_ip), act_component_code=TransFileComponent.code, kwargs=asdict(act_kwargs), ) @@ -68,11 +81,19 @@ def RedisBatchInstallAtomJob(root_id, ticket_data, act_kwargs: ActKwargs, param: # ./dbactuator_redis --atom-job-list="sys_init" act_kwargs.get_redis_payload_func = RedisActPayload.get_sys_init_payload.__name__ sub_pipeline.add_act( - act_name=_("Redis-002-{}-初始化机器").format(exec_ip), + act_name=_("Redis-{}-初始化机器").format(exec_ip), act_component_code=ExecuteDBActuatorScriptComponent.code, kwargs=asdict(act_kwargs), ) + sub_pipeline.add_act( + act_name=_("Redis-{}-安装backup-client工具").format(exec_ip), + act_component_code=DownloadBackupClientComponent.code, + kwargs=asdict( + DownloadBackupClientKwargs(bk_cloud_id=act_kwargs.cluster["bk_cloud_id"], download_host_list=[exec_ip]), + ), + ) + # 安装Redis实例 act_kwargs.cluster["exec_ip"] = exec_ip act_kwargs.cluster["start_port"] = param["start_port"] @@ -87,21 +108,25 @@ def RedisBatchInstallAtomJob(root_id, ticket_data, act_kwargs: ActKwargs, param: else: act_kwargs.get_redis_payload_func = RedisActPayload.get_redis_install_4_scene.__name__ sub_pipeline.add_act( - act_name=_("Redis-003-{}-安装实例").format(exec_ip), + act_name=_("Redis-{}-安装实例").format(exec_ip), act_component_code=ExecuteDBActuatorScriptComponent.code, kwargs=asdict(act_kwargs), ) # 写入元数据 + act_kwargs.cluster["spec_id"] = param.get("spec_id", 0) + act_kwargs.cluster["spec_config"] = param.get("spec_config", {}) act_kwargs.cluster["meta_func_name"] = RedisDBMeta.redis_install.__name__ if InstanceRole.REDIS_SLAVE.value == param["meta_role"]: + act_kwargs.cluster["new_master_ips"] = [] act_kwargs.cluster["new_slave_ips"] = [exec_ip] elif InstanceRole.REDIS_MASTER.value == param["meta_role"]: act_kwargs.cluster["new_master_ips"] = [exec_ip] + act_kwargs.cluster["new_slave_ips"] = [] else: raise Exception("unkown instance role {}:{}", param["meta_role"], exec_ip) sub_pipeline.add_act( - act_name=_("Redis-004-{}-写入元数据").format(exec_ip), + act_name=_("Redis-{}-写入元数据").format(exec_ip), act_component_code=RedisDBMetaComponent.code, kwargs=asdict(act_kwargs), ) @@ -113,15 +138,19 @@ def RedisBatchInstallAtomJob(root_id, ticket_data, act_kwargs: ActKwargs, param: "app_name": app_name, "bk_biz_id": str(act_kwargs.cluster["bk_biz_id"]), "bk_cloud_id": int(act_kwargs.cluster["bk_cloud_id"]), + "server_ip": exec_ip, "server_ports": param["ports"], "meta_role": param["meta_role"], + "cluster_name": act_kwargs.cluster["cluster_name"], "cluster_type": act_kwargs.cluster["cluster_type"], "cluster_domain": act_kwargs.cluster["immute_domain"], + "server_shards": param.get("server_shards", {}), + "cache_backup_mode": param.get("cache_backup_mode", ""), } ] act_kwargs.get_redis_payload_func = RedisActPayload.bkdbmon_install.__name__ sub_pipeline.add_act( - act_name=_("Redis-005-{}-安装监控").format(exec_ip), + act_name=_("Redis-{}-安装监控").format(exec_ip), act_component_code=ExecuteDBActuatorScriptComponent.code, kwargs=asdict(act_kwargs), ) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/redis_makesync.py b/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/redis_makesync.py index 84c10d68d7..507b93bdfe 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/redis_makesync.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/redis_makesync.py @@ -9,19 +9,20 @@ specific language governing permissions and limitations under the License. """ import logging.config +from copy import deepcopy from dataclasses import asdict from typing import Dict, Optional from django.utils.translation import ugettext as _ from backend.configuration.constants import DBType -from backend.constants import IP_PORT_DIVIDER -from backend.db_meta.enums import ClusterType +from backend.db_meta.enums import ClusterType, InstanceRole from backend.db_meta.models import AppCache -from backend.flow.consts import DEFAULT_REDIS_START_PORT, SyncType +from backend.flow.consts import SyncType from backend.flow.engine.bamboo.scene.common.builder import SubBuilder from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList from backend.flow.plugins.components.collections.redis.exec_actuator_script import ExecuteDBActuatorScriptComponent +from backend.flow.plugins.components.collections.redis.redis_trans_files import RedisBackupFileTransComponent from backend.flow.plugins.components.collections.redis.trans_flies import TransFileComponent from backend.flow.utils.redis.redis_act_playload import RedisActPayload from backend.flow.utils.redis.redis_context_dataclass import ActKwargs @@ -29,7 +30,7 @@ logger = logging.getLogger("flow") -def RedisMakeSyncAtomJob(root_id, ticket_data, act_kwargs: ActKwargs, params: Dict) -> SubBuilder: +def RedisMakeSyncAtomJob(root_id, ticket_data, sub_kwargs: ActKwargs, params: Dict) -> SubBuilder: """### SubBuilder: Redis建Sync关系 #### 支持多种同步关系建立 包含 MMS,MS,SMS params (Dict): { @@ -41,6 +42,7 @@ def RedisMakeSyncAtomJob(root_id, ticket_data, act_kwargs: ActKwargs, params: Di "ins_link":[{"origin_1":"port","origin_2":"port","sync_dst1":"port","sync_dst2":"port"}], } """ + act_kwargs = deepcopy(sub_kwargs) sub_pipeline = SubBuilder(root_id=root_id, data=ticket_data) ins_sync_type = params["sync_type"] app = AppCache.get_app_attr(act_kwargs.cluster["bk_biz_id"], "db_app_abbr") @@ -53,7 +55,7 @@ def RedisMakeSyncAtomJob(root_id, ticket_data, act_kwargs: ActKwargs, params: Di act_kwargs.exec_ip = exec_ip act_kwargs.file_list = GetFileList(db_type=DBType.Redis).redis_actuator() sub_pipeline.add_act( - act_name=_("Redis-101-{}-下发介质包").format(exec_ip), + act_name=_("下发介质包-{}").format(exec_ip), act_component_code=TransFileComponent.code, kwargs=asdict(act_kwargs), ) @@ -68,12 +70,13 @@ def RedisMakeSyncAtomJob(root_id, ticket_data, act_kwargs: ActKwargs, params: Di "bk_biz_id": str(act_kwargs.cluster["bk_biz_id"]), "bk_cloud_id": int(act_kwargs.cluster["bk_cloud_id"]), "server_ports": [], + "server_shards": {}, "cluster_domain": act_kwargs.cluster["immute_domain"], } ] act_kwargs.get_redis_payload_func = RedisActPayload.bkdbmon_install.__name__ sub_pipeline.add_act( - act_name=_("Redis-102-{}-卸载dbmon").format(exec_ip), + act_name=_("卸载dbmon-{}").format(exec_ip), act_component_code=ExecuteDBActuatorScriptComponent.code, kwargs=asdict(act_kwargs), ) @@ -83,18 +86,19 @@ def RedisMakeSyncAtomJob(root_id, ticket_data, act_kwargs: ActKwargs, params: Di RedisCacheMakeSyncAtomJob(sub_pipeline=sub_pipeline, act_kwargs=act_kwargs, params=params) # sub_pipeline = RedisCacheMakeSyncAtomJob(sub_pipeline=sub_pipeline, act_kwargs=act_kwargs, params=params) elif act_kwargs.cluster["cluster_type"] == ClusterType.TwemproxyTendisSSDInstance: - # TODO 具体方案再定 + # 备份这里act需要的是string, cluster里的参数会覆盖掉playload里的。所以这里需要转一下 + act_kwargs.cluster["bk_biz_id"] = str(act_kwargs.cluster["bk_biz_id"]) RedisSSDMakeSyncAtomJob(sub_pipeline=sub_pipeline, act_kwargs=act_kwargs, params=params) + act_kwargs.cluster["bk_biz_id"] = int(act_kwargs.cluster["bk_biz_id"]) + elif act_kwargs.cluster["cluster_type"] == ClusterType.TendisPredixyTendisplusCluster: + RedisClusterMakeSyncAtomJob(sub_pipeline=sub_pipeline, sub_kwargs=act_kwargs, params=params) else: raise Exception("unsupport cluster type 4 make sync {}".format(params["cluster_type"])) # 拉起dbmon - exec_ip = [params["sync_dst1"]] - server_ports = [] + exec_ip, server_ports = [params["sync_dst1"]], [] for sync_link in params["ins_link"]: - server_ports.append(int(sync_link["sync_dst1"].split(IP_PORT_DIVIDER)[1])) - if ins_sync_type in [SyncType.SYNC_MMS, SyncType.SYNC_SMS]: - exec_ip.append(params["sync_dst2"]) + server_ports.append(int(sync_link["sync_dst1"])) act_kwargs.exec_ip = exec_ip act_kwargs.cluster["servers"] = [ { @@ -102,18 +106,41 @@ def RedisMakeSyncAtomJob(root_id, ticket_data, act_kwargs: ActKwargs, params: Di "app_name": app_name, "bk_biz_id": str(act_kwargs.cluster["bk_biz_id"]), "bk_cloud_id": int(act_kwargs.cluster["bk_cloud_id"]), + "meta_role": InstanceRole.REDIS_SLAVE.value, # 可能是master/slave 角色 + "server_ip": params["sync_dst1"], "server_ports": server_ports, + "server_shards": params.get("server_shards", {}), + "cache_backup_mode": params.get("cache_backup_mode", ""), + "cluster_name": act_kwargs.cluster["cluster_name"], "cluster_type": act_kwargs.cluster["cluster_type"], "cluster_domain": act_kwargs.cluster["immute_domain"], } ] + + if ins_sync_type in [SyncType.SYNC_MMS, SyncType.SYNC_SMS]: + act_kwargs.cluster["servers"][0]["meta_role"] = InstanceRole.REDIS_MASTER.value act_kwargs.get_redis_payload_func = RedisActPayload.bkdbmon_install.__name__ sub_pipeline.add_act( - act_name=_("Redis-{}-拉起dbmon").format(exec_ip), + act_name=_("拉起dbmon-{}").format(exec_ip), act_component_code=ExecuteDBActuatorScriptComponent.code, kwargs=asdict(act_kwargs), ) + # 通常是slave 角色 + if ins_sync_type in [SyncType.SYNC_MMS, SyncType.SYNC_SMS]: + exec_ip, server_ports = [params["sync_dst2"]], [] + act_kwargs.exec_ip = exec_ip + for sync_link in params["ins_link"]: + server_ports.append(int(sync_link["sync_dst2"])) + act_kwargs.cluster["servers"][0]["meta_role"] = InstanceRole.REDIS_SLAVE.value + act_kwargs.cluster["servers"][0]["server_ip"] = params["sync_dst2"] + act_kwargs.cluster["servers"][0]["server_ports"] = server_ports + sub_pipeline.add_act( + act_name=_("拉起dbmon-{}").format(exec_ip), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(act_kwargs), + ) + return sub_pipeline.build_sub_process(sub_name=_("Redis-{}-创建同步关系原子任务").format(exec_ip)) @@ -139,7 +166,7 @@ def RedisCacheMakeSyncAtomJob(sub_pipeline: SubBuilder, act_kwargs: ActKwargs, p act_kwargs.exec_ip = data_to act_kwargs.get_redis_payload_func = RedisActPayload.get_redis_batch_replicate.__name__ sub_pipeline.add_act( - act_name=_("Redis-103-{}-建立主从关系".format(data_to)), + act_name=_("建立主从关系-{}".format(data_to)), act_component_code=ExecuteDBActuatorScriptComponent.code, kwargs=asdict(act_kwargs), ) @@ -159,7 +186,7 @@ def RedisCacheMakeSyncAtomJob(sub_pipeline: SubBuilder, act_kwargs: ActKwargs, p act_kwargs.exec_ip = data_to act_kwargs.get_redis_payload_func = RedisActPayload.get_redis_batch_replicate.__name__ sub_pipeline.add_act( - act_name=_("Redis-104-{}-建立主从关系".format(data_to)), + act_name=_("建立主从关系-{}".format(data_to)), act_component_code=ExecuteDBActuatorScriptComponent.code, kwargs=asdict(act_kwargs), ) @@ -179,66 +206,122 @@ def RedisSSDMakeSyncAtomJob(sub_pipeline: SubBuilder, act_kwargs: ActKwargs, par 3. 调用GSE 远程传输文件 (from master to slave) 4. Actor: restore dr 任务 """ - # 创建同步关系 - act_kwargs.cluster["ms_link"] = [] - for sync_direct in params["ins_link"]: - data_from = "origin_1" - data_to = params["sync_dst1"] - act_kwargs.cluster["ms_link"].append( - { - "master_ip": params[data_from], - "master_port": sync_direct[data_from], - "slave_ip": data_to, - "slave_port": sync_direct["sync_dst1"], - } - ) - backup_and_restore(sub_pipeline, act_kwargs, params) - if params["sync_type"] in [SyncType.SYNC_MMS, SyncType.SYNC_SMS]: - data_from = "sync_dst1" - data_to = params["sync_dst2"] - act_kwargs.cluster["ms_link"].append( - { - "master_ip": params[data_from], - "master_port": sync_direct[data_from], - "slave_ip": data_to, - "slave_port": sync_direct["sync_dst2"], - } - ) - backup_and_restore(sub_pipeline, act_kwargs, params) - - return sub_pipeline + data_from = "origin_1" + data_to = "sync_dst1" + backup_and_restore(sub_pipeline, act_kwargs, params, data_from, data_to) + if params["sync_type"] in [SyncType.SYNC_MMS, SyncType.SYNC_SMS]: + sub_kwargs = deepcopy(act_kwargs) + data_from = "sync_dst1" + data_to = "sync_dst2" + backup_and_restore(sub_pipeline, sub_kwargs, params, data_from, data_to) -# def make_sync_param(srcd, dst: str, params: Dict) -> Dict: -# return { -# "master_ip": srcd.split(IP_PORT_DIVIDER)[0], -# "master_port": srcd.split(IP_PORT_DIVIDER)[1], -# "slave_ip": dst.split(IP_PORT_DIVIDER)[0], -# "slave_port": dst.split(IP_PORT_DIVIDER)[1], -# } + return sub_pipeline def backup_and_restore( - sub_pipeline: SubBuilder, - act_kwargs: ActKwargs, - params: Dict, + sub_pipeline: SubBuilder, act_kwargs: ActKwargs, params: Dict, data_from: str, data_to: str ) -> SubBuilder: """### 封装 备份、远程传输文件、以及恢复实例 TODO""" + # params (Dict): { + # "sync_type": (ms,mms,sms) + # "origin_1": "x.12.1.2", # old_master + # "origin_2": "x.12.1.2", # old_slave + # "sync_dst1":"1.1.1.x", # new_master + # "sync_dst2":"2.2.x.1", # new_slave + # "ins_link":[{"origin_1":"port","origin_2":"port","sync_dst1":"port","sync_dst2":"port"}], + # } + logger.info("need do make sync for ssd type from {} 2 {}".format(params[data_from], params[data_to])) + # 发起备份 - act_kwargs.exec_ip = params["slave_ip"] - act_kwargs.cluster["exec_ip"] = params["slave_ip"] - act_kwargs.cluster["backup_instance"] = params["slave_port"] - act_kwargs.cluster["ssd_log_count"] = {"log-count": 6600000, "slave-log-keep-count": 6600000} - act_kwargs.cluster["bk_biz_id"] = act_kwargs.cluster["bk_biz_id"] + act_kwargs.exec_ip = params[data_from] + act_kwargs.cluster["backup_host"] = params[data_from] + act_kwargs.cluster["backup_instances"] = [] + act_kwargs.cluster["ssd_log_count"] = { + "log-count": 600000, + "log-keep-count": 7650000, + "slave-log-keep-count": 7650000, + } act_kwargs.cluster["domain_name"] = act_kwargs.cluster["immute_domain"] + for sync_direct in params["ins_link"]: + act_kwargs.cluster["backup_instances"].append(int(sync_direct[data_from])) act_kwargs.get_redis_payload_func = RedisActPayload.redis_cluster_backup_4_scene.__name__ sub_pipeline.add_act( - act_name=_("Redis-103-{}-发起备份").format(params["slave_ip"]), + act_name=_("发起备份-{}").format(params[data_from]), act_component_code=ExecuteDBActuatorScriptComponent.code, kwargs=asdict(act_kwargs), + write_payload_var="tendis_backup_info", ) # 远程传输文件 + act_kwargs.cluster["soruce_ip"] = params[data_from] + act_kwargs.cluster["target_ip"] = params[data_to] + act_kwargs.exec_ip = params[data_to] + sub_pipeline.add_act( + act_name=_("发送备份文件-{}==>>{}").format(params[data_from], params[data_to]), + act_component_code=RedisBackupFileTransComponent.code, + kwargs=asdict(act_kwargs), + ) # 恢复备份 + act_kwargs.cluster["master_ip"] = params[data_from] + act_kwargs.cluster["slave_ip"] = params[data_to] + act_kwargs.cluster["slave_ports"] = [int(sync_direct[data_to]) for sync_direct in params["ins_link"]] + act_kwargs.cluster["master_ports"] = [int(sync_direct[data_from]) for sync_direct in params["ins_link"]] + act_kwargs.get_redis_payload_func = RedisActPayload.redis_tendisssd_dr_restore_4_scene.__name__ + sub_pipeline.add_act( + act_name=_("恢复备份-{}").format(params[data_to]), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(act_kwargs), + ) + + return sub_pipeline + + +def RedisClusterMakeSyncAtomJob(sub_pipeline: SubBuilder, sub_kwargs: ActKwargs, params: Dict) -> SubBuilder: + """ + 1. cluster meet + 2. cluster replicateat + params (Dict): { + "sync_type": (ms,mms,sms) + "origin_1": "x.12.1.2", # old_master + "sync_dst1":"1.1.1.x", # new_master + "ins_link":[{"origin_1":"port","origin_2":"port","sync_dst1":"port","sync_dst2":"port"}], + } + """ + act_kwargs = deepcopy(sub_kwargs) + data_from = "origin_1" + data_to = "sync_dst1" + + # 第一步 新节点加入集群 + act_kwargs.exec_ip = params[data_to] + act_kwargs.cluster["meet_instances"] = [ + {"master_ip": params[data_to], "master_port": int(portlink[data_from])} for portlink in params["ins_link"] + ] + act_kwargs.get_redis_payload_func = RedisActPayload.redis_cluster_meet_4_scene.__name__ + sub_pipeline.add_act( + act_name=_("加入集群-{}").format(params[data_to]), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(act_kwargs), + ) + + # 第二步 指定新节点的master + act_kwargs.cluster["ms_link"] = [] + for sync_direct in params["ins_link"]: + act_kwargs.cluster["ms_link"].append( + { + "master_ip": params[data_from], + "master_port": int(sync_direct[data_from]), + "slave_ip": params[data_to], + "slave_port": int(sync_direct[data_to]), + } + ) + act_kwargs.get_redis_payload_func = RedisActPayload.get_redis_batch_replicate.__name__ + sub_pipeline.add_act( + act_name=_("同步数据-{}".format(params[data_to])), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(act_kwargs), + ) + + return sub_pipeline diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/redis_shutdown.py b/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/redis_shutdown.py index 6e37d1b1d5..02f4cc9ff9 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/redis_shutdown.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/redis_shutdown.py @@ -9,12 +9,14 @@ specific language governing permissions and limitations under the License. """ import logging.config +from copy import deepcopy from dataclasses import asdict from typing import Dict, Optional from django.utils.translation import ugettext as _ from backend.configuration.constants import DBType +from backend.db_meta.enums import ClusterType from backend.flow.consts import DEFAULT_MONITOR_TIME, DEFAULT_REDIS_SYSTEM_CMDS from backend.flow.engine.bamboo.scene.common.builder import SubBuilder from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList @@ -24,11 +26,12 @@ from backend.flow.utils.redis.redis_act_playload import RedisActPayload from backend.flow.utils.redis.redis_context_dataclass import ActKwargs from backend.flow.utils.redis.redis_db_meta import RedisDBMeta +from backend.ticket.constants import TicketType logger = logging.getLogger("flow") -def RedisBatchShutdownAtomJob(root_id, ticket_data, act_kwargs: ActKwargs, shutdown_param: Dict) -> SubBuilder: +def RedisBatchShutdownAtomJob(root_id, ticket_data, sub_kwargs: ActKwargs, shutdown_param: Dict) -> SubBuilder: """ SubBuilder: Redis卸载原籽任务 「暂时是整机卸载」800 TODO 需要支持部分实例下架(扩缩容场景) @@ -42,16 +45,7 @@ def RedisBatchShutdownAtomJob(root_id, ticket_data, act_kwargs: ActKwargs, shutd """ sub_pipeline = SubBuilder(root_id=root_id, data=ticket_data) exec_ip = shutdown_param["ip"] - - # 下发介质包 - trans_files = GetFileList(db_type=DBType.Redis) - act_kwargs.file_list = trans_files.redis_cluster_apply_backend(act_kwargs.cluster["db_version"]) - act_kwargs.exec_ip = exec_ip - sub_pipeline.add_act( - act_name=_("Redis-801-{}-下发介质包").format(exec_ip), - act_component_code=TransFileComponent.code, - kwargs=asdict(act_kwargs), - ) + act_kwargs = deepcopy(sub_kwargs) # 监听请求。集群是先关闭再下架,所以理论上这里是没请求才对 act_kwargs.exec_ip = exec_ip @@ -63,7 +57,7 @@ def RedisBatchShutdownAtomJob(root_id, ticket_data, act_kwargs: ActKwargs, shutd # act_kwargs.cluster["ignore_keys"].extend(shutdown_param["ignore_ips"]) act_kwargs.get_redis_payload_func = RedisActPayload.redis_capturer_4_scene.__name__ sub_pipeline.add_act( - act_name=_("Redis-802-{}-请求检查").format(exec_ip), + act_name=_("请求检查-{}").format(exec_ip), act_component_code=ExecuteDBActuatorScriptComponent.code, kwargs=asdict(act_kwargs), ) @@ -75,18 +69,32 @@ def RedisBatchShutdownAtomJob(root_id, ticket_data, act_kwargs: ActKwargs, shutd act_kwargs.cluster["idle_time"] = 600 act_kwargs.get_redis_payload_func = RedisActPayload.redis_killconn_4_scene.__name__ sub_pipeline.add_act( - act_name=_("Redis-803-{}-干掉非活跃链接").format(exec_ip), + act_name=_("干掉非活跃链接-{}").format(exec_ip), act_component_code=ExecuteDBActuatorScriptComponent.code, kwargs=asdict(act_kwargs), ) + # 从集群踢掉 + if act_kwargs.cluster["cluster_type"] == ClusterType.TendisPredixyTendisplusCluster: + # 定点构造的节点没有加入集群,所以这里不能执行这个逻辑 + if act_kwargs.cluster.get("operate", "") != TicketType.REDIS_DATA_STRUCTURE_TASK_DELETE.value: + act_kwargs.cluster["forget_instances"] = [ + {"ip": exec_ip, "port": port} for port in shutdown_param["ports"] + ] + act_kwargs.get_redis_payload_func = RedisActPayload.redis_cluster_forget_4_scene.__name__ + sub_pipeline.add_act( + act_name=_("踢掉旧节点-{}").format(exec_ip), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(act_kwargs), + ) + # 下架实例 act_kwargs.exec_ip = exec_ip act_kwargs.cluster["exec_ip"] = exec_ip act_kwargs.cluster["shutdown_ports"] = shutdown_param["ports"] act_kwargs.get_redis_payload_func = RedisActPayload.redis_shutdown_4_scene.__name__ sub_pipeline.add_act( - act_name=_("Redis-804-{}-下架实例").format(exec_ip), + act_name=_("下架实例-{}").format(exec_ip), act_component_code=ExecuteDBActuatorScriptComponent.code, kwargs=asdict(act_kwargs), ) @@ -96,26 +104,24 @@ def RedisBatchShutdownAtomJob(root_id, ticket_data, act_kwargs: ActKwargs, shutd { "bk_biz_id": str(act_kwargs.cluster["bk_biz_id"]), "bk_cloud_id": act_kwargs.bk_cloud_id, - "domain": act_kwargs.cluster["immute_domain"], + # "domain": act_kwargs.cluster["immute_domain"], } ] act_kwargs.get_redis_payload_func = RedisActPayload.bkdbmon_install.__name__ sub_pipeline.add_act( - act_name=_("Redis-805-{}-卸载监控").format(exec_ip), + act_name=_("卸载监控-{}").format(exec_ip), act_component_code=ExecuteDBActuatorScriptComponent.code, kwargs=asdict(act_kwargs), ) # 清理元数据 @这里如果是master, 需要等slave 清理后才能执行 - act_kwargs.cluster = { - "meta_func_name": RedisDBMeta.instances_uninstall.__name__, - "ports": shutdown_param["ports"], - "ip": exec_ip, - "bk_cloud_id": act_kwargs.bk_cloud_id, - "created_by": ticket_data["created_by"], - } + act_kwargs.cluster["meta_func_name"] = RedisDBMeta.instances_uninstall.__name__ + act_kwargs.cluster["ports"] = shutdown_param["ports"] + act_kwargs.cluster["ip"] = exec_ip + act_kwargs.cluster["bk_cloud_id"] = act_kwargs.bk_cloud_id + act_kwargs.cluster["created_by"] = ticket_data["created_by"] sub_pipeline.add_act( - act_name=_("Redis-806-{}-清理元数据").format(exec_ip), + act_name=_("清理元数据-{}").format(exec_ip), act_component_code=RedisDBMetaComponent.code, kwargs=asdict(act_kwargs), ) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/redis_switch.py b/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/redis_switch.py index 4ed25fc903..57e4ba2a09 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/redis_switch.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/redis/atom_jobs/redis_switch.py @@ -17,6 +17,8 @@ from backend.configuration.constants import DBType from backend.constants import IP_PORT_DIVIDER from backend.db_meta.api.cluster import nosqlcomm +from backend.db_meta.enums import ClusterType +from backend.flow.consts import SwitchType, SyncType from backend.flow.engine.bamboo.scene.common.builder import SubBuilder from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList from backend.flow.plugins.components.collections.common.pause import PauseComponent @@ -69,19 +71,25 @@ def RedisClusterSwitchAtomJob(root_id, data, act_kwargs: ActKwargs, sync_params: } ) act_kwargs.cluster["meta_func_name"] = RedisDBMeta.redis_replace_pair.__name__ - sub_pipeline.add_act( - act_name=_("Redis-501-元数据加入集群"), act_component_code=RedisDBMetaComponent.code, kwargs=asdict(act_kwargs) - ) + if not SyncType.SYNC_MS.value == act_kwargs.cluster["switch_condition"]["sync_type"]: + sub_pipeline.add_act( + act_name=_("Redis-元数据加入集群"), act_component_code=RedisDBMetaComponent.code, kwargs=asdict(act_kwargs) + ) # 人工确认 - sub_pipeline.add_act(act_name=_("Redis-502-人工确认"), act_component_code=PauseComponent.code, kwargs={}) + if ( + act_kwargs.cluster.get("switch_option", SwitchType.SWITCH_WITH_CONFIRM.value) + == SwitchType.SWITCH_WITH_CONFIRM.value + ): + sub_pipeline.add_act(act_name=_("Redis-人工确认"), act_component_code=PauseComponent.code, kwargs={}) # 下发介质包 + act_kwargs.exec_ip = exec_ip trans_files = GetFileList(db_type=DBType.Redis) act_kwargs.file_list = trans_files.redis_dbmon() act_kwargs.cluster["exec_ip"] = exec_ip sub_pipeline.add_act( - act_name=_("Redis-003-{}-下发介质包").format(exec_ip), + act_name=_("Redis-{}-下发介质包").format(exec_ip), act_component_code=TransFileComponent.code, kwargs=asdict(act_kwargs), ) @@ -92,30 +100,40 @@ def RedisClusterSwitchAtomJob(root_id, data, act_kwargs: ActKwargs, sync_params: for sync_port in sync_host["ins_link"]: act_kwargs.cluster["switch_info"].append( { - { - "master": { - "ip": sync_host["origin_1"], - "port": sync_port["origin_1"], - }, - "slave": { - "ip": sync_host["sync_dst1"], - "port": sync_port["sync_dst1"], - }, - } + "master": { + "ip": sync_host["origin_1"], + "port": int(sync_port["origin_1"]), + }, + "slave": { + "ip": sync_host["sync_dst1"], + "port": int(sync_port["sync_dst1"]), + }, } ) act_kwargs.cluster["domain_name"] = act_kwargs.cluster["immute_domain"] act_kwargs.cluster["switch_condition"] = act_kwargs.cluster["switch_condition"] - act_kwargs.cluster["cluster_meta"] = nosqlcomm.other.get_cluster_detail( - cluster_id=act_kwargs.cluster["cluster_id"] - )[0] act_kwargs.get_redis_payload_func = RedisActPayload.redis_twemproxy_arch_switch_4_scene.__name__ sub_pipeline.add_act( - act_name=_("Redis-504-{}-实例切换").format(exec_ip), + act_name=_("Redis-{}-实例切换").format(exec_ip), act_component_code=ExecuteDBActuatorScriptComponent.code, kwargs=asdict(act_kwargs), ) + # 检查Proxy后端一致性 + if act_kwargs.cluster["cluster_type"] in [ + ClusterType.TwemproxyTendisSSDInstance, + ClusterType.TendisTwemproxyRedisInstance, + ]: + act_kwargs.cluster["instances"] = nosqlcomm.other.get_cluster_proxies( + cluster_id=act_kwargs.cluster["cluster_id"] + ) + act_kwargs.get_redis_payload_func = RedisActPayload.redis_twemproxy_backends_4_scene.__name__ + sub_pipeline.add_act( + act_name=_("Redis-{}-检查切换状态").format(exec_ip), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(act_kwargs), + ) + # 修改元数据指向,并娜动CC模块 act_kwargs.cluster["sync_relation"] = [] for sync_host in sync_params: @@ -124,17 +142,17 @@ def RedisClusterSwitchAtomJob(root_id, data, act_kwargs: ActKwargs, sync_params: { "ejector": { "ip": sync_host["origin_1"], - "port": sync_port["origin_1"], + "port": int(sync_port["origin_1"]), }, "receiver": { "ip": sync_host["sync_dst1"], - "port": sync_port["sync_dst1"], + "port": int(sync_port["sync_dst1"]), }, } ) act_kwargs.cluster["meta_func_name"] = RedisDBMeta.tendis_switch_4_scene.__name__ sub_pipeline.add_act( - act_name=_("Redis-505-元数据切换"), act_component_code=RedisDBMetaComponent.code, kwargs=asdict(act_kwargs) + act_name=_("Redis-元数据切换"), act_component_code=RedisDBMetaComponent.code, kwargs=asdict(act_kwargs) ) return sub_pipeline.build_sub_process(sub_name=_("Redis-{}-实例切换").format(act_kwargs.cluster["immute_domain"])) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_add_dts_server.py b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_add_dts_server.py new file mode 100644 index 0000000000..f895d43b15 --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_add_dts_server.py @@ -0,0 +1,134 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging.config +from dataclasses import asdict + +from django.utils.translation import ugettext as _ + +from backend.components import DBConfigApi +from backend.components.dbconfig.constants import FormatType, LevelName +from backend.configuration.constants import DBType +from backend.core.encrypt.constants import RSAConfigType +from backend.core.encrypt.handlers import RSAHandler +from backend.db_proxy.models import DBCloudProxy +from backend.flow.consts import CloudServiceName, ConfigFileEnum, ConfigTypeEnum, NameSpaceEnum +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList +from backend.flow.plugins.components.collections.redis.exec_actuator_script import ExecuteDBActuatorScriptComponent +from backend.flow.plugins.components.collections.redis.get_redis_payload import GetRedisActPayloadComponent +from backend.flow.plugins.components.collections.redis.redis_dts_server_meta import RedisDtsServerMetaComponent +from backend.flow.plugins.components.collections.redis.trans_flies import TransFileComponent +from backend.flow.utils.redis.redis_act_playload import RedisActPayload +from backend.flow.utils.redis.redis_context_dataclass import ActKwargs, CommonContext + +logger = logging.getLogger("flow") + + +class RedisAddDtsServerFlow(object): + """ + Redis 添加 DTS Server + """ + + def __init__(self, root_id, data): + self.root_id = root_id + self.data = data + + def redis_add_dts_server_flow(self): + redis_pipeline = Builder(root_id=self.root_id, data=self.data) + trans_files = GetFileList(db_type=DBType.Redis) + sub_pipelines = [] + sub_pipeline = SubBuilder(root_id=self.root_id, data=self.data) + + act_kwargs = ActKwargs() + act_kwargs.set_trans_data_dataclass = CommonContext.__name__ + act_kwargs.file_list = trans_files.redis_add_dts_server() + act_kwargs.is_update_trans_data = True + + for info in self.data["infos"]: + """ + info: {"ip": "3.3.3.1", "bk_cloud_id": 0, "bk_host_id": 2,"bk_city_name":"上海"} + """ + logger.info("redis_add_dts_server_flow info:{}".format(info)) + nginx_url = self.__get_cloud_nginx_url(info["bk_cloud_id"]) + cloud_token = self.__get_cloud_token(info["bk_cloud_id"]) + system_user_info = self.__get_system_user_info(self.data["bk_biz_id"]) + + cluster = { + **info, + "nginx_url": nginx_url, + "cloud_token": cloud_token, + **system_user_info, + } + act_kwargs.cluster = cluster + act_kwargs.exec_ip = info["ip"] + sub_pipeline.add_act( + act_name=_("DTS_Server-{}-下发介质").format(info["ip"]), + act_component_code=TransFileComponent.code, + kwargs=asdict(act_kwargs), + ) + + sub_pipeline.add_act( + act_name=_("初始化配置"), act_component_code=GetRedisActPayloadComponent.code, kwargs=asdict(act_kwargs) + ) + + sub_pipeline.add_act( + act_name=_("下发介质包"), act_component_code=TransFileComponent.code, kwargs=asdict(act_kwargs) + ) + + act_kwargs.get_redis_payload_func = RedisActPayload.get_add_dts_server_payload.__name__ + sub_pipeline.add_act( + act_name=_("DTS_Server-{}-部署").format(info["ip"]), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(act_kwargs), + ) + + sub_pipeline.add_act( + act_name=_("DTS_Server-{}-写入dbmeta").format(info["ip"]), + act_component_code=RedisDtsServerMetaComponent.code, + kwargs=asdict(act_kwargs), + ) + + sub_pipelines.append(sub_pipeline.build_sub_process(sub_name=_("ADD DTS_SERVER"))) + redis_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) + redis_pipeline.run_pipeline() + + def __get_cloud_nginx_url(self, bk_cloud_id): + """ + 获取云区域nginx地址 + """ + nginx = DBCloudProxy.objects.filter(bk_cloud_id=bk_cloud_id).last() + return "http://{}".format(nginx.internal_address) + + def __get_cloud_token(self, bk_cloud_id) -> str: + """ + 获取云区域token + """ + service_type = CloudServiceName.RedisDTS.value + db_cloud_token = f"{bk_cloud_id}_{service_type}_token" + rsa = RSAHandler.get_or_generate_rsa_in_db(RSAConfigType.PROXYPASS.value) + return RSAHandler.encrypt_password(rsa.rsa_public_key.content, db_cloud_token) + + def __get_system_user_info(self, bk_biz_id): + """ + 获取云区域系统用户信息 + """ + data = DBConfigApi.query_conf_item( + params={ + "bk_biz_id": bk_biz_id, + "level_name": LevelName.APP, + "level_value": bk_biz_id, + "conf_file": ConfigFileEnum.OS, + "conf_type": ConfigTypeEnum.OSConf, + "namespace": NameSpaceEnum.Common, + "format": FormatType.MAP, + } + ) + return {"system_user": data["content"]["user"], "system_password": data["content"]["user_pwd"]} diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_backend_scale.py b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_backend_scale.py new file mode 100644 index 0000000000..c9810080f0 --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_backend_scale.py @@ -0,0 +1,334 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging.config +from collections import defaultdict +from dataclasses import asdict +from typing import Dict, Optional + +from django.utils.translation import ugettext as _ + +from backend.configuration.constants import DBType +from backend.constants import IP_PORT_DIVIDER +from backend.db_meta.enums import InstanceRole +from backend.db_meta.enums.cluster_type import ClusterType +from backend.db_meta.models import Cluster, Machine +from backend.flow.consts import ( + DEFAULT_LAST_IO_SECOND_AGO, + DEFAULT_MASTER_DIFF_TIME, + DEFAULT_REDIS_START_PORT, + SyncType, +) +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList +from backend.flow.engine.bamboo.scene.redis.atom_jobs import ( + RedisBatchInstallAtomJob, + RedisBatchShutdownAtomJob, + RedisClusterSwitchAtomJob, + RedisMakeSyncAtomJob, +) +from backend.flow.plugins.components.collections.redis.get_redis_payload import GetRedisActPayloadComponent +from backend.flow.utils.redis.redis_context_dataclass import ActKwargs, CommonContext + +logger = logging.getLogger("flow") + + +class RedisBackendScaleFlow(object): + def __init__(self, root_id: str, data: Optional[Dict]): + """ + @param root_id : 任务流程定义的root_id + @param data : 单据传递过来的参数列表,是dict格式 + """ + self.root_id = root_id + self.data = data + + def __pre_check(self, bk_biz_id, cluster_id, master_ips, slave_ips, new_shard_num, group_num): + cluster = Cluster.objects.get(id=cluster_id, bk_biz_id=bk_biz_id) + old_shard_num = len(cluster.storageinstance_set.filter(instance_role=InstanceRole.REDIS_MASTER.value)) + ips = master_ips + slave_ips + if len(set(ips)) != len(ips): + raise Exception("have ip address has been used multiple times.") + if len(master_ips) != len(slave_ips): + raise Exception("master machine len != slave machine len.") + if len(master_ips) != group_num: + raise Exception("machine len != group_num.") + if old_shard_num != new_shard_num: + raise Exception("old_shard_num {} != new_shard_num {}.".format(old_shard_num, new_shard_num)) + if new_shard_num % group_num != 0: + raise Exception("shard_num ({}) % group_num ({}) != 0.".format(new_shard_num, group_num)) + m = Machine.objects.filter(ip__in=ips).values("ip") + if len(m) != 0: + raise Exception("[{}] is used.".format(m)) + + @staticmethod + def __get_cluster_info(bk_biz_id: int, cluster_id: int, version: str) -> dict: + """ + 获取集群现有信息 + 1. master对应的端口 {"x.x.x.1":[30000,30001]...} + 2. master、slave实例对应关系 {"x.x.x.1:30000":"x.x.x.2:30000"...} + 3. proxy实例列表 [x.x.x.3:50000...] + 4. master、slave机器对应关系 + """ + cluster = Cluster.objects.get(id=cluster_id, bk_biz_id=bk_biz_id) + + master_ip_ports_map = defaultdict(list) + ins_pair_map = defaultdict() + master_slave_map = defaultdict() + for master_obj in cluster.storageinstance_set.filter(instance_role=InstanceRole.REDIS_MASTER.value): + slave_obj = master_obj.as_ejector.get().receiver + master_ip_ports_map[master_obj.machine.ip].append(master_obj.port) + ins_pair_map["{}:{}".format(master_obj.machine.ip, master_obj.port)] = "{}:{}".format( + slave_obj.machine.ip, slave_obj.port + ) + + ifslave = master_slave_map.get(master_obj.machine.ip) + if ifslave and ifslave != slave_obj.machine.ip: + raise Exception( + "unsupport mutil slave with cluster {} 4:{}".format(cluster.immute_domain, master_obj.machine.ip) + ) + + master_slave_map[master_obj.machine.ip] = slave_obj.machine.ip + version = version or cluster.major_version + return { + "cluster_id": cluster.id, + "immute_domain": cluster.immute_domain, + "cluster_name": cluster.name, + "bk_biz_id": cluster.bk_biz_id, + "bk_cloud_id": cluster.bk_cloud_id, + "cluster_type": cluster.cluster_type, + "master_ip_ports_map": dict(master_ip_ports_map), + "ins_pair_map": dict(ins_pair_map), + "proxy_ips": [proxy_obj.machine.ip for proxy_obj in cluster.proxyinstance_set.all()], + "master_slave_map": dict(master_slave_map), + "db_version": version, + } + + def __init_builder(self, operate_name: str, info: dict): + cluster_info = self.__get_cluster_info(self.data["bk_biz_id"], info["cluster_id"], info["db_version"]) + sync_type = SyncType.SYNC_MMS # ssd sync from master + if cluster_info["cluster_type"] == ClusterType.TendisTwemproxyRedisInstance.value: + sync_type = SyncType.SYNC_SMS + + flow_data = {**info, **self.data} + for k, v in cluster_info.items(): + flow_data[k] = v + sub_pipeline = SubBuilder(root_id=self.root_id, data=flow_data) + trans_files = GetFileList(db_type=DBType.Redis) + act_kwargs = ActKwargs() + act_kwargs.set_trans_data_dataclass = CommonContext.__name__ + act_kwargs.file_list = trans_files.redis_base() + act_kwargs.is_update_trans_data = True + act_kwargs.cluster = { + **cluster_info, + "operate": operate_name, + "sync_type": sync_type, + } + act_kwargs.bk_cloud_id = cluster_info["bk_cloud_id"] + logger.info("+===+++++===current tick_data info+++++===++++ :: {}".format(act_kwargs)) + + sub_pipeline.add_act( + act_name=_("初始化配置"), act_component_code=GetRedisActPayloadComponent.code, kwargs=asdict(act_kwargs) + ) + return sub_pipeline, act_kwargs + + def generate_sync_relation(self, act_kwargs, master_ips, slave_ips, ins_num) -> list: + """ + TODO 需要重点验证这里的算法,机器分别变多变少时,得到的结果是否满足预期 + 计算新老实例对应关系 + 可能一对多,也可能多对一 + """ + sync_relations = [] + new_port_offset = 0 + new_host_index = 0 + sync_type = act_kwargs.cluster["sync_type"] + for old_master, old_master_ports in act_kwargs.cluster["master_ip_ports_map"].items(): + # old_master 或者 new_master变化了就需要append后初始化 + ins_link = [] + old_slave = "" + + old_master_ports.sort() + for old_master_port in old_master_ports: + # 获取老slave的ip和端口。这里端口不一定跟老master一致 + old_master_ins = "{}:{}".format(old_master, old_master_port) + old_slave_ins = act_kwargs.cluster["ins_pair_map"][old_master_ins] + old_slave = old_slave_ins.split(IP_PORT_DIVIDER)[0] + old_slave_port = old_slave_ins.split(IP_PORT_DIVIDER)[1] + # 新部署的实例端口肯定一致 + new_port = DEFAULT_REDIS_START_PORT + new_port_offset + ins_link.append( + { + "origin_1": int(old_master_port), + "origin_2": int(old_slave_port), + "sync_dst1": new_port, + "sync_dst2": new_port, + } + ) + new_port_offset += 1 + + if new_host_index >= len(master_ips): + raise Exception("origin cluster shard_num > new cluster shard_num. pleace use dts") + + # 如果达到了新机器需要部署的实例个数,下一个就要用新机器了。这个地方初始化一些参数 + if new_port_offset >= ins_num: + new_master = master_ips[new_host_index] + new_slave = slave_ips[new_host_index] + sync_relations.append( + { + "sync_type": sync_type, + "origin_1": old_master, + "origin_2": old_slave, + "sync_dst1": new_master, + "sync_dst2": new_slave, + "ins_link": ins_link, + } + ) + + ins_link = [] + new_port_offset = 0 + new_host_index += 1 + + # 遍历完老机器上的端口,如果ins_link里有数据,此时需要先处理一下 + if ins_link: + new_master = master_ips[new_host_index] + new_slave = slave_ips[new_host_index] + sync_relations.append( + { + "sync_type": sync_type, + "origin_1": old_master, + "origin_2": old_slave, + "sync_dst1": new_master, + "sync_dst2": new_slave, + "ins_link": ins_link, + } + ) + return sync_relations + + def generate_shutdown_ins(self, act_kwargs) -> dict: + """ + master和slave机器上的端口可能不一致,需要计算slave上的ports + """ + shutdown_ip_ports_map = defaultdict(list) + for old_master, old_master_ports in act_kwargs.cluster["master_ip_ports_map"].items(): + for old_master_port in old_master_ports: + old_master_ins = "{}:{}".format(old_master, old_master_port) + old_slave_ins = act_kwargs.cluster["ins_pair_map"][old_master_ins] + old_slave = old_slave_ins.split(IP_PORT_DIVIDER)[0] + old_slave_port = old_slave_ins.split(IP_PORT_DIVIDER)[1] + + shutdown_ip_ports_map[old_master].append(int(old_master_port)) + shutdown_ip_ports_map[old_slave].append(int(old_slave_port)) + return dict(shutdown_ip_ports_map) + + def redis_backend_scale_flow(self): + """ + redis 扩缩容流程: + 实例上架->新实例主从关系 -> 同步数据 -> 切换 -> 实例下架 + 需要注意: 这里得new_master/new_slave可以相同。需要提前处理一下 + """ + redis_pipeline = Builder(root_id=self.root_id, data=self.data) + sub_pipelines = [] + for info in self.data["infos"]: + sub_pipeline, act_kwargs = self.__init_builder(_("Redis集群扩缩容"), info) + # 初始化计算一些常用参数 + ins_num = int(info["shard_num"] / info["group_num"]) + new_master_ips = [] + new_slave_ips = [] + new_ports = [] + for group_info in info["backend_group"]: + new_master_ips.append(group_info["master"]["ip"]) + new_slave_ips.append(group_info["slave"]["ip"]) + for i in range(0, ins_num): + new_ports.append(DEFAULT_REDIS_START_PORT + i) + + self.__pre_check( + self.data["bk_biz_id"], + info["cluster_id"], + new_master_ips, + new_slave_ips, + info["shard_num"], + info["group_num"], + ) + # 安装实例 + redis_install_sub_pipelines = [] + params = { + "meta_role": InstanceRole.REDIS_MASTER.value, + "start_port": DEFAULT_REDIS_START_PORT, + "ports": new_ports, + "instance_numb": ins_num, + "spec_id": info["resource_spec"]["master"]["id"], + "spec_config": info["resource_spec"]["master"], + } + for ip in new_master_ips: + params["ip"] = ip + redis_install_sub_pipelines.append( + RedisBatchInstallAtomJob(self.root_id, self.data, act_kwargs, params) + ) + + params = { + "meta_role": InstanceRole.REDIS_SLAVE.value, + "start_port": DEFAULT_REDIS_START_PORT, + "ports": new_ports, + "instance_numb": ins_num, + "spec_id": info["resource_spec"]["slave"]["id"], + "spec_config": info["resource_spec"]["slave"], + } + for ip in new_slave_ips: + params["ip"] = ip + redis_install_sub_pipelines.append( + RedisBatchInstallAtomJob(self.root_id, self.data, act_kwargs, params) + ) + sub_pipeline.add_parallel_sub_pipeline(sub_flow_list=redis_install_sub_pipelines) + + # 计算同步参数 + sync_relations = self.generate_sync_relation( + act_kwargs=act_kwargs, master_ips=new_master_ips, slave_ips=new_slave_ips, ins_num=ins_num + ) + + redis_sync_sub_pipelines = [] + for sync_params in sync_relations: + if act_kwargs.cluster["cluster_type"] == ClusterType.TendisTwemproxyRedisInstance: + pass + sub_builder = RedisMakeSyncAtomJob(self.root_id, self.data, act_kwargs, sync_params) + redis_sync_sub_pipelines.append(sub_builder) + sub_pipeline.add_parallel_sub_pipeline(sub_flow_list=redis_sync_sub_pipelines) + + # TODO 增加一个等待节点 + # 进行切换 + act_kwargs.cluster["cluster_id"] = info["cluster_id"] + act_kwargs.cluster["switch_condition"] = { + "is_check_sync": True, # 不强制切换 + "slave_master_diff_time": DEFAULT_MASTER_DIFF_TIME, + "last_io_second_ago": DEFAULT_LAST_IO_SECOND_AGO, + "can_write_before_switch": True, + "sync_type": act_kwargs.cluster["sync_type"], + } + sub_builder = RedisClusterSwitchAtomJob(self.root_id, self.data, act_kwargs, sync_relations) + sub_pipeline.add_sub_pipeline(sub_flow=sub_builder) + + # 下架老实例 + redis_shutdown_sub_pipelines = [] + act_kwargs.cluster["created_by"] = self.data["created_by"] + shutdown_ip_ports = self.generate_shutdown_ins(act_kwargs) + for ip, ports in shutdown_ip_ports.items(): + params = { + "ip": ip, + "ports": ports, + } + redis_shutdown_sub_pipelines.append( + RedisBatchShutdownAtomJob(self.root_id, self.data, act_kwargs, params) + ) + + sub_pipeline.add_parallel_sub_pipeline(sub_flow_list=redis_shutdown_sub_pipelines) + sub_pipelines.append( + sub_pipeline.build_sub_process(sub_name=_("{}backend扩缩容").format(act_kwargs.cluster["cluster_name"])) + ) + + redis_pipeline.add_parallel_sub_pipeline(sub_pipelines) + redis_pipeline.run_pipeline() diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_add_slave.py b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_add_slave.py new file mode 100644 index 0000000000..77cdeeffbd --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_add_slave.py @@ -0,0 +1,278 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging.config +from collections import defaultdict +from copy import deepcopy +from dataclasses import asdict +from typing import Dict, Optional + +from django.utils.translation import ugettext as _ + +from backend.configuration.constants import DBType +from backend.constants import IP_PORT_DIVIDER +from backend.db_meta.enums import InstanceRole, InstanceStatus +from backend.db_meta.models import Cluster +from backend.flow.consts import DEFAULT_REDIS_START_PORT, SyncType +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList +from backend.flow.engine.bamboo.scene.redis.atom_jobs.redis_install import RedisBatchInstallAtomJob +from backend.flow.engine.bamboo.scene.redis.atom_jobs.redis_makesync import RedisMakeSyncAtomJob +from backend.flow.engine.bamboo.scene.redis.atom_jobs.redis_shutdown import RedisBatchShutdownAtomJob +from backend.flow.plugins.components.collections.redis.get_redis_payload import GetRedisActPayloadComponent +from backend.flow.plugins.components.collections.redis.redis_db_meta import RedisDBMetaComponent +from backend.flow.utils.redis.redis_context_dataclass import ActKwargs, CommonContext +from backend.flow.utils.redis.redis_db_meta import RedisDBMeta +from backend.flow.utils.redis.redis_proxy_util import get_cache_backup_mode, get_twemproxy_cluster_server_shards + +logger = logging.getLogger("flow") + + +class RedisClusterAddSlaveFlow(object): + """ + redis集群添加slave + """ + + def __init__(self, root_id: str, data: Optional[Dict]): + """ + @param root_id : 任务流程定义的root_id + @param data : 单据传递过来的参数列表,是dict格式 + """ + self.root_id = root_id + self.data = data + self.precheck() + + def get_cluster_info(self, bk_biz_id, cluster_id): + cluster = Cluster.objects.get(id=cluster_id, bk_biz_id=bk_biz_id) + + master_ports, slave_ports = defaultdict(list), defaultdict(list) + ins_pair_map, slave_ins_map = defaultdict(), defaultdict() + master_slave_map, slave_master_map = defaultdict(), defaultdict() + + for master_obj in cluster.storageinstance_set.filter(instance_role=InstanceRole.REDIS_MASTER.value): + master_ports[master_obj.machine.ip].append(master_obj.port) + if master_obj.as_ejector and master_obj.as_ejector.first(): + my_slave_obj = master_obj.as_ejector.get().receiver + slave_ports[my_slave_obj.machine.ip].append(my_slave_obj.port) + ins_pair_map[ + "{}{}{}".format(master_obj.machine.ip, IP_PORT_DIVIDER, master_obj.port) + ] = "{}{}{}".format(my_slave_obj.machine.ip, IP_PORT_DIVIDER, my_slave_obj.port) + ifslave = master_slave_map.get(master_obj.machine.ip) + if ifslave and ifslave != my_slave_obj.machine.ip: + raise Exception( + "unsupport mutil slave with cluster {} 4:{}".format( + cluster.immute_domain, master_obj.machine.ip + ) + ) + else: + master_slave_map[master_obj.machine.ip] = my_slave_obj.machine.ip + + slave_ins_map[ + "{}{}{}".format(my_slave_obj.machine.ip, IP_PORT_DIVIDER, my_slave_obj.port) + ] = "{}{}{}".format(master_obj.machine.ip, IP_PORT_DIVIDER, master_obj.port) + + ifmaster = slave_master_map.get(my_slave_obj.machine.ip) + if ifmaster and ifmaster != master_obj.machine.ip: + raise Exception( + "unsupport mutil master for cluster {}:{}".format( + cluster.immute_domain, my_slave_obj.machine.ip + ) + ) + else: + slave_master_map[my_slave_obj.machine.ip] = master_obj.machine.ip + return { + "immute_domain": cluster.immute_domain, + "bk_biz_id": str(cluster.bk_biz_id), + "bk_cloud_id": cluster.bk_cloud_id, + "cluster_type": cluster.cluster_type, + "cluster_name": cluster.name, + "cluster_id": cluster.id, + "slave_ports": dict(slave_ports), + "master_ports": dict(master_ports), + "ins_pair_map": dict(ins_pair_map), + "slave_ins_map": dict(slave_ins_map), + "slave_master_map": dict(slave_master_map), + "master_slave_map": dict(master_slave_map), + "proxy_port": cluster.proxyinstance_set.first().port, + "proxy_ips": [proxy_obj.machine.ip for proxy_obj in cluster.proxyinstance_set.all()], + "db_version": cluster.major_version, + } + + def add_slave_flow(self): + redis_pipeline = Builder(root_id=self.root_id, data=self.data) + + trans_files = GetFileList(db_type=DBType.Redis) + act_kwargs = ActKwargs() + act_kwargs.set_trans_data_dataclass = CommonContext.__name__ + act_kwargs.file_list = trans_files.redis_base() + act_kwargs.is_update_trans_data = True + bk_biz_id = self.data["bk_biz_id"] + sub_pipelines = [] + for input_item in self.data["infos"]: + sub_pipeline = SubBuilder(root_id=self.root_id, data=self.data) + cluster_kwargs = deepcopy(act_kwargs) + cluster_info = self.get_cluster_info(bk_biz_id, input_item["cluster_id"]) + cluster_kwargs.cluster.update(cluster_info) + cluster_kwargs.cluster["created_by"] = self.data["created_by"] + + newslave_to_master = {} + for host_pair in input_item["pairs"]: + master_ip = host_pair["redis_master"]["ip"] + for new_slave_item in host_pair["redis_slave"]: + for port in cluster_info["master_ports"][master_ip]: + newslave_to_master[ + "{}{}{}".format(new_slave_item["ip"], IP_PORT_DIVIDER, port) + ] = "{}{}{}".format(master_ip, IP_PORT_DIVIDER, port) + + twemproxy_server_shards = get_twemproxy_cluster_server_shards( + bk_biz_id, input_item["cluster_id"], newslave_to_master + ) + + sub_pipeline.add_act( + act_name=_("初始化配置-{}".format(cluster_info["immute_domain"])), + act_component_code=GetRedisActPayloadComponent.code, + kwargs=asdict(cluster_kwargs), + ) + child_pipelines = [] + for host_pair in input_item["pairs"]: + master_ip = host_pair["redis_master"]["ip"] + for new_slave_item in host_pair["redis_slave"]: + install_builder = RedisBatchInstallAtomJob( + root_id=self.root_id, + ticket_data=self.data, + sub_kwargs=cluster_kwargs, + param={ + "ip": new_slave_item["ip"], + "meta_role": InstanceRole.REDIS_SLAVE.value, + "start_port": DEFAULT_REDIS_START_PORT, + "ports": cluster_info["master_ports"][master_ip], + "instance_numb": len(cluster_info["master_ports"][master_ip]), + "spec_id": input_item["resource_spec"][master_ip].get("id", 0), + "spec_config": input_item["resource_spec"][master_ip], + "server_shards": twemproxy_server_shards.get(new_slave_item["ip"], {}), + "cache_backup_mode": get_cache_backup_mode(bk_biz_id, input_item["cluster_id"]), + }, + ) + child_pipelines.append(install_builder) + sub_pipeline.add_parallel_sub_pipeline(child_pipelines) + + child_pipelines = [] + for host_pair in input_item["pairs"]: + master_ip = host_pair["redis_master"]["ip"] + for new_slave_item in host_pair["redis_slave"]: + sync_param = { + "sync_type": SyncType.SYNC_MS, + "origin_1": master_ip, + "sync_dst1": new_slave_item["ip"], + "ins_link": [], + "server_shards": twemproxy_server_shards.get(new_slave_item["ip"], {}), + "cache_backup_mode": get_cache_backup_mode(bk_biz_id, input_item["cluster_id"]), + } + for port in cluster_info["master_ports"][master_ip]: + sync_param["ins_link"].append({"origin_1": str(port), "sync_dst1": str(port)}) + sync_builder = RedisMakeSyncAtomJob( + root_id=self.root_id, ticket_data=self.data, sub_kwargs=cluster_kwargs, params=sync_param + ) + child_pipelines.append(sync_builder) + sub_pipeline.add_parallel_sub_pipeline(child_pipelines) + + # 新节点加入集群 ################################################################################ + cluster_kwargs.cluster["meta_func_name"] = RedisDBMeta.redis_redo_slaves.__name__ + cluster_kwargs.cluster["old_slaves"] = [] + cluster_kwargs.cluster["created_by"] = self.data["created_by"] + cluster_kwargs.cluster["tendiss"] = [] + child_pipelines = [] + for host_pair in input_item["pairs"]: + master_ip = host_pair["redis_master"]["ip"] + old_slave_ip = cluster_info["master_slave_map"].get(master_ip) + if old_slave_ip: + old_slave_ports = cluster_info["slave_ports"][old_slave_ip] + cluster_kwargs.cluster["old_slaves"].append({"ip": old_slave_ip, "ports": old_slave_ports}) + for new_slave_item in host_pair["redis_slave"]: + for port in cluster_info["master_ports"][master_ip]: + cluster_kwargs.cluster["tendiss"].append( + { + "ejector": { + "ip": master_ip, + "port": port, + }, + "receiver": {"ip": new_slave_item["ip"], "port": port}, + } + ) + sub_pipeline.add_act( + act_name=_("Redis-新节点加入集群"), + act_component_code=RedisDBMetaComponent.code, + kwargs=asdict(cluster_kwargs), + ) + + # #### 下架旧实例 ############################################################################ + child_pipelines = [] + for host_pair in input_item["pairs"]: + master_ip = host_pair["redis_master"]["ip"] + old_slave_ip = cluster_info["master_slave_map"].get(master_ip) + if old_slave_ip: + old_slave_ports = cluster_info["slave_ports"][old_slave_ip] + shutdown_builder = RedisBatchShutdownAtomJob( + self.root_id, + self.data, + cluster_kwargs, + { + "ignore_ips": [], + "ip": old_slave_ip, + "ports": old_slave_ports, + }, + ) + child_pipelines.append(shutdown_builder) + sub_pipeline.add_parallel_sub_pipeline(child_pipelines) + + sub_pipelines.append( + sub_pipeline.build_sub_process(sub_name=_("Redis-{}-新建从库").format(cluster_info["immute_domain"])) + ) + redis_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) + return redis_pipeline.run_pipeline() + + def precheck(self): + """ + a. 检查集群是否存在 + b. 检查集群中主节点是否存在 + c. 检查主节点是否有running的从节点 + """ + bk_biz_id = self.data["bk_biz_id"] + for input_item in self.data["infos"]: + try: + cluster = Cluster.objects.get(bk_biz_id=bk_biz_id, id=input_item["cluster_id"]) + except Cluster.DoesNotExist: + raise Exception("redis cluster {} does not exist".format(input_item["cluster_id"])) + + for host_pair in input_item["pairs"]: + master_insts = cluster.storageinstance_set.filter( + machine__ip=host_pair["redis_master"]["ip"], instance_role=InstanceRole.REDIS_MASTER.value + ) + if not master_insts: + raise Exception( + "master {} does not exist in cluster {}".format( + host_pair["redis_master"]["ip"], cluster.immute_domain + ) + ) + running_slaves_cnt = 0 + for master_obj in master_insts: + # running的slave 个数与 master 个数不相同,则可以继续执行 + # 换句话说:存在redis_master 没有 slave 或者 某个slave不是 RUNNING状态,就能继续执行 + if master_obj.as_ejector and master_obj.as_ejector.first(): + slave_obj = master_obj.as_ejector.get().receiver + if slave_obj.status == InstanceStatus.RUNNING: + running_slaves_cnt += 1 + if running_slaves_cnt >= len(master_insts): + raise Exception( + "master({}) in cluster {} all instances has a running slave".format( + master_obj.machine.ip, + cluster.immute_domain, + ) + ) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_apply_flow.py b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_apply_flow.py index cb6ab64bba..98fb474887 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_apply_flow.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_apply_flow.py @@ -9,7 +9,9 @@ specific language governing permissions and limitations under the License. """ import copy +import json import logging.config +from collections import defaultdict from dataclasses import asdict from typing import Dict, Optional @@ -17,6 +19,7 @@ from backend.db_meta.enums import InstanceRole from backend.db_meta.enums.cluster_type import ClusterType +from backend.db_meta.models import Cluster, Machine from backend.flow.consts import DEFAULT_REDIS_START_PORT, DEFAULT_TWEMPROXY_SEG_TOTOL_NUM, ClusterStatus, DnsOpType from backend.flow.engine.bamboo.scene.common.builder import Builder from backend.flow.engine.bamboo.scene.redis.atom_jobs import ProxyBatchInstallAtomJob, RedisBatchInstallAtomJob @@ -28,6 +31,8 @@ from backend.flow.utils.redis.redis_act_playload import RedisActPayload from backend.flow.utils.redis.redis_context_dataclass import ActKwargs, CommonContext, DnsKwargs from backend.flow.utils.redis.redis_db_meta import RedisDBMeta +from backend.flow.utils.redis.redis_proxy_util import get_cache_backup_mode +from backend.flow.utils.redis.redis_util import check_domain logger = logging.getLogger("flow") @@ -45,18 +50,59 @@ def __init__(self, root_id: str, data: Optional[Dict]): self.root_id = root_id self.data = data - def cal_twemproxy_serveres(self, master_ips, shard_num, inst_num, name) -> list: + # 兼容手工部署,填充fake的规格信息,将master/slave进行分组。TODO:去掉手动部署后需要废弃 + if "resource_spec" not in self.data: + self.data["resource_spec"] = { + "master": {"id": 0}, + "slave": {"id": 0}, + "proxy": {"id": 0}, + } + self.data["nodes"]["backend_group"] = [] + for index in range(len(self.data["nodes"]["master"])): + master, slave = self.data["nodes"]["master"][index], self.data["nodes"]["slave"][index] + self.data["nodes"]["backend_group"].append({"master": master, "slave": slave}) + + self.data["nodes"].pop("master") + self.data["nodes"].pop("slave") + + def __pre_check(self, proxy_ips, master_ips, slave_ips, group_num, shard_num, servers, domain): + """ + 前置检查,检查传参 + """ + ips = proxy_ips + master_ips + slave_ips + if len(set(ips)) != len(ips): + raise Exception("have ip address has been used multiple times.") + if len(master_ips) != len(slave_ips): + raise Exception("master machine len != slave machine len.") + if len(master_ips) != group_num: + raise Exception("machine len != group_num.") + if len(servers) != shard_num: + raise Exception("servers len ({}) != shard_num ({}).".format(len(servers), shard_num)) + if shard_num % group_num != 0: + raise Exception("shard_num ({}) % group_num ({}) != 0.".format(shard_num, group_num)) + if not check_domain(domain): + raise Exception("domain[{}] is illegality.".format(domain)) + d = Cluster.objects.filter(immute_domain=domain).values("immute_domain") + if len(d) != 0: + raise Exception("domain [{}] is used.".format(domain)) + m = Machine.objects.filter(ip__in=ips).values("ip") + if len(m) != 0: + raise Exception("[{}] is used.".format(m)) + + def cal_twemproxy_serveres(self, master_ips, slave_ips, shard_num, inst_num, name): """ 计算twemproxy的servers 列表 - redisip:redisport:1 app beginSeg-endSeg 1 - "servers": ["1.1.1.1:30000 xxx 0-219999 1","1.1.1.1:30001 xxx 220000-419999 1"] + "servers": ["x.x.x.x:30000 xxx 0-219999 1","x.x.x.x:30001 xxx 220000-419999 1"] """ seg_num = DEFAULT_TWEMPROXY_SEG_TOTOL_NUM // shard_num seg_no = 0 # 计算分片 servers = [] - for _index, ip in enumerate(master_ips): + twemproxy_server_shards = defaultdict(dict) + for _index, master_ip in enumerate(master_ips): + slave_ip = slave_ips[_index] for inst_no in range(0, inst_num): port = DEFAULT_REDIS_START_PORT + inst_no begin_seg = seg_no * seg_num @@ -64,9 +110,15 @@ def cal_twemproxy_serveres(self, master_ips, shard_num, inst_num, name) -> list: if _index == len(master_ips) - 1: if inst_no == inst_num - 1 and end_seg != DEFAULT_TWEMPROXY_SEG_TOTOL_NUM: end_seg = DEFAULT_TWEMPROXY_SEG_TOTOL_NUM - 1 + if begin_seg >= DEFAULT_TWEMPROXY_SEG_TOTOL_NUM or end_seg >= DEFAULT_TWEMPROXY_SEG_TOTOL_NUM: + raise Exception("cal_twemproxy_serveres error. pleace check params") seg_no = seg_no + 1 - servers.append("{}:{} {} {}-{} {}".format(ip, port, name, begin_seg, end_seg, 1)) - return servers + servers.append("{}:{} {} {}-{} {}".format(master_ip, port, name, begin_seg, end_seg, 1)) + master_inst = "{}:{}".format(master_ip, port) + slave_inst = "{}:{}".format(slave_ip, port) + twemproxy_server_shards[master_ip][master_inst] = "{}-{}".format(begin_seg, end_seg) + twemproxy_server_shards[slave_ip][slave_inst] = "{}-{}".format(begin_seg, end_seg) + return servers, twemproxy_server_shards def deploy_redis_cluster_flow(self): """ @@ -79,11 +131,24 @@ def deploy_redis_cluster_flow(self): act_kwargs.bk_cloud_id = self.data["bk_cloud_id"] proxy_ips = [info["ip"] for info in self.data["nodes"]["proxy"]] - master_ips = [info["ip"] for info in self.data["nodes"]["master"]] - slave_ips = [info["ip"] for info in self.data["nodes"]["slave"]] + master_ips = [info["master"]["ip"] for info in self.data["nodes"]["backend_group"]] + slave_ips = [info["slave"]["ip"] for info in self.data["nodes"]["backend_group"]] + ins_num = self.data["shard_num"] // self.data["group_num"] ports = list(map(lambda i: i + DEFAULT_REDIS_START_PORT, range(ins_num))) - servers = self.cal_twemproxy_serveres(master_ips, self.data["shard_num"], ins_num, self.data["cluster_name"]) + servers, twemproxy_server_shards = self.cal_twemproxy_serveres( + master_ips, slave_ips, self.data["shard_num"], ins_num, self.data["cluster_name"] + ) + + self.__pre_check( + proxy_ips, + master_ips, + slave_ips, + self.data["group_num"], + self.data["shard_num"], + servers, + self.data["domain_name"], + ) cluster_tpl = { "immute_domain": self.data["domain_name"], "cluster_type": self.data["cluster_type"], @@ -112,7 +177,11 @@ def deploy_redis_cluster_flow(self): act_kwargs.cluster = copy.deepcopy(cluster_tpl) params["ip"] = ip + params["spec_id"] = int(self.data["resource_spec"]["master"]["id"]) + params["spec_config"] = self.data["resource_spec"]["master"] params["meta_role"] = InstanceRole.REDIS_MASTER.value + params["server_shards"] = twemproxy_server_shards[ip] + params["cache_backup_mode"] = get_cache_backup_mode(self.data["bk_biz_id"], 0) sub_builder = RedisBatchInstallAtomJob(self.root_id, self.data, act_kwargs, params) sub_pipelines.append(sub_builder) for ip in slave_ips: @@ -120,7 +189,11 @@ def deploy_redis_cluster_flow(self): act_kwargs.cluster = copy.deepcopy(cluster_tpl) params["ip"] = ip + params["spec_id"] = int(self.data["resource_spec"]["slave"]["id"]) + params["spec_config"] = self.data["resource_spec"]["slave"] params["meta_role"] = InstanceRole.REDIS_SLAVE.value + params["server_shards"] = twemproxy_server_shards[ip] + params["cache_backup_mode"] = get_cache_backup_mode(self.data["bk_biz_id"], 0) sub_builder = RedisBatchInstallAtomJob(self.root_id, self.data, act_kwargs, params) sub_pipelines.append(sub_builder) redis_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) @@ -163,6 +236,8 @@ def deploy_redis_cluster_flow(self): # 安装proxy子流程 sub_pipelines = [] params = { + "spec_id": int(self.data["resource_spec"]["proxy"]["id"]), + "spec_config": self.data["resource_spec"]["proxy"], "redis_pwd": self.data["redis_pwd"], "proxy_pwd": self.data["proxy_pwd"], "proxy_port": self.data["proxy_port"], @@ -180,6 +255,16 @@ def deploy_redis_cluster_flow(self): act_kwargs.cluster = { "new_proxy_ips": proxy_ips, "servers": servers, + "proxy_port": self.data["proxy_port"], + "cluster_type": self.data["cluster_type"], + "bk_biz_id": self.data["bk_biz_id"], + "bk_cloud_id": self.data["bk_cloud_id"], + "cluster_name": self.data["cluster_name"], + "cluster_alias": self.data["cluster_alias"], + "db_version": self.data["db_version"], + "immute_domain": self.data["domain_name"], + "created_by": self.data["created_by"], + "region": self.data.get("city_code", ""), "meta_func_name": RedisDBMeta.redis_make_cluster.__name__, } redis_pipeline.add_act( @@ -187,30 +272,25 @@ def deploy_redis_cluster_flow(self): ) acts_list = [] - if self.data["cluster_type"] == ClusterType.TwemproxyTendisSSDInstance.value: - act_kwargs.cluster = { - "conf": { - "maxmemory": str(self.data["maxmemory"]), - "databases": str(self.data["databases"]), - "requirepass": self.data["redis_pwd"], - } - } - else: - act_kwargs.cluster = { - "conf": { - "maxmemory": str(self.data["maxmemory"]), - "databases": str(self.data["databases"]), - "requirepass": self.data["redis_pwd"], - "cluster-enabled": ClusterStatus.REDIS_CLUSTER_NO, - } - } + act_kwargs.cluster = { + "conf": { + "maxmemory": str(self.data["maxmemory"]), + "databases": str(self.data["databases"]), + "requirepass": self.data["redis_pwd"], + }, + "db_version": self.data["db_version"], + "domain_name": self.data["domain_name"], + } + if self.data["cluster_type"] != ClusterType.TwemproxyTendisSSDInstance.value: + act_kwargs.cluster["conf"]["cluster-enabled"] = ClusterStatus.REDIS_CLUSTER_NO + act_kwargs.get_redis_payload_func = RedisActPayload.set_redis_config.__name__ acts_list.append( { "act_name": _("回写集群配置[Redis]"), "act_component_code": RedisConfigComponent.code, "kwargs": asdict(act_kwargs), - } + }, ) act_kwargs.cluster = { @@ -218,7 +298,8 @@ def deploy_redis_cluster_flow(self): "password": self.data["proxy_pwd"], "redis_password": self.data["redis_pwd"], "port": str(self.data["proxy_port"]), - } + }, + "domain_name": self.data["domain_name"], } act_kwargs.get_redis_payload_func = RedisActPayload.set_proxy_config.__name__ acts_list.append( diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_data_check_repair.py b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_data_check_repair.py new file mode 100644 index 0000000000..03e769fa19 --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_data_check_repair.py @@ -0,0 +1,228 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import base64 +import logging.config +from dataclasses import asdict +from datetime import datetime, timezone + +from django.db.models import Q +from django.utils.translation import ugettext as _ + +from backend.configuration.constants import DBType +from backend.db_meta.enums import InstanceStatus +from backend.db_meta.models import Cluster +from backend.db_services.redis.redis_dts.enums import DtsCopyType, DtsDataRepairMode, ExecuteMode +from backend.db_services.redis.redis_dts.models import TbTendisDTSJob, TbTendisDtsTask +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList +from backend.flow.plugins.components.collections.common.pause import PauseComponent +from backend.flow.plugins.components.collections.common.sleep_timer_service import SleepTimerComponent +from backend.flow.plugins.components.collections.redis.exec_actuator_script import ExecuteDBActuatorScriptComponent +from backend.flow.plugins.components.collections.redis.get_redis_payload import GetRedisActPayloadComponent +from backend.flow.plugins.components.collections.redis.trans_flies import TransFileComponent +from backend.flow.utils.mysql.mysql_act_dataclass import IfTimingAfterNowKwargs +from backend.flow.utils.redis.redis_act_playload import RedisActPayload +from backend.flow.utils.redis.redis_context_dataclass import ActKwargs, CommonContext + +logger = logging.getLogger("flow") + + +class RedisClusterDataCheckRepairFlow(object): + """ + redis集群数据校验、修复流程 + """ + + def __init__(self, root_id, data): + self.root_id = root_id + self.data = data + + def redis_cluster_data_check_repair_flow(self): + redis_pipeline = Builder(root_id=self.root_id, data=self.data) + trans_files = GetFileList(db_type=DBType.Redis) + bk_biz_id = self.data["bk_biz_id"] + execution_time: dict = self.__get_exection_time() + sub_pipelines = [] + for info in self.data["infos"]: + sub_pipeline = SubBuilder(root_id=self.root_id, data={**self.data, **execution_time}) + + if self.data["execute_mode"] == ExecuteMode.SCHEDULED_EXECUTION: + # 定时执行 + sub_pipeline.add_act( + act_name=_("定时"), + act_component_code=SleepTimerComponent.code, + kwargs=asdict(IfTimingAfterNowKwargs(True)), + ) + + act_kwargs = ActKwargs() + act_kwargs.set_trans_data_dataclass = CommonContext.__name__ + act_kwargs.file_list = trans_files.redis_base() + act_kwargs.is_update_trans_data = True + act_kwargs.cluster = self.__get_dts_job_data(info) + + sub_pipeline.add_act( + act_name=_("初始化配置"), act_component_code=GetRedisActPayloadComponent.code, kwargs=asdict(act_kwargs) + ) + + exec_ips = set() + for ip in act_kwargs.cluster["src_ips"]: + exec_ips.add(act_kwargs.cluster["actuator_exec_ip"][ip]) + log_ips_short = "" + if len(exec_ips) > 1: + log_ips_short = "{}...{}".format(list(exec_ips)[0], list(exec_ips)[-1]) + else: + log_ips_short = list(exec_ips)[0] + act_kwargs.exec_ip = list(exec_ips) + sub_pipeline.add_act( + act_name=_("下发介质包,ips:{}").format(log_ips_short), + act_component_code=TransFileComponent.code, + kwargs=asdict(act_kwargs), + ) + + acts_list = [] + for ip in act_kwargs.cluster["src_ips"]: + act_kwargs.cluster["current_src_ip"] = ip + act_kwargs.exec_ip = act_kwargs.cluster["actuator_exec_ip"][ip] + act_kwargs.get_redis_payload_func = RedisActPayload.redis_dts_datacheck_payload.__name__ + acts_list.append( + { + "act_name": _("redis dts数据校验: {}").format(ip), + "act_component_code": ExecuteDBActuatorScriptComponent.code, + "kwargs": asdict(act_kwargs), + } + ) + sub_pipeline.add_parallel_acts(acts_list) + + if self.data["data_repair_enabled"]: + if self.data["repair_mode"] == DtsDataRepairMode.MANUAL_CONFIRM: + # 人工确认 + sub_pipeline.add_act(act_name=_("数据修复人工确认"), act_component_code=PauseComponent.code, kwargs={}) + + acts_list = [] + for ip in act_kwargs.cluster["src_ips"]: + act_kwargs.cluster["current_src_ip"] = ip + act_kwargs.exec_ip = act_kwargs.cluster["actuator_exec_ip"][ip] + act_kwargs.get_redis_payload_func = RedisActPayload.redis_dts_datarepair_payload.__name__ + acts_list.append( + { + "act_name": _("redis dts数据修复: {}").format(ip), + "act_component_code": ExecuteDBActuatorScriptComponent.code, + "kwargs": asdict(act_kwargs), + } + ) + sub_pipeline.add_parallel_acts(acts_list) + + sub_pipelines.append( + sub_pipeline.build_sub_process( + sub_name=_("数据校验与修复,源集群:{} 目的集群:{}").format(info["src_cluster"], info["dst_cluster"]), + ) + ) + redis_pipeline.add_parallel_sub_pipeline(sub_pipelines) + return redis_pipeline.run_pipeline() + + def __get_exection_time(self) -> dict: + if self.data["execute_mode"] == ExecuteMode.AUTO_EXECUTION: + return {} + elif self.data["execute_mode"] == ExecuteMode.SCHEDULED_EXECUTION: + return {"timing": self.data["specified_execution_time"]} + + def __get_dst_proxy_ip(self, info: dict) -> list: + where = Q(bill_id=info["bill_id"]) & Q(src_cluster=info["src_cluster"]) & Q(dst_cluster=info["dst_cluster"]) + job_row = TbTendisDTSJob.objects.filter(where).first() + dst_cluster = Cluster.objects.get(id=job_row.dst_cluster_id) + for proxy in dst_cluster.proxyinstance_set.filter(status=InstanceStatus.RUNNING): + return [proxy.machine.ip] + + def __get_dts_job_data(self, info: dict) -> dict: + ret: dict = {} + first_task: TbTendisDtsTask = None + where = Q(bill_id=info["bill_id"]) & Q(src_cluster=info["src_cluster"]) & Q(dst_cluster=info["dst_cluster"]) + job_row = TbTendisDTSJob.objects.filter(where).first() + if not job_row: + logger.error( + "get dts job not found,bill_id:{} src_cluster:{} dst_cluster:{}".format( + info["bill_id"], + info["src_cluster"], + info["dst_cluster"], + ) + ) + raise Exception( + "get dts job not found,bill_id:{} src_cluster:{} dst_cluster:{}".format( + info["bill_id"], + info["src_cluster"], + info["dst_cluster"], + ) + ) + + # update job last data_check_and_repair info + job_row.last_data_check_repair_flow_id = self.root_id + job_row.last_data_check_repair_flow_execute_time = datetime.now(timezone.utc).astimezone() + job_row.save(update_fields=["last_data_check_repair_flow_id", "last_data_check_repair_flow_execute_time"]) + + ret["dts_copy_type"] = job_row.dts_copy_type + src_ips_set = set() + if len(info["src_instances"]) == 1 and info["src_instances"][0].upper() == "ALL": + for row in TbTendisDtsTask.objects.filter(where).all(): + if first_task is None: + first_task = row + src_ips_set.add(row.src_ip) + if row.src_ip in ret: + ret[row.src_ip].append( + {"port": row.src_port, "segment_start": row.src_seg_start, "segment_end": row.src_seg_end} + ) + else: + ret[row.src_ip] = [ + {"port": row.src_port, "segment_start": row.src_seg_start, "segment_end": row.src_seg_end} + ] + else: + for src_inst in info["src_instances"]: + src_ip, src_port = src_inst.split(":") + for row in TbTendisDtsTask.objects.filter(where).filter(src_ip=src_ip, src_port=int(src_port)).all(): + if first_task is None: + first_task = row + src_ips_set.add(row.src_ip) + if row.src_ip in ret: + ret[row.src_ip].append( + {"port": row.src_port, "segment_start": row.src_seg_start, "segment_end": row.src_seg_end} + ) + else: + ret[row.src_ip] = [ + {"port": row.src_port, "segment_start": row.src_seg_start, "segment_end": row.src_seg_end} + ] + if first_task is None: + logger.error( + "get dts task not found,bill_id:{} src_cluster:{} dst_cluster:{} src_instances:{}".format( + info["bill_id"], info["src_cluster"], info["dst_cluster"], info["src_instances"] + ) + ) + raise Exception( + "get dts task not found,bill_id:{} src_cluster:{} dst_cluster:{}".format( + info["bill_id"], info["src_cluster"], info["dst_cluster"] + ) + ) + # 如果是用户自建集群到dbm集群的迁移,则actuator执行机器为目的集群proxy ip + ret["actuator_exec_ip"] = {} + src_ips = list(src_ips_set) + if ret["dts_copy_type"] == DtsCopyType.USER_BUILT_TO_DBM.value: + proxy_ips = self.__get_dst_proxy_ip(info) + for ip in src_ips: + ret["actuator_exec_ip"][ip] = proxy_ips[0] + else: + for ip in src_ips: + ret["actuator_exec_ip"][ip] = ip + ret["src_hash_tag"] = True if first_task.src_twemproxy_hash_tag_enabled > 0 else False + ret["src_redis_password"] = base64.b64decode(first_task.src_password).decode("utf-8") + ret["src_cluster_addr"] = first_task.src_cluster + ret["dst_cluster_addr"] = first_task.dst_cluster + ret["dst_cluster_password"] = base64.b64decode(first_task.dst_password).decode("utf-8") + ret["key_white_regex"] = info["key_white_regex"] + ret["key_black_regex"] = info["key_black_regex"] + ret["src_ips"] = src_ips + return ret diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_data_copy.py b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_data_copy.py new file mode 100644 index 0000000000..a6ddb37486 --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_data_copy.py @@ -0,0 +1,1003 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import ast +import base64 +import logging.config +from collections import defaultdict +from dataclasses import asdict + +from django.db.models import Q +from django.utils.translation import ugettext as _ + +from backend.components import DRSApi +from backend.configuration.constants import DBType +from backend.constants import IP_PORT_DIVIDER +from backend.db_meta.enums import InstanceRole, InstanceStatus +from backend.db_meta.models import AppCache, Cluster +from backend.db_package.models import Package +from backend.db_services.redis.redis_dts.constants import ( + DTS_SWITCH_PREDIXY_PRECHECK, + DTS_SWITCH_TWEMPROXY_PRECHECK, + SERVERS_ADD_ETC_HOSTS, + SERVERS_DEL_ETC_HOSTS, +) +from backend.db_services.redis.redis_dts.enums import ( + DtsBillType, + DtsCopyType, + DtsDataCheckFreq, + DtsDataCheckType, + DtsOnlineSwitchType, + DtsSyncDisconnReminderFreq, + DtsSyncDisconnType, + DtsWriteMode, +) +from backend.db_services.redis.redis_dts.models import TbTendisDTSJob, TbTendisDtsTask +from backend.db_services.redis.redis_dts.util import ( + complete_redis_dts_kwargs_dst_data, + complete_redis_dts_kwargs_src_data, + get_cluster_info_by_id, + get_etc_hosts_lines_and_ips, + is_in_incremental_sync, +) +from backend.db_services.redis.rollback.models import TbTendisRollbackTasks +from backend.db_services.redis.util import ( + is_predixy_proxy_type, + is_redis_instance_type, + is_tendisplus_instance_type, + is_tendisssd_instance_type, + is_twemproxy_proxy_type, +) +from backend.flow.consts import ConfigFileEnum, MediumEnum, StateType, WriteContextOpType +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList +from backend.flow.engine.bamboo.scene.redis.atom_jobs.redis_dts import ( + redis_dst_cluster_backup_and_flush, + redis_dts_data_copy_atom_job, +) +from backend.flow.models import FlowTree +from backend.flow.plugins.components.collections.common.pause import PauseComponent +from backend.flow.plugins.components.collections.redis.exec_actuator_script import ExecuteDBActuatorScriptComponent +from backend.flow.plugins.components.collections.redis.exec_shell_script import ExecuteShellScriptComponent +from backend.flow.plugins.components.collections.redis.get_redis_payload import GetRedisActPayloadComponent +from backend.flow.plugins.components.collections.redis.redis_config import RedisConfigComponent +from backend.flow.plugins.components.collections.redis.redis_db_meta import RedisDBMetaComponent +from backend.flow.plugins.components.collections.redis.redis_dts import ( + NewDstClusterCloseJobAndWatchStatusComponent, + NewDstClusterFlushJobAndWatchStatusComponent, + NewDstClusterInstallJobAndWatchStatusComponent, + NewDstClusterShutdownJobAndWatchStatusComponent, + NewDtsOnlineSwitchJobAndWatchStatusComponent, + RedisDtsDisconnectSyncComponent, +) +from backend.flow.plugins.components.collections.redis.trans_flies import TransFileComponent +from backend.flow.utils.redis.redis_act_playload import RedisActPayload +from backend.flow.utils.redis.redis_context_dataclass import ActKwargs, RedisDtsContext, RedisDtsOnlineSwitchContext +from backend.flow.utils.redis.redis_db_meta import RedisDBMeta +from backend.flow.utils.redis.redis_proxy_util import ( + check_cluster_proxy_backends_consistent, + get_cache_backup_mode, + get_twemproxy_cluster_server_shards, +) +from backend.utils.time import datetime2str + +logger = logging.getLogger("flow") + + +class RedisClusterDataCopyFlow(object): + """ + redis集群数据复制 + """ + + def __init__(self, root_id, data): + self.root_id = root_id + self.data = data + + def redis_cluster_data_copy_flow(self): + self.data_copy_precheck() + + redis_pipeline = Builder(root_id=self.root_id, data=self.data) + trans_files = GetFileList(db_type=DBType.Redis) + bk_biz_id = self.data["bk_biz_id"] + write_mode = self.data["write_mode"] + dts_copy_type = self.__get_dts_copy_type() + sub_pipelines = [] + # redis_pipeline.add_act(act_name=_("Redis-人工确认"), act_component_code=PauseComponent.code, kwargs={}) + if self.data["ticket_type"] == DtsBillType.REDIS_CLUSTER_ROLLBACK_DATA_COPY.value: + self.data["sync_disconnect_setting"] = {} + self.data["sync_disconnect_setting"]["type"] = "" + self.data["sync_disconnect_setting"]["reminder_frequency"] = "" + + # 回档实例数据回写 不用校验 + self.data["data_check_repair_setting"] = {} + self.data["data_check_repair_setting"]["type"] = DtsDataCheckType.NO_CHECK_NO_REPAIR.value + self.data["data_check_repair_setting"]["execution_frequency"] = "" + + for info in self.data["infos"]: + sub_pipeline = SubBuilder(root_id=self.root_id, data=self.data) + act_kwargs = ActKwargs() + act_kwargs.set_trans_data_dataclass = RedisDtsContext.__name__ + act_kwargs.file_list = trans_files.redis_base() + act_kwargs.is_update_trans_data = True + act_kwargs.cluster = {} + act_kwargs.cluster["dts_bill_type"] = self.data["ticket_type"] + act_kwargs.cluster["dts_copy_type"] = dts_copy_type + act_kwargs.cluster["info"] = info + complete_redis_dts_kwargs_src_data(bk_biz_id, dts_copy_type, info, act_kwargs) + complete_redis_dts_kwargs_dst_data(self.__get_dts_biz_id(info), dts_copy_type, "", info, act_kwargs) + + if ( + dts_copy_type != DtsCopyType.COPY_TO_OTHER_SYSTEM + and write_mode == DtsWriteMode.FLUSHALL_AND_WRITE_TO_REDIS + ): + sub_pipeline.add_sub_pipeline(redis_dst_cluster_backup_and_flush(self.root_id, self.data, act_kwargs)) + + etc_hosts_param = get_etc_hosts_lines_and_ips(bk_biz_id, dts_copy_type, info, None) + + # 添加/etc/hosts + act_kwargs.exec_ip = etc_hosts_param["ip_list"] + act_kwargs.write_op = WriteContextOpType.APPEND.value + act_kwargs.cluster["shell_command"] = SERVERS_ADD_ETC_HOSTS.format(etc_hosts_param["etc_hosts_lines"]) + sub_pipeline.add_act( + act_name=_("{}等添加etc_hosts").format(etc_hosts_param["ip_list"][0]), + act_component_code=ExecuteShellScriptComponent.code, + kwargs=asdict(act_kwargs), + ) + + # 数据复制 + sub_pipeline.add_sub_pipeline(redis_dts_data_copy_atom_job(self.root_id, self.data, act_kwargs)) + + if self.data["ticket_type"] == DtsBillType.REDIS_CLUSTER_ROLLBACK_DATA_COPY.value: + act_kwargs.cluster["bill_id"] = int(self.data["uid"]) + act_kwargs.cluster["src_cluster"] = act_kwargs.cluster["src"]["cluster_addr"] + act_kwargs.cluster["dst_cluster"] = act_kwargs.cluster["dst"]["cluster_addr"] + sub_pipeline.add_act( + act_name=_("断开同步关系"), + act_component_code=RedisDtsDisconnectSyncComponent.code, + kwargs=asdict(act_kwargs), + ) + + # 删除/etc/hosts + act_kwargs.exec_ip = etc_hosts_param["ip_list"] + act_kwargs.write_op = WriteContextOpType.APPEND.value + act_kwargs.cluster["shell_command"] = SERVERS_DEL_ETC_HOSTS.format(etc_hosts_param["etc_hosts_lines"]) + sub_pipeline.add_act( + act_name=_("{}等删除etc_hosts").format(etc_hosts_param["ip_list"][0]), + act_component_code=ExecuteShellScriptComponent.code, + kwargs=asdict(act_kwargs), + ) + + sub_pipelines.append( + sub_pipeline.build_sub_process( + sub_name=_("数据复制:{}->{}").format( + act_kwargs.cluster["src"]["cluster_addr"], act_kwargs.cluster["dst"]["cluster_addr"] + ) + ) + ) + redis_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) + redis_pipeline.run_pipeline() + + def __get_dts_copy_type(self) -> str: + if self.data["ticket_type"] in [ + DtsBillType.REDIS_CLUSTER_SHARD_NUM_UPDATE.value, + DtsBillType.REDIS_CLUSTER_TYPE_UPDATE.value, + ]: + return self.data["ticket_type"] + else: + return self.data["dts_copy_type"] + + def __get_dts_biz_id(self, info: dict) -> int: + if ( + self.data["ticket_type"] == DtsBillType.REDIS_CLUSTER_DATA_COPY + and self.data["dts_copy_type"] == DtsCopyType.DIFF_APP_DIFF_CLUSTER + ): + return info["dst_bk_biz_id"] + else: + return self.data["bk_biz_id"] + + def data_copy_precheck(self): + src_cluster_set: set = set() + bk_biz_id = self.data["bk_biz_id"] + dts_copy_type = self.__get_dts_copy_type() + src_cluster: Cluster = None + dst_cluster: Cluster = None + for info in self.data["infos"]: + if info["src_cluster"] in src_cluster_set: + raise Exception(_("源集群{}重复了").format(info["src_cluster"])) + src_cluster_set.add(info["src_cluster"]) + + if dts_copy_type not in [ + DtsCopyType.USER_BUILT_TO_DBM.value, + DtsCopyType.COPY_FROM_ROLLBACK_INSTANCE.value, + ]: + # 源集群是否存在 + try: + src_cluster = Cluster.objects.get(bk_biz_id=bk_biz_id, id=int(info["src_cluster"])) + except Cluster.DoesNotExist: + raise Exception("src_cluster {} does not exist".format(info["src_cluster"])) + # 源集群是否每个running的master都有slave + running_masters = src_cluster.storageinstance_set.filter( + status=InstanceStatus.RUNNING.value, instance_role=InstanceRole.REDIS_MASTER.value + ) + for master in running_masters: + if not master.as_ejector or not master.as_ejector.first(): + master_inst = "{}:{}".format(master.machine.ip, master.port) + raise Exception(_("源集群{}存在master:{}没有slave").format(info["src_cluster"], master_inst)) + + if dts_copy_type != DtsCopyType.COPY_TO_OTHER_SYSTEM.value: + try: + dst_cluster = Cluster.objects.get(bk_biz_id=bk_biz_id, id=int(info["dst_cluster"])) + except Cluster.DoesNotExist: + raise Exception("dst_cluster {} does not exist".format(info["dst_cluster"])) + + if dts_copy_type == DtsCopyType.COPY_FROM_ROLLBACK_INSTANCE.value: + # 数据构造任务是否存在 + rollback_task: TbTendisRollbackTasks = None + recovery_time = None + try: + recovery_time = datetime2str(info["recovery_time_point"]) + rollback_task = TbTendisRollbackTasks.objects.get( + temp_cluster_proxy=info["src_cluster"], recovery_time_point=recovery_time, destroyed_status=0 + ) + except TbTendisRollbackTasks.DoesNotExist: + raise Exception( + "rollback task(src_cluster:{} recovery_time_point:{} \ + destroyed_status:0) does not exist".format( + info["src_cluster"], info["recovery_time_point"] + ) + ) + # 数据构造临时集群是否可访问 + proxy_password_bytes = ast.literal_eval(rollback_task.temp_proxy_password) + redis_password_bytes = ast.literal_eval(rollback_task.temp_redis_password) + try: + DRSApi.redis_rpc( + { + "addresses": [rollback_task.temp_cluster_proxy], + "db_num": 0, + "password": base64.b64decode(proxy_password_bytes).decode("utf-8"), + "command": "ping", + "bk_cloud_id": dst_cluster.bk_cloud_id, + } + ) + except Exception as e: + raise Exception( + "rollback task(temp_cluster_proxy:{}) redis ping failed".format( + rollback_task.temp_cluster_proxy + ) + ) + try: + DRSApi.redis_rpc( + { + "addresses": rollback_task.temp_instance_range, + "db_num": 0, + "password": base64.b64decode(redis_password_bytes).decode("utf-8"), + "command": "ping", + "bk_cloud_id": dst_cluster.bk_cloud_id, + } + ) + except Exception as e: + raise Exception( + _("数据构造临时集群存在 redis 访问失败的情况,临时集群 redis:{}").format(rollback_task.temp_instance_range) + ) + + def __get_domain_prefix_by_cluster_type(self, cluster_type: str) -> str: + if is_redis_instance_type(cluster_type): + return "cache" + elif is_tendisplus_instance_type(cluster_type): + return "tendisplus" + elif is_tendisssd_instance_type(cluster_type): + return "ssd" + return "" + + def get_dst_cluster_install_param(self, info: dict) -> dict: + install_param = {} + src_cluster_info = get_cluster_info_by_id( + bk_biz_id=self.data["bk_biz_id"], cluster_id=int(info["src_cluster"]) + ) + app_info = AppCache.objects.get(bk_biz_id=self.data["bk_biz_id"]) + install_param["bk_biz_id"] = self.data["bk_biz_id"] + install_param["bk_cloud_id"] = src_cluster_info["bk_cloud_id"] + install_param["created_by"] = self.data["created_by"] + install_param["shard_num"] = info["cluster_shard_num"] + install_param["cluster_name"] = src_cluster_info["cluster_name"] + "migrate" + install_param["cluster_alias"] = src_cluster_info["cluster_name"] + " shard num update" + install_param["cluster_type"] = info.get("target_cluster_type", src_cluster_info["cluster_type"]) + install_param["src_cluster_type"] = src_cluster_info["cluster_type"] + install_param["db_version"] = info.get("db_version", src_cluster_info["cluster_version"]) + install_param["src_db_version"] = src_cluster_info["cluster_version"] + install_param["cluster_password"] = src_cluster_info["cluster_password"] + install_param["redis_password"] = src_cluster_info["redis_password"] + install_param["redis_databases"] = src_cluster_info["redis_databases"] + install_param["max_disk"] = info["max_disk"] + install_param["maxmemory"] = info["maxmemory"] + install_param["region"] = src_cluster_info["region"] + install_param["cluster_port"] = int(src_cluster_info["cluster_port"]) + 100 + domain_prefix = self.__get_domain_prefix_by_cluster_type(install_param["cluster_type"]) + install_param["cluster_domain"] = "{}{}.{}.{}.db".format( + domain_prefix, install_param["cluster_port"], install_param["cluster_name"], app_info.db_app_abbr + ) + install_param["proxy"] = info["proxy"] + install_param["backend_group"] = info["backend_group"] + install_param["resource_spec"] = info["resource_spec"] + return install_param + + def get_db_versions_by_cluster_type(self, cluster_type: str) -> list: + if is_redis_instance_type(cluster_type): + ret = Package.objects.filter(db_type=DBType.Redis.value, pkg_type=MediumEnum.Redis.value).values_list( + "version", flat=True + ) + return list(ret) + elif is_tendisplus_instance_type(cluster_type): + ret = Package.objects.filter(db_type=DBType.Redis.value, pkg_type=MediumEnum.TendisPlus.value).values_list( + "version", flat=True + ) + return list(ret) + elif is_tendisssd_instance_type(cluster_type): + ret = Package.objects.filter(db_type=DBType.Redis.value, pkg_type=MediumEnum.TendisSsd.value).values_list( + "version", flat=True + ) + return list(ret) + raise Exception("cluster_type:{} not a redis cluster type?".format(cluster_type)) + + def shard_num_or_cluster_type_update_precheck(self): + src_cluster_set: set = set() + bk_biz_id = self.data["bk_biz_id"] + for info in self.data["infos"]: + if info["src_cluster"] in src_cluster_set: + raise Exception(_("源集群{}重复了").format(info["src_cluster"])) + src_cluster_set.add(info["src_cluster"]) + + src_cluster: Cluster = None + try: + src_cluster = Cluster.objects.get(bk_biz_id=bk_biz_id, id=int(info["src_cluster"])) + except Cluster.DoesNotExist: + raise Exception("src_cluster {} does not exist".format(info["src_cluster"])) + + if self.data["ticket_type"] == DtsBillType.REDIS_CLUSTER_SHARD_NUM_UPDATE.value: + if info["current_shard_num"] == info["cluster_shard_num"]: + raise Exception( + "current_shard_num:{} == cluster_shard_num:{}".format( + info["current_shard_num"], info["cluster_shard_num"] + ), + ) + running_masters = src_cluster.storageinstance_set.filter( + status=InstanceStatus.RUNNING.value, instance_role=InstanceRole.REDIS_MASTER.value + ) + if running_masters.count() == info["cluster_shard_num"]: + raise Exception( + "src_cluster:{} running_masters:{} == cluster_shard_num:{}".format( + src_cluster.immute_domain, running_masters.count(), info["cluster_shard_num"] + ), + ) + if info.get("db_version", "") != "": + if info["db_version"] not in self.get_db_versions_by_cluster_type(src_cluster.cluster_type): + raise Exception( + "src_cluster:{} db_version:{} not in src_cluster_type:{} db_versions:{}".format( + src_cluster.immute_domain, + info["db_version"], + src_cluster.cluster_type, + self.get_db_versions_by_cluster_type(src_cluster.cluster_type), + ) + ) + elif self.data["ticket_type"] == DtsBillType.REDIS_CLUSTER_TYPE_UPDATE.value: + if info["current_cluster_type"] == info["target_cluster_type"]: + raise Exception( + "current_cluster_type:{} == target_cluster_type:{}".format( + info["current_cluster_type"], info["target_cluster_type"] + ), + ) + if info["target_cluster_type"] == src_cluster.cluster_type: + raise Exception( + "target_cluster_type:{} == src_cluster:{} cluster_type:{}".format( + info["target_cluster_type"], src_cluster.immute_domain, src_cluster.cluster_type + ), + ) + if info.get("db_version", "") == "": + raise Exception("src_cluster:{} db_version is empty".format(info["src_cluster"])) + if info["db_version"] not in self.get_db_versions_by_cluster_type(info["target_cluster_type"]): + raise Exception( + "src_cluster:{} db_version:{} not in target_cluster_type:{} db_versions:{}".format( + info["src_cluster"], + info["db_version"], + info["target_cluster_type"], + self.get_db_versions_by_cluster_type(info["target_cluster_type"]), + ) + ) + # 检查所有 src proxys backends 一致 + check_cluster_proxy_backends_consistent(cluster_id=int(info["src_cluster"]), cluster_password="") + + def is_dst_cluster_installed(self, info: dict, taregt_param: dict) -> bool: + """ + 判断目标集群是否已经安装; + 如果集群存在,shard_num 和 密码都相同,则认为已经安装 + """ + cluster: Cluster = None + try: + cluster = Cluster.objects.get(bk_biz_id=taregt_param["bk_biz_id"], name=taregt_param["cluster_name"]) + except Cluster.DoesNotExist: + return False + running_masters = cluster.storageinstance_set.filter( + status=InstanceStatus.RUNNING.value, instance_role=InstanceRole.REDIS_MASTER.value + ) + if len(running_masters) != info["cluster_shard_num"]: + logger.error( + "target cluster {} installed,but shard_num:%{} != cluster_shard_num:{}".format( + taregt_param["cluster_name"], len(running_masters), info["cluster_shard_num"] + ) + ) + raise Exception( + "target cluster {} installed,but shard_num:%{} != cluster_shard_num:{}".format( + taregt_param["cluster_name"], len(running_masters), info["cluster_shard_num"] + ) + ) + cluster_info = get_cluster_info_by_id(bk_biz_id=cluster.bk_biz_id, cluster_id=cluster.id) + if cluster_info["cluster_password"] != taregt_param["cluster_password"]: + logger.error( + "target cluster {} installed,but proxy_password != target proxy_password".format( + taregt_param["cluster_name"] + ) + ) + raise Exception( + "target cluster {} installed,but proxy_password != target proxy_password".format( + taregt_param["cluster_name"] + ) + ) + return True + + def shard_num_or_cluster_type_update_flow(self): + self.shard_num_or_cluster_type_update_precheck() + + redis_pipeline = Builder(root_id=self.root_id, data=self.data) + trans_files = GetFileList(db_type=DBType.Redis) + bk_biz_id = self.data["bk_biz_id"] + dts_copy_type = self.__get_dts_copy_type() + self.data["dts_copy_type"] = dts_copy_type + self.data["write_mode"] = DtsWriteMode.DELETE_AND_WRITE_TO_REDIS.value + self.data["sync_disconnect_setting"] = {} + self.data["sync_disconnect_setting"]["type"] = DtsSyncDisconnType.KEEP_SYNC_WITH_REMINDER.value + self.data["sync_disconnect_setting"]["reminder_frequency"] = DtsSyncDisconnReminderFreq.ONCE_DAILY.value + # self.data["data_check_repair_setting"]["type"] = DtsDataCheckType.DATA_CHECK_AND_REPAIR.value + self.data["data_check_repair_setting"]["execution_frequency"] = DtsDataCheckFreq.ONCE_AFTER_REPLICATION.value + for info in self.data["infos"]: + act_kwargs = ActKwargs() + act_kwargs.set_trans_data_dataclass = RedisDtsContext.__name__ + act_kwargs.is_update_trans_data = True + act_kwargs.file_list = trans_files.redis_base() + act_kwargs.cluster = {} + act_kwargs.cluster["dts_bill_type"] = self.data["ticket_type"] + act_kwargs.cluster["dts_copy_type"] = dts_copy_type + dst_install_param = self.get_dst_cluster_install_param(info) + act_kwargs.cluster["dst_install_param"] = dst_install_param + act_kwargs.cluster["cluster_type"] = dst_install_param["src_cluster_type"] + self.data["db_version"] = dst_install_param["src_db_version"] + + etc_hosts_param = get_etc_hosts_lines_and_ips(bk_biz_id, dts_copy_type, info, dst_install_param) + + data_copy_info = { + "src_cluster": str(info["src_cluster"]), + "dst_cluster": dst_install_param["cluster_domain"] + + IP_PORT_DIVIDER + + str(dst_install_param["cluster_port"]), + "dst_cluster_password": dst_install_param["cluster_password"], + "key_white_regex": "*", + "key_black_regex": "", + } + complete_redis_dts_kwargs_src_data(bk_biz_id, dts_copy_type, info, act_kwargs) + complete_redis_dts_kwargs_dst_data( + self.__get_dts_biz_id(data_copy_info), + dts_copy_type, + dst_install_param["cluster_type"], + data_copy_info, + act_kwargs, + ) + redis_pipeline.add_act( + act_name=_("初始化配置"), act_component_code=GetRedisActPayloadComponent.code, kwargs=asdict(act_kwargs) + ) + + dst_cluster_installed = self.is_dst_cluster_installed(info, dst_install_param) + if not dst_cluster_installed: + # 如果目标集群未安装,则安装 + redis_pipeline.add_act( + act_name=_("集群:{}安装任务并检测任务状态").format(dst_install_param["cluster_name"]), + act_component_code=NewDstClusterInstallJobAndWatchStatusComponent.code, + kwargs=asdict(act_kwargs), + ) + else: + # 如果目标集群已安装,则备份+清理 + # redis_pipeline.add_sub_pipeline( + # redis_dst_cluster_backup_and_flush(self.root_id, self.data, act_kwargs) + # ) + redis_pipeline.add_act( + act_name=_("集群:{}清档任务并检测任务状态".format(act_kwargs.cluster["dst"]["cluster_addr"])), + act_component_code=NewDstClusterFlushJobAndWatchStatusComponent.code, + kwargs=asdict(act_kwargs), + ) + + # 添加/etc/hosts + act_kwargs.exec_ip = etc_hosts_param["ip_list"] + act_kwargs.write_op = WriteContextOpType.APPEND.value + act_kwargs.cluster["shell_command"] = SERVERS_ADD_ETC_HOSTS.format(etc_hosts_param["etc_hosts_lines"]) + redis_pipeline.add_act( + act_name=_("{}等添加etc_hosts").format(etc_hosts_param["ip_list"][0]), + act_component_code=ExecuteShellScriptComponent.code, + kwargs=asdict(act_kwargs), + ) + + act_kwargs.cluster["info"] = data_copy_info + redis_pipeline.add_sub_pipeline(redis_dts_data_copy_atom_job(self.root_id, self.data, act_kwargs)) + + if info.get("online_switch_type", "") == DtsOnlineSwitchType.USER_CONFIRM.value: + redis_pipeline.add_act(act_name=_("Redis-人工确认"), act_component_code=PauseComponent.code, kwargs={}) + + redis_pipeline.add_act( + act_name=_("在线切换任务并检测任务状态"), + act_component_code=NewDtsOnlineSwitchJobAndWatchStatusComponent.code, + kwargs=asdict(act_kwargs), + ) + redis_pipeline.add_act( + act_name=_("集群:{}禁用任务并检测任务状态").format(dst_install_param["cluster_name"]), + act_component_code=NewDstClusterCloseJobAndWatchStatusComponent.code, + kwargs=asdict(act_kwargs), + ) + redis_pipeline.add_act( + act_name=_("集群:{}下架任务并检测任务状态").format(dst_install_param["cluster_name"]), + act_component_code=NewDstClusterShutdownJobAndWatchStatusComponent.code, + kwargs=asdict(act_kwargs), + ) + + # 删除/etc/hosts + act_kwargs.exec_ip = etc_hosts_param["ip_list"] + act_kwargs.write_op = WriteContextOpType.APPEND.value + act_kwargs.cluster["shell_command"] = SERVERS_DEL_ETC_HOSTS.format(etc_hosts_param["etc_hosts_lines"]) + redis_pipeline.add_act( + act_name=_("{}等删除etc_hosts").format(etc_hosts_param["ip_list"][0]), + act_component_code=ExecuteShellScriptComponent.code, + kwargs=asdict(act_kwargs), + ) + redis_pipeline.run_pipeline() + + def __online_switch_precheck(self): + for info in self.data["infos"]: + where = ( + Q(bill_id=info["bill_id"]) & Q(src_cluster=info["src_cluster"]) & Q(dst_cluster=info["dst_cluster"]) + ) + job_row = TbTendisDTSJob.objects.filter(where).first() + if not job_row: + logger.error( + "get dts job not found,bill_id:{} src_cluster:{} dst_cluster:{}".format( + info["bill_id"], + info["src_cluster"], + info["dst_cluster"], + ) + ) + raise Exception( + "get dts job not found,bill_id:{} src_cluster:{} dst_cluster:{}".format( + info["bill_id"], + info["src_cluster"], + info["dst_cluster"], + ) + ) + if job_row.last_data_check_repair_flow_id != "": + stat = FlowTree.objects.get(root_id=job_row.last_data_check_repair_flow_id).status + if stat != StateType.FINISHED.value: + logger.error( + "dts job({}:{}->{}) last data check repair flow status:{}".format( + info["bill_id"], + info["src_cluster"], + info["dst_cluster"], + stat, + ) + ) + raise Exception( + "dts job({}:{}->{}) last data check repair flow status:{}".format( + info["bill_id"], + info["src_cluster"], + info["dst_cluster"], + stat, + ) + ) + + for row in TbTendisDtsTask.objects.filter(where).all(): + if not is_in_incremental_sync(row): + logger.error( + "dts task({}:{}->{}) not in incremental sync".format( + row.src_ip, + row.src_port, + info["dst_cluster"], + ) + ) + raise Exception( + "dts task({}:{}->{}) not in incremental sync".format( + row.src_ip, + row.src_port, + info["dst_cluster"], + ) + ) + + def __get_proxy_ips(self, cluster_id) -> list: + proxy_ips = [] + cluster = Cluster.objects.get(id=cluster_id) + for proxy in cluster.proxyinstance_set.all(): + proxy_ips.append(proxy.machine.ip) + return proxy_ips + + def __get_proxy_precheck_template(self, cluster_type: str) -> str: + precheck_template: str = "" + if is_twemproxy_proxy_type(cluster_type): + precheck_template = DTS_SWITCH_TWEMPROXY_PRECHECK + elif is_predixy_proxy_type(cluster_type): + precheck_template = DTS_SWITCH_PREDIXY_PRECHECK + return precheck_template + + def __is_proxy_type_update(self, src_cluster_type: str, dst_cluster_type: str) -> bool: + if is_twemproxy_proxy_type(src_cluster_type) and is_twemproxy_proxy_type(dst_cluster_type): + return False + if is_predixy_proxy_type(src_cluster_type) and is_predixy_proxy_type(dst_cluster_type): + return False + return True + + def __get_proxy_version_by_cluster_type(self, cluster_type: str) -> str: + if is_twemproxy_proxy_type(cluster_type): + return ConfigFileEnum.Twemproxy.value + elif is_predixy_proxy_type(cluster_type): + return ConfigFileEnum.Predixy.value + return "" + + def __get_cluster_master_slave_ports(self, bk_biz_id: int, cluster_id: int) -> dict: + cluster = Cluster.objects.get(id=cluster_id, bk_biz_id=bk_biz_id) + master_ports, slave_ports = defaultdict(list), defaultdict(list) + + for master_obj in cluster.storageinstance_set.filter(instance_role=InstanceRole.REDIS_MASTER.value): + master_ports[master_obj.machine.ip].append(master_obj.port) + if master_obj.as_ejector and master_obj.as_ejector.first(): + my_slave_obj = master_obj.as_ejector.get().receiver + slave_ports[my_slave_obj.machine.ip].append(my_slave_obj.port) + return { + "master_ports": master_ports, + "slave_ports": slave_ports, + } + + def online_switch_flow(self): + self.__online_switch_precheck() + redis_pipeline = Builder(root_id=self.root_id, data=self.data) + trans_files = GetFileList(db_type=DBType.Redis) + + for info in self.data["infos"]: + where = ( + Q(bill_id=info["bill_id"]) & Q(src_cluster=info["src_cluster"]) & Q(dst_cluster=info["dst_cluster"]) + ) + job_row = TbTendisDTSJob.objects.filter(where).first() + src_cluster_info = get_cluster_info_by_id(int(job_row.app), job_row.src_cluster_id) + src_cluster_info["proxy_ips"] = self.__get_proxy_ips(job_row.src_cluster_id) + + cluster = Cluster.objects.get(bk_biz_id=int(job_row.app), id=job_row.src_cluster_id) + src_running_master = cluster.storageinstance_set.filter( + instance_role=InstanceRole.REDIS_MASTER.value, status=InstanceStatus.RUNNING + ).first() + + dst_cluster_info = get_cluster_info_by_id(int(job_row.app), job_row.dst_cluster_id) + dst_cluster_info["proxy_ips"] = self.__get_proxy_ips(job_row.dst_cluster_id) + cluster = Cluster.objects.get(bk_biz_id=int(job_row.app), id=job_row.dst_cluster_id) + dst_running_master = cluster.storageinstance_set.filter( + instance_role=InstanceRole.REDIS_MASTER.value, status=InstanceStatus.RUNNING + ).first() + + act_kwargs = ActKwargs() + act_kwargs.set_trans_data_dataclass = RedisDtsOnlineSwitchContext.__name__ + act_kwargs.is_update_trans_data = True + act_kwargs.file_list = trans_files.redis_base() + act_kwargs.cluster = {} + + redis_pipeline.add_act( + act_name=_("初始化配置"), act_component_code=GetRedisActPayloadComponent.code, kwargs=asdict(act_kwargs) + ) + + acts_list = [] + for proxy_ip in src_cluster_info["proxy_ips"]: + sh_cmd = self.__get_proxy_precheck_template(src_cluster_info["cluster_type"]) + sh_cmd = sh_cmd.replace("{{SRC_PROXY_IP}}", proxy_ip) + sh_cmd = sh_cmd.replace("{{SRC_PROXY_PORT}}", src_cluster_info["cluster_port"]) + sh_cmd = sh_cmd.replace("{{SRC_PROXY_PASSWORD}}", src_cluster_info["cluster_password"]) + + act_kwargs.exec_ip = proxy_ip + act_kwargs.write_op = WriteContextOpType.APPEND.value + act_kwargs.cluster["shell_command"] = sh_cmd + acts_list.append( + { + "act_name": _("{} 前置检查").format(proxy_ip), + "act_component_code": ExecuteShellScriptComponent.code, + "kwargs": asdict(act_kwargs), + "write_payload_var": "src_proxy_config", + } + ) + redis_pipeline.add_parallel_acts(acts_list) + + acts_list = [] + for proxy_ip in dst_cluster_info["proxy_ips"]: + sh_cmd = self.__get_proxy_precheck_template(dst_cluster_info["cluster_type"]) + sh_cmd = sh_cmd.replace("{{SRC_PROXY_IP}}", proxy_ip) + sh_cmd = sh_cmd.replace("{{SRC_PROXY_PORT}}", dst_cluster_info["cluster_port"]) + sh_cmd = sh_cmd.replace("{{SRC_PROXY_PASSWORD}}", dst_cluster_info["cluster_password"]) + + act_kwargs.exec_ip = proxy_ip + act_kwargs.write_op = WriteContextOpType.APPEND.value + act_kwargs.cluster["shell_command"] = sh_cmd + acts_list.append( + { + "act_name": _("{} 前置检查").format(proxy_ip), + "act_component_code": ExecuteShellScriptComponent.code, + "kwargs": asdict(act_kwargs), + "write_payload_var": "dst_proxy_config", + } + ) + redis_pipeline.add_parallel_acts(acts_list) + + # 再次初始化 redis_act_payload,以便actuator中能获取前面 前置检查结果 + redis_pipeline.add_act( + act_name=_("初始化配置"), act_component_code=GetRedisActPayloadComponent.code, kwargs=asdict(act_kwargs) + ) + + # 下发 proxy 安装介质,actuator介质 + acts_list = [] + act_kwargs.file_list = trans_files.redis_cluster_apply_proxy(dst_cluster_info["cluster_type"]) + act_kwargs.exec_ip = src_cluster_info["proxy_ips"] + acts_list.append( + { + "act_name": _("{} proxys下发介质").format(src_cluster_info["cluster_domain"]), + "act_component_code": TransFileComponent.code, + "kwargs": asdict(act_kwargs), + } + ) + + act_kwargs.file_list = trans_files.redis_cluster_apply_proxy(src_cluster_info["cluster_type"]) + act_kwargs.exec_ip = dst_cluster_info["proxy_ips"] + acts_list.append( + { + "act_name": _("{} proxys下发介质").format(dst_cluster_info["cluster_domain"]), + "act_component_code": TransFileComponent.code, + "kwargs": asdict(act_kwargs), + } + ) + redis_pipeline.add_parallel_acts(acts_list) + + # src proxy 执行在线切换 + target_proxy_ip = dst_cluster_info["proxy_ips"][0] + acts_list = [] + for proxy_ip in src_cluster_info["proxy_ips"]: + act_kwargs.exec_ip = proxy_ip + act_kwargs.cluster = {} + act_kwargs.cluster["dts_bill_id"] = int(info["bill_id"]) + act_kwargs.cluster["src_proxy_ip"] = proxy_ip + act_kwargs.cluster["src_proxy_port"] = int(src_cluster_info["cluster_port"]) + act_kwargs.cluster["src_proxy_password"] = src_cluster_info["cluster_password"] + act_kwargs.cluster["src_cluster_type"] = src_cluster_info["cluster_type"] + act_kwargs.cluster["my_dst_proxy_ip"] = target_proxy_ip + act_kwargs.cluster["dst_proxy_port"] = int(dst_cluster_info["cluster_port"]) + act_kwargs.cluster["dst_proxy_password"] = dst_cluster_info["cluster_password"] + act_kwargs.cluster["dst_cluster_type"] = dst_cluster_info["cluster_type"] + act_kwargs.cluster["dst_redis_ip"] = dst_running_master.machine.ip + act_kwargs.cluster["dst_redis_port"] = int(dst_running_master.port) + act_kwargs.get_redis_payload_func = RedisActPayload.redis_dts_src_proxys_online_switch_payload.__name__ + acts_list.append( + { + "act_name": _("{} 执行在线切换").format(proxy_ip), + "act_component_code": ExecuteDBActuatorScriptComponent.code, + "kwargs": asdict(act_kwargs), + } + ) + redis_pipeline.add_parallel_acts(acts_list=acts_list) + + act_kwargs.cluster = { + "bill_id": int(info["bill_id"]), + "src_cluster": info["src_cluster"], + "dst_cluster": info["dst_cluster"], + } + redis_pipeline.add_act( + act_name=_("断开同步关系"), + act_component_code=RedisDtsDisconnectSyncComponent.code, + kwargs=asdict(act_kwargs), + ) + + # dst proxy 执行在线切换 + target_proxy_ip = src_cluster_info["proxy_ips"][0] + acts_list = [] + for proxy_ip in dst_cluster_info["proxy_ips"]: + act_kwargs.exec_ip = proxy_ip + act_kwargs.cluster = {} + act_kwargs.cluster["dts_bill_id"] = int(info["bill_id"]) + act_kwargs.cluster["src_proxy_ip"] = proxy_ip + act_kwargs.cluster["src_proxy_port"] = int(dst_cluster_info["cluster_port"]) + act_kwargs.cluster["src_proxy_password"] = dst_cluster_info["cluster_password"] + act_kwargs.cluster["src_cluster_type"] = dst_cluster_info["cluster_type"] + act_kwargs.cluster["my_dst_proxy_ip"] = target_proxy_ip + act_kwargs.cluster["dst_proxy_port"] = int(src_cluster_info["cluster_port"]) + act_kwargs.cluster["dst_proxy_password"] = src_cluster_info["cluster_password"] + act_kwargs.cluster["dst_cluster_type"] = src_cluster_info["cluster_type"] + act_kwargs.cluster["dst_redis_ip"] = src_running_master.machine.ip + act_kwargs.cluster["dst_redis_port"] = int(src_running_master.port) + act_kwargs.get_redis_payload_func = RedisActPayload.redis_dts_dst_proxys_online_switch_payload.__name__ + acts_list.append( + { + "act_name": _("{} 执行在线切换").format(proxy_ip), + "act_component_code": ExecuteDBActuatorScriptComponent.code, + "kwargs": asdict(act_kwargs), + } + ) + redis_pipeline.add_parallel_acts(acts_list=acts_list) + + # 交换源集群、目标集群的storageinstance 元数据 + act_kwargs.cluster = { + "src_cluster_id": src_cluster_info["cluster_id"], + "dst_cluster_id": dst_cluster_info["cluster_id"], + "meta_func_name": RedisDBMeta.dts_online_switch_swap_two_cluster_storage.__name__, + } + redis_pipeline.add_act( + act_name=_("交换源集群、目标集群的storageinstance 元数据"), + act_component_code=RedisDBMetaComponent.code, + kwargs=asdict(act_kwargs), + ) + + acts_list = [] + # 交换源集群、目标集群的 redis 配置 + act_kwargs.cluster = { + "bk_biz_id": int(job_row.app), + "src_cluster_domain": src_cluster_info["cluster_domain"], + "src_cluster_version": src_cluster_info["cluster_version"], + "src_cluster_type": src_cluster_info["cluster_type"], + "dst_cluster_domain": dst_cluster_info["cluster_domain"], + "dst_cluster_version": dst_cluster_info["cluster_version"], + "dst_cluster_type": dst_cluster_info["cluster_type"], + } + act_kwargs.get_redis_payload_func = RedisActPayload.dts_swap_redis_config.__name__ + acts_list.append( + { + "act_name": _("交换源集群、目标集群的 redis 配置"), + "act_component_code": RedisConfigComponent.code, + "kwargs": asdict(act_kwargs), + } + ) + + # 交换源集群、目标集群的 proxy 版本信息 + act_kwargs.cluster = { + "bk_biz_id": int(job_row.app), + "src_cluster_domain": src_cluster_info["cluster_domain"], + "src_proxy_version": self.__get_proxy_version_by_cluster_type(src_cluster_info["cluster_type"]), + "src_cluster_type": src_cluster_info["cluster_type"], + "dst_cluster_domain": dst_cluster_info["cluster_domain"], + "dst_proxy_version": self.__get_proxy_version_by_cluster_type(dst_cluster_info["cluster_type"]), + "dst_cluster_type": dst_cluster_info["cluster_type"], + } + act_kwargs.get_redis_payload_func = RedisActPayload.dts_swap_proxy_config_version.__name__ + acts_list.append( + { + "act_name": _("交换源集群、目标集群的 proxy 版本信息"), + "act_component_code": RedisConfigComponent.code, + "kwargs": asdict(act_kwargs), + } + ) + redis_pipeline.add_parallel_acts(acts_list=acts_list) + + # src_cluster的 new master/salve 重装 dbmon + app = AppCache.get_app_attr(job_row.app, "db_app_abbr") + app_name = AppCache.get_app_attr(job_row.app, "bk_biz_name") + act_kwargs.cluster = {} + act_kwargs.cluster["servers"] = [ + { + "app": app, + "app_name": app_name, + "bk_biz_id": str(job_row.app), + "bk_cloud_id": int(job_row.bk_cloud_id), + "cluster_name": src_cluster_info["cluster_name"], + "cluster_type": src_cluster_info["cluster_type"], + "cluster_domain": src_cluster_info["cluster_domain"], + } + ] + # 执行到这里重装dbmon,此时src_cluster和 dst_cluster 已经交换了 storageinstances + # 但是在确定flow 流程静态信息时,这些 master/slave 依然属于 dst_cluster + # 所以这里获取的是 dst_cluster的 master/slave ip ports + src_master_slave_ports = self.__get_cluster_master_slave_ports(int(job_row.app), job_row.dst_cluster_id) + src_twemproxy_server_shards = get_twemproxy_cluster_server_shards( + int(job_row.app), job_row.dst_cluster_id, {} + ) + acts_list = [] + for ip, ports in src_master_slave_ports["master_ports"].items(): + act_kwargs.cluster["servers"][0]["server_ip"] = ip + act_kwargs.cluster["servers"][0]["server_ports"] = ports + act_kwargs.cluster["servers"][0]["meta_role"] = InstanceRole.REDIS_MASTER.value + act_kwargs.cluster["servers"][0]["server_shards"] = src_twemproxy_server_shards.get(ip, {}) + act_kwargs.cluster["servers"][0]["cache_backup_mode"] = get_cache_backup_mode( + int(job_row.app), job_row.dst_cluster_id + ) + act_kwargs.exec_ip = ip + act_kwargs.get_redis_payload_func = RedisActPayload.bkdbmon_install.__name__ + acts_list.append( + { + "act_name": _("{} 重装 dbmon").format(ip), + "act_component_code": ExecuteDBActuatorScriptComponent.code, + "kwargs": asdict(act_kwargs), + } + ) + for ip, ports in src_master_slave_ports["slave_ports"].items(): + act_kwargs.cluster["servers"][0]["server_ip"] = ip + act_kwargs.cluster["servers"][0]["server_ports"] = ports + act_kwargs.cluster["servers"][0]["meta_role"] = InstanceRole.REDIS_SLAVE.value + act_kwargs.cluster["servers"][0]["server_shards"] = src_twemproxy_server_shards.get(ip, {}) + act_kwargs.cluster["servers"][0]["cache_backup_mode"] = get_cache_backup_mode( + int(job_row.app), job_row.dst_cluster_id + ) + act_kwargs.exec_ip = ip + act_kwargs.get_redis_payload_func = RedisActPayload.bkdbmon_install.__name__ + acts_list.append( + { + "act_name": _("{} 重装 dbmon").format(ip), + "act_component_code": ExecuteDBActuatorScriptComponent.code, + "kwargs": asdict(act_kwargs), + } + ) + redis_pipeline.add_parallel_acts(acts_list=acts_list) + + # dst_cluster的 new master/slave 重装 dbmon + act_kwargs.cluster = {} + act_kwargs.cluster["servers"] = [ + { + "app": app, + "app_name": app_name, + "bk_biz_id": str(job_row.app), + "bk_cloud_id": int(job_row.bk_cloud_id), + "cluster_name": dst_cluster_info["cluster_name"], + "cluster_type": dst_cluster_info["cluster_type"], + "cluster_domain": dst_cluster_info["cluster_domain"], + } + ] + # 执行到这里重装dbmon,此时src_cluster和 dst_cluster 已经交换了 storageinstances + # 但是在确定flow 流程静态信息时,这些 master/slave 依然属于 src_cluster + # 所以这里获取的是 src_cluster的 master/slave ip ports + dst_master_slave_ports = self.__get_cluster_master_slave_ports(int(job_row.app), job_row.src_cluster_id) + dst_twemproxy_server_shards = get_twemproxy_cluster_server_shards( + int(job_row.app), job_row.src_cluster_id, {} + ) + acts_list = [] + for ip, ports in dst_master_slave_ports["master_ports"].items(): + act_kwargs.cluster["servers"][0]["server_ip"] = ip + act_kwargs.cluster["servers"][0]["server_ports"] = ports + act_kwargs.cluster["servers"][0]["meta_role"] = InstanceRole.REDIS_MASTER.value + act_kwargs.cluster["servers"][0]["server_shards"] = dst_twemproxy_server_shards.get(ip, {}) + act_kwargs.cluster["servers"][0]["cache_backup_mode"] = get_cache_backup_mode( + int(job_row.app), job_row.src_cluster_id + ) + act_kwargs.exec_ip = ip + act_kwargs.get_redis_payload_func = RedisActPayload.bkdbmon_install.__name__ + acts_list.append( + { + "act_name": _("{} 重装 dbmon").format(ip), + "act_component_code": ExecuteDBActuatorScriptComponent.code, + "kwargs": asdict(act_kwargs), + } + ) + for ip, ports in dst_master_slave_ports["slave_ports"].items(): + act_kwargs.cluster["servers"][0]["server_ip"] = ip + act_kwargs.cluster["servers"][0]["server_ports"] = ports + act_kwargs.cluster["servers"][0]["meta_role"] = InstanceRole.REDIS_SLAVE.value + act_kwargs.cluster["servers"][0]["server_shards"] = dst_twemproxy_server_shards.get(ip, {}) + act_kwargs.cluster["servers"][0]["cache_backup_mode"] = get_cache_backup_mode( + int(job_row.app), job_row.src_cluster_id + ) + act_kwargs.exec_ip = ip + act_kwargs.get_redis_payload_func = RedisActPayload.bkdbmon_install.__name__ + acts_list.append( + { + "act_name": _("{} 重装 dbmon").format(ip), + "act_component_code": ExecuteDBActuatorScriptComponent.code, + "kwargs": asdict(act_kwargs), + } + ) + redis_pipeline.add_parallel_acts(acts_list=acts_list) + + redis_pipeline.run_pipeline() diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_dts.py b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_dts.py deleted file mode 100644 index a19131e4d7..0000000000 --- a/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_dts.py +++ /dev/null @@ -1,188 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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. -""" -import logging.config -from dataclasses import asdict - -from django.utils.translation import ugettext as _ - -from backend.configuration.constants import DBType -from backend.db_services.redis_dts.constants import DtsCopyType -from backend.flow.consts import WriteContextOpType -from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder -from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList -from backend.flow.plugins.components.collections.redis.exec_actuator_script import ExecuteDBActuatorScriptComponent -from backend.flow.plugins.components.collections.redis.exec_shell_script import ExecuteShellScriptComponent -from backend.flow.plugins.components.collections.redis.get_redis_payload import GetRedisActPayloadComponent -from backend.flow.plugins.components.collections.redis.redis_dts import ( - GetRedisDtsDataComponent, - GetRedisDtsDataService, - RedisDtsExecuteComponent, - RedisDtsPrecheckComponent, -) -from backend.flow.plugins.components.collections.redis.trans_flies import TransFileComponent -from backend.flow.utils.redis.redis_act_playload import RedisActPayload -from backend.flow.utils.redis.redis_context_dataclass import ActKwargs, RedisDtsContext - -logger = logging.getLogger("flow") - - -class RedisClusterDtsFlow(object): - """ - redis集群dts - """ - - def __init__(self, root_id, data): - self.root_id = root_id - self.data = data - - def redis_cluster_dts_flow(self): - redis_pipeline = Builder(root_id=self.root_id, data=self.data) - trans_files = GetFileList(db_type=DBType.Redis) - sub_pipelines = [] - for rule in self.data["rules"]: - logger.info("redis_cluster_dts_flow rule:{}".format(rule)) - slave_hosts = [] - if self.data["dts_copy_type"] != DtsCopyType.USER_BUILT_TO_DBM.value: - instances_ret = GetRedisDtsDataService.get_cluster_slaves_data( - self.data["bk_biz_id"], rule["src_cluster"] - ) - slave_hosts = instances_ret[1] - cluster = { - **rule, - "dts_bill_type": self.data["dts_bill_type"], - "dts_copy_type": self.data["dts_copy_type"], - } - - src_cluster_info = self.__get_src_cluster_info(self.data["bk_biz_id"], self.data["dts_copy_type"], cluster) - dst_cluster_info = self.__get_dst_cluster_info(self.data["bk_biz_id"], self.data["dts_copy_type"], cluster) - cluster["meta_src_cluster_data"] = src_cluster_info - cluster["meta_dst_cluster_data"] = dst_cluster_info - - exec_ip = list(src_cluster_info["ports_group_by_ip"].keys()) - logger.info("redis_cluster_dts_flow src_slave_ips:{}".format(exec_ip)) - - act_kwargs = ActKwargs() - act_kwargs.set_trans_data_dataclass = RedisDtsContext.__name__ - act_kwargs.file_list = trans_files.redis_base() - act_kwargs.is_update_trans_data = True - act_kwargs.cluster = cluster - sub_pipeline = SubBuilder(root_id=self.root_id, data=self.data) - sub_pipeline.add_act( - act_name=_("获取源集群、目的集群信息"), act_component_code=GetRedisDtsDataComponent.code, kwargs=asdict(act_kwargs) - ) - if self.data["dts_copy_type"] != DtsCopyType.USER_BUILT_TO_DBM.value: - acts_list = [] - for host in slave_hosts: - # 获取slave磁盘信息 - act_kwargs.exec_ip = host["ip"] - act_kwargs.write_op = WriteContextOpType.APPEND.value - act_kwargs.cluster[ - "shell_command" - ] = """ - d=`df -k $REDIS_BACKUP_DIR | grep -iv Filesystem` - echo "{\\\"data\\\":\\\"${d}\\\"}" - """ - acts_list.append( - { - "act_name": _("获取磁盘使用情况: {}").format(host["ip"]), - "act_component_code": ExecuteShellScriptComponent.code, - "kwargs": asdict(act_kwargs), - "write_payload_var": "disk_used", - } - ) - sub_pipeline.add_parallel_acts(acts_list=acts_list) - - sub_pipeline.add_act( - act_name=_("redis dts前置检查"), - act_component_code=RedisDtsPrecheckComponent.code, - kwargs=asdict(act_kwargs), - ) - sub_pipeline.add_act( - act_name=_("redis dts发起任务并等待至增量同步阶段"), - act_component_code=RedisDtsExecuteComponent.code, - kwargs=asdict(act_kwargs), - ) - sub_pipeline.add_act( - act_name=_("初始化配置"), act_component_code=GetRedisActPayloadComponent.code, kwargs=asdict(act_kwargs) - ) - - act_kwargs.exec_ip = exec_ip - sub_pipeline.add_act( - act_name=_("下发介质包"), act_component_code=TransFileComponent.code, kwargs=asdict(act_kwargs) - ) - - acts_list = [] - for ip in src_cluster_info["ports_group_by_ip"]: - act_kwargs.exec_ip = ip - act_kwargs.get_redis_payload_func = RedisActPayload.redis_dts_datacheck_payload.__name__ - acts_list.append( - { - "act_name": _("redis dts数据校验: {}").format(ip), - "act_component_code": ExecuteDBActuatorScriptComponent.code, - "kwargs": asdict(act_kwargs), - } - ) - sub_pipeline.add_parallel_acts(acts_list=acts_list) - sub_pipelines.append( - sub_pipeline.build_sub_process( - sub_name=_("源集群:{} 数据迁移到 目的集群:{}").format(cluster["src_cluster"], cluster["dst_cluster"]) - ) - ) - redis_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) - redis_pipeline.run_pipeline() - - def __get_src_cluster_info(self, bk_biz_id: int, dts_copy_type: str, input_rule: dict) -> dict: - src_cluster = {} - ports_group_by_ip = {} - if dts_copy_type == DtsCopyType.USER_BUILT_TO_DBM: - src_cluster["src_cluster_addr"] = input_rule.get("src_cluster") - src_cluster["src_cluster_password"] = input_rule.get("src_cluster_password") - src_cluster["src_cluster_type"] = input_rule.get("src_cluster_type") - else: - src_cluster_data = GetRedisDtsDataService.get_cluster_info_by_domain(bk_biz_id, input_rule["src_cluster"]) - src_cluster["src_cluster_addr"] = ( - src_cluster_data["cluster_domain"] + ":" + str(src_cluster_data["cluster_port"]) - ) - src_cluster["src_cluster_password"] = src_cluster_data["cluster_password"] - src_cluster["src_cluster_type"] = src_cluster_data["cluster_type"] - src_cluster["src_redis_password"] = src_cluster_data["redis_password"] - - src_redis_data = GetRedisDtsDataService.get_cluster_slaves_data( - bk_biz_id, src_cluster_data["cluster_domain"] - ) - logger.info("src_redis_data:{}".format(src_redis_data)) - for instance in src_redis_data[1]: - ip = instance["ip"] - if ip not in ports_group_by_ip: - ports_group_by_ip[ip] = [] - ports_group_by_ip[ip].append( - { - "port": instance["port"], - "segment_start": instance["segment_start"], - "segment_end": instance["segment_end"], - } - ) - src_cluster["ports_group_by_ip"] = ports_group_by_ip - return src_cluster - - def __get_dst_cluster_info(self, bk_biz_id: int, dts_copy_type: str, input_rule: dict) -> dict: - dst_cluster = {} - if dts_copy_type == DtsCopyType.COPY_TO_OTHER_SYSTEM: - dst_cluster["dst_cluster_addr"] = input_rule.get("dst_cluster") - dst_cluster["dst_cluster_password"] = input_rule.get("dst_cluster_password") - else: - dst_cluster_data = GetRedisDtsDataService.get_cluster_info_by_domain(bk_biz_id, input_rule["dst_cluster"]) - dst_cluster["dst_cluster_addr"] = ( - dst_cluster_data["cluster_domain"] + ":" + str(dst_cluster_data["cluster_port"]) - ) - dst_cluster["dst_cluster_password"] = dst_cluster_data["cluster_password"] - dst_cluster["dst_cluster_type"] = dst_cluster_data["cluster_type"] - return dst_cluster diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_instance_shutdown.py b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_instance_shutdown.py new file mode 100644 index 0000000000..088c68d3d0 --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_instance_shutdown.py @@ -0,0 +1,153 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging.config +from collections import defaultdict +from copy import deepcopy +from dataclasses import asdict +from typing import Any, Dict, List, Optional + +from django.utils.translation import ugettext as _ + +from backend.configuration.constants import DBType +from backend.db_meta.enums import InstanceRole +from backend.db_meta.models import Cluster +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList +from backend.flow.engine.bamboo.scene.redis.atom_jobs import ProxyUnInstallAtomJob, RedisBatchShutdownAtomJob +from backend.flow.plugins.components.collections.redis.get_redis_payload import GetRedisActPayloadComponent +from backend.flow.utils.redis.redis_context_dataclass import ActKwargs, CommonContext + +logger = logging.getLogger("flow") + + +class RedisClusterInstanceShutdownSceneFlow(object): + """ + ## proxy/storage 下架 通用入口 [只做实例下架, clb/dns..这里不干活!!] + { + "bk_biz_id": 3, + "uid": "2022051612120001", + "created_by":"vitox", + "ticket_type":"REDIS_CLUSTER_INSTANCE_SHUTDOWN", + "infos": [ + { + "cluster_id": 1, + "proxy": ["1.1.1.a"], + "redis_slave": ["1.1.1.a"], + } + ] + } + """ + + def __init__(self, root_id: str, data: Optional[Dict]): + """ + @param root_id : 任务流程定义的root_id + @param data : 单据传递过来的参数列表,是dict格式 + """ + self.root_id = root_id + self.data = data + self.precheck_for_compelete_replace() + + @staticmethod + def get_cluster_info(bk_biz_id: int, cluster_id: int) -> dict: + """获取集群现有信息 + 1. master 对应 slave 机器 + 2. master 上的端口列表 + 3. 实例对应关系:{master:port:slave:port} + """ + cluster = Cluster.objects.get(id=cluster_id, bk_biz_id=bk_biz_id) + slave_ports = defaultdict(list) + + for master_obj in cluster.storageinstance_set.filter(instance_role=InstanceRole.REDIS_MASTER.value): + slave_obj = master_obj.as_ejector.get().receiver + slave_ports[slave_obj.machine.ip].append(slave_obj.port) + return { + "immute_domain": cluster.immute_domain, + "bk_biz_id": str(cluster.bk_biz_id), + "bk_cloud_id": cluster.bk_cloud_id, + "cluster_type": cluster.cluster_type, + "cluster_name": cluster.name, + "cluster_id": cluster.id, + "slave_ports": dict(slave_ports), + "proxy_port": cluster.proxyinstance_set.first().port, + "proxy_ips": [proxy_obj.machine.ip for proxy_obj in cluster.proxyinstance_set.all()], + "db_version": cluster.major_version, + } + + def __init_builder(self, operate_name: str): + redis_pipeline = Builder(root_id=self.root_id, data=self.data) + trans_files = GetFileList(db_type=DBType.Redis) + act_kwargs = ActKwargs() + act_kwargs.set_trans_data_dataclass = CommonContext.__name__ + act_kwargs.file_list = trans_files.redis_base() + act_kwargs.is_update_trans_data = True + act_kwargs.cluster = { + "operate": operate_name, + } + return redis_pipeline, act_kwargs + + # flow 入口 + def start_instance_shutdown(self): + redis_pipeline, act_kwargs = self.__init_builder(_("REDIS-实例下架")) + sub_pipelines = [] + for cluster_instances in self.data["infos"]: + cluster_kwargs = deepcopy(act_kwargs) + cluster_info = self.get_cluster_info(self.data["bk_biz_id"], cluster_instances["cluster_id"]) + + flow_data = self.data + for k, v in cluster_info.items(): + cluster_kwargs.cluster[k] = v + cluster_kwargs.cluster["created_by"] = self.data["created_by"] + flow_data["shutdown_instances"] = cluster_instances + redis_pipeline.add_act( + act_name=_("初始化配置-{}".format(cluster_info["immute_domain"])), + act_component_code=GetRedisActPayloadComponent.code, + kwargs=asdict(cluster_kwargs), + ) + sub_pipeline = self.instance_shutdown(cluster_kwargs, cluster_instances) + sub_pipelines.append(sub_pipeline) + + redis_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) + + # 对每个集群操作 + def instance_shutdown(self, sub_kwargs, shutdown_params): + sub_pipeline = SubBuilder(root_id=self.root_id, data=self.data) + # #### 下架旧实例 ############################################################################ + sub_pipelines = [] + for shutdown_ip in shutdown_params.get("redis_slave", {}): + sub_builder = RedisBatchShutdownAtomJob( + self.root_id, + self.data, + deepcopy(sub_kwargs), + { + "ip": shutdown_ip, + "ports": sub_kwargs.cluster["slave_ports"][shutdown_ip], + }, + ) + sub_pipelines.append(sub_builder) + if len(sub_pipelines) > 0: + sub_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) + # #### 下架旧实例 ###################################################################### 完毕 ### + + # #### 下架旧实例 ############################################################################ + sub_pipelines = [] + for proxy_ip in shutdown_params.get("proxy", {}): + sub_builder = ProxyUnInstallAtomJob( + self.root_id, + self.data, + deepcopy(sub_kwargs), + {"ip": proxy_ip, "proxy_port": sub_kwargs.cluster["proxy_port"]}, + ) + sub_pipelines.append(sub_builder) + if len(sub_pipelines) > 0: + sub_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) + # #### 下架旧实例 ###################################################################### 完毕 ### + + return sub_pipeline.build_sub_process(sub_name=_("实例下架-{}").format(sub_kwargs.cluster["immute_domain"])) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_open_close.py b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_open_close.py index fa18ab1bbf..1bc350a993 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_open_close.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_open_close.py @@ -89,7 +89,6 @@ def redis_cluster_open_close_flow(self): ) act_kwargs.cluster = { **cluster_info, - "operate": DBActuatorTypeEnum.Proxy.value + "_" + op, **cluster_info["proxy_map"], "monitor_time_ms": DEFAULT_MONITOR_TIME, "ignore_req": False, @@ -143,6 +142,7 @@ def redis_cluster_open_close_flow(self): { "bk_biz_id": str(self.data["bk_biz_id"]), "bk_cloud_id": act_kwargs.bk_cloud_id, + "server_ip": ip, "server_ports": [] if self.data["ticket_type"] == TicketType.REDIS_CLOSE else [cluster_info["proxy_map"][ip]], @@ -168,6 +168,11 @@ def redis_cluster_open_close_flow(self): # 执行启停 act_kwargs.exec_ip = ip + act_kwargs.cluster = { + "operate": DBActuatorTypeEnum.Proxy.value + "_" + op, + "ip": ip, + "port": cluster_info["proxy_map"][ip], + } act_kwargs.get_redis_payload_func = RedisActPayload.proxy_operate_payload.__name__ sub_pipeline.add_act( act_name="{}: {}".format(TicketType.get_choice_label(self.data["ticket_type"]), ip), diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_scene_auotfix.py b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_scene_auotfix.py new file mode 100644 index 0000000000..bbbfc8a14e --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_scene_auotfix.py @@ -0,0 +1,431 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import copy +import logging.config +from collections import defaultdict +from copy import deepcopy +from dataclasses import asdict +from typing import Any, Dict, List, Optional + +from django.utils.translation import ugettext as _ + +from backend.components import DBConfigApi +from backend.components.dbconfig.constants import FormatType, LevelName +from backend.configuration.constants import DBType +from backend.constants import IP_PORT_DIVIDER +from backend.db_meta import api +from backend.db_meta.enums import ClusterType, InstanceRole +from backend.db_meta.models import Cluster +from backend.flow.consts import ( + DEFAULT_DB_MODULE_ID, + DEFAULT_REDIS_START_PORT, + ConfigFileEnum, + ConfigTypeEnum, + DnsOpType, + SyncType, +) +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList +from backend.flow.engine.bamboo.scene.redis.atom_jobs import ( + AccessManagerAtomJob, + ProxyBatchInstallAtomJob, + RedisBatchInstallAtomJob, + RedisMakeSyncAtomJob, +) +from backend.flow.plugins.components.collections.redis.get_redis_payload import GetRedisActPayloadComponent +from backend.flow.plugins.components.collections.redis.redis_db_meta import RedisDBMetaComponent +from backend.flow.plugins.components.collections.redis.redis_ticket import RedisTicketComponent +from backend.flow.utils.redis.redis_context_dataclass import ActKwargs, CommonContext, DnsKwargs +from backend.flow.utils.redis.redis_db_meta import RedisDBMeta +from backend.ticket.constants import TicketStatus, TicketType + +logger = logging.getLogger("flow") + + +class RedisClusterAutoFixSceneFlow(object): + """ + tendis fault autofix 4 host + 这里只做新增,以及管理层的清理操作,真正下发 actor 下架实例的操作单独提单据 + { + "bk_biz_id": 3, + "uid": "2022051612120001", + "created_by":"vitox", + "ticket_type":"REDIS_CLUSTER_AUTOFIX", + "infos": [ + { + "cluster_id": 1, + "proxy": [ + {"ip": "1.1.1.a","spec_id": 17, + "target": {"bk_cloud_id": 0,"bk_host_id": 216,"status": 1,"ip": "2.2.2.b"} + }], + "redis_slave": [ + {"ip": "1.1.1.a","spec_id": 17, + "target": {"bk_cloud_id": 0,"bk_host_id": 216,"status": 1,"ip": "2.2.2.b"} + }], + } + ] + } + """ + + def __init__(self, root_id: str, data: Optional[Dict]): + """ + @param root_id : 任务流程定义的root_id + @param data : 单据传递过来的参数列表,是dict格式 + """ + self.root_id = root_id + self.data = data + self.precheck_for_instance_fix() + + @staticmethod + def get_cluster_info(bk_biz_id: int, cluster_id: int) -> dict: + """获取集群现有信息 + 1. master 对应 slave 机器 + 2. master 上的端口列表 + 3. 实例对应关系:{master:port:slave:port} + """ + cluster = Cluster.objects.get(id=cluster_id, bk_biz_id=bk_biz_id) + + master_ports, slave_ports = defaultdict(list), defaultdict(list) + slave_master_map, slave_ins_map = defaultdict(), defaultdict() + + for master_obj in cluster.storageinstance_set.filter(instance_role=InstanceRole.REDIS_MASTER.value): + slave_obj = master_obj.as_ejector.get().receiver + master_ports[master_obj.machine.ip].append(master_obj.port) + slave_ports[slave_obj.machine.ip].append(slave_obj.port) + + slave_ins_map["{}{}{}".format(slave_obj.machine.ip, IP_PORT_DIVIDER, slave_obj.port)] = "{}{}{}".format( + master_obj.machine.ip, IP_PORT_DIVIDER, master_obj.port + ) + + ifmaster = slave_master_map.get(slave_obj.machine.ip) + if ifmaster and ifmaster != master_obj.machine.ip: + raise Exception( + "unsupport mutil master for cluster {}:{}".format(cluster.immute_domain, slave_obj.machine.ip) + ) + else: + slave_master_map[slave_obj.machine.ip] = master_obj.machine.ip + + cluster_info = api.cluster.nosqlcomm.other.get_cluster_detail(cluster_id)[0] + cluster_name = cluster_info["name"] + cluster_type = cluster_info["cluster_type"] + redis_master_set = cluster_info["redis_master_set"] + redis_slave_set = cluster_info["redis_slave_set"] + servers = [] + if cluster_type in [ClusterType.TendisTwemproxyRedisInstance, ClusterType.TwemproxyTendisSSDInstance]: + for set in redis_master_set: + ip_port, seg_range = str.split(set) + servers.append("{} {} {} {}".format(ip_port, cluster_name, seg_range, 1)) + else: + servers = redis_master_set + redis_slave_set + + return { + "immute_domain": cluster.immute_domain, + "bk_biz_id": str(cluster.bk_biz_id), + "bk_cloud_id": cluster.bk_cloud_id, + "cluster_type": cluster.cluster_type, + "cluster_name": cluster.name, + "cluster_id": cluster.id, + "slave_ports": dict(slave_ports), + "slave_ins_map": dict(slave_ins_map), + "slave_master_map": dict(slave_master_map), + "proxy_port": cluster.proxyinstance_set.first().port, + "proxy_ips": [proxy_obj.machine.ip for proxy_obj in cluster.proxyinstance_set.all()], + "db_version": cluster.major_version, + "backend_servers": servers, + } + + @staticmethod + def __get_cluster_config(bk_biz_id: int, namespace: str, domain_name: str, db_version: str) -> Any: + data = DBConfigApi.query_conf_item( + params={ + "bk_biz_id": str(bk_biz_id), + "level_name": LevelName.CLUSTER.value, + "level_value": domain_name, + "level_info": {"module": str(DEFAULT_DB_MODULE_ID)}, + "conf_file": db_version, + "conf_type": ConfigTypeEnum.ProxyConf.value, + "namespace": namespace, + "format": FormatType.MAP.value, + } + ) + return data["content"] + + def __init_builder(self, operate_name: str): + redis_pipeline = Builder(root_id=self.root_id, data=self.data) + trans_files = GetFileList(db_type=DBType.Redis) + act_kwargs = ActKwargs() + act_kwargs.set_trans_data_dataclass = CommonContext.__name__ + act_kwargs.file_list = trans_files.redis_base() + act_kwargs.is_update_trans_data = True + act_kwargs.cluster = { + "operate": operate_name, + } + return redis_pipeline, act_kwargs + + # 这里整理替换所需要的参数 + def start_redis_auotfix(self): + redis_pipeline, act_kwargs = self.__init_builder(_("REDIS-故障自愈")) + sub_pipelines = [] + for cluster_fix in self.data["infos"]: + cluster_kwargs = deepcopy(act_kwargs) + cluster_info = self.get_cluster_info(self.data["bk_biz_id"], cluster_fix["cluster_id"]) + flow_data = self.data + for k, v in cluster_info.items(): + cluster_kwargs.cluster[k] = v + cluster_kwargs.cluster["created_by"] = self.data["created_by"] + flow_data["fix_info"] = cluster_fix + redis_pipeline.add_act( + act_name=_("初始化配置-{}".format(cluster_info["immute_domain"])), + act_component_code=GetRedisActPayloadComponent.code, + kwargs=asdict(cluster_kwargs), + ) + sub_pipeline = self.cluster_fix(flow_data, cluster_kwargs, cluster_fix) + sub_pipelines.append(sub_pipeline) + + redis_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) + return redis_pipeline.run_pipeline() + + # 组装&控制 集群替换流程 + def cluster_fix(self, flow_data, act_kwargs, fix_params): + sub_pipeline = SubBuilder(root_id=self.root_id, data=flow_data) + + # 先补充slave + if fix_params.get("redis_slave"): + slave_kwargs = deepcopy(act_kwargs) + self.slave_fix( + flow_data, + slave_kwargs, + { + "redis_slave": fix_params.get("redis_slave"), + "slave_spec": fix_params.get("resource_spec", {}).get("redis_slave", {}), + }, + ) + + # 然后在搞定proxy + if fix_params.get("proxy"): + proxy_kwargs = deepcopy(act_kwargs) + self.proxy_fix( + flow_data, + proxy_kwargs, + { + "proxy": fix_params.get("proxy"), + "proxy_spec": fix_params.get("resource_spec", {}).get("proxy", {}), + }, + ) + return sub_pipeline.build_sub_process(sub_name=_("故障自愈-{}").format(act_kwargs.cluster["immute_domain"])) + + def proxy_fix(self, flow_data, act_kwargs, proxy_fix_info): + old_proxies, new_proxies, proxy_fix_details = [], [], proxy_fix_info["proxy"] + sub_pipeline = SubBuilder(root_id=self.root_id, data=flow_data) + + for fix_link in proxy_fix_details: + old_proxies.append(fix_link["ip"]) + new_proxies.append(fix_link["target"]["ip"]) + + # 第一步:安装Proxy + sub_pipelines = [] + if act_kwargs.cluster["cluster_type"] in [ + ClusterType.TendisTwemproxyRedisInstance.value, + ClusterType.TwemproxyTendisSSDInstance.value, + ]: + proxy_version = ConfigFileEnum.Twemproxy + else: + proxy_version = ConfigFileEnum.Predixy + + config_info = self.__get_cluster_config( + self.data["bk_biz_id"], + act_kwargs.cluster["cluster_type"], + act_kwargs.cluster["immute_domain"], + proxy_version, + ) + + for proxy_ip in new_proxies: + replace_kwargs = copy.deepcopy(act_kwargs) + params = { + "ip": proxy_ip, + "redis_pwd": config_info["redis_password"], + "proxy_pwd": config_info["password"], + "proxy_port": int(config_info["port"]), + "servers": replace_kwargs.cluster["backend_servers"], + "spec_id": proxy_fix_info["proxy_spec"].get("id", 0), + "spec_config": proxy_fix_info["proxy_spec"], + } + sub_pipelines.append(ProxyBatchInstallAtomJob(self.root_id, self.data, replace_kwargs, params)) + sub_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) + + # 第二步:元数据加入集群 + act_kwargs.cluster["proxy_ips"] = new_proxies + act_kwargs.cluster["proxy_port"] = int(config_info["port"]) + act_kwargs.cluster["meta_func_name"] = RedisDBMeta.proxy_add_cluster.__name__ + act_kwargs.cluster["domain_name"] = act_kwargs.cluster["immute_domain"] + sub_pipeline.add_act( + act_name=_("Proxy-加入集群-{}".format(new_proxies)), + act_component_code=RedisDBMetaComponent.code, + kwargs=asdict(act_kwargs), + ) + + # 第三步: 新-接入层注册 + sub_pipeline.add_sub_pipeline( + sub_flow=AccessManagerAtomJob( + self.root_id, + self.data, + act_kwargs, + { + "cluster_id": act_kwargs.cluster["cluster_id"], + "port": act_kwargs.cluster["proxy_port"], + "add_ips": new_proxies, + "op_type": DnsOpType.CREATE.value, + }, + ) + ) + + # 第四步: 旧-接入层剔除 + sub_pipeline.add_sub_pipeline( + sub_flow=AccessManagerAtomJob( + self.root_id, + self.data, + act_kwargs, + { + "cluster_id": act_kwargs.cluster["cluster_id"], + "port": act_kwargs.cluster["proxy_port"], + "del_ips": old_proxies, + "op_type": DnsOpType.RECYCLE_RECORD.value, + }, + ) + ) + + # # 第五步:卸载Proxy (生产Ticket单据) + sub_pipeline.add_act( + act_name=_("提交Proxy下架单-{}".format(old_proxies)), + act_component_code=RedisTicketComponent.code, + kwargs={ + "bk_biz_id": act_kwargs.cluster["bk_biz_id"], + "cluster_id": act_kwargs.cluster["cluster_id"], + "immute_domain": act_kwargs.cluster["immute_domain"], + "ticket_type": TicketType.REDIS_CLUSTER_INSTANCE_SHUTDOWN.value, + "ticket_details": { + "cluster_id": act_kwargs.cluster["cluster_id"], + "proxy": old_proxies, + }, + }, + ) + + return sub_pipeline.build_sub_process(sub_name=_("Proxy自愈-{}").format(act_kwargs.cluster["immute_domain"])) + + def slave_fix(self, flow_data, sub_kwargs, slave_fix_info): + sub_pipeline = SubBuilder(root_id=self.root_id, data=flow_data) + # ### 部署实例 ############################################################################### + sub_pipelines = [] + slave_fix_detail = slave_fix_info["redis_slave"] + for fix_link in slave_fix_detail: + old_slave = fix_link["ip"] + new_slave = fix_link["target"]["ip"] + params = { + "ip": new_slave, + "meta_role": InstanceRole.REDIS_SLAVE.value, + "start_port": DEFAULT_REDIS_START_PORT, + "ports": sub_kwargs.cluster["slave_ports"][old_slave], + "instance_numb": len(sub_kwargs.cluster["slave_ports"][old_slave]), + "spec_id": slave_fix_info["slave_spec"].get("id", 0), + "spec_config": slave_fix_info["slave_spec"], + } + sub_builder = RedisBatchInstallAtomJob(self.root_id, flow_data, sub_kwargs, params) + sub_pipelines.append(sub_builder) + sub_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) + # ### 部署实例 ######################################################################## 完毕 ### + + # #### 建同步关系 ############################################################################## + sub_pipelines = [] + for fix_link in slave_fix_detail: + # "Old": {"ip": "2.2.a.4", "bk_cloud_id": 0, "bk_host_id": 123}, + old_slave = fix_link["ip"] + new_slave = fix_link["target"]["ip"] + install_params = { + "sync_type": SyncType.SYNC_MS, + "origin_1": sub_kwargs.cluster["slave_master_map"][old_slave], + "origin_2": old_slave, + "sync_dst1": new_slave, + "ins_link": [], + } + for slave_port in sub_kwargs.cluster["slave_ports"][old_slave]: + old_ins = "{}{}{}".format(old_slave, IP_PORT_DIVIDER, slave_port) + master_port = sub_kwargs.cluster["slave_ins_map"].get(old_ins).split(IP_PORT_DIVIDER)[1] + install_params["ins_link"].append({"origin_1": master_port, "sync_dst1": slave_port}) + sub_builder = RedisMakeSyncAtomJob(self.root_id, flow_data, sub_kwargs, install_params) + sub_pipelines.append(sub_builder) + sub_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) + # #### 建同步关系 ##################################################################### 完毕 #### + + # 新节点加入集群 ################################################################################ + sub_kwargs.cluster["meta_func_name"] = RedisDBMeta.redis_redo_slaves.__name__ + sub_kwargs.cluster["old_slaves"] = [] + sub_kwargs.cluster["created_by"] = flow_data["created_by"] + sub_kwargs.cluster["tendiss"] = [] + for fix_link in slave_fix_detail: + old_slave, new_slave = fix_link["ip"], fix_link["target"]["ip"] + sub_kwargs.cluster["old_slaves"].append( + {"ip": old_slave, "ports": sub_kwargs.cluster["slave_ports"][old_slave]} + ) + for slave_port in sub_kwargs.cluster["slave_ports"][old_slave]: + old_ins = "{}{}{}".format(old_slave, IP_PORT_DIVIDER, slave_port) + master = sub_kwargs.cluster["slave_ins_map"].get(old_ins) + sub_kwargs.cluster["tendiss"].append( + { + "ejector": { + "ip": master.split(IP_PORT_DIVIDER)[0], + "port": int(master.split(IP_PORT_DIVIDER)[1]), + }, + "receiver": {"ip": new_slave, "port": int(slave_port)}, + } + ) + sub_pipeline.add_act( + act_name=_("Redis-新节点加入集群"), act_component_code=RedisDBMetaComponent.code, kwargs=asdict(sub_kwargs) + ) + # #### 新节点加入集群 ################################################################# 完毕 ### + + # # #### 下架旧实例 (生产Ticket单据) ################################################## 完毕 ### + old_slaves = [fix_link["ip"] for fix_link in slave_fix_detail] + sub_pipeline.add_act( + act_name=_("提交Redis下架单-{}".format(old_slaves)), + act_component_code=RedisTicketComponent.code, + kwargs={ + "bk_biz_id": sub_kwargs.cluster["bk_biz_id"], + "cluster_id": sub_kwargs.cluster["cluster_id"], + "immute_domain": sub_kwargs.cluster["immute_domain"], + "ticket_type": TicketType.REDIS_CLUSTER_INSTANCE_SHUTDOWN.value, + "ticket_details": { + "cluster_id": sub_kwargs.cluster["cluster_id"], + "redis_slave": old_slaves, + }, + }, + ) + # # #### 下架旧实例 ###################################################################### 完毕 ### + + return sub_pipeline.build_sub_process(sub_name=_("Slave替换-{}").format(sub_kwargs.cluster["cluster_type"])) + + # 存在性检查 + + def precheck_for_instance_fix(self): + for cluster_fix in self.data["infos"]: + try: + cluster = Cluster.objects.get(id=cluster_fix["cluster_id"], bk_biz_id=self.data["bk_biz_id"]) + except Cluster.DoesNotExist as e: + raise Exception("redis cluster does not exist,{}", e) + # check proxy + for proxy in cluster_fix.get("proxy", []): + if not cluster.proxyinstance_set.filter(machine__ip=proxy["ip"]): + raise Exception("proxy {} does not exist in cluster {}", proxy["ip"], cluster.immute_domain) + # check slave + for slave in cluster_fix.get("redis_slave", []): + if not cluster.storageinstance_set.filter( + machine__ip=slave["ip"], instance_role=InstanceRole.REDIS_SLAVE.value + ): + raise Exception("slave {} does not exist in cluster {}", slave["ip"], cluster.immute_domain) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_scene_cmr.py b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_scene_cmr.py new file mode 100644 index 0000000000..695e7f505c --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_scene_cmr.py @@ -0,0 +1,377 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import copy +import logging.config +from collections import defaultdict +from copy import deepcopy +from dataclasses import asdict +from typing import Any, Dict, List, Optional + +from django.utils.translation import ugettext as _ + +from backend.components import DBConfigApi +from backend.components.dbconfig.constants import FormatType, LevelName +from backend.configuration.constants import DBType +from backend.constants import IP_PORT_DIVIDER +from backend.db_meta import api +from backend.db_meta.enums import ClusterType, InstanceRole +from backend.db_meta.models import Cluster +from backend.flow.consts import DEFAULT_DB_MODULE_ID, ConfigFileEnum, ConfigTypeEnum, DnsOpType, SyncType +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList +from backend.flow.engine.bamboo.scene.redis.atom_jobs import ( + ProxyBatchInstallAtomJob, + ProxyUnInstallAtomJob, + RedisClusterMasterReplaceJob, + RedisClusterSlaveReplaceJob, +) +from backend.flow.plugins.components.collections.redis.dns_manage import RedisDnsManageComponent +from backend.flow.plugins.components.collections.redis.get_redis_payload import GetRedisActPayloadComponent +from backend.flow.plugins.components.collections.redis.redis_db_meta import RedisDBMetaComponent +from backend.flow.utils.redis.redis_context_dataclass import ActKwargs, CommonContext, DnsKwargs +from backend.flow.utils.redis.redis_db_meta import RedisDBMeta + +logger = logging.getLogger("flow") + + +class RedisClusterCMRSceneFlow(object): + """ + Complete machine replacement + + #### Master 会执行成对替换 + #### 替换顺序: 优先Slave,然后Proxy,最后Master + #### 最后会生成 proxy下架单/集群切换单据 + { + "bk_biz_id": 3, + "uid": "2022051612120001", + "created_by":"vitox", + "ticket_type":"REDIS_CLUSTER_CUTOFF", + "infos": [ + { + "cluster_id": 1, + "proxy": [ + {"ip": "1.1.1.a","spec_id": 17, + "target": {"bk_cloud_id": 0,"bk_host_id": 216,"status": 1,"ip": "2.2.2.b"} + }], + "redis_slave": [ + {"ip": "1.1.1.a","spec_id": 17, + "target": {"bk_cloud_id": 0,"bk_host_id": 216,"status": 1,"ip": "2.2.2.b"} + }], + "redis_master": [ + {"ip": "1.1.1.c","spec_id": 17, + "target": { + "master": {"bk_cloud_id": 0,"bk_host_id": 195,"status": 1,"ip": "2.2.2.b"}, + "slave": {"bk_cloud_id": 0,"bk_host_id": 187,"status": 1,"ip": "3.3.3.x"}} + }] + } + ] + } + """ + + def __init__(self, root_id: str, data: Optional[Dict]): + """ + @param root_id : 任务流程定义的root_id + @param data : 单据传递过来的参数列表,是dict格式 + """ + self.root_id = root_id + self.data = data + self.precheck_for_compelete_replace() + + @staticmethod + def get_cluster_info(bk_biz_id: int, cluster_id: int) -> dict: + """获取集群现有信息 + 1. master 对应 slave 机器 + 2. master 上的端口列表 + 3. 实例对应关系:{master:port:slave:port} + """ + cluster = Cluster.objects.get(id=cluster_id, bk_biz_id=bk_biz_id) + + master_ports, slave_ports = defaultdict(list), defaultdict(list) + ins_pair_map, slave_ins_map = defaultdict(), defaultdict() + master_slave_map, slave_master_map = defaultdict(), defaultdict() + + for master_obj in cluster.storageinstance_set.filter(instance_role=InstanceRole.REDIS_MASTER.value): + slave_obj = master_obj.as_ejector.get().receiver + master_ports[master_obj.machine.ip].append(master_obj.port) + slave_ports[slave_obj.machine.ip].append(slave_obj.port) + ins_pair_map["{}{}{}".format(master_obj.machine.ip, IP_PORT_DIVIDER, master_obj.port)] = "{}{}{}".format( + slave_obj.machine.ip, IP_PORT_DIVIDER, slave_obj.port + ) + + ifslave = master_slave_map.get(master_obj.machine.ip) + if ifslave and ifslave != slave_obj.machine.ip: + raise Exception( + "unsupport mutil slave with cluster {} 4:{}".format(cluster.immute_domain, master_obj.machine.ip) + ) + else: + master_slave_map[master_obj.machine.ip] = slave_obj.machine.ip + + slave_ins_map["{}{}{}".format(slave_obj.machine.ip, IP_PORT_DIVIDER, slave_obj.port)] = "{}{}{}".format( + master_obj.machine.ip, IP_PORT_DIVIDER, master_obj.port + ) + + ifmaster = slave_master_map.get(slave_obj.machine.ip) + if ifmaster and ifmaster != master_obj.machine.ip: + raise Exception( + "unsupport mutil master for cluster {}:{}".format(cluster.immute_domain, slave_obj.machine.ip) + ) + else: + slave_master_map[slave_obj.machine.ip] = master_obj.machine.ip + + cluster_info = api.cluster.nosqlcomm.other.get_cluster_detail(cluster_id)[0] + cluster_name = cluster_info["name"] + cluster_type = cluster_info["cluster_type"] + redis_master_set = cluster_info["redis_master_set"] + redis_slave_set = cluster_info["redis_slave_set"] + servers = [] + if cluster_type in [ClusterType.TendisTwemproxyRedisInstance, ClusterType.TwemproxyTendisSSDInstance]: + for set in redis_master_set: + ip_port, seg_range = str.split(set) + servers.append("{} {} {} {}".format(ip_port, cluster_name, seg_range, 1)) + else: + servers = redis_master_set + redis_slave_set + + return { + "immute_domain": cluster.immute_domain, + "bk_biz_id": str(cluster.bk_biz_id), + "bk_cloud_id": cluster.bk_cloud_id, + "cluster_type": cluster.cluster_type, + "cluster_name": cluster.name, + "cluster_id": cluster.id, + "slave_ports": dict(slave_ports), + "master_ports": dict(master_ports), + "ins_pair_map": dict(ins_pair_map), + "slave_ins_map": dict(slave_ins_map), + "slave_master_map": dict(slave_master_map), + "master_slave_map": dict(master_slave_map), + "proxy_port": cluster.proxyinstance_set.first().port, + "proxy_ips": [proxy_obj.machine.ip for proxy_obj in cluster.proxyinstance_set.all()], + "db_version": cluster.major_version, + "backend_servers": servers, + } + + @staticmethod + def __get_cluster_config(bk_biz_id: int, namespace: str, domain_name: str, db_version: str) -> Any: + data = DBConfigApi.query_conf_item( + params={ + "bk_biz_id": str(bk_biz_id), + "level_name": LevelName.CLUSTER.value, + "level_value": domain_name, + "level_info": {"module": str(DEFAULT_DB_MODULE_ID)}, + "conf_file": db_version, + "conf_type": ConfigTypeEnum.ProxyConf.value, + "namespace": namespace, + "format": FormatType.MAP.value, + } + ) + return data["content"] + + def __init_builder(self, operate_name: str): + redis_pipeline = Builder(root_id=self.root_id, data=self.data) + trans_files = GetFileList(db_type=DBType.Redis) + act_kwargs = ActKwargs() + act_kwargs.set_trans_data_dataclass = CommonContext.__name__ + act_kwargs.file_list = trans_files.redis_base() + act_kwargs.is_update_trans_data = True + act_kwargs.cluster = { + "operate": operate_name, + } + return redis_pipeline, act_kwargs + + # 这里整理替换所需要的参数 + def complete_machine_replace(self): + redis_pipeline, act_kwargs = self.__init_builder(_("REDIS-整机替换")) + sub_pipelines = [] + for cluster_replacement in self.data["infos"]: + + cluster_kwargs = deepcopy(act_kwargs) + cluster_info = self.get_cluster_info(self.data["bk_biz_id"], cluster_replacement["cluster_id"]) + sync_type = SyncType.SYNC_MMS.value # ssd sync from master + if cluster_info["cluster_type"] == ClusterType.TendisTwemproxyRedisInstance.value: + sync_type = SyncType.SYNC_SMS.value + + flow_data = self.data + for k, v in cluster_info.items(): + cluster_kwargs.cluster[k] = v + cluster_kwargs.cluster["created_by"] = self.data["created_by"] + flow_data["sync_type"] = sync_type + flow_data["replace_info"] = cluster_replacement + redis_pipeline.add_act( + act_name=_("初始化配置-{}".format(cluster_info["immute_domain"])), + act_component_code=GetRedisActPayloadComponent.code, + kwargs=asdict(cluster_kwargs), + ) + sub_pipeline = self.generate_cluster_replacement(flow_data, cluster_kwargs, cluster_replacement) + sub_pipelines.append(sub_pipeline) + + redis_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) + return redis_pipeline.run_pipeline() + + # 组装&控制 集群替换流程 + def generate_cluster_replacement(self, flow_data, act_kwargs, replacement_param): + sub_pipeline = SubBuilder(root_id=self.root_id, data=flow_data) + + # 先添加Slave替换流程 + if replacement_param.get("redis_slave"): + slave_kwargs = deepcopy(act_kwargs) + slave_replace_pipe = RedisClusterSlaveReplaceJob( + self.root_id, + flow_data, + slave_kwargs, + { + "redis_slave": replacement_param.get("redis_slave"), + "slave_spec": replacement_param.get("resource_spec", {}).get("redis_slave", {}), + }, + ) + sub_pipeline.add_sub_pipeline(slave_replace_pipe) + + # 再添加Proxy替换流程 + if replacement_param.get("proxy"): + proxy_kwargs = deepcopy(act_kwargs) + self.proxy_replacement( + sub_pipeline, + proxy_kwargs, + { + "proxy": replacement_param.get("proxy"), + "proxy_spec": replacement_param.get("resource_spec", {}).get("proxy", {}), + }, + ) + + # 最后添加Master替换流程 , reget proxy info. + if replacement_param.get("redis_master"): + master_kwargs = deepcopy(act_kwargs) + cluster = Cluster.objects.get( + id=master_kwargs.cluster["cluster_id"], bk_biz_id=master_kwargs.cluster["bk_biz_id"] + ) + master_kwargs.cluster["proxy_ips"] = [ + proxy_obj.machine.ip for proxy_obj in cluster.proxyinstance_set.all() + ] + master_kwargs.cluster["sync_type"] = flow_data["sync_type"] + master_replace_pipe = RedisClusterMasterReplaceJob( + self.root_id, + flow_data, + master_kwargs, + { + "redis_master": replacement_param.get("redis_master"), + "master_spec": replacement_param.get("resource_spec", {}).get("master", {}), + "slave_spec": replacement_param.get("resource_spec", {}).get("slave", {}), + }, + ) + sub_pipeline.add_sub_pipeline(master_replace_pipe) + + return sub_pipeline.build_sub_process(sub_name=_("整机替换-{}").format(act_kwargs.cluster["immute_domain"])) + + def proxy_replacement(self, sub_pipeline, act_kwargs, proxy_replace_info): + old_proxies, new_proxies = [], [] + proxy_replace_details = proxy_replace_info["proxy"] + for replace_link in proxy_replace_details: + # {"ip": "1.1.1.a","spec_id": 17,"target": {"bk_cloud_id": 0,"bk_host_id": 216,"status": 1,"ip": "2.2.2.b"}} + old_proxies.append(replace_link["ip"]) + new_proxies.append(replace_link["target"]["ip"]) + + # 第一步:安装Proxy + sub_pipelines = [] + if act_kwargs.cluster["cluster_type"] in [ + ClusterType.TendisTwemproxyRedisInstance.value, + ClusterType.TwemproxyTendisSSDInstance.value, + ]: + proxy_version = ConfigFileEnum.Twemproxy + else: + proxy_version = ConfigFileEnum.Predixy + + config_info = self.__get_cluster_config( + self.data["bk_biz_id"], + act_kwargs.cluster["cluster_type"], + act_kwargs.cluster["immute_domain"], + proxy_version, + ) + + for proxy_ip in new_proxies: + replace_kwargs = copy.deepcopy(act_kwargs) + params = { + "ip": proxy_ip, + "redis_pwd": config_info["redis_password"], + "proxy_pwd": config_info["password"], + "proxy_port": int(config_info["port"]), + "servers": replace_kwargs.cluster["backend_servers"], + "spec_id": proxy_replace_info["proxy_spec"].get("id", 0), + "spec_config": proxy_replace_info["proxy_spec"], + } + sub_builder = ProxyBatchInstallAtomJob(self.root_id, self.data, replace_kwargs, params) + sub_pipelines.append(sub_builder) + sub_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) + + act_kwargs.cluster["proxy_ips"] = new_proxies + act_kwargs.cluster["proxy_port"] = int(config_info["port"]) + act_kwargs.cluster["meta_func_name"] = RedisDBMeta.proxy_add_cluster.__name__ + act_kwargs.cluster["domain_name"] = act_kwargs.cluster["immute_domain"] + sub_pipeline.add_act( + act_name=_("Proxy-加入集群-{}".format(act_kwargs.cluster["immute_domain"])), + act_component_code=RedisDBMetaComponent.code, + kwargs=asdict(act_kwargs), + ) + + # 第二步:接入层管理:填加新接入层 + dns_kwargs = DnsKwargs( + dns_op_type=DnsOpType.CREATE.value, + add_domain_name=act_kwargs.cluster["immute_domain"], + dns_op_exec_port=int(config_info["port"]), + ) + act_kwargs.exec_ip = new_proxies + sub_pipeline.add_act( + act_name=_("Proxy-注册域名-{}".format(act_kwargs.cluster["immute_domain"])), + act_component_code=RedisDnsManageComponent.code, + kwargs={**asdict(act_kwargs), **asdict(dns_kwargs)}, + ) + # 第三步:接入层管理:清理旧接入层(这里可能需要留点时间然后在执行下一步) + dns_kwargs = DnsKwargs( + dns_op_type=DnsOpType.RECYCLE_RECORD, + add_domain_name=act_kwargs.cluster["immute_domain"], + dns_op_exec_port=int(config_info["port"]), + ) + act_kwargs.exec_ip = old_proxies + sub_pipeline.add_act( + act_name=_("Proxy-回收域名-{}".format(act_kwargs.cluster["immute_domain"])), + act_component_code=RedisDnsManageComponent.code, + kwargs={**asdict(act_kwargs), **asdict(dns_kwargs)}, + ) + # 第四步:卸载Proxy + proxy_down_pipelines = [] + for proxy_ip in old_proxies: + params = {"ip": proxy_ip, "proxy_port": act_kwargs.cluster["proxy_port"]} + uninstall_kwargs = copy.deepcopy(act_kwargs) + sub_builder = ProxyUnInstallAtomJob(self.root_id, self.data, uninstall_kwargs, params) + proxy_down_pipelines.append(sub_builder) + sub_pipeline.add_parallel_sub_pipeline(sub_flow_list=proxy_down_pipelines) + + # 存在性检查 + def precheck_for_compelete_replace(self): + for cluster_replacement in self.data["infos"]: + try: + cluster = Cluster.objects.get(id=cluster_replacement["cluster_id"], bk_biz_id=self.data["bk_biz_id"]) + except Cluster.DoesNotExist as e: + raise Exception("redis cluster does not exist,{}", e) + # check proxy + for proxy in cluster_replacement.get("proxy", []): + if not cluster.proxyinstance_set.filter(machine__ip=proxy["ip"]): + raise Exception("proxy {} does not exist in cluster {}", proxy["ip"], cluster.immute_domain) + # check slave + for slave in cluster_replacement.get("redis_slave", []): + if not cluster.storageinstance_set.filter( + machine__ip=slave["ip"], instance_role=InstanceRole.REDIS_SLAVE.value + ): + raise Exception("slave {} does not exist in cluster {}", slave["ip"], cluster.immute_domain) + # check master + for master in cluster_replacement.get("redis_master", []): + if not cluster.storageinstance_set.filter( + machine__ip=master["ip"], instance_role=InstanceRole.REDIS_MASTER.value + ): + raise Exception("master {} does not exist in cluster {}", master["ip"], cluster.immute_domain) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_scene_master.py b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_scene_master.py deleted file mode 100644 index d9c4503bdb..0000000000 --- a/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_scene_master.py +++ /dev/null @@ -1,253 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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. -""" -import logging.config -from collections import defaultdict -from dataclasses import asdict -from typing import Dict, List, Optional - -from django.utils.translation import ugettext as _ - -from backend.configuration.constants import DBType -from backend.constants import IP_PORT_DIVIDER -from backend.db_meta.enums import ClusterType, InstanceRole -from backend.db_meta.models import Cluster -from backend.flow.consts import ( - DEFAULT_LAST_IO_SECOND_AGO, - DEFAULT_MASTER_DIFF_TIME, - DEFAULT_REDIS_START_PORT, - SyncType, -) -from backend.flow.engine.bamboo.scene.common.builder import Builder -from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList -from backend.flow.engine.bamboo.scene.redis.atom_jobs import ( - RedisBatchInstallAtomJob, - RedisBatchShutdownAtomJob, - RedisClusterSwitchAtomJob, - RedisDbmonAtomJob, - RedisMakeSyncAtomJob, -) -from backend.flow.plugins.components.collections.redis.get_redis_payload import GetRedisActPayloadComponent -from backend.flow.utils.redis.redis_context_dataclass import ActKwargs, CommonContext - -logger = logging.getLogger("flow") - - -class RedisClusterMasterSceneFlow(object): - """ - ## Redis cluster Master 裁撤/迁移替换, 成对替换 - { - "uid": "2022051612120001", - "cluster_id":111, # 必须有 - "domain_name":"xxx.abc.dba.db", - "bk_biz_id":"", - "bk_cloud_id":11, - "region":"xxxyw", # 可选 - "device_class":"S5.Large8" # 可选 - "ip_source": "manual_input", # 手动输入/自动匹配资源池 - "replace_relation":{"1.1.a.1":[6.6.b.6,7.c.7.7],"2.d.2.2":[8.e.8.8,9.9.f.9] # 可选 - "ticket_type": "REDIS_CLUSTER_MASTER_CUTOFF" - "created_by":"u r great!", - } - """ - - def __init__(self, root_id: str, data: Optional[Dict]): - """ - @param root_id : 任务流程定义的root_id - @param data : 单据传递过来的参数列表,是dict格式 - """ - self.root_id = root_id - self.data = data - - @staticmethod - def __get_cluster_info(bk_biz_id: int, cluster_id: int) -> dict: - """获取集群现有信息 - 1. master 对应 slave 机器 - 2. master 上的端口列表 - 3. 实例对应关系:{master:port:slave:port} - """ - cluster = Cluster.objects.get(id=cluster_id, bk_biz_id=bk_biz_id) - - master_ports = defaultdict(list) - slave_ports = defaultdict(list) - ins_pair_map = defaultdict() - master_slave_map = defaultdict() - for master_obj in cluster.storageinstance_set.filter(instance_role=InstanceRole.REDIS_MASTER.value): - slave_obj = master_obj.as_ejector.get().receiver - master_ports[master_obj.machine.ip].append(master_obj.port) - slave_ports[slave_obj.machine.ip].append(slave_obj.port) - ins_pair_map["{}{}{}".format(master_obj.machine.ip, IP_PORT_DIVIDER, master_obj.port)] = "{}{}{}".format( - slave_obj.machine.ip, IP_PORT_DIVIDER, slave_obj.port - ) - - ifslave = master_slave_map.get(master_obj.machine.ip) - if ifslave and ifslave != slave_obj.machine.ip: - raise Exception( - "unsupport mutil slave with cluster {} 4:{}".format(cluster.immute_domain, master_obj.machine.ip) - ) - else: - master_slave_map[master_obj.machine.ip] = slave_obj.machine.ip - - return { - "immute_domain": cluster.immute_domain, - "bk_biz_id": cluster.bk_biz_id, - "bk_cloud_id": cluster.bk_cloud_id, - "cluster_type": cluster.cluster_type, - "cluster_id": cluster.id, - "slave_ports": dict(slave_ports), - "master_ports": dict(master_ports), - "ins_pair_map": dict(ins_pair_map), - "proxy_ips": [proxy_obj.machine.ip for proxy_obj in cluster.proxyinstance_set.all()], - "master_slave_map": dict(master_slave_map), - "db_version": cluster.major_version, - } - - def __init_builder(self, operate_name: str): - cluster_info = self.__get_cluster_info(self.data["bk_biz_id"], self.data["cluster_id"]) - sync_type = SyncType.SYNC_MMS # ssd sync from master - if cluster_info["cluster_type"] == ClusterType.TendisTwemproxyRedisInstance.value: - sync_type = SyncType.SYNC_SMS - - flow_data = self.data - for k, v in cluster_info.items(): - flow_data[k] = v - redis_pipeline = Builder(root_id=self.root_id, data=flow_data) - trans_files = GetFileList(db_type=DBType.Redis) - act_kwargs = ActKwargs() - act_kwargs.set_trans_data_dataclass = CommonContext.__name__ - act_kwargs.file_list = trans_files.redis_base() - act_kwargs.is_update_trans_data = True - act_kwargs.cluster = { - **cluster_info, - "operate": operate_name, - "sync_type": sync_type, - } - act_kwargs.bk_cloud_id = cluster_info["bk_cloud_id"] - logger.info("+===+++++===+++++===++++current cluster info :: {}".format(cluster_info)) - logger.info("+===+++++===+++++===++++current tick_data info :: {}".format(act_kwargs)) - - redis_pipeline.add_act( - act_name=_("初始化配置"), act_component_code=GetRedisActPayloadComponent.code, kwargs=asdict(act_kwargs) - ) - return redis_pipeline, act_kwargs - - def work_4_replace(self): - """### 适用于 集群中Master 机房裁撤/迁移替换场景 (成对替换) - - 步骤: 获取变更锁--> 新实例部署--> - 建Sync关系--> 检测同步状态 - Kill Dead链接--> 下架旧实例 - """ - redis_pipeline, act_kwargs = self.__init_builder(_("REDIS_MASTER-裁撤替换")) - - # ### 部署实例 ############################################################################# - sub_pipelines = [] - for old_master, new_hosts in self.data["replace_relation"].items(): - for one_host in new_hosts: - params = { - "ip": one_host, - "meta_role": InstanceRole.REDIS_MASTER.value, - "start_port": DEFAULT_REDIS_START_PORT, - "instance_numb": len(act_kwargs.cluster["master_ports"][old_master]), - } - sub_builder = RedisBatchInstallAtomJob(self.root_id, self.data, act_kwargs, params) - sub_pipelines.append(sub_builder) - redis_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) - # ### 部署实例 ########################################################################## 完毕 ### - - sync_relations, new_slave_ports = [], [] # 按照机器对组合 - # #### 建同步关系 ############################################################################# - sub_pipelines = [] - for old_master, new_hosts in self.data["replace_relation"].items(): - sync_params = { - "sync_type": act_kwargs.cluster["sync_type"], - "origin_1": old_master, - "origin_2": act_kwargs.cluster["master_slave_map"][old_master], - "sync_dst1": new_hosts[0], - "sync_dst2": new_hosts[1], - "ins_link": [], - } - new_ins_port = DEFAULT_REDIS_START_PORT - for old_master_port in act_kwargs.cluster["master_ports"][old_master].sort(): - old_master_ins = "{}{}{}".format(old_master, IP_PORT_DIVIDER, old_master_port) - old_slave_ins = act_kwargs.cluster["ins_pair_map"][old_master_ins] - new_slave_ports.append(new_ins_port) - sync_params["ins_link"].append( - { - "origin_1": old_master_port, - "origin_2": old_slave_ins.split(IP_PORT_DIVIDER)[1], - "sync_dst1": new_ins_port, - "sync_dst2": new_ins_port, - } - ) - new_ins_port = new_ins_port + 1 - sync_relations.append(sync_params) - sub_builder = RedisMakeSyncAtomJob(self.root_id, self.data, act_kwargs, sync_params) - sub_pipelines.append(sub_builder) - redis_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) - # ### 建同步关系 ########################################################################## 完毕 ### - - # 执行切换 ############################################################################# - act_kwargs.cluster["switch_condition"] = { - "is_check_sync": True, # 不强制切换 - "slave_master_diff_time": DEFAULT_MASTER_DIFF_TIME, - "last_io_second_ago": DEFAULT_LAST_IO_SECOND_AGO, - "can_write_before_switch": True, - "sync_type": act_kwargs.cluster["sync_type"], - } - sub_pipeline = RedisClusterSwitchAtomJob(self.root_id, self.data, act_kwargs, sync_relations) - redis_pipeline.add_sub_pipeline(sub_flow=sub_pipeline) - # #### 执行切换 ############################################################################# - - # 刷新监控 ############################################################################# - sub_pipelines = [] - for old_master, new_hosts in self.data["replace_relation"].items(): - params = { - "ip": new_hosts[1], - "ports": new_slave_ports, - "meta_role": InstanceRole.REDIS_SLAVE.value, - "cluster_name": act_kwargs.cluster["cluster_name"], - "cluster_type": act_kwargs.cluster["cluster_type"], - "immute_domain": act_kwargs.cluster["immute_domain"], - } - sub_builder = RedisDbmonAtomJob(self.root_id, self.data, act_kwargs, params) - sub_pipelines.append(sub_builder) - redis_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) - # 刷新监控 ########################################################################## 完毕 ### - - # #### 下架旧实例 ############################################################################# - sub_pipelines = [] - for old_master in self.data["replace_relation"].keys(): - params = { - "ip": old_master, - "ports": act_kwargs.cluster["master_ports"][old_master], - } - sub_builder = RedisBatchShutdownAtomJob(self.root_id, self.data, act_kwargs, params) - sub_pipelines.append(sub_builder) - - old_slave = act_kwargs.cluster["master_slave_map"][old_master] - params = { - "ip": old_slave, - "ports": act_kwargs.cluster["slave_ports"][old_slave], - } - sub_builder = RedisBatchShutdownAtomJob(self.root_id, self.data, act_kwargs, params) - sub_pipelines.append(sub_builder) - - redis_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) - # #### 下架旧实例 ########################################################################## 完毕 ### - - redis_pipeline.run_pipeline() - - def work_4_auotfix(self): - redis_pipeline, act_kwargs = self.__init_builder(_("REDIS_MASTER-故障自愈")) - # # 探测故障实例状态 ## ## 修改元数据状态 ## - # # 根据需要申请替换 ## - # 这里可以直接复用 slave 裁撤场景 - pass diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_scene_mss.py b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_scene_mss.py new file mode 100644 index 0000000000..1c7bffa552 --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_scene_mss.py @@ -0,0 +1,255 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging.config +from collections import defaultdict +from copy import deepcopy +from dataclasses import asdict +from typing import Any, Dict, List, Optional + +from django.utils.translation import ugettext as _ + +from backend.components import DBConfigApi +from backend.configuration.constants import DBType +from backend.constants import IP_PORT_DIVIDER +from backend.db_meta import api +from backend.db_meta.enums import ClusterType, InstanceRole, InstanceStatus +from backend.db_meta.models import AppCache, Cluster, StorageInstance +from backend.flow.consts import DEFAULT_LAST_IO_SECOND_AGO, DEFAULT_MASTER_DIFF_TIME, SyncType +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList +from backend.flow.engine.bamboo.scene.redis.atom_jobs import RedisBatchShutdownAtomJob, RedisClusterSwitchAtomJob +from backend.flow.plugins.components.collections.redis.exec_actuator_script import ExecuteDBActuatorScriptComponent +from backend.flow.plugins.components.collections.redis.get_redis_payload import ( + GetRedisActPayloadComponent, + RedisActPayload, +) +from backend.flow.plugins.components.collections.redis.redis_db_meta import RedisDBMetaComponent +from backend.flow.utils.redis.redis_context_dataclass import ActKwargs, CommonContext +from backend.flow.utils.redis.redis_db_meta import RedisDBMeta + +logger = logging.getLogger("flow") + + +class RedisClusterMSSSceneFlow(object): + """ + Master Slave Switch + + ### 把Slave提升为Master + #### 1. 正常手动切换 + #### 2. 异常情况,强制切换 (1. 整机切换;2.部分切换) + #### 3. 这里只做元数据层的 master/slave 对调,不对old master 下架 + { + "bk_biz_id": 3, + "uid": "2023051612120001", + "created_by":"vitox", + "ticket_type":"REDIS_CLUSTER_MASTER_FAILOVER", + "force":false, # 是否需要强制切换 + "infos": [ + { + "cluster_id": 1, + "online_switch_type":"user_confirm/no_confirm", + "pairs": [ + {"redis_master": "1.1.a.3", "redis_slave": "1.1.2.b"} + ] + } + ] + } + """ + + def __init__(self, root_id: str, data: Optional[Dict]): + """ + @param root_id : 任务流程定义的root_id + @param data : 单据传递过来的参数列表,是dict格式 + """ + self.root_id = root_id + self.data = data + + @staticmethod + def __get_cluster_info(bk_biz_id: int, cluster_id: int) -> dict: + """获取集群现有信息 + 1. master 对应 slave 机器 + 2. master 上的端口列表 + 3. 实例对应关系:{master:port:slave:port} + """ + master_pair_map, master_slave_map, master_ports = defaultdict(), defaultdict(), defaultdict(list) + cluster = Cluster.objects.get(id=cluster_id, bk_biz_id=bk_biz_id) + + for master_obj in cluster.storageinstance_set.filter(instance_role=InstanceRole.REDIS_MASTER.value): + slave_obj = master_obj.as_ejector.get().receiver + master_ports[master_obj.machine.ip].append(master_obj.port) + master_pair_map[ + "{}{}{}".format(master_obj.machine.ip, IP_PORT_DIVIDER, master_obj.port) + ] = "{}{}{}".format(slave_obj.machine.ip, IP_PORT_DIVIDER, slave_obj.port) + + ifslave = master_slave_map.get(master_obj.machine.ip) + if ifslave and ifslave != slave_obj.machine.ip: + raise Exception( + "unsupport mutil slave with cluster {} 4:{}".format(cluster.immute_domain, master_obj.machine.ip) + ) + + master_slave_map[master_obj.machine.ip] = slave_obj.machine.ip + + return { + "immute_domain": cluster.immute_domain, + "bk_biz_id": cluster.bk_biz_id, + "bk_cloud_id": cluster.bk_cloud_id, + "cluster_type": cluster.cluster_type, + "cluster_name": cluster.name, + "cluster_id": cluster.id, + "master_ports": dict(master_ports), + "master_pair_map": dict(master_pair_map), + "master_slave_map": dict(master_slave_map), + "proxy_port": cluster.proxyinstance_set.first().port, + "proxy_ips": [proxy_obj.machine.ip for proxy_obj in cluster.proxyinstance_set.all()], + "db_version": cluster.major_version, + } + + def __init_builder(self, operate_name: str): + redis_pipeline = Builder(root_id=self.root_id, data=self.data) + trans_files = GetFileList(db_type=DBType.Redis) + act_kwargs = ActKwargs() + act_kwargs.set_trans_data_dataclass = CommonContext.__name__ + act_kwargs.file_list = trans_files.redis_base() + act_kwargs.is_update_trans_data = True + act_kwargs.cluster = { + "operate": operate_name, + } + return redis_pipeline, act_kwargs + + # 执行 正常/异常 情况下主从切换逻辑 + def redis_ms_switch(self): + redis_pipeline, act_kwargs = self.__init_builder(_("REDIS-主从切换")) + sub_pipelines = [] + force_switch = self.data.get("force", False) + for ms_switch in self.data["infos"]: + cluster_kwargs = deepcopy(act_kwargs) + cluster_info = self.__get_cluster_info(self.data["bk_biz_id"], ms_switch["cluster_id"]) + + flow_data = self.data + for k, v in cluster_info.items(): + cluster_kwargs.cluster[k] = v + cluster_kwargs.cluster["created_by"] = self.data["created_by"] + cluster_kwargs.cluster["switch_option"] = ms_switch["online_switch_type"] + flow_data["switch_input"] = ms_switch + redis_pipeline.add_act( + act_name=_("初始化配置-{}".format(cluster_info["immute_domain"])), + act_component_code=GetRedisActPayloadComponent.code, + kwargs=asdict(cluster_kwargs), + ) + sub_pipeline = self.generate_ms_switch_flow(flow_data, cluster_kwargs, ms_switch, force_switch) + sub_pipelines.append(sub_pipeline) + + redis_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) + return redis_pipeline.run_pipeline() + + # 组装&控制 集群切换流程 + def generate_ms_switch_flow(self, flow_data, act_kwargs, ms_switch, force=False): + """ + 1. 切换前同步检查 + 2. 执行切换 3. 切换backends校验 + 4. 刷新 new master 监控 + 5. 元数据修改 old-master 2 unavliable. + """ + # "pairs": [ + # {"redis_master": "1.1.2.3", "redis_slave": "1.1.2.4"} + # ] + redis_pipeline = SubBuilder(root_id=self.root_id, data=flow_data) + # 执行切换 ##################################################################################### + sync_relations, master_ips, slave_ips = [], [], [] + for ms_pair in ms_switch["pairs"]: + master_ip = ms_pair.get("redis_master", "why.no.ip.input") + slave_ip = ms_pair.get("redis_slave", "why.no.ip.input") + master_ips.append(master_ip) + slave_ips.append(slave_ip) + sync_params = { + "origin_1": master_ip, + "origin_2": slave_ip, + "sync_dst1": slave_ip, + "sync_dst2": slave_ip, + "ins_link": [], + } + for master_port in act_kwargs.cluster["master_ports"][master_ip]: + master_addr = "{}{}{}".format(master_ip, IP_PORT_DIVIDER, master_port) + slave_addr = act_kwargs.cluster["master_pair_map"][master_addr] + slave_port = slave_addr.split(IP_PORT_DIVIDER)[1] + sync_params["ins_link"].append( + { + "origin_1": int(master_port), + "origin_2": int(slave_port), + "sync_dst1": int(slave_port), + "sync_dst2": int(slave_port), + } + ) + sync_relations.append(sync_params) + act_kwargs.cluster["switch_condition"] = { + "sync_type": SyncType.SYNC_MS.value, + "is_check_sync": force, # 强制切换 + "slave_master_diff_time": DEFAULT_MASTER_DIFF_TIME, + "last_io_second_ago": DEFAULT_LAST_IO_SECOND_AGO, + "can_write_before_switch": True, + } + sub_pipeline = RedisClusterSwitchAtomJob(self.root_id, flow_data, act_kwargs, sync_relations) + redis_pipeline.add_sub_pipeline(sub_flow=sub_pipeline) + # 执行切换 ###########################################################################完成###### + + # 元数据修改 ################################################################################### + sub_acts = [] + for master_ip in master_ips: + sub_kwargs = deepcopy(act_kwargs) + sub_kwargs.cluster["meta_update_ip"] = master_ip + sub_kwargs.cluster["meta_udpate_ports"] = act_kwargs.cluster["master_ports"][master_ip] + sub_kwargs.cluster["meta_update_status"] = InstanceStatus.UNAVAILABLE.value + sub_kwargs.cluster["meta_func_name"] = RedisDBMeta.instances_failover_4_scene.__name__ + sub_acts.append( + { + "act_name": _("Redis-{}-元数据修改".format(master_ip)), + "act_component_code": RedisDBMetaComponent.code, + "kwargs": asdict(sub_kwargs), + } + ) + redis_pipeline.add_parallel_acts(acts_list=sub_acts) + # 元数据修改 ###########################################################################完成###### + + # 刷新监控 ##################################################################################### + app = AppCache.get_app_attr(act_kwargs.cluster["bk_biz_id"], "db_app_abbr") + app_name = AppCache.get_app_attr(act_kwargs.cluster["bk_biz_id"], "bk_biz_name") + sub_acts = [] + act_kwargs.get_redis_payload_func = RedisActPayload.bkdbmon_install.__name__ + for slave_ip in slave_ips: + sub_kwargs = deepcopy(act_kwargs) + sub_kwargs.exec_ip = slave_ip + sub_kwargs.cluster["servers"] = [ + { + "app": app, + "app_name": app_name, + "bk_biz_id": str(act_kwargs.cluster["bk_biz_id"]), + "bk_cloud_id": int(act_kwargs.cluster["bk_cloud_id"]), + "server_ip": slave_ip, + "server_ports": [s_obj.port for s_obj in StorageInstance.objects.filter(machine__ip=slave_ip)], + "meta_role": InstanceRole.REDIS_MASTER.value, + "cluster_name": act_kwargs.cluster["cluster_name"], + "cluster_type": act_kwargs.cluster["cluster_type"], + "cluster_domain": act_kwargs.cluster["immute_domain"], + } + ] + sub_acts.append( + { + "act_name": _("Redis-{}-刷新监控".format(slave_ip)), + "act_component_code": ExecuteDBActuatorScriptComponent.code, + "kwargs": asdict(sub_kwargs), + } + ) + redis_pipeline.add_parallel_acts(acts_list=sub_acts) + # 刷新监控 ###########################################################################完成###### + + return redis_pipeline.build_sub_process( + sub_name=_("Redis-{}-主从切换").format(act_kwargs.cluster["immute_domain"]) + ) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_scene_slave.py b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_scene_slave.py deleted file mode 100644 index 82452bab82..0000000000 --- a/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_scene_slave.py +++ /dev/null @@ -1,242 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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. -""" -import logging.config -from collections import defaultdict -from dataclasses import asdict -from typing import Dict, Optional - -from django.utils.translation import ugettext as _ - -from backend.configuration.constants import DBType -from backend.constants import IP_PORT_DIVIDER -from backend.db_meta.enums import InstanceRole -from backend.db_meta.models import Cluster -from backend.flow.consts import DEFAULT_REDIS_START_PORT, SyncType -from backend.flow.engine.bamboo.scene.common.builder import Builder -from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList -from backend.flow.engine.bamboo.scene.redis.atom_jobs import ( - RedisBatchInstallAtomJob, - RedisBatchShutdownAtomJob, - RedisLocalRepairAtomJob, - RedisMakeSyncAtomJob, -) -from backend.flow.plugins.components.collections.redis.get_redis_payload import GetRedisActPayloadComponent -from backend.flow.plugins.components.collections.redis.redis_db_meta import RedisDBMetaComponent -from backend.flow.utils.redis.redis_context_dataclass import ActKwargs, CommonContext -from backend.flow.utils.redis.redis_db_meta import RedisDBMeta - -logger = logging.getLogger("flow") - - -class RedisClusterSlaveSceneFlow(object): - """ - ## Redis cluster Slave 裁撤/迁移替换 - { - "uid": "2022051612120001", - "cluster_id":111, # 必须有 - "domain_name":"xxx.abc.dba.db", - "bk_biz_id":"", - "bk_cloud_id":11, - "region":"xxxyw", # 可选 - "device_class":"S5.Large8" # 可选 - "ip_source": "manual_input", # 手动输入/自动匹配资源池 - "replace_relation":{"1.1.x.1":"6.6.y.6","2.2.z.2":"7.7.a.7"} # 可选 - "ticket_type": "REDIS_CLUSTER_SLAVE_CUTOFF" - "created_by":"u r great!", - } - """ - - def __init__(self, root_id: str, data: Optional[Dict]): - """ - @param root_id : 任务流程定义的root_id - @param data : 单据传递过来的参数列表,是dict格式 - """ - self.root_id = root_id - self.data = data - self.__precheck_() - - def __precheck_(self) -> dict: - cluster = Cluster.objects.get(id=self.data["cluster_id"], bk_biz_id=self.data["bk_biz_id"]) - if not cluster: - raise Exception( - "cluster does not exist bk_biz_id:{}, cluster_id{}".format( - self.data["bk_biz_id"], self.data["cluster_id"] - ) - ) - for old_slave in self.data["replace_relation"].keys(): - if not cluster.storageinstance_set.filter(machine__ip=old_slave).exists(): - raise Exception("bad slave for cluster {} slave_not_exists:{}".format(cluster, old_slave)) - - @staticmethod - def __get_cluster_info(bk_biz_id: int, cluster_id: int) -> dict: - """获取集群现有信息 - 1. slave 对应 master 机器 - 2. slave 上的端口列表 - 3. 实例对应关系:{slave:port : master:port} - """ - cluster = Cluster.objects.get(id=cluster_id, bk_biz_id=bk_biz_id) - - slave_master_map = defaultdict() - slave_ports = defaultdict(list) - slave_ins_map = defaultdict() - for master_obj in cluster.storageinstance_set.filter(instance_role=InstanceRole.REDIS_MASTER.value): - slave_obj = master_obj.as_ejector.get().receiver - slave_ports[slave_obj.machine.ip].append(slave_obj.port) - slave_ins_map["{}{}{}".format(slave_obj.machine.ip, IP_PORT_DIVIDER, slave_obj.port)] = "{}{}{}".format( - master_obj.machine.ip, IP_PORT_DIVIDER, master_obj.port - ) - - ifmaster = slave_master_map.get(slave_obj.machine.ip) - if ifmaster and ifmaster != master_obj.machine.ip: - raise Exception( - "unsupport mutil master for cluster {}:{}".format(cluster.immute_domain, slave_obj.machine.ip) - ) - else: - slave_master_map[slave_obj.machine.ip] = master_obj.machine.ip - - return { - "immute_domain": cluster.immute_domain, - "bk_biz_id": cluster.bk_biz_id, - "bk_cloud_id": cluster.bk_cloud_id, - "cluster_type": cluster.cluster_type, - "slave_ports": dict(slave_ports), - "slave_ins_map": dict(slave_ins_map), - "slave_master_map": dict(slave_master_map), - "db_version": cluster.major_version, - } - - def __init_builder(self, operate_name: str): - cluster_info = self.__get_cluster_info(self.data["bk_biz_id"], self.data["cluster_id"]) - flow_data = self.data - for k, v in cluster_info.items(): - flow_data[k] = v - redis_pipeline = Builder(root_id=self.root_id, data=flow_data) - trans_files = GetFileList(db_type=DBType.Redis) - act_kwargs = ActKwargs() - act_kwargs.set_trans_data_dataclass = CommonContext.__name__ - act_kwargs.file_list = trans_files.redis_base() - act_kwargs.is_update_trans_data = True - act_kwargs.cluster = { - **cluster_info, - "operate": operate_name, - } - act_kwargs.bk_cloud_id = cluster_info["bk_cloud_id"] - logger.info("+===+++++===current tick_data info+++++===++++ :: {}".format(act_kwargs)) - - redis_pipeline.add_act( - act_name=_("初始化配置"), act_component_code=GetRedisActPayloadComponent.code, kwargs=asdict(act_kwargs) - ) - return redis_pipeline, act_kwargs - - def work_4_replace(self): - """适用于 集群中Slave 机房裁撤/迁移替换场景 - - 步骤: 获取变更锁--> 新实例部署--> - 重建热备--> 检测同步状态--> - Kill Dead链接--> 下架旧实例 - """ - redis_pipeline, act_kwargs = self.__init_builder(_("REDIS_SLAVE-裁撤替换")) - - # ### 部署实例 ############################################################################# - sub_pipelines = [] - for old_slave, new_slave in self.data["replace_relation"].items(): - params = { - "ip": new_slave, - "meta_role": InstanceRole.REDIS_SLAVE.value, - "start_port": DEFAULT_REDIS_START_PORT, - "ports": act_kwargs.cluster["slave_ports"][old_slave], - "instance_numb": len(act_kwargs.cluster["slave_ports"][old_slave]), - } - sub_builder = RedisBatchInstallAtomJob(self.root_id, self.data, act_kwargs, params) - sub_pipelines.append(sub_builder) - redis_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) - # ### 部署实例 ########################################################################## 完毕 ### - - # #### 建同步关系 ############################################################################# - sub_pipelines = [] - for old_slave, new_slave in self.data["replace_relation"].items(): - install_params = { - "sync_type": SyncType.SYNC_MS, - "origin_1": act_kwargs.cluster["slave_master_map"][old_slave], - "origin_2": old_slave, - "sync_dst1": new_slave, - "ins_link": [], - } - for slave_port in act_kwargs.cluster["slave_ports"][old_slave]: - old_ins = "{}{}{}".format(old_slave, IP_PORT_DIVIDER, slave_port) - master_port = act_kwargs.cluster["slave_ins_map"].get(old_ins).split(IP_PORT_DIVIDER)[1] - install_params["ins_link"].append({"origin_1": master_port, "sync_dst1": slave_port}) - sub_builder = RedisMakeSyncAtomJob(self.root_id, self.data, act_kwargs, install_params) - sub_pipelines.append(sub_builder) - redis_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) - # #### 建同步关系 ########################################################################## 完毕 ### - - # 新节点加入集群 ############################################################################# - act_kwargs.cluster["meta_func_name"] = RedisDBMeta.redis_redo_slaves.__name__ - act_kwargs.cluster["old_slaves"] = [] - act_kwargs.cluster["created_by"] = self.data["created_by"] - act_kwargs.cluster["tendiss"] = [] - for old_slave, new_slave in self.data["replace_relation"].items(): - act_kwargs.cluster["old_slaves"].append( - {"ip": old_slave, "ports": act_kwargs.cluster["slave_ports"][old_slave]} - ) - for slave_port in act_kwargs.cluster["slave_ports"][old_slave]: - old_ins = "{}{}{}".format(old_slave, IP_PORT_DIVIDER, slave_port) - master = act_kwargs.cluster["slave_ins_map"].get(old_ins) - act_kwargs.cluster["tendiss"].append( - { - "ejector": { - "ip": master.split(IP_PORT_DIVIDER)[0], - "port": int(master.split(IP_PORT_DIVIDER)[1]), - }, - "receiver": {"ip": new_slave, "port": int(slave_port)}, - } - ) - redis_pipeline.add_act( - act_name=_("Redis-新节点加入集群"), act_component_code=RedisDBMetaComponent.code, kwargs=asdict(act_kwargs) - ) - # #### 新节点加入集群 ########################################################################## 完毕 ### - - # #### 下架旧实例 ############################################################################# - sub_pipelines = [] - for old_slave in self.data["replace_relation"].keys(): - params = { - "ignore_ips": act_kwargs.cluster["slave_master_map"][old_slave], - "ip": old_slave, - "ports": act_kwargs.cluster["slave_ports"][old_slave], - } - sub_builder = RedisBatchShutdownAtomJob(self.root_id, self.data, act_kwargs, params) - sub_pipelines.append(sub_builder) - redis_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) - # #### 下架旧实例 ########################################################################## 完毕 ### - - redis_pipeline.run_pipeline() - - def work_4_auotfix(self): - redis_pipeline, act_kwargs = self.__init_builder(_("REDIS_SLAVE-故障自愈")) - """适用于 集群中Slave 故障自愈模型 - # # 探测故障实例状态 ## ## 修改元数据状态 ## - # # 根据需要申请替换 ## - 步骤: 获取变更锁--> 新实例部署--> - 重建热备--> 检测同步状态--> - Kill Dead链接--> 下架旧实例 - """ - # # ### 尝试修复故障实例 ###################################################################### - # sub_pipelines = [] - # for old_slave, new_slave in self.data["replace_relation"].items(): - # params = { - # "ip": new_slave, - # "ports": act_kwargs.cluster["slave_ports"][old_slave], - # "wait_seconds": 600, "last_io_second_ago":10, - # } - # sub_builder = RedisLocalRepairAtomJob(self.root_id, self.data, act_kwargs, params) - # sub_pipelines.append(sub_builder) - # redis_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_shutdown.py b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_shutdown.py index 5daf8c4b80..627a574f6a 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_shutdown.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_cluster_shutdown.py @@ -17,6 +17,7 @@ from backend.configuration.constants import DBType from backend.db_meta.enums import InstanceRole +from backend.db_meta.enums.cluster_type import ClusterType from backend.db_meta.models import AppCache, Cluster from backend.flow.consts import ( DEFAULT_MONITOR_TIME, @@ -28,14 +29,14 @@ ) from backend.flow.engine.bamboo.scene.common.builder import Builder from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList -from backend.flow.plugins.components.collections.redis.dns_manage import RedisDnsManageComponent +from backend.flow.engine.bamboo.scene.redis.atom_jobs import AccessManagerAtomJob from backend.flow.plugins.components.collections.redis.exec_actuator_script import ExecuteDBActuatorScriptComponent from backend.flow.plugins.components.collections.redis.get_redis_payload import GetRedisActPayloadComponent from backend.flow.plugins.components.collections.redis.redis_config import RedisConfigComponent from backend.flow.plugins.components.collections.redis.redis_db_meta import RedisDBMetaComponent from backend.flow.plugins.components.collections.redis.trans_flies import TransFileComponent from backend.flow.utils.redis.redis_act_playload import RedisActPayload -from backend.flow.utils.redis.redis_context_dataclass import ActKwargs, CommonContext, DnsKwargs +from backend.flow.utils.redis.redis_context_dataclass import ActKwargs, CommonContext from backend.flow.utils.redis.redis_db_meta import RedisDBMeta logger = logging.getLogger("flow") @@ -101,7 +102,6 @@ def redis_cluster_shutdown_flow(self): act_kwargs.is_update_trans_data = True act_kwargs.cluster = { **cluster_info, - "operate": DBActuatorTypeEnum.Proxy.value + "_" + RedisActuatorActionEnum.Shutdown.value, "backup_type": RedisBackupEnum.NORMAL_BACKUP.value, **cluster_info["redis_map"], **cluster_info["proxy_map"], @@ -151,18 +151,22 @@ def redis_cluster_shutdown_flow(self): redis_pipeline.add_parallel_acts(acts_list=acts_list) - # 删除域名 - dns_kwargs = DnsKwargs(dns_op_type=DnsOpType.CLUSTER_DELETE, delete_cluster_id=self.data["cluster_id"]) - redis_pipeline.add_act( - act_name=_("删除域名"), - act_component_code=RedisDnsManageComponent.code, - kwargs={**asdict(act_kwargs), **asdict(dns_kwargs)}, - ) + params = { + "cluster_id": self.data["cluster_id"], + "op_type": DnsOpType.CLUSTER_DELETE, + } + access_sub_builder = AccessManagerAtomJob(self.root_id, self.data, act_kwargs, params) + redis_pipeline.add_sub_pipeline(sub_flow=access_sub_builder) acts_list = [] for ip in proxy_ips: # proxy执行下架 act_kwargs.exec_ip = ip + act_kwargs.cluster = { + "ip": ip, + "port": cluster_info["proxy_map"][ip], + "operate": DBActuatorTypeEnum.Proxy.value + "_" + RedisActuatorActionEnum.Shutdown.value, + } act_kwargs.get_redis_payload_func = RedisActPayload.proxy_operate_payload.__name__ acts_list.append( { @@ -172,7 +176,32 @@ def redis_cluster_shutdown_flow(self): } ) + act_kwargs.cluster = { + "servers": [ + { + "bk_biz_id": str(self.data["bk_biz_id"]), + "bk_cloud_id": act_kwargs.bk_cloud_id, + "server_ports": [], + "meta_role": "", + "cluster_domain": cluster_info["domain_name"], + "app": app, + "app_name": app_name, + "cluster_name": cluster_info["cluster_name"], + "cluster_type": cluster_info["cluster_type"], + } + ] + } + act_kwargs.get_redis_payload_func = RedisActPayload.bkdbmon_install.__name__ + acts_list.append( + { + "act_name": _("{}卸载bkdbmon").format(ip), + "act_component_code": ExecuteDBActuatorScriptComponent.code, + "kwargs": asdict(act_kwargs), + } + ) + for ip in redis_ips: + act_kwargs.cluster = {} act_kwargs.exec_ip = ip act_kwargs.get_redis_payload_func = RedisActPayload.redis_shutdown_payload.__name__ acts_list.append( @@ -202,7 +231,7 @@ def redis_cluster_shutdown_flow(self): act_kwargs.get_redis_payload_func = RedisActPayload.bkdbmon_install.__name__ acts_list.append( { - "act_name": _("[redis]卸载bkdbmon"), + "act_name": _("{}卸载bkdbmon").format(ip), "act_component_code": ExecuteDBActuatorScriptComponent.code, "kwargs": asdict(act_kwargs), } @@ -212,13 +241,22 @@ def redis_cluster_shutdown_flow(self): acts_list = [] # 清理config # TODO 这里等提供新接口后修改 - act_kwargs.cluster = { - "conf": { - "requirepass": "", - "cluster-enabled": "", - }, - "cluster_id": self.data["cluster_id"], - } + if cluster_info["cluster_type"] == ClusterType.TwemproxyTendisSSDInstance.value: + act_kwargs.cluster = { + "conf": { + "requirepass": "", + }, + "cluster_id": self.data["cluster_id"], + } + else: + act_kwargs.cluster = { + "conf": { + "requirepass": "", + "cluster-enabled": "", + }, + "cluster_id": self.data["cluster_id"], + } + act_kwargs.get_redis_payload_func = RedisActPayload.delete_redis_config.__name__ acts_list.append( { diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_data_structure.py b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_data_structure.py new file mode 100644 index 0000000000..d634a64f87 --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_data_structure.py @@ -0,0 +1,537 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +import logging.config +from collections import defaultdict +from copy import deepcopy +from dataclasses import asdict +from typing import Any, Dict, Optional + +from django.utils.translation import ugettext as _ + +from backend.components import DBConfigApi +from backend.components.dbconfig.constants import FormatType, LevelName +from backend.configuration.constants import DBType +from backend.constants import IP_PORT_DIVIDER +from backend.db_meta import api +from backend.db_meta.enums import DBCCModule, InstanceInnerRole, InstanceRole, InstanceStatus, SyncType +from backend.db_meta.enums.cluster_type import ClusterType +from backend.db_meta.models import Cluster, StorageInstance, StorageInstanceTuple +from backend.flow.consts import DEFAULT_DB_MODULE_ID, DEFAULT_REDIS_START_PORT, DEFAULT_TWEMPROXY_SEG_TOTOL_NUM +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList +from backend.flow.engine.bamboo.scene.redis.atom_jobs import RedisBatchInstallAtomJob +from backend.flow.plugins.components.collections.redis.exec_actuator_script import ExecuteDBActuatorScriptComponent +from backend.flow.plugins.components.collections.redis.get_redis_payload import GetRedisActPayloadComponent +from backend.flow.plugins.components.collections.redis.redis_db_meta import RedisDBMetaComponent +from backend.flow.plugins.components.collections.redis.trans_flies import TransFileComponent +from backend.flow.utils.redis.redis_act_playload import RedisActPayload +from backend.flow.utils.redis.redis_context_dataclass import ActKwargs, CommonContext, RedisDataStructureContext +from backend.flow.utils.redis.redis_db_meta import RedisDBMeta + +logger = logging.getLogger("flow") + + +class RedisDataStructureFlow(object): + """ + ## redis 数据构造 + { + "bk_biz_id": 3, + "uid": "2022051612120001", + "created_by":"admin", + "ticket_type":"REDIS_DATA_STRUCTURE", + "infos": [ + { + "cluster_id": 1, + "bk_cloud_id": 1, + "master_instances":[ + "127.0.0.1:30000", "127.0.0.1:30002" + ], + "recovery_time_point": "2022-12-12 11:11:11", + "redis": [ + {"ip": "3.3.3.1", "bk_cloud_id": 0, "bk_host_id": 2}, + {"ip": "3.3.3.2", "bk_cloud_id": 0, "bk_host_id": 2}, + ], + "resource_spec": { + "redis": {"id": 1} + } + } + ] + } + """ + + def __init__(self, root_id: str, data: Optional[Dict]): + """ + @param root_id : 任务流程定义的root_id + @param data : 单据传递过来的参数列表,是dict格式 + """ + self.root_id = root_id + self.data = data + + def redis_data_structure_flow(self): + + redis_pipeline_all = Builder(root_id=self.root_id, data=self.data) + + # 支持批量操作 + sub_pipelines_multi_cluster = [] + for info in self.data["infos"]: + """""" + logger.info("redis_data_structure_flow info:{}".format(info)) + redis_pipeline, act_kwargs = self.__init_builder(_("REDIS_DATA_STRUCTURE"), info) + cluster_kwargs = deepcopy(act_kwargs) + # 源节点列表 + cluster_src_instance = [] + # sass 层传入节点信息(集群维度传入所有节点) + if info["master_instances"]: + # 根据传入的master_instance 获取其slave_instance + for slave_instance, master_instance in act_kwargs.cluster["slave_ins_map"].items(): + for backup_master_instance in info["master_instances"]: + if backup_master_instance == master_instance: + cluster_src_instance.append(slave_instance) + + # 计算每台主机部署的节点数 + avg = int(len(cluster_src_instance) // len(info["redis"])) + # 计算整除后多于的节点数 + remainder = int(len(cluster_src_instance) % len(info["redis"])) + logger.info("redis_data_structure_flow cluster_src_instance: {}".format(cluster_src_instance)) + + # ### 部署redis ############################################################ + sub_pipelines = [] + cluster_dst_instance = [] + cluster_type = act_kwargs.cluster["cluster_type"] + resource_spec = info["resource_spec"]["redis"] + for index, new_master in enumerate([host["ip"] for host in info["redis"]]): + # 将整除后多于的节点一个一个地分配给每台主机 + instance_numb = avg + 1 if index < remainder else avg + sub_builder = RedisBatchInstallAtomJob( + self.root_id, + self.data, + act_kwargs, + { + "ip": new_master, + "meta_role": InstanceRole.REDIS_MASTER.value, + "start_port": DEFAULT_REDIS_START_PORT, + "ports": [], + "instance_numb": instance_numb, + "spec_id": resource_spec["id"], + "spec_config": resource_spec, + }, + ) + sub_pipelines.append(sub_builder) + + # 将部署信息存入cluster_dst_instance + for inst_no in range(0, instance_numb): + port = DEFAULT_REDIS_START_PORT + inst_no + cluster_dst_instance.append("{}{}{}".format(new_master, IP_PORT_DIVIDER, port)) + + # 检查节点总数是否相等 + if len(info["master_instances"]) != len(cluster_dst_instance): + raise ValueError("The total number of nodes in both clusters must be equal.") + + # 使用zip函数将源集群和临时集群的节点一一对应 + node_pairs = list(zip(cluster_src_instance, cluster_dst_instance)) + + # ### 下发actuator包############################################################ + acts_lists = [] + first_act_kwargs = deepcopy(act_kwargs) + for index, new_master in enumerate([host["ip"] for host in info["redis"]]): + trans_files = GetFileList(db_type=DBType.Redis) + first_act_kwargs.file_list = trans_files.redis_actuator_backend() + first_act_kwargs.exec_ip = new_master + acts_lists.append( + { + "act_name": _("Redis-{}-下发actuator包").format(new_master), + "act_component_code": TransFileComponent.code, + "kwargs": asdict(first_act_kwargs), + } + ) + redis_pipeline.add_parallel_acts(acts_list=acts_lists) + # ### 下发actuator包完成############################################################ + + # ### 初始化机器,有时机器混用环境变量没处理,会导致部分目录不存在,会有影响############### + acts_lists = [] + first_act_kwargs = deepcopy(act_kwargs) + for index, new_master in enumerate([host["ip"] for host in info["redis"]]): + first_act_kwargs.exec_ip = new_master + first_act_kwargs.get_redis_payload_func = RedisActPayload.get_sys_init_payload.__name__ + acts_lists.append( + { + "act_name": _("初始化机器"), + "act_component_code": ExecuteDBActuatorScriptComponent.code, + "kwargs": asdict(first_act_kwargs), + } + ) + redis_pipeline.add_parallel_acts(acts_list=acts_lists) + # ### 初始化机器完成############################################################ + + # ### 数据构造下发actuator 检查备份文件是否存在,新机器磁盘空间是否够############################################## + # GetTendisType 获取redis类型 + if cluster_type == ClusterType.TendisTwemproxyRedisInstance.value: + tendis_type = ClusterType.RedisInstance.value + elif cluster_type == ClusterType.TendisPredixyTendisplusCluster.value: + tendis_type = ClusterType.TendisplusInstance.value + elif cluster_type == ClusterType.TwemproxyTendisSSDInstance.value: + tendis_type = ClusterType.TendisSSDInstance.value + else: + raise NotImplementedError("Not supported tendis type: %s" % cluster_type) + # 整理数据构造下发actuator 源节点和临时集群节点之间的对应关系, + acts_list = self.get_prod_temp_instance_pairs(act_kwargs, node_pairs, info, True, tendis_type) + redis_pipeline.add_parallel_acts(acts_list=acts_list) + # 检查备份信息存在,机器磁盘是否够,再部署redis 节点 + redis_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) + + # # ###cc 转移机器模块 ################################################################ + # 直接挪机器 + cluster_kwargs.cluster["meta_func_name"] = RedisDBMeta.redis_rollback_host_transfer.__name__ + cluster_kwargs.cluster["tendiss"] = [] + for instance in cluster_dst_instance: + ip, port = instance.split(":") + cluster_kwargs.cluster["tendiss"].append({"receiver": {"ip": ip, "port": int(port)}}) + redis_pipeline.add_act( + act_name=_("Redis-临时节点加入源集群cc模块"), + act_component_code=RedisDBMetaComponent.code, + kwargs=asdict(cluster_kwargs), + ) + # # ### cc 转移机器模块完成 ############################################################ + + # ### 如果是tendisplus,需要构建tendis cluster关系 ############################################################ + if cluster_type == ClusterType.TendisPredixyTendisplusCluster.value: + logger.info("cluster_type is:{} need tendis cluster relation".format(cluster_type)) + act_kwargs.cluster["all_instance"] = cluster_dst_instance + act_kwargs.get_redis_payload_func = RedisActPayload.rollback_clustermeet_payload.__name__ + # 选第一台作为下发执行任务的机器 + act_kwargs.exec_ip = info["redis"][0]["ip"] + redis_pipeline.add_act( + act_name=_("建立meet关系"), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(act_kwargs), + ) + # ### 构建tendisplus集群关系结束 ############################################################################# + + # ### 部署proxy实例 ############################################################################# + # 选第一台机器作为部署proxy的机器 + act_kwargs.new_install_proxy_exec_ip = info["redis"][0]["ip"] + act_kwargs.get_trans_data_ip_var = RedisDataStructureContext.get_proxy_exec_ip_var_name() + + trans_files = GetFileList(db_type=DBType.Redis) + if cluster_type in [ + ClusterType.TendisTwemproxyRedisInstance.value, + ClusterType.TwemproxyTendisSSDInstance.value, + ]: + # 部署proxy pkg包 + act_kwargs.file_list = trans_files.redis_cluster_apply_proxy(cluster_type) + proxy_payload = RedisActPayload.add_twemproxy_payload.__name__ + elif cluster_type == ClusterType.TendisPredixyTendisplusCluster.value: + act_kwargs.file_list = trans_files.tendisplus_apply_proxy() + proxy_payload = RedisActPayload.add_predixy_payload.__name__ + else: + raise NotImplementedError("Not supported cluster type: %s" % cluster_type) + + act_kwargs.get_trans_data_ip_var = RedisDataStructureContext.get_proxy_exec_ip_var_name() + act_kwargs.exec_ip = act_kwargs.new_install_proxy_exec_ip + redis_pipeline.add_act( + act_name=_("{}proxy下发介质包").format(act_kwargs.exec_ip), + act_component_code=TransFileComponent.code, + kwargs=asdict(act_kwargs), + ) + act_kwargs.get_redis_payload_func = RedisActPayload.get_sys_init_payload.__name__ + redis_pipeline.add_act( + act_name=_("初始化机器"), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(act_kwargs), + ) + redis_pipeline.add_act( + act_name=_("初始化配置"), act_component_code=GetRedisActPayloadComponent.code, kwargs=asdict(act_kwargs) + ) + + # 构造proxy server信息 + if cluster_type in [ClusterType.TendisTwemproxyRedisInstance, ClusterType.TwemproxyTendisSSDInstance]: + servers = self.cal_twemproxy_serveres("admin", act_kwargs.cluster["redis_slave_set"], node_pairs) + elif cluster_type == ClusterType.TendisPredixyTendisplusCluster: + servers = cluster_dst_instance + else: + raise NotImplementedError("Not supported cluster type: %s" % cluster_type) + + act_kwargs.cluster["servers"] = servers + logger.info("proxy servers: {}".format(act_kwargs.cluster["servers"])) + act_kwargs.get_redis_payload_func = proxy_payload + act_kwargs.exec_ip = act_kwargs.new_install_proxy_exec_ip + redis_pipeline.add_act( + act_name=_("{}安装proxy实例").format(act_kwargs.new_install_proxy_exec_ip), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(act_kwargs), + ) + + # ### 数据构造下发actuator ############################################################################# + # 整理数据构造下发actuator 源节点和临时集群节点之间的对应关系, + acts_list = self.get_prod_temp_instance_pairs(act_kwargs, node_pairs, info, False, tendis_type) + redis_pipeline.add_parallel_acts(acts_list=acts_list) + + # # ### # ### 如果是tendisplus,需要重新构建 cluster关系,因为tendisplus数据构造需要reset集群关系 ############## + if cluster_type == ClusterType.TendisPredixyTendisplusCluster.value: + logger.info("cluster_type is:{} cluster meet and check finish relation".format(cluster_type)) + act_kwargs.cluster["all_instance"] = cluster_dst_instance + act_kwargs.get_redis_payload_func = RedisActPayload.clustermeet_check_payload.__name__ + # 选第一台作为下发执行任务的机器 + act_kwargs.exec_ip = info["redis"][0]["ip"] + redis_pipeline.add_act( + act_name=_("meet建立集群关系并检查集群状态"), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(act_kwargs), + ) + + # ### 写入构造记录元数据 ###################################################### + act_kwargs.cluster = { + # 记录元数据 + "domain_name": act_kwargs.cluster["domain_name"], + "bk_cloud_id": act_kwargs.cluster["bk_cloud_id"], + "prod_cluster_type": cluster_type, + "prod_cluster": act_kwargs.cluster["domain_name"], + "prod_cluster_id": info["cluster_id"], + "specification": resource_spec, + "prod_instance_range": cluster_src_instance, + "temp_cluster_type": cluster_type, + "temp_instance_range": cluster_dst_instance, + "temp_cluster_proxy": "{}:{}".format( + act_kwargs.new_install_proxy_exec_ip, act_kwargs.cluster["proxy_port"] + ), + "prod_temp_instance_pairs": node_pairs, + "host_count": len(info["redis"]), + "recovery_time_point": info["recovery_time_point"], + "status": 2, + "meta_func_name": RedisDBMeta.data_construction_tasks_operate.__name__, + "cluster_type": cluster_type, + } + redis_pipeline.add_act( + act_name=_("写入构造记录元数据"), act_component_code=RedisDBMetaComponent.code, kwargs=asdict(act_kwargs) + ) + + sub_pipelines_multi_cluster.append( + redis_pipeline.build_sub_process(sub_name=_("集群[{}]数据构造").format(act_kwargs.cluster["domain_name"])) + ) + + redis_pipeline_all.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines_multi_cluster) + redis_pipeline_all.run_pipeline() + + @staticmethod + def __get_cluster_info(bk_biz_id: int, cluster_id: int) -> dict: + """获取集群现有信息 + 1. slave 对应 master 机器 + 2. slave 上的端口列表 + 3. 实例对应关系:{slave:port : master:port} + """ + + cluster = Cluster.objects.get(id=cluster_id, bk_biz_id=bk_biz_id) + slave_master_map = defaultdict() + slave_ports = defaultdict(list) + slave_ins_map = defaultdict() + master_nums = len(cluster.storageinstance_set.filter(instance_role=InstanceRole.REDIS_MASTER.value)) + for master_obj in cluster.storageinstance_set.filter(instance_role=InstanceRole.REDIS_MASTER.value): + slave_obj = master_obj.as_ejector.get().receiver + slave_ports[slave_obj.machine.ip].append(slave_obj.port) + slave_ins_map["{}{}{}".format(slave_obj.machine.ip, IP_PORT_DIVIDER, slave_obj.port)] = "{}{}{}".format( + master_obj.machine.ip, IP_PORT_DIVIDER, master_obj.port + ) + + ifmaster = slave_master_map.get(slave_obj.machine.ip) + if ifmaster and ifmaster != master_obj.machine.ip: + raise Exception( + "unsupport mutil master for cluster {}:{}".format(cluster.immute_domain, slave_obj.machine.ip) + ) + + slave_master_map[slave_obj.machine.ip] = master_obj.machine.ip + + cluster_info = api.cluster.nosqlcomm.get_cluster_detail(cluster_id)[0] + cluster_name = cluster_info["name"] + cluster_type = cluster_info["cluster_type"] + redis_slave_set = "" + if cluster_type in [ClusterType.TendisTwemproxyRedisInstance, ClusterType.TwemproxyTendisSSDInstance]: + redis_slave_set = cluster_info["redis_slave_set"] + + return { + "immute_domain": cluster.immute_domain, + "bk_biz_id": cluster.bk_biz_id, + "bk_cloud_id": cluster.bk_cloud_id, + "cluster_type": cluster.cluster_type, + "cluster_name": cluster_name, + "proxy_port": cluster_info["twemproxy_ports"][0], + "master_nums": master_nums, + "slave_ports": dict(slave_ports), + "slave_ins_map": dict(slave_ins_map), + "slave_master_map": dict(slave_master_map), + "db_version": cluster.major_version, + "domain_name": cluster_info["clusterentry_set"]["dns"][0]["domain"], + "redis_slave_set": redis_slave_set, + } + + def __init_builder(self, operate_name: str, info: dict): + cluster_info = self.__get_cluster_info(self.data["bk_biz_id"], info["cluster_id"]) + flow_data = self.data + flow_data.update(cluster_info) + + redis_pipeline = SubBuilder(root_id=self.root_id, data=flow_data) + trans_files = GetFileList(db_type=DBType.Redis) + act_kwargs = ActKwargs() + act_kwargs.set_trans_data_dataclass = RedisDataStructureContext.__name__ + act_kwargs.file_list = trans_files.redis_base() + act_kwargs.is_update_trans_data = True + act_kwargs.cluster = { + **cluster_info, + "operate": operate_name, + } + act_kwargs.bk_cloud_id = cluster_info["bk_cloud_id"] + logger.info("+===+++++===current tick_data info+++++===++++ :: {}".format(act_kwargs)) + + redis_pipeline.add_act( + act_name=_("初始化配置"), act_component_code=GetRedisActPayloadComponent.code, kwargs=asdict(act_kwargs) + ) + return redis_pipeline, act_kwargs + + def __get_cluster_config(self, domain_name: str, db_version: str, conf_type: str, namespace: str) -> Any: + """ + 获取已部署的实例配置 + """ + data = DBConfigApi.query_conf_item( + params={ + "bk_biz_id": str(self.data["bk_biz_id"]), + "level_name": LevelName.CLUSTER, + "level_value": domain_name, + "level_info": {"module": str(DEFAULT_DB_MODULE_ID)}, + "conf_file": db_version, + "conf_type": conf_type, + "namespace": namespace, + "format": FormatType.MAP, + } + ) + return data["content"] + + def cal_twemproxy_serveres(self, name, redis_slave_set, node_pairs) -> list: + """ + 计算twemproxy的servers 列表 + - redisip:redisport:1 app beginSeg-endSeg 1 + "servers": ["1.1.1.1:30000 xxx 0-219999 1","1.1.1.1:30001 xxx 220000-419999 1"] + """ + # actuator 会校验seg_range和是否为420000 + miss_range_instance = "127.0.0.1:6379" + servers = [] + node_dict = dict(node_pairs) + for slave in redis_slave_set: + instance, seg_range = slave.split(" ") + servers.append("{} {} {} 1".format(node_dict.get(instance, miss_range_instance), name, seg_range)) + return servers + + def get_prod_temp_instance_pairs( + self, + sub_kwargs: ActKwargs, + node_pairs: list, + info: dict, + is_precheck: bool, + tendis_type: str, + ) -> list: + # ### 整理 数据构造源节点和临时集群节点之间的对应关系 ###################################################### + """ + 1、有一对多:1台源主机对应多台临时主机->加快数据构造进度 + 2、有多对一:多台源主机对应一台临时主机->节省成本 + 下发actuator 格式: + { + "source_ip":"127.0.0.1", + "source_ports":[30000,30001,30002], + "new_temp_ip":"127.0.0.2", + "new_temp_ports":[13000,13001,13002], + "recovery_time_point":"2023-05-09 22:44:53" + } + 即,源主机只能有一个(备份系统按主机查询的,而且咱们的部署方式也是主机维度的),临时的IP,即下发actuator的IP也只能有一个(这个决定了后面节点下发对应关系) + todo 源主机可以是多台,传入形式改为ip:port ? + """ + # 并发执行redis数据构造 + act_kwargs = deepcopy(sub_kwargs) + acts_list = [] + for new_temp_ip in [host["ip"] for host in info["redis"]]: + + source_ports = [] + new_temp_ports = [] + + # 遍历所有 + new_temp_node_pairs = [] + source_ip_map = set() + for pair in node_pairs: + # 找到新机器相同的对应关系,source_ip可能有多个 + if new_temp_ip in str(pair): + new_temp_node_pairs.append(pair) + source_ip_map.add(pair[0].split(IP_PORT_DIVIDER)[0]) + source_ports.append(int(pair[0].split(IP_PORT_DIVIDER)[1])) + new_temp_ports.append(int(pair[1].split(IP_PORT_DIVIDER)[1])) + + # TODO-MY: 下面两段代码的重复率太高了 + # 将多个source_ip的情况继续拆分,每个source_ip是一个actuator + if len(source_ip_map) > 1: + for source_temp_ip in source_ip_map: + source_ports = [] + new_temp_ports = [] + source_ip = "" + for temp_pair in new_temp_node_pairs: + # 找到新机器相同的对应关系,source_ip只有一个 + if source_temp_ip in str(temp_pair): + source_ports.append(int(temp_pair[0].split(IP_PORT_DIVIDER)[1])) + new_temp_ports.append(int(temp_pair[1].split(IP_PORT_DIVIDER)[1])) + source_ip = source_temp_ip + # TODO-MY: 重复代码片段 + act_kwargs.cluster["data_params"] = { + "source_ip": source_ip, + "source_ports": source_ports, + "new_temp_ip": new_temp_ip, + "new_temp_ports": new_temp_ports, + "recovery_time_point": info["recovery_time_point"], + "is_precheck": is_precheck, + "tendis_type": tendis_type, + } + logger.info("Data structure delivery data_params:{}".format(act_kwargs.cluster["data_params"])) + act_kwargs.exec_ip = new_temp_ip + act_kwargs.get_redis_payload_func = RedisActPayload.redis_data_structure.__name__ + if is_precheck: + act_kwargs.act_name = _("检查{}备份临时机{}").format(source_ip, act_kwargs.exec_ip) + else: + act_kwargs.act_name = _("源{}构造到临时机{}").format(source_ip, act_kwargs.exec_ip) + acts_list.append( + { + "act_name": act_kwargs.act_name, + "act_component_code": ExecuteDBActuatorScriptComponent.code, + "kwargs": asdict(act_kwargs), + } + ) + elif len(source_ip_map) == 1: + source_ip = next(iter(source_ip_map)) + # TODO-MY: 重复代码片段 + data_params = { + "source_ip": source_ip, + "source_ports": source_ports, + "new_temp_ip": new_temp_ip, + "new_temp_ports": new_temp_ports, + "recovery_time_point": info["recovery_time_point"], + "is_precheck": is_precheck, + "tendis_type": tendis_type, + } + act_kwargs.cluster["data_params"] = data_params + logger.info("Data structure delivery data_params:{}".format(act_kwargs.cluster["data_params"])) + act_kwargs.exec_ip = new_temp_ip + act_kwargs.get_redis_payload_func = RedisActPayload.redis_data_structure.__name__ + if is_precheck: + act_kwargs.act_name = _("检查{}备份临时机{}").format(source_ip, act_kwargs.exec_ip) + else: + act_kwargs.act_name = _("源{}构造到临时机{}").format(source_ip, act_kwargs.exec_ip) + acts_list.append( + { + "act_name": act_kwargs.act_name, + "act_component_code": ExecuteDBActuatorScriptComponent.code, + "kwargs": asdict(act_kwargs), + } + ) + return acts_list diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_data_structure_task_delete.py b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_data_structure_task_delete.py new file mode 100644 index 0000000000..30269c4ce0 --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_data_structure_task_delete.py @@ -0,0 +1,216 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + + +import ast +import logging.config +from copy import deepcopy +from dataclasses import asdict +from datetime import datetime +from typing import Dict, Optional + +from django.utils.translation import ugettext as _ + +from backend.configuration.constants import DBType +from backend.db_meta.enums import DestroyedStatus +from backend.db_services.redis.rollback.models import TbTendisRollbackTasks +from backend.flow.consts import DBActuatorTypeEnum, DnsOpType, RedisActuatorActionEnum +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList +from backend.flow.engine.bamboo.scene.redis.atom_jobs import RedisBatchShutdownAtomJob +from backend.flow.plugins.components.collections.common.pause import PauseComponent +from backend.flow.plugins.components.collections.redis.exec_actuator_script import ExecuteDBActuatorScriptComponent +from backend.flow.plugins.components.collections.redis.get_redis_payload import GetRedisActPayloadComponent +from backend.flow.plugins.components.collections.redis.redis_db_meta import RedisDBMetaComponent +from backend.flow.plugins.components.collections.redis.trans_flies import TransFileComponent +from backend.flow.utils.redis.redis_act_playload import RedisActPayload +from backend.flow.utils.redis.redis_context_dataclass import ActKwargs, CommonContext +from backend.flow.utils.redis.redis_db_meta import RedisDBMeta + +logger = logging.getLogger("flow") + + +class RedisDataStructureTaskDeleteFlow(object): + """ + redis 构造删除 + { + "bk_biz_id":3, + "uid": "2022061612120001", + "created_by":"admin", + "ticket_type": "REDIS_DATA_STRUCTURE_TASK_DELETE", + "infos":[ + { + "related_rollback_bill_id":2022061612120001, + "prod_cluster":"xxxx.xxxx.xxxx.xxxx", + "bk_cloud_id":2 + } + ] + + } + """ + + def __init__(self, root_id: str, data: Optional[Dict]): + """ + @param root_id : 任务流程定义的root_id + @param data : 单据传递过来的参数列表,是dict格式 + """ + self.root_id = root_id + self.data = data + + @staticmethod + def __get_cluster_info(bk_biz_id: int, related_rollback_bill_id: int, prod_cluster: str) -> dict: + """ + 1、删除构造记录:需要提供哪些参数呢? (bk_cloud_id,源集群名,记录id (related_rollback_bill_id)) + """ + + task = TbTendisRollbackTasks.objects.filter( + related_rollback_bill_id=related_rollback_bill_id, bk_biz_id=bk_biz_id, prod_cluster=prod_cluster + ).order_by("-update_at") + task_list = list(task.values()) + # 这里需要加吗 + # if len(task_list) != 1: + # raise Exception( + # "单据{},构造源集群{},返回{}条记录不唯一,请检查!!!".format(related_rollback_bill_id, prod_cluster, len(task_list)) + # ) + formatted_tasks = [] + for task in task_list: + formatted_task = {} + for key, value in task.items(): + if isinstance(value, datetime): + formatted_task[key] = value.strftime("%Y-%m-%d %H:%M:%S") + else: + formatted_task[key] = value + formatted_tasks.append(formatted_task) + return dict(formatted_tasks[0]) + + def redis_rollback_task_delete_flow(self): + """ + 1、删除包含删除redis 实例的cmdb,下掉redis实例,下掉proxy实例,最后再更新构造记录为已销毁 + 构造记录销毁需要元数据: + 1、master ip_ports 下架,元数据处理 + 2、proxy下架 + """ + redis_pipeline_all = Builder(root_id=self.root_id, data=self.data) + trans_files = GetFileList(db_type=DBType.Redis) + sub_pipelines_multi_cluster = [] + for info in self.data["infos"]: + + tasks_info = self.__get_cluster_info( + self.data["bk_biz_id"], info["related_rollback_bill_id"], info["prod_cluster"] + ) + + logger.info("redis_rollback_task_delete_flow tasks_info:{}".format(tasks_info)) + redis_pipeline = SubBuilder(root_id=self.root_id, data=self.data) + act_kwargs = ActKwargs() + act_kwargs.set_trans_data_dataclass = CommonContext.__name__ + act_kwargs.file_list = trans_files.redis_base() + act_kwargs.is_update_trans_data = True + act_kwargs.cluster = { + **tasks_info, + "operate": self.data["ticket_type"], + } + act_kwargs.cluster["cluster_type"] = act_kwargs.cluster["temp_cluster_type"] + + cluster_kwargs = deepcopy(act_kwargs) + # 更新构造记录为销毁中 + cluster_kwargs.cluster = { + "related_rollback_bill_id": info["related_rollback_bill_id"], + "bk_biz_id": self.data["bk_biz_id"], + "prod_cluster": info["prod_cluster"], + "meta_func_name": RedisDBMeta.update_rollback_task_status.__name__, + "cluster_type": cluster_kwargs.cluster["cluster_type"], + "destroyed_status": DestroyedStatus.DESTROYING, + } + redis_pipeline.add_act( + act_name=_("更新构造记录为销毁中"), act_component_code=RedisDBMetaComponent.code, kwargs=asdict(cluster_kwargs) + ) + # 初始化 + redis_pipeline.add_act( + act_name=_("初始化配置"), act_component_code=GetRedisActPayloadComponent.code, kwargs=asdict(act_kwargs) + ) + + master_ports = {} + for instance in act_kwargs.cluster["temp_instance_range"]: + ip, port = instance.split(":") + if ip in master_ports: + master_ports[ip].append(int(port)) + else: + master_ports[ip] = [int(port)] + act_kwargs.cluster["master_ports"] = master_ports + + # ### 下发工具包############################################################ + # 这里构造销毁的时候,如果缺失actuator,那么dbtools,dbmon估计也是没有了的,构造销毁需要一起下发 + acts_lists = [] + first_act_kwargs = deepcopy(act_kwargs) + for ip_address, ports in master_ports.items(): + trans_files = GetFileList(db_type=DBType.Redis) + first_act_kwargs.file_list = trans_files.redis_dbmon() + first_act_kwargs.exec_ip = ip_address + acts_lists.append( + { + "act_name": _("Redis-{}-下发工具包").format(ip_address), + "act_component_code": TransFileComponent.code, + "kwargs": asdict(first_act_kwargs), + } + ) + redis_pipeline.add_parallel_acts(acts_list=acts_lists) + # ### 下发工具包完成############################################################ + + # #### 下架旧redis实例 ############################################################################# + sub_pipelines = [] + for ip_address, ports in master_ports.items(): + params = { + "ip": ip_address, + "ports": ports, + } + sub_builder = RedisBatchShutdownAtomJob(self.root_id, self.data, act_kwargs, params) + sub_pipelines.append(sub_builder) + redis_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) + + # #### 下架旧proxy实例 ############################################################################# + # 重新赋值,因为下架redis时cluster会被赋值 + # act_kwargs.cluster = {**tasks_info} + act_kwargs.cluster["cluster_type"] = act_kwargs.cluster["temp_cluster_type"] + + act_kwargs.cluster["operate"] = ( + DBActuatorTypeEnum.Proxy.value + "_" + RedisActuatorActionEnum.Shutdown.value + ) + proxy_ip, proxy_port = act_kwargs.cluster["temp_cluster_proxy"].split(":") + act_kwargs.cluster["proxy_ip"] = proxy_ip + act_kwargs.cluster["proxy_port"] = int(proxy_port) + + act_kwargs.exec_ip = act_kwargs.cluster["proxy_ip"] + act_kwargs.get_redis_payload_func = RedisActPayload.proxy_shutdown_payload.__name__ + redis_pipeline.add_act( + act_name=_("{}下架proxy实例").format(act_kwargs.cluster["proxy_ip"]), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(act_kwargs), + ) + # #### 下架旧实例完成 ############################################################################# + # 更新构造记录为已销毁 + act_kwargs.cluster = { + "related_rollback_bill_id": info["related_rollback_bill_id"], + "bk_biz_id": self.data["bk_biz_id"], + "prod_cluster": info["prod_cluster"], + "meta_func_name": RedisDBMeta.update_rollback_task_status.__name__, + "cluster_type": act_kwargs.cluster["cluster_type"], + "destroyed_status": DestroyedStatus.DESTROYED, + } + redis_pipeline.add_act( + act_name=_("更新构造记录为已销毁"), act_component_code=RedisDBMetaComponent.code, kwargs=asdict(act_kwargs) + ) + + sub_pipelines_multi_cluster.append( + redis_pipeline.build_sub_process(sub_name=_("集群[{}]数据构造销毁").format(info["prod_cluster"])) + ) + + redis_pipeline_all.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines_multi_cluster) + redis_pipeline_all.run_pipeline() diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_proxy_scale.py b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_proxy_scale.py index 15f301e55d..f08f2d8a92 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_proxy_scale.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_proxy_scale.py @@ -8,32 +8,32 @@ 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. """ +import copy import logging.config from collections import defaultdict from dataclasses import asdict -from typing import Dict, Optional +from typing import Any, Dict, Optional from django.utils.translation import ugettext as _ -from backend.configuration.constants import DBType +from backend.components import DBConfigApi +from backend.components.dbconfig.constants import FormatType, LevelName from backend.db_meta import api from backend.db_meta.enums.cluster_type import ClusterType -from backend.db_meta.enums.machine_type import MachineType -from backend.flow.consts import DBActuatorTypeEnum, DnsOpType, RedisActuatorActionEnum +from backend.db_meta.models import Machine +from backend.flow.consts import DEFAULT_DB_MODULE_ID, ConfigFileEnum, ConfigTypeEnum, DnsOpType from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder -from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList -from backend.flow.plugins.components.collections.common.cc_service import ( - TransferHostDestroyClusterComponent, - TransferHostScaleComponent, +from backend.flow.engine.bamboo.scene.redis.atom_jobs import ( + AccessManagerAtomJob, + ProxyBatchInstallAtomJob, + ProxyUnInstallAtomJob, ) -from backend.flow.plugins.components.collections.redis.dns_manage import RedisDnsManageComponent -from backend.flow.plugins.components.collections.redis.exec_actuator_script import ExecuteDBActuatorScriptComponent +from backend.flow.plugins.components.collections.common.pause import PauseComponent from backend.flow.plugins.components.collections.redis.get_redis_payload import GetRedisActPayloadComponent from backend.flow.plugins.components.collections.redis.redis_db_meta import RedisDBMetaComponent -from backend.flow.plugins.components.collections.redis.trans_flies import TransFileComponent -from backend.flow.utils.redis.redis_act_playload import RedisActPayload -from backend.flow.utils.redis.redis_context_dataclass import ActKwargs, CommonContext, DnsKwargs +from backend.flow.utils.redis.redis_context_dataclass import ActKwargs, CommonContext from backend.flow.utils.redis.redis_db_meta import RedisDBMeta +from backend.ticket.constants import SwitchConfirmType, TicketType logger = logging.getLogger("flow") @@ -51,34 +51,64 @@ def __init__(self, root_id: str, data: Optional[Dict]): self.root_id = root_id self.data = data + def __up_pre_check(self, new_ips): + if len(set(new_ips)) != len(new_ips): + raise Exception("have ip address has been used multiple times.") + m = Machine.objects.filter(ip__in=new_ips).values("ip") + if len(m) != 0: + raise Exception("[{}] is used.".format(m)) + + def __down_pre_check(self, old_ips, target_proxy_count): + if target_proxy_count < 2: + raise Exception("target_proxy_count is {} < 2".format(target_proxy_count)) + if len(old_ips) == 0: + raise Exception("old ips len <= target_proxy_count {}".format(target_proxy_count)) + @staticmethod def __get_cluster_info(bk_biz_id: int, cluster_id: int) -> dict: - cluster_info = api.cluster.nosqlcomm.get_cluster_detail(cluster_id)[0] + cluster_info = api.cluster.nosqlcomm.other.get_cluster_detail(cluster_id)[0] cluster_name = cluster_info["name"] cluster_type = cluster_info["cluster_type"] redis_master_set = cluster_info["redis_master_set"] redis_slave_set = cluster_info["redis_slave_set"] + proxy_port = cluster_info["twemproxy_ports"][0] servers = [] if cluster_type in [ClusterType.TendisTwemproxyRedisInstance, ClusterType.TwemproxyTendisSSDInstance]: for set in redis_master_set: ip_port, seg_range = str.split(set) servers.append("{} {} {} {}".format(ip_port, cluster_name, seg_range, 1)) - elif cluster_type == ClusterType.TendisPredixyTendisplusCluster: + else: servers = redis_master_set + redis_slave_set return { - "proxy_port": cluster_info["twemproxy_ports"][0], + "proxy_port": proxy_port, "cluster_type": cluster_type, "cluster_name": cluster_name, "bk_cloud_id": cluster_info["bk_cloud_id"], "servers": servers, - "domain_name": cluster_info["clusterentry_set"]["dns"][0]["domain"], + "immute_domain": cluster_info["clusterentry_set"]["dns"][0]["domain"], } - def redis_proxy_scale_flow(self): + @staticmethod + def __get_cluster_config(bk_biz_id: int, namespace: str, domain_name: str, db_version: str) -> Any: + data = DBConfigApi.query_conf_item( + params={ + "bk_biz_id": str(bk_biz_id), + "level_name": LevelName.CLUSTER, + "level_value": domain_name, + "level_info": {"module": str(DEFAULT_DB_MODULE_ID)}, + "conf_file": db_version, + "conf_type": ConfigTypeEnum.ProxyConf, + "namespace": namespace, + "format": FormatType.MAP, + } + ) + return data["content"] + + def redis_proxy_scale_up_flow(self): """ - 主要逻辑: - 0、获取信息(proxy配置、密码、端口、分片信息)(twemproxy和predixy是两套不同逻辑) + 扩容流程: + 0、获取信息(proxy配置、密码、端口、分片信息)(需要区分twemproxy和predixy) 1、初始化信息 2、下发介质包 3、新增proxy @@ -86,176 +116,178 @@ def redis_proxy_scale_flow(self): 3.2 元数据 3.3 域名 3.4 cc模块 - 4、下架proxy """ redis_pipeline = Builder(root_id=self.root_id, data=self.data) - trans_files = GetFileList(db_type=DBType.Redis) - cluster_info = self.__get_cluster_info(self.data["bk_biz_id"], self.data["cluster_id"]) - act_kwargs = ActKwargs() - act_kwargs.set_trans_data_dataclass = CommonContext.__name__ - act_kwargs.is_update_trans_data = True - act_kwargs.cluster = {**cluster_info} - act_kwargs.bk_cloud_id = cluster_info["bk_cloud_id"] - # TODO if self.data["ip_source"] == IpSource.RESOURCE_POOL 从资源池获取资源 待删除 - if self.data["del_proxy_list"]: - ips = [info["ip"] for info in self.data["del_proxy_list"]] - old_proxy_list = defaultdict(list) - for proxy_ip in ips: - old_proxy_list[proxy_ip] = cluster_info["proxy_port"] + sub_pipelines = [] + for info in self.data["infos"]: + proxy_ips = [] + for proxy_info in info["proxy"]: + proxy_ips.append(proxy_info["ip"]) + + self.__up_pre_check(proxy_ips) + sub_pipeline = SubBuilder(root_id=self.root_id, data=self.data) + cluster_info = self.__get_cluster_info(self.data["bk_biz_id"], info["cluster_id"]) + if cluster_info["cluster_type"] in [ + ClusterType.TendisTwemproxyRedisInstance.value, + ClusterType.TwemproxyTendisSSDInstance.value, + ]: + proxy_version = ConfigFileEnum.Twemproxy + else: + proxy_version = ConfigFileEnum.Predixy + + config_info = self.__get_cluster_config( + self.data["bk_biz_id"], cluster_info["cluster_type"], cluster_info["immute_domain"], proxy_version + ) + + act_kwargs = ActKwargs() + act_kwargs.set_trans_data_dataclass = CommonContext.__name__ + act_kwargs.is_update_trans_data = True + cluster_tpl = {**cluster_info, "bk_biz_id": self.data["bk_biz_id"], "created_by": self.data["created_by"]} + act_kwargs.bk_cloud_id = cluster_info["bk_cloud_id"] + + sub_pipeline.add_act( + act_name=_("初始化配置"), act_component_code=GetRedisActPayloadComponent.code, kwargs=asdict(act_kwargs) + ) + + # 安装proxy子流程 + sub_proxy_pipelines = [] + params = { + "redis_pwd": config_info["redis_password"], + "proxy_pwd": config_info["password"], + "proxy_port": cluster_info["proxy_port"], + "servers": cluster_info["servers"], + "conf_configs": config_info, + "spec_id": info["resource_spec"]["proxy"]["id"], + "spec_config": info["resource_spec"]["proxy"], + } + for proxy_info in info["proxy"]: + ip = proxy_info["ip"] + act_kwargs.cluster = copy.deepcopy(cluster_tpl) + + params["ip"] = ip + sub_builder = ProxyBatchInstallAtomJob(self.root_id, self.data, act_kwargs, params) + sub_proxy_pipelines.append(sub_builder) + sub_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_proxy_pipelines) + act_kwargs.cluster = { - **cluster_info, - # twemproxy下架的时候需要 - "operate": DBActuatorTypeEnum.Proxy.value + "_" + RedisActuatorActionEnum.Shutdown.value, - **dict(old_proxy_list), + "proxy_ips": proxy_ips, + "proxy_port": cluster_info["proxy_port"], + "meta_func_name": RedisDBMeta.proxy_add_cluster.__name__, + "domain_name": cluster_info["immute_domain"], } - proxy_payload = "" - machine_type = "" - if cluster_info["cluster_type"] in [ - ClusterType.TendisTwemproxyRedisInstance.value, - ClusterType.TwemproxyTendisSSDInstance.value, - ]: - act_kwargs.file_list = trans_files.redis_cluster_apply_proxy() - proxy_payload = RedisActPayload.add_twemproxy_payload.__name__ - machine_type = MachineType.TWEMPROXY.value - elif cluster_info["cluster_type"] == ClusterType.TendisPredixyTendisplusCluster.value: - act_kwargs.file_list = trans_files.tendisplus_apply_proxy() - proxy_payload = RedisActPayload.add_predixy_payload.__name__ - machine_type = MachineType.PREDIXY.value - else: - pass + sub_pipeline.add_act( + act_name=_("proxy加入集群元数据"), + act_component_code=RedisDBMetaComponent.code, + kwargs=asdict(act_kwargs), + ) - redis_pipeline.add_act( - act_name=_("初始化配置"), act_component_code=GetRedisActPayloadComponent.code, kwargs=asdict(act_kwargs) - ) + params = { + "cluster_id": info["cluster_id"], + "port": cluster_info["proxy_port"], + "add_ips": proxy_ips, + "op_type": DnsOpType.CREATE, + } + access_sub_builder = AccessManagerAtomJob(self.root_id, self.data, act_kwargs, params) + sub_pipeline.add_sub_pipeline(sub_flow=access_sub_builder) - # 增加proxy - if self.data["add_proxy_list"]: - ips = [info["ip"] for info in self.data["add_proxy_list"]] - act_kwargs.exec_ip = ips - redis_pipeline.add_act( - act_name=_("proxy下发介质包"), act_component_code=TransFileComponent.code, kwargs=asdict(act_kwargs) + sub_pipelines.append( + sub_pipeline.build_sub_process(sub_name=_("{}新增proxy实例").format(cluster_info["cluster_name"])) ) - act_kwargs.get_redis_payload_func = RedisActPayload.get_sys_init_payload.__name__ - redis_pipeline.add_act( - act_name=_("初始化机器"), - act_component_code=ExecuteDBActuatorScriptComponent.code, - kwargs=asdict(act_kwargs), + + redis_pipeline.add_parallel_sub_pipeline(sub_pipelines) + redis_pipeline.run_pipeline() + + @staticmethod + def __scale_down_cluster_info(bk_biz_id: int, cluster_id: int, target_proxy_count: int) -> dict: + cluster_info = api.cluster.nosqlcomm.other.get_cluster_detail(cluster_id)[0] + cluster_name = cluster_info["name"] + cluster_type = cluster_info["cluster_type"] + proxy_port = cluster_info["twemproxy_ports"][0] + proxy_ips = cluster_info["twemproxy_ips_set"] + + # 统计proxy的idc情况 + idc_ips = defaultdict(list) + max_count = 0 + for proxy_ip in proxy_ips: + m = Machine.objects.get(bk_biz_id=bk_biz_id, ip=proxy_ip) + proxy_bk_idc_id = m.bk_idc_id + idc_ips[proxy_bk_idc_id].append(proxy_ip) + if len(idc_ips[proxy_bk_idc_id]) > max_count: + max_count = len(idc_ips[proxy_bk_idc_id]) + idc_ips_dict = dict(idc_ips) + + # 计算需要裁撤的proxy列表。大概就是从多的pop出来 + proxy_now_count = len(proxy_ips) + scale_down_ips = [] + while proxy_now_count > target_proxy_count: + for idc in idc_ips_dict: + idc_ips = idc_ips_dict[idc] + if len(idc_ips) == max_count: + ip = idc_ips.pop() + scale_down_ips.append(ip) + proxy_now_count -= 1 + if proxy_now_count <= target_proxy_count: + break + max_count -= 1 + + return { + "proxy_port": proxy_port, + "cluster_type": cluster_type, + "cluster_name": cluster_name, + "scale_down_ips": scale_down_ips, + "bk_cloud_id": cluster_info["bk_cloud_id"], + "immute_domain": cluster_info["clusterentry_set"]["dns"][0]["domain"], + } + + def redis_proxy_scale_down_flow(self): + redis_pipeline = Builder(root_id=self.root_id, data=self.data) + sub_pipelines = [] + for info in self.data["infos"]: + sub_pipeline = SubBuilder(root_id=self.root_id, data=self.data) + cluster_info = self.__scale_down_cluster_info( + self.data["bk_biz_id"], info["cluster_id"], info["target_proxy_count"] ) - # 并发安装proxy - sub_pipelines = [] - for proxy_ip in ips: - sub_pipeline = SubBuilder(root_id=self.root_id, data=self.data) - act_kwargs.exec_ip = proxy_ip - act_kwargs.get_redis_payload_func = proxy_payload - sub_pipeline.add_act( - act_name=_("{}安装proxy实例").format(proxy_ip), - act_component_code=ExecuteDBActuatorScriptComponent.code, - kwargs=asdict(act_kwargs), - ) - - act_kwargs.get_redis_payload_func = RedisActPayload.bkdbmon_install.__name__ - sub_pipeline.add_act( - act_name=_("{}部署bkdbmon").format(proxy_ip), - act_component_code=ExecuteDBActuatorScriptComponent.code, - kwargs=asdict(act_kwargs), - ) - - act_kwargs.cluster = { - "proxy_ips": [proxy_ip], - "proxy_port": cluster_info["proxy_port"], - "meta_func_name": RedisDBMeta.proxy_add_cluster.__name__, - "domain_name": cluster_info["domain_name"], - "machine_type": machine_type, - } - sub_pipeline.add_act( - act_name=_("{}proxy扩容 元数据").format(proxy_ip), - act_component_code=RedisDBMetaComponent.code, - kwargs=asdict(act_kwargs), - ) - - # 添加域名 - dns_kwargs = DnsKwargs( - dns_op_type=DnsOpType.CREATE, - add_domain_name=cluster_info["domain_name"], - dns_op_exec_port=cluster_info["proxy_port"], - ) - act_kwargs.exec_ip = proxy_ip - sub_pipeline.add_act( - act_name=_("{}添加域名").format(proxy_ip), - act_component_code=RedisDnsManageComponent.code, - kwargs={**asdict(act_kwargs), **asdict(dns_kwargs)}, - ) - - act_kwargs.cluster = { - "cluster_name": cluster_info["cluster_name"], - "cluster_type": cluster_info["cluster_type"], - "machine_list": [ - { - "machine_type": machine_type, - "ips": [proxy_ip], - } - ], - } - sub_pipeline.add_act( - act_name=_("主机转移"), act_component_code=TransferHostScaleComponent.code, kwargs=asdict(act_kwargs) - ) - - sub_pipelines.append(sub_pipeline.build_sub_process(sub_name=_("新增proxy实例"))) - redis_pipeline.add_parallel_sub_pipeline(sub_pipelines) - - # 下架proxy - if self.data["del_proxy_list"]: - ips = [info["ip"] for info in self.data["del_proxy_list"]] - act_kwargs.exec_ip = ips - redis_pipeline.add_act( - act_name=_("proxy下发介质包"), - act_component_code=TransFileComponent.code, - kwargs=asdict(act_kwargs), - error_ignorable=True, + cluster_tpl = {**cluster_info, "bk_biz_id": self.data["bk_biz_id"]} + act_kwargs = ActKwargs() + act_kwargs.set_trans_data_dataclass = CommonContext.__name__ + act_kwargs.is_update_trans_data = True + act_kwargs.bk_cloud_id = cluster_info["bk_cloud_id"] + act_kwargs.exec_ip = cluster_info["scale_down_ips"] + act_kwargs.cluster = {**cluster_info} + + self.__down_pre_check(cluster_info["scale_down_ips"], info["target_proxy_count"]) + + sub_pipeline.add_act( + act_name=_("初始化配置"), act_component_code=GetRedisActPayloadComponent.code, kwargs=asdict(act_kwargs) ) - sub_pipelines = [] - for proxy_ip in ips: - sub_pipeline = SubBuilder(root_id=self.root_id, data=self.data) - # 清理域名 - dns_kwargs = DnsKwargs( - dns_op_type=DnsOpType.RECYCLE_RECORD, - dns_op_exec_port=cluster_info["proxy_port"], - ) - act_kwargs.exec_ip = proxy_ip - sub_pipeline.add_act( - act_name=_("{}删除域名").format(proxy_ip), - act_component_code=RedisDnsManageComponent.code, - kwargs={**asdict(act_kwargs), **asdict(dns_kwargs)}, - ) - - act_kwargs.exec_ip = proxy_ip - act_kwargs.get_redis_payload_func = RedisActPayload.proxy_operate_payload.__name__ - sub_pipeline.add_act( - act_name=_("{}下架proxy实例").format(proxy_ip), - act_component_code=ExecuteDBActuatorScriptComponent.code, - kwargs=asdict(act_kwargs), - error_ignorable=True, - ) - - # TODO 这个地方模块转移后面可能需要修改成待回收模块 - # 这个节点得放在删除元数据之前,否则会找不到bk_host_id - act_kwargs.cluster = {"ips": [proxy_ip]} - sub_pipeline.add_act( - act_name=_("主机转移到空闲机"), - act_component_code=TransferHostDestroyClusterComponent.code, - kwargs=asdict(act_kwargs), - ) - - sub_pipelines.append(sub_pipeline.build_sub_process(sub_name=_("下架proxy实例"))) - redis_pipeline.add_parallel_sub_pipeline(sub_pipelines) - # 删除元数据这里,放在子流程里可能会发生死锁。所以给放在外面统一清理 - act_kwargs.cluster = { - "proxy_ips": ips, - "proxy_port": cluster_info["proxy_port"], - "meta_func_name": RedisDBMeta.proxy_uninstall.__name__, - "domain_name": cluster_info["domain_name"], + if info["online_switch_type"] == SwitchConfirmType.USER_CONFIRM.value: + sub_pipeline.add_act(act_name=_("人工确认"), act_component_code=PauseComponent.code, kwargs={}) + + params = { + "cluster_id": info["cluster_id"], + "port": cluster_info["proxy_port"], + "del_ips": cluster_info["scale_down_ips"], + "op_type": DnsOpType.RECYCLE_RECORD, } - redis_pipeline.add_act( - act_name=_("proxy下架元数据"), act_component_code=RedisDBMetaComponent.code, kwargs=asdict(act_kwargs) + access_sub_builder = AccessManagerAtomJob(self.root_id, self.data, act_kwargs, params) + sub_pipeline.add_sub_pipeline(sub_flow=access_sub_builder) + + proxy_down_pipelines = [] + for proxy_ip in cluster_info["scale_down_ips"]: + act_kwargs.cluster = copy.deepcopy(cluster_tpl) + params = {"ip": proxy_ip, "proxy_port": cluster_info["proxy_port"]} + sub_builder = ProxyUnInstallAtomJob(self.root_id, self.data, act_kwargs, params) + proxy_down_pipelines.append(sub_builder) + sub_pipeline.add_parallel_sub_pipeline(sub_flow_list=proxy_down_pipelines) + sub_pipelines.append( + sub_pipeline.build_sub_process(sub_name=_("{}卸载proxy实例").format(cluster_info["cluster_name"])) ) + + redis_pipeline.add_parallel_sub_pipeline(sub_pipelines) redis_pipeline.run_pipeline() + + def redis_proxy_scale_flow(self): + if self.data["ticket_type"] == TicketType.PROXY_SCALE_DOWN.value: + self.redis_proxy_scale_down_flow() + else: + self.redis_proxy_scale_up_flow() diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_remove_dts_server.py b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_remove_dts_server.py new file mode 100644 index 0000000000..5614204cb2 --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/redis/redis_remove_dts_server.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging.config +from dataclasses import asdict + +from django.utils.translation import ugettext as _ + +from backend.configuration.constants import DBType +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList +from backend.flow.plugins.components.collections.redis.exec_actuator_script import ExecuteDBActuatorScriptComponent +from backend.flow.plugins.components.collections.redis.get_redis_payload import GetRedisActPayloadComponent +from backend.flow.plugins.components.collections.redis.redis_dts_server_meta import RedisDtsServerMetaComponent +from backend.flow.plugins.components.collections.redis.trans_flies import TransFileComponent +from backend.flow.utils.redis.redis_act_playload import RedisActPayload +from backend.flow.utils.redis.redis_context_dataclass import ActKwargs, CommonContext + +logger = logging.getLogger("flow") + + +class RedisRemoveDtsServerFlow(object): + """ + Redis 删除 DTS Server + """ + + def __init__(self, root_id, data): + self.root_id = root_id + self.data = data + + def redis_remove_dts_server_flow(self): + redis_pipeline = Builder(root_id=self.root_id, data=self.data) + trans_files = GetFileList(db_type=DBType.Redis) + sub_pipelines = [] + sub_pipeline = SubBuilder(root_id=self.root_id, data=self.data) + + act_kwargs = ActKwargs() + act_kwargs.set_trans_data_dataclass = CommonContext.__name__ + act_kwargs.file_list = trans_files.redis_remove_dts_server() + act_kwargs.is_update_trans_data = True + + for info in self.data["infos"]: + """ + info: {"ip": "3.3.3.1", "bk_cloud_id": 0} + """ + logger.info("redis_remove_dts_server_flow info:{}".format(info)) + cluster = {**info} + act_kwargs.cluster = cluster + act_kwargs.exec_ip = info["ip"] + + sub_pipeline.add_act( + act_name=_("DTS_Server-{}-下发介质").format(info["ip"]), + act_component_code=TransFileComponent.code, + kwargs=asdict(act_kwargs), + ) + + sub_pipeline.add_act( + act_name=_("初始化配置"), act_component_code=GetRedisActPayloadComponent.code, kwargs=asdict(act_kwargs) + ) + + sub_pipeline.add_act( + act_name=_("下发介质包"), act_component_code=TransFileComponent.code, kwargs=asdict(act_kwargs) + ) + + act_kwargs.get_redis_payload_func = RedisActPayload.get_remove_dts_server_payload.__name__ + sub_pipeline.add_act( + act_name=_("DTS_Server-{}-删除").format(info["ip"]), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(act_kwargs), + ) + + sub_pipeline.add_act( + act_name=_("DTS_Server-{}-清理dbmeta").format(info["ip"]), + act_component_code=RedisDtsServerMetaComponent.code, + kwargs=asdict(act_kwargs), + ) + + sub_pipelines.append(sub_pipeline.build_sub_process(sub_name=_("REMOVE DTS_SERVER"))) + redis_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) + redis_pipeline.run_pipeline() diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/redis/singele_redis_shutdown.py b/dbm-ui/backend/flow/engine/bamboo/scene/redis/singele_redis_shutdown.py index 7df7d28ce8..6d57e481e0 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/redis/singele_redis_shutdown.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/redis/singele_redis_shutdown.py @@ -9,32 +9,20 @@ specific language governing permissions and limitations under the License. """ import logging.config -from collections import defaultdict from dataclasses import asdict from typing import Dict, Optional from django.utils.translation import ugettext as _ from backend.configuration.constants import DBType -from backend.db_meta.enums import InstanceRole from backend.db_meta.models import StorageInstance -from backend.flow.consts import ( - DEFAULT_MONITOR_TIME, - DEFAULT_REDIS_SYSTEM_CMDS, - DBActuatorTypeEnum, - DnsOpType, - RedisActuatorActionEnum, - RedisBackupEnum, -) -from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.consts import RedisBackupEnum +from backend.flow.engine.bamboo.scene.common.builder import Builder from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList -from backend.flow.plugins.components.collections.common.cc_service import TransferHostDestroyClusterComponent from backend.flow.plugins.components.collections.redis.exec_actuator_script import ExecuteDBActuatorScriptComponent -from backend.flow.plugins.components.collections.redis.get_redis_payload import GetRedisActPayloadComponent from backend.flow.plugins.components.collections.redis.trans_flies import TransFileComponent from backend.flow.utils.redis.redis_act_playload import RedisActPayload from backend.flow.utils.redis.redis_context_dataclass import ActKwargs, CommonContext -from backend.flow.utils.redis.redis_db_meta import RedisDBMeta logger = logging.getLogger("flow") @@ -130,13 +118,7 @@ def single_redis_shutdown_flow(self): ) redis_pipeline.add_parallel_acts(acts_list=acts_list) - # 挪CC到回收模块 - act_kwargs.cluster = {"ips": ins_ips} - redis_pipeline.add_act( - act_name=_("主机转移到空闲机"), - act_component_code=TransferHostDestroyClusterComponent.code, - kwargs=asdict(act_kwargs), - ) + # TODO 挪CC到回收模块 # TODO 孤立redis实例下架 # act_kwargs.cluster = { diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/redis/tendis_plus_apply_flow.py b/dbm-ui/backend/flow/engine/bamboo/scene/redis/tendis_plus_apply_flow.py index 3459734c1e..19cef61bb9 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/redis/tendis_plus_apply_flow.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/redis/tendis_plus_apply_flow.py @@ -9,6 +9,7 @@ specific language governing permissions and limitations under the License. """ import copy +import json import logging.config from dataclasses import asdict from typing import Dict, Optional @@ -16,6 +17,7 @@ from django.utils.translation import ugettext as _ from backend.db_meta.enums import InstanceRole +from backend.db_meta.models import Cluster, Machine from backend.flow.consts import DEFAULT_REDIS_START_PORT, ClusterStatus, DnsOpType from backend.flow.engine.bamboo.scene.common.builder import Builder from backend.flow.engine.bamboo.scene.redis.atom_jobs import ProxyBatchInstallAtomJob, RedisBatchInstallAtomJob @@ -27,6 +29,7 @@ from backend.flow.utils.redis.redis_act_playload import RedisActPayload from backend.flow.utils.redis.redis_context_dataclass import ActKwargs, CommonContext, DnsKwargs from backend.flow.utils.redis.redis_db_meta import RedisDBMeta +from backend.flow.utils.redis.redis_util import check_domain logger = logging.getLogger("flow") @@ -44,15 +47,55 @@ def __init__(self, root_id: str, data: Optional[Dict]): self.root_id = root_id self.data = data - def cal_predixy_servers(self, master_ips, inst_num) -> list: + # 兼容手工部署,填充fake的规格信息,将master/slave进行分组。TODO:去掉手动部署后需要废弃 + if "resource_spec" not in self.data: + self.data["resource_spec"] = { + "master": {"id": 0}, + "slave": {"id": 0}, + "proxy": {"id": 0}, + } + self.data["nodes"]["backend_group"] = [] + for index in range(len(self.data["nodes"]["master"])): + master, slave = self.data["nodes"]["master"][index], self.data["nodes"]["slave"][index] + self.data["nodes"]["backend_group"].append({"master": master, "slave": slave}) + + self.data["nodes"].pop("master") + self.data["nodes"].pop("slave") + + def __pre_check(self, proxy_ips, master_ips, slave_ips, group_num, shard_num, servers, domain): + """ + 前置检查,检查传参 + """ + ips = proxy_ips + master_ips + slave_ips + if len(set(ips)) != len(ips): + raise Exception("have ip address has been used multiple times.") + if len(master_ips) != len(slave_ips): + raise Exception("master machine len != slave machine len.") + if len(master_ips) != group_num: + raise Exception("machine len != group_num.") + # tendisplus这里因为把slave也给算进去了,所以需要*2 + if len(servers) != 2 * shard_num: + raise Exception("servers len ({}) != shard_num ({}).".format(len(servers), shard_num)) + if shard_num % group_num != 0: + raise Exception("shard_num ({}) % group_num ({}) != 0.".format(shard_num, group_num)) + if not check_domain(domain): + raise Exception("domain[{}] is illegality.".format(domain)) + d = Cluster.objects.filter(immute_domain=domain).values("immute_domain") + if len(d) != 0: + raise Exception("domain [{}] is used.".format(domain)) + m = Machine.objects.filter(ip__in=ips).values("ip") + if len(m) != 0: + raise Exception("[{}] is used.".format(m)) + + def cal_predixy_servers(self, ips, inst_num) -> list: """ 计算predixy的servers列表 - "servers": ["1.1.1.1:30000", "1.1.1.1:30001", "2.2.2.2:30000", "2.2.2.2:30001"] + "servers": ["x.x.x.1:30000", "x.x.x.1:30001", "x.x.x.2:30000", "x.x.x.2:30001"] """ # 计算分片 servers = [] - for ip in master_ips: + for ip in ips: for inst_no in range(0, inst_num): port = DEFAULT_REDIS_START_PORT + inst_no servers.append("{}:{}".format(ip, port)) @@ -69,11 +112,22 @@ def deploy_tendisplus_cluster_flow(self): act_kwargs.bk_cloud_id = self.data["bk_cloud_id"] proxy_ips = [info["ip"] for info in self.data["nodes"]["proxy"]] - master_ips = [info["ip"] for info in self.data["nodes"]["master"]] - slave_ips = [info["ip"] for info in self.data["nodes"]["slave"]] + master_ips = [info["master"]["ip"] for info in self.data["nodes"]["backend_group"]] + slave_ips = [info["slave"]["ip"] for info in self.data["nodes"]["backend_group"]] + ins_num = self.data["shard_num"] // self.data["group_num"] ports = list(map(lambda i: i + DEFAULT_REDIS_START_PORT, range(ins_num))) - servers = self.cal_predixy_servers(master_ips, ins_num) + servers = self.cal_predixy_servers(master_ips + slave_ips, ins_num) + + self.__pre_check( + proxy_ips, + master_ips, + slave_ips, + self.data["group_num"], + self.data["shard_num"], + servers, + self.data["domain_name"], + ) cluster_tpl = { "immute_domain": self.data["domain_name"], "cluster_type": self.data["cluster_type"], @@ -102,6 +156,8 @@ def deploy_tendisplus_cluster_flow(self): act_kwargs.cluster = copy.deepcopy(cluster_tpl) params["ip"] = ip + params["spec_id"] = int(self.data["resource_spec"]["master"]["id"]) + params["spec_config"] = self.data["resource_spec"]["master"] params["meta_role"] = InstanceRole.REDIS_MASTER.value sub_builder = RedisBatchInstallAtomJob(self.root_id, self.data, act_kwargs, params) sub_pipelines.append(sub_builder) @@ -110,6 +166,8 @@ def deploy_tendisplus_cluster_flow(self): act_kwargs.cluster = copy.deepcopy(cluster_tpl) params["ip"] = ip + params["spec_id"] = int(self.data["resource_spec"]["slave"]["id"]) + params["spec_config"] = self.data["resource_spec"]["slave"] params["meta_role"] = InstanceRole.REDIS_SLAVE.value sub_builder = RedisBatchInstallAtomJob(self.root_id, self.data, act_kwargs, params) sub_pipelines.append(sub_builder) @@ -160,6 +218,8 @@ def deploy_tendisplus_cluster_flow(self): # 安装proxy子流程 sub_pipelines = [] params = { + "spec_id": int(self.data["resource_spec"]["proxy"]["id"]), + "spec_config": self.data["resource_spec"]["proxy"], "redis_pwd": self.data["redis_pwd"], "proxy_pwd": self.data["proxy_pwd"], "proxy_port": self.data["proxy_port"], @@ -174,6 +234,15 @@ def deploy_tendisplus_cluster_flow(self): redis_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) act_kwargs.cluster = { + "proxy_port": self.data["proxy_port"], + "bk_biz_id": self.data["bk_biz_id"], + "bk_cloud_id": self.data["bk_cloud_id"], + "cluster_name": self.data["cluster_name"], + "cluster_alias": self.data["cluster_alias"], + "db_version": self.data["db_version"], + "immute_domain": self.data["domain_name"], + "created_by": self.data["created_by"], + "region": self.data.get("city_code"), "new_proxy_ips": proxy_ips, "inst_num": ins_num, "start_port": DEFAULT_REDIS_START_PORT, @@ -189,7 +258,9 @@ def deploy_tendisplus_cluster_flow(self): "conf": { "requirepass": self.data["redis_pwd"], "cluster-enabled": ClusterStatus.REDIS_CLUSTER_YES, - } + }, + "db_version": self.data["db_version"], + "domain_name": self.data["domain_name"], } act_kwargs.get_redis_payload_func = RedisActPayload.set_redis_config.__name__ acts_list.append( @@ -205,7 +276,8 @@ def deploy_tendisplus_cluster_flow(self): "password": self.data["proxy_pwd"], "redis_password": self.data["redis_pwd"], "port": str(self.data["proxy_port"]), - } + }, + "domain_name": self.data["domain_name"], } act_kwargs.get_redis_payload_func = RedisActPayload.set_proxy_config.__name__ acts_list.append( diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/riak/riak_cluster_apply_flow.py b/dbm-ui/backend/flow/engine/bamboo/scene/riak/riak_cluster_apply_flow.py index db89d52796..c7fb7d8022 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/riak/riak_cluster_apply_flow.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/riak/riak_cluster_apply_flow.py @@ -8,24 +8,23 @@ 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. """ -import copy import logging.config from dataclasses import asdict from typing import Dict, Optional -from unittest import case from django.utils.translation import ugettext as _ +from backend.components import DBConfigApi +from backend.components.dbconfig.constants import ConfType, FormatType, LevelName, ReqType from backend.configuration.constants import DBType -from backend.flow.consts import DBA_ROOT_USER +from backend.flow.consts import DBA_ROOT_USER, LevelInfoEnum, NameSpaceEnum from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList -from backend.flow.plugins.components.collections.mysql.exec_actuator_script import ExecuteDBActuatorScriptComponent -from backend.flow.plugins.components.collections.mysql.trans_flies import TransFileComponent from backend.flow.plugins.components.collections.riak.exec_actuator_script import ExecuteRiakActuatorScriptComponent from backend.flow.plugins.components.collections.riak.get_riak_resource import GetRiakResourceComponent from backend.flow.plugins.components.collections.riak.riak_db_meta import RiakDBMetaComponent -from backend.flow.utils.riak.riak_act_dataclass import DBMetaFuncKwargs, DownloadMediaKwargs, ExecActuatorKwargs +from backend.flow.plugins.components.collections.riak.trans_files import TransFileComponent +from backend.flow.utils.riak.riak_act_dataclass import DBMetaFuncKwargs, DownloadMediaKwargs from backend.flow.utils.riak.riak_act_payload import RiakActPayload from backend.flow.utils.riak.riak_context_dataclass import ApplyManualContext, RiakActKwargs from backend.flow.utils.riak.riak_db_meta import RiakDBMeta @@ -41,12 +40,9 @@ class RiakClusterApplyFlow(object): "root_id": 123, "created_by": "admin", "bk_biz_id": 0, - "ticket_type": "RIAK_APPLY", - "timing": "2022-11-21 12:04:10", + "ticket_type": "RIAK_CLUSTER_APPLY", "bk_app_abbr": "testtest", "ip_source": "manual_input", - "zone": "hh", - "db_module_id": 0, "major_version": "0-0", "cluster_alias": "测试集群", "city_code": "深圳", @@ -74,10 +70,9 @@ def deploy_riak_cluster_flow(self): """ riak_pipeline = Builder(root_id=self.root_id, data=self.data) sub_pipeline = SubBuilder(root_id=self.root_id, data=self.data) - - # 获取机器资源 done + # 获取机器资源 sub_pipeline.add_act(act_name=_("获取机器信息"), act_component_code=GetRiakResourceComponent.code, kwargs={}) - ips = [ip for ip in self.data["nodes"]] + ips = [node["ip"] for node in self.data["nodes"]] sub_pipeline.add_act( act_name=_("下发actuator以及riak介质"), act_component_code=TransFileComponent.code, @@ -85,10 +80,11 @@ def deploy_riak_cluster_flow(self): DownloadMediaKwargs( bk_cloud_id=self.data["bk_cloud_id"], exec_ip=ips, - file_list=GetFileList(db_type=DBType.Riak).riak_install_package(), + file_list=GetFileList(db_type=DBType.Riak).riak_install_package(self.data["db_version"]), ) ), ) + sub_pipeline.add_act( act_name=_("actuator_riak系统配置初始化"), act_component_code=ExecuteRiakActuatorScriptComponent.code, @@ -140,7 +136,6 @@ def deploy_riak_cluster_flow(self): bk_cloud_id=self.data["bk_cloud_id"], run_as_system_user=DBA_ROOT_USER, get_riak_payload_func=RiakActPayload.get_commit_cluster_change_payload.__name__, - cluster=cluster, ) ), ) @@ -154,41 +149,62 @@ def deploy_riak_cluster_flow(self): bk_cloud_id=self.data["bk_cloud_id"], run_as_system_user=DBA_ROOT_USER, get_riak_payload_func=RiakActPayload.get_commit_cluster_change_payload.__name__, - cluster=cluster, ) ), ) + sub_pipeline.add_act( act_name=_("riak修改元数据"), act_component_code=RiakDBMetaComponent.code, kwargs=asdict( DBMetaFuncKwargs( - db_meta_class_func=RiakDBMeta.riak_deploy_node.__name__, - cluster={"nodes": ips}, + db_meta_class_func=RiakDBMeta.riak_cluster_apply.__name__, is_update_trans_data=True, ) ), ) + + acts_list = [] + for ip in ips: + monitor_kwargs = RiakActKwargs( + exec_ip=ip, + bk_cloud_id=self.data["bk_cloud_id"], + run_as_system_user=DBA_ROOT_USER, + get_riak_payload_func=RiakActPayload.get_install_monitor_payload.__name__, + ) + act_info = dict() + act_info["act_name"] = (_("actuator_{}部署定时任务和riak监控".format(ip)),) + act_info["act_component_code"] = ExecuteRiakActuatorScriptComponent.code + act_info["kwargs"] = asdict(monitor_kwargs) + acts_list.append(act_info) + sub_pipeline.add_parallel_acts(acts_list=acts_list) + riak_pipeline.add_sub_pipeline(sub_pipeline.build_sub_process(sub_name=_("部署Riak集群"))) riak_pipeline.run_pipeline(init_trans_data_class=ApplyManualContext()) def _get_riak_config(self): - bucket_types = [] - if self.data["db_module_id"] == 0: - distributed_cookie = "mhsriak" - elif self.data["db_module_id"] == 1: - distributed_cookie = "legsriak" - elif self.data["db_module_id"] == 2: - distributed_cookie = "ppriak" - bucket_types = ["player_preferences"] - elif self.data["db_module_id"] == 3: - distributed_cookie = "testriak" - bucket_types = ["player_preferences"] - elif self.data["db_module_id"] == 4: - distributed_cookie = "mixedtriak" - bucket_types = ["player_preferences"] - else: - logger.error("「 db_module_id = {} 」 not exists".format(self.data["db_module_id"])) - raise Exception("「 db_module_id = {} 」 not exists".format(self.data["db_module_id"])) - cluster = {"distributed_cookie": distributed_cookie, "bucket_types": bucket_types, "ring_size": 256} + # 从dbconfig获取配置信息 + resp = DBConfigApi.get_instance_config( + { + "bk_biz_id": str(self.data["bk_biz_id"]), + "level_name": LevelName.CLUSTER, + "level_value": self.data["domain"], + "level_info": {"module": LevelInfoEnum.TendataModuleDefault}, + "conf_file": "riak-{}-{}".format(self.data["db_version"], self.data["module_name"]), + "conf_type": ConfType.DBCONF, + "namespace": NameSpaceEnum.Riak, + "format": FormatType.MAP_LEVEL, + "method": ReqType.GENERATE_AND_PUBLISH, + } + ) + config = resp["content"] + bucket_types = config["bucket_types"].split(",") + cluster = { + "distributed_cookie": config["distributed_cookie"], + "bucket_types": bucket_types, + "ring_size": config["ring_size"], + "leveldb.expiration": config["leveldb_expiration"], + "leveldb.expiration.mode": config["leveldb_expiration_mode"], + "leveldb.expiration.retention_time": config["leveldb_expiration_retention_time"], + } return cluster diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/riak/riak_cluster_destroy_flow.py b/dbm-ui/backend/flow/engine/bamboo/scene/riak/riak_cluster_destroy_flow.py new file mode 100644 index 0000000000..87b0d11dfe --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/riak/riak_cluster_destroy_flow.py @@ -0,0 +1,115 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging.config +from dataclasses import asdict +from typing import Dict, Optional + +from django.utils.translation import ugettext as _ + +from backend.configuration.constants import DBType +from backend.flow.consts import DBA_ROOT_USER +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList +from backend.flow.plugins.components.collections.common.pause import PauseComponent +from backend.flow.plugins.components.collections.riak.exec_actuator_script import ExecuteRiakActuatorScriptComponent +from backend.flow.plugins.components.collections.riak.get_riak_cluster_node import GetRiakClusterNodeComponent +from backend.flow.plugins.components.collections.riak.riak_db_meta import RiakDBMetaComponent +from backend.flow.plugins.components.collections.riak.trans_files import TransFileComponent +from backend.flow.utils.riak.riak_act_dataclass import DBMetaFuncKwargs, DownloadMediaKwargsFromTrans +from backend.flow.utils.riak.riak_act_payload import RiakActPayload +from backend.flow.utils.riak.riak_context_dataclass import NodesContext, RiakActKwargs +from backend.flow.utils.riak.riak_db_meta import RiakDBMeta + +logger = logging.getLogger("flow") + + +class RiakClusterDestroyFlow(object): + """ + Riak集群下架流程的抽象类 + { + "uid": "2022111212001000", + "root_id": 123, + "created_by": "admin", + "bk_biz_id": 0, + "ticket_type": "RIAK_CLUSTER_DESTROY", + "cluster_id": 5, + } + """ + + def __init__(self, root_id: str, data: Optional[Dict]): + """ + @param root_id : 任务流程定义的root_id + @param data : 单据传递过来的参数列表,是dict格式 + """ + self.root_id = root_id + self.data = data + + def riak_cluster_destroy_flow(self): + """ + Riak集群下架 + """ + + riak_pipeline = Builder(root_id=self.root_id, data=self.data) + sub_pipeline = SubBuilder(root_id=self.root_id, data=self.data) + + sub_pipeline.add_act(act_name=_("获取集群中的节点"), act_component_code=GetRiakClusterNodeComponent.code, kwargs={}) + + sub_pipeline.add_act( + act_name=_("下发actuator"), + act_component_code=TransFileComponent.code, + kwargs=asdict( + DownloadMediaKwargsFromTrans( + get_trans_data_ip_var=NodesContext.get_nodes_var_name(), + bk_cloud_id=self.data["bk_cloud_id"], + file_list=GetFileList(db_type=DBType.Riak).get_db_actuator_package(), + ) + ), + ) + + sub_pipeline.add_act( + act_name=_("actuator_连接检查"), + act_component_code=ExecuteRiakActuatorScriptComponent.code, + kwargs=asdict( + RiakActKwargs( + get_trans_data_ip_var=NodesContext.get_nodes_var_name(), + bk_cloud_id=self.data["bk_cloud_id"], + run_as_system_user=DBA_ROOT_USER, + get_riak_payload_func=RiakActPayload.get_check_connections_payload.__name__, + ) + ), + ) + + sub_pipeline.add_act( + act_name=_("actuator_下架"), + act_component_code=ExecuteRiakActuatorScriptComponent.code, + kwargs=asdict( + RiakActKwargs( + get_trans_data_ip_var=NodesContext.get_nodes_var_name(), + bk_cloud_id=self.data["bk_cloud_id"], + run_as_system_user=DBA_ROOT_USER, + get_riak_payload_func=RiakActPayload.get_uninstall_payload.__name__, + ) + ), + ) + + sub_pipeline.add_act( + act_name=_("riak修改元数据"), + act_component_code=RiakDBMetaComponent.code, + kwargs=asdict( + DBMetaFuncKwargs( + db_meta_class_func=RiakDBMeta.riak_cluster_destroy.__name__, + is_update_trans_data=True, + ) + ), + ) + + riak_pipeline.add_sub_pipeline(sub_pipeline.build_sub_process(sub_name=_("Riak集群下架"))) + riak_pipeline.run_pipeline(init_trans_data_class=NodesContext()) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/riak/riak_cluster_disable_flow.py b/dbm-ui/backend/flow/engine/bamboo/scene/riak/riak_cluster_disable_flow.py new file mode 100644 index 0000000000..036bdef24b --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/riak/riak_cluster_disable_flow.py @@ -0,0 +1,126 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging.config +from dataclasses import asdict +from typing import Dict, Optional + +from django.utils.translation import ugettext as _ + +from backend.configuration.constants import DBType +from backend.flow.consts import DBA_ROOT_USER +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList +from backend.flow.plugins.components.collections.riak.exec_actuator_script import ExecuteRiakActuatorScriptComponent +from backend.flow.plugins.components.collections.riak.get_riak_cluster_node import GetRiakClusterNodeComponent +from backend.flow.plugins.components.collections.riak.riak_db_meta import RiakDBMetaComponent +from backend.flow.plugins.components.collections.riak.trans_files import TransFileComponent +from backend.flow.utils.riak.riak_act_dataclass import DBMetaFuncKwargs, DownloadMediaKwargsFromTrans +from backend.flow.utils.riak.riak_act_payload import RiakActPayload +from backend.flow.utils.riak.riak_context_dataclass import NodesContext, RiakActKwargs +from backend.flow.utils.riak.riak_db_meta import RiakDBMeta + +logger = logging.getLogger("flow") + + +class RiakClusterDisableFlow(object): + """ + Riak集群禁用流程的抽象类 + { + "uid": "2022111212001000", + "root_id": 123, + "created_by": "admin", + "bk_biz_id": 0, + "ticket_type": "RIAK_CLUSTER_DISABLE", + "cluster_id": 5 + } + """ + + def __init__(self, root_id: str, data: Optional[Dict]): + """ + @param root_id : 任务流程定义的root_id + @param data : 单据传递过来的参数列表,是dict格式 + """ + self.root_id = root_id + self.data = data + + def riak_cluster_disable_flow(self): + """ + Riak集群禁用 + """ + riak_pipeline = Builder(root_id=self.root_id, data=self.data) + sub_pipeline = SubBuilder(root_id=self.root_id, data=self.data) + + sub_pipeline.add_act(act_name=_("获取集群中的节点"), act_component_code=GetRiakClusterNodeComponent.code, kwargs={}) + + sub_pipeline.add_act( + act_name=_("下发actuator"), + act_component_code=TransFileComponent.code, + kwargs=asdict( + DownloadMediaKwargsFromTrans( + get_trans_data_ip_var=NodesContext.get_nodes_var_name(), + bk_cloud_id=self.data["bk_cloud_id"], + file_list=GetFileList(db_type=DBType.Riak).get_db_actuator_package(), + ) + ), + ) + + sub_pipeline.add_act( + act_name=_("actuator_连接检查"), + act_component_code=ExecuteRiakActuatorScriptComponent.code, + kwargs=asdict( + RiakActKwargs( + get_trans_data_ip_var=NodesContext.get_nodes_var_name(), + bk_cloud_id=self.data["bk_cloud_id"], + run_as_system_user=DBA_ROOT_USER, + get_riak_payload_func=RiakActPayload.get_check_connections_payload.__name__, + ) + ), + ) + + sub_pipeline.add_act( + act_name=_("actuator_关闭riak监控"), + act_component_code=ExecuteRiakActuatorScriptComponent.code, + kwargs=asdict( + RiakActKwargs( + get_trans_data_ip_var=NodesContext.get_nodes_var_name(), + bk_cloud_id=self.data["bk_cloud_id"], + run_as_system_user=DBA_ROOT_USER, + get_riak_payload_func=RiakActPayload.get_stop_monitor_payload.__name__, + ) + ), + ) + + sub_pipeline.add_act( + act_name=_("actuator_关闭riak实例"), + act_component_code=ExecuteRiakActuatorScriptComponent.code, + kwargs=asdict( + RiakActKwargs( + get_trans_data_ip_var=NodesContext.get_nodes_var_name(), + bk_cloud_id=self.data["bk_cloud_id"], + run_as_system_user=DBA_ROOT_USER, + get_riak_payload_func=RiakActPayload.get_stop_payload.__name__, + ) + ), + ) + + sub_pipeline.add_act( + act_name=_("riak修改元数据"), + act_component_code=RiakDBMetaComponent.code, + kwargs=asdict( + DBMetaFuncKwargs( + db_meta_class_func=RiakDBMeta.riak_cluster_disable.__name__, + is_update_trans_data=True, + ) + ), + ) + + riak_pipeline.add_sub_pipeline(sub_pipeline.build_sub_process(sub_name=_("Riak集群禁用"))) + riak_pipeline.run_pipeline(init_trans_data_class=NodesContext()) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/riak/riak_cluster_enable_flow.py b/dbm-ui/backend/flow/engine/bamboo/scene/riak/riak_cluster_enable_flow.py new file mode 100644 index 0000000000..1e753c18ae --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/riak/riak_cluster_enable_flow.py @@ -0,0 +1,113 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging.config +from dataclasses import asdict +from typing import Dict, Optional + +from django.utils.translation import ugettext as _ + +from backend.configuration.constants import DBType +from backend.flow.consts import DBA_ROOT_USER +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList +from backend.flow.plugins.components.collections.riak.exec_actuator_script import ExecuteRiakActuatorScriptComponent +from backend.flow.plugins.components.collections.riak.get_riak_cluster_node import GetRiakClusterNodeComponent +from backend.flow.plugins.components.collections.riak.riak_db_meta import RiakDBMetaComponent +from backend.flow.plugins.components.collections.riak.trans_files import TransFileComponent +from backend.flow.utils.riak.riak_act_dataclass import DBMetaFuncKwargs, DownloadMediaKwargsFromTrans +from backend.flow.utils.riak.riak_act_payload import RiakActPayload +from backend.flow.utils.riak.riak_context_dataclass import NodesContext, RiakActKwargs +from backend.flow.utils.riak.riak_db_meta import RiakDBMeta + +logger = logging.getLogger("flow") + + +class RiakClusterEnableFlow(object): + """ + Riak集群启用的抽象类 + { + "uid": "2022111212001000", + "root_id": 123, + "created_by": "admin", + "bk_biz_id": 0, + "ticket_type": "RIAK_CLUSTER_ENABLE", + "cluster_id": 5 + } + """ + + def __init__(self, root_id: str, data: Optional[Dict]): + """ + @param root_id : 任务流程定义的root_id + @param data : 单据传递过来的参数列表,是dict格式 + """ + self.root_id = root_id + self.data = data + + def riak_cluster_enable_flow(self): + """ + Riak集群禁用 + """ + riak_pipeline = Builder(root_id=self.root_id, data=self.data) + sub_pipeline = SubBuilder(root_id=self.root_id, data=self.data) + + sub_pipeline.add_act(act_name=_("获取集群中的节点"), act_component_code=GetRiakClusterNodeComponent.code, kwargs={}) + + sub_pipeline.add_act( + act_name=_("下发actuator以及riak介质"), + act_component_code=TransFileComponent.code, + kwargs=asdict( + DownloadMediaKwargsFromTrans( + get_trans_data_ip_var=NodesContext.get_nodes_var_name(), + bk_cloud_id=self.data["bk_cloud_id"], + file_list=GetFileList(db_type=DBType.Riak).get_db_actuator_package(), + ) + ), + ) + + sub_pipeline.add_act( + act_name=_("actuator_开启riak实例"), + act_component_code=ExecuteRiakActuatorScriptComponent.code, + kwargs=asdict( + RiakActKwargs( + get_trans_data_ip_var=NodesContext.get_nodes_var_name(), + bk_cloud_id=self.data["bk_cloud_id"], + run_as_system_user=DBA_ROOT_USER, + get_riak_payload_func=RiakActPayload.get_start_payload.__name__, + ) + ), + ) + + sub_pipeline.add_act( + act_name=_("riak修改元数据"), + act_component_code=RiakDBMetaComponent.code, + kwargs=asdict( + DBMetaFuncKwargs( + db_meta_class_func=RiakDBMeta.riak_cluster_enable.__name__, + is_update_trans_data=True, + ) + ), + ) + + sub_pipeline.add_act( + act_name=_("actuator_开启riak监控"), + act_component_code=ExecuteRiakActuatorScriptComponent.code, + kwargs=asdict( + RiakActKwargs( + get_trans_data_ip_var=NodesContext.get_nodes_var_name(), + bk_cloud_id=self.data["bk_cloud_id"], + run_as_system_user=DBA_ROOT_USER, + get_riak_payload_func=RiakActPayload.get_start_monitor_payload.__name__, + ) + ), + ) + + riak_pipeline.add_sub_pipeline(sub_pipeline.build_sub_process(sub_name=_("Riak集群启用"))) + riak_pipeline.run_pipeline(init_trans_data_class=NodesContext()) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/riak/riak_cluster_scale_in_flow.py b/dbm-ui/backend/flow/engine/bamboo/scene/riak/riak_cluster_scale_in_flow.py new file mode 100644 index 0000000000..c31a8c0035 --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/riak/riak_cluster_scale_in_flow.py @@ -0,0 +1,179 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging.config +from dataclasses import asdict +from typing import Dict, Optional + +from django.utils.translation import ugettext as _ + +from backend.configuration.constants import DBType +from backend.flow.consts import DBA_ROOT_USER +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList +from backend.flow.plugins.components.collections.common.pause import PauseComponent +from backend.flow.plugins.components.collections.riak.exec_actuator_script import ExecuteRiakActuatorScriptComponent +from backend.flow.plugins.components.collections.riak.get_riak_cluster_node import GetRiakClusterNodeComponent +from backend.flow.plugins.components.collections.riak.get_riak_resource import GetRiakResourceComponent +from backend.flow.plugins.components.collections.riak.riak_db_meta import RiakDBMetaComponent +from backend.flow.plugins.components.collections.riak.trans_files import TransFileComponent +from backend.flow.utils.riak.riak_act_dataclass import DBMetaFuncKwargs, DownloadMediaKwargsFromTrans +from backend.flow.utils.riak.riak_act_payload import RiakActPayload +from backend.flow.utils.riak.riak_context_dataclass import RiakActKwargs, ScaleInManualContext +from backend.flow.utils.riak.riak_db_meta import RiakDBMeta + +logger = logging.getLogger("flow") + + +class RiakClusterScaleInFlow(object): + """ + Riak缩容流程的抽象类 + { + "uid": "2022111212001000", + "root_id": 123, + "created_by": "admin", + "bk_biz_id": 0, + "ticket_type": "RIAK_CLUSTER_SCALE_IN", + "ip_source": "manual_input", + "bk_cloud_id": 0, + "cluster_id": 5, + "nodes": [ + { + "ip": "127.0.0.1", + "bk_host_id": 0 + } + ] + } + """ + + def __init__(self, root_id: str, data: Optional[Dict]): + """ + @param root_id : 任务流程定义的root_id + @param data : 单据传递过来的参数列表,是dict格式 + """ + self.root_id = root_id + self.data = data + + def riak_cluster_scale_in_flow(self): + """ + Riak集群缩容 + """ + riak_pipeline = Builder(root_id=self.root_id, data=self.data) + sub_pipeline = SubBuilder(root_id=self.root_id, data=self.data) + + sub_pipeline.add_act(act_name=_("获取机器信息"), act_component_code=GetRiakResourceComponent.code, kwargs={}) + sub_pipeline.add_act(act_name=_("获取集群中的节点"), act_component_code=GetRiakClusterNodeComponent.code, kwargs={}) + + sub_pipeline.add_act( + act_name=_("下发actuator"), + act_component_code=TransFileComponent.code, + kwargs=asdict( + DownloadMediaKwargsFromTrans( + get_trans_data_ip_var=ScaleInManualContext.get_nodes_var_name(), + bk_cloud_id=self.data["bk_cloud_id"], + file_list=GetFileList(db_type=DBType.Riak).get_db_actuator_package(), + ) + ), + ) + + # 运维修改配置后才剔除 + sub_pipeline.add_act(act_name=_("人工确认"), act_component_code=PauseComponent.code, kwargs={}) + + sub_pipeline.add_act( + act_name=_("actuator_连接检查"), + act_component_code=ExecuteRiakActuatorScriptComponent.code, + kwargs=asdict( + RiakActKwargs( + get_trans_data_ip_var=ScaleInManualContext.get_operate_nodes_var_name(), + bk_cloud_id=self.data["bk_cloud_id"], + run_as_system_user=DBA_ROOT_USER, + get_riak_payload_func=RiakActPayload.get_check_connections_payload.__name__, + ) + ), + ) + + sub_pipeline.add_act( + act_name=_("actuator_关闭riak监控"), + act_component_code=ExecuteRiakActuatorScriptComponent.code, + kwargs=asdict( + RiakActKwargs( + get_trans_data_ip_var=ScaleInManualContext.get_operate_nodes_var_name(), + bk_cloud_id=self.data["bk_cloud_id"], + run_as_system_user=DBA_ROOT_USER, + get_riak_payload_func=RiakActPayload.get_stop_monitor_payload.__name__, + ) + ), + ) + + sub_pipeline.add_act( + act_name=_("actuator_riak集群剔除节点"), + act_component_code=ExecuteRiakActuatorScriptComponent.code, + kwargs=asdict( + RiakActKwargs( + get_trans_data_ip_var=ScaleInManualContext.get_base_node_var_name(), + bk_cloud_id=self.data["bk_cloud_id"], + run_as_system_user=DBA_ROOT_USER, + get_riak_payload_func=RiakActPayload.get_remove_node_payload.__name__, + ) + ), + ) + + sub_pipeline.add_act( + act_name=_("actuator_集群变更生效"), + act_component_code=ExecuteRiakActuatorScriptComponent.code, + kwargs=asdict( + RiakActKwargs( + get_trans_data_ip_var=ScaleInManualContext.get_base_node_var_name(), + bk_cloud_id=self.data["bk_cloud_id"], + run_as_system_user=DBA_ROOT_USER, + get_riak_payload_func=RiakActPayload.get_commit_cluster_change_payload.__name__, + ) + ), + ) + + sub_pipeline.add_act( + act_name=_("actuator_检查数据搬迁进度"), + act_component_code=ExecuteRiakActuatorScriptComponent.code, + kwargs=asdict( + RiakActKwargs( + get_trans_data_ip_var=ScaleInManualContext.get_operate_nodes_var_name(), + bk_cloud_id=self.data["bk_cloud_id"], + run_as_system_user=DBA_ROOT_USER, + get_riak_payload_func=RiakActPayload.get_transfer_payload.__name__, + ) + ), + ) + + sub_pipeline.add_act( + act_name=_("actuator_下架"), + act_component_code=ExecuteRiakActuatorScriptComponent.code, + kwargs=asdict( + RiakActKwargs( + get_trans_data_ip_var=ScaleInManualContext.get_operate_nodes_var_name(), + bk_cloud_id=self.data["bk_cloud_id"], + run_as_system_user=DBA_ROOT_USER, + get_riak_payload_func=RiakActPayload.get_uninstall_payload.__name__, + ) + ), + ) + + sub_pipeline.add_act( + act_name=_("riak修改元数据"), + act_component_code=RiakDBMetaComponent.code, + kwargs=asdict( + DBMetaFuncKwargs( + db_meta_class_func=RiakDBMeta.riak_scale_in.__name__, + is_update_trans_data=True, + ) + ), + ) + + riak_pipeline.add_sub_pipeline(sub_pipeline.build_sub_process(sub_name=_("Riak集群缩容"))) + riak_pipeline.run_pipeline(init_trans_data_class=ScaleInManualContext()) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/riak/riak_cluster_scale_out_flow.py b/dbm-ui/backend/flow/engine/bamboo/scene/riak/riak_cluster_scale_out_flow.py new file mode 100644 index 0000000000..0ec60fdc53 --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/riak/riak_cluster_scale_out_flow.py @@ -0,0 +1,192 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging.config +from dataclasses import asdict +from typing import Dict, Optional + +from django.utils.translation import ugettext as _ + +from backend.configuration.constants import DBType +from backend.flow.consts import DBA_ROOT_USER +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList +from backend.flow.plugins.components.collections.riak.exec_actuator_script import ExecuteRiakActuatorScriptComponent +from backend.flow.plugins.components.collections.riak.get_riak_cluster_node import GetRiakClusterNodeComponent +from backend.flow.plugins.components.collections.riak.get_riak_resource import GetRiakResourceComponent +from backend.flow.plugins.components.collections.riak.riak_db_meta import RiakDBMetaComponent +from backend.flow.plugins.components.collections.riak.trans_files import TransFileComponent +from backend.flow.utils.riak.riak_act_dataclass import DBMetaFuncKwargs, DownloadMediaKwargsFromTrans +from backend.flow.utils.riak.riak_act_payload import RiakActPayload +from backend.flow.utils.riak.riak_context_dataclass import RiakActKwargs, ScaleOutManualContext +from backend.flow.utils.riak.riak_db_meta import RiakDBMeta + +logger = logging.getLogger("flow") + + +class RiakClusterScaleOutFlow(object): + """ + Riak扩容流程的抽象类 + { + "uid": "2022111212001000", + "root_id": 123, + "created_by": "admin", + "bk_biz_id": 0, + "ticket_type": "RIAK_CLUSTER_SCALE_OUT", + "ip_source": "manual_input", + "bk_cloud_id": 0, + "cluster_id": 5, + "nodes": [ + { + "ip": "127.0.0.1", + "bk_host_id": 0 + } + ] + } + """ + + def __init__(self, root_id: str, data: Optional[Dict]): + """ + @param root_id : 任务流程定义的root_id + @param data : 单据传递过来的参数列表,是dict格式 + """ + self.root_id = root_id + self.data = data + + def riak_cluster_scale_out_flow(self): + """ + Riak集群扩容 + """ + riak_pipeline = Builder(root_id=self.root_id, data=self.data) + sub_pipeline = SubBuilder(root_id=self.root_id, data=self.data) + + sub_pipeline.add_act(act_name=_("获取机器信息"), act_component_code=GetRiakResourceComponent.code, kwargs={}) + sub_pipeline.add_act(act_name=_("获取集群中的节点"), act_component_code=GetRiakClusterNodeComponent.code, kwargs={}) + ips = [node["ip"] for node in self.data["nodes"]] + + sub_pipeline.add_act( + act_name=_("下发actuator以及riak介质"), + act_component_code=TransFileComponent.code, + kwargs=asdict( + DownloadMediaKwargsFromTrans( + get_trans_data_ip_var=ScaleOutManualContext.get_nodes_var_name(), + bk_cloud_id=self.data["bk_cloud_id"], + file_list=GetFileList(db_type=DBType.Riak).riak_install_package(self.data["db_version"]), + ) + ), + ) + + sub_pipeline.add_act( + act_name=_("actuator_riak获取集群配置"), + act_component_code=ExecuteRiakActuatorScriptComponent.code, + kwargs=asdict( + RiakActKwargs( + get_trans_data_ip_var=ScaleOutManualContext.get_base_node_var_name(), + bk_cloud_id=self.data["bk_cloud_id"], + run_as_system_user=DBA_ROOT_USER, + get_riak_payload_func=RiakActPayload.get_config_payload.__name__, + ) + ), + write_payload_var="configs", + ) + + sub_pipeline.add_act( + act_name=_("actuator_riak系统配置初始化"), + act_component_code=ExecuteRiakActuatorScriptComponent.code, + kwargs=asdict( + RiakActKwargs( + get_trans_data_ip_var=ScaleOutManualContext.get_operate_nodes_var_name(), + bk_cloud_id=self.data["bk_cloud_id"], + run_as_system_user=DBA_ROOT_USER, + get_riak_payload_func=RiakActPayload.get_sysinit_payload.__name__, + ) + ), + ) + + sub_pipeline.add_act( + act_name=_("actuator_riak部署节点"), + act_component_code=ExecuteRiakActuatorScriptComponent.code, + kwargs=asdict( + RiakActKwargs( + get_trans_data_ip_var=ScaleOutManualContext.get_operate_nodes_var_name(), + bk_cloud_id=self.data["bk_cloud_id"], + run_as_system_user=DBA_ROOT_USER, + get_riak_payload_func=RiakActPayload.get_deploy_trans_payload.__name__, + ) + ), + ) + + sub_pipeline.add_act( + act_name=_("actuator_riak节点加入集群"), + act_component_code=ExecuteRiakActuatorScriptComponent.code, + kwargs=asdict( + RiakActKwargs( + get_trans_data_ip_var=ScaleOutManualContext.get_operate_nodes_var_name(), + bk_cloud_id=self.data["bk_cloud_id"], + run_as_system_user=DBA_ROOT_USER, + get_riak_payload_func=RiakActPayload.get_join_cluster_trans_payload.__name__, + ) + ), + ) + + sub_pipeline.add_act( + act_name=_("actuator_集群变更生效"), + act_component_code=ExecuteRiakActuatorScriptComponent.code, + kwargs=asdict( + RiakActKwargs( + get_trans_data_ip_var=ScaleOutManualContext.get_base_node_var_name(), + bk_cloud_id=self.data["bk_cloud_id"], + run_as_system_user=DBA_ROOT_USER, + get_riak_payload_func=RiakActPayload.get_commit_cluster_change_payload.__name__, + ) + ), + ) + + sub_pipeline.add_act( + act_name=_("actuator_检查数据搬迁进度"), + act_component_code=ExecuteRiakActuatorScriptComponent.code, + kwargs=asdict( + RiakActKwargs( + get_trans_data_ip_var=ScaleOutManualContext.get_operate_nodes_var_name(), + bk_cloud_id=self.data["bk_cloud_id"], + run_as_system_user=DBA_ROOT_USER, + get_riak_payload_func=RiakActPayload.get_transfer_payload.__name__, + ) + ), + ) + + sub_pipeline.add_act( + act_name=_("riak修改元数据"), + act_component_code=RiakDBMetaComponent.code, + kwargs=asdict( + DBMetaFuncKwargs( + db_meta_class_func=RiakDBMeta.riak_scale_out.__name__, + is_update_trans_data=True, + ) + ), + ) + + acts_list = [] + for ip in ips: + monitor_kwargs = RiakActKwargs( + exec_ip=ip, + bk_cloud_id=self.data["bk_cloud_id"], + run_as_system_user=DBA_ROOT_USER, + get_riak_payload_func=RiakActPayload.get_install_monitor_payload.__name__, + ) + act_info = dict() + act_info["act_name"] = (_("actuator_{}部署定时任务和riak监控".format(ip)),) + act_info["act_component_code"] = ExecuteRiakActuatorScriptComponent.code + act_info["kwargs"] = asdict(monitor_kwargs) + acts_list.append(act_info) + sub_pipeline.add_parallel_acts(acts_list=acts_list) + + riak_pipeline.add_sub_pipeline(sub_pipeline.build_sub_process(sub_name=_("Riak集群扩容"))) + riak_pipeline.run_pipeline(init_trans_data_class=ScaleOutManualContext()) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/spider/common/common_sub_flow.py b/dbm-ui/backend/flow/engine/bamboo/scene/spider/common/common_sub_flow.py index ece68d07b4..193f7fa752 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/spider/common/common_sub_flow.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/spider/common/common_sub_flow.py @@ -13,26 +13,42 @@ from django.utils.translation import ugettext as _ from backend.configuration.constants import DBType -from backend.db_meta.enums import ClusterType, InstanceStatus, TenDBClusterSpiderRole -from backend.db_meta.models import Cluster, ProxyInstance +from backend.constants import IP_PORT_DIVIDER +from backend.db_meta.enums import ClusterType, InstanceStatus, MachineType, TenDBClusterSpiderRole +from backend.db_meta.models import Cluster from backend.flow.consts import AUTH_ADDRESS_DIVIDER, DBA_ROOT_USER, TDBCTL_USER from backend.flow.engine.bamboo.scene.common.builder import SubBuilder from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList +from backend.flow.engine.bamboo.scene.mysql.common.common_sub_flow import check_sub_flow +from backend.flow.plugins.components.collections.common.delete_cc_service_instance import DelCCServiceInstComponent +from backend.flow.plugins.components.collections.common.download_backup_client import DownloadBackupClientComponent +from backend.flow.plugins.components.collections.mysql.clear_machine import MySQLClearMachineComponent from backend.flow.plugins.components.collections.mysql.clone_user import CloneUserComponent from backend.flow.plugins.components.collections.mysql.dns_manage import MySQLDnsManageComponent from backend.flow.plugins.components.collections.mysql.exec_actuator_script import ExecuteDBActuatorScriptComponent from backend.flow.plugins.components.collections.mysql.trans_flies import TransFileComponent from backend.flow.plugins.components.collections.spider.add_spider_routing import AddSpiderRoutingComponent +from backend.flow.plugins.components.collections.spider.ctl_drop_routing import CtlDropRoutingComponent +from backend.flow.plugins.components.collections.spider.ctl_switch_to_slave import CtlSwitchToSlaveComponent +from backend.flow.plugins.components.collections.spider.remote_migrate_cut_over import RemoteMigrateCutOverComponent +from backend.flow.plugins.components.collections.spider.spider_db_meta import SpiderDBMetaComponent +from backend.flow.utils.common_act_dataclass import DownloadBackupClientKwargs from backend.flow.utils.mysql.mysql_act_dataclass import ( CreateDnsKwargs, + DBMetaOPKwargs, + DelServiceInstKwargs, DownloadMediaKwargs, ExecActuatorKwargs, InstanceUserCloneKwargs, ) from backend.flow.utils.mysql.mysql_act_playload import MysqlActPayload from backend.flow.utils.spider.get_spider_incr import get_spider_master_incr -from backend.flow.utils.spider.spider_act_dataclass import AddSpiderRoutingKwargs -from backend.flow.utils.spider.spider_bk_config import get_spider_version_and_charset +from backend.flow.utils.spider.spider_act_dataclass import ( + AddSpiderRoutingKwargs, + CtlDropRoutingKwargs, + CtlSwitchToSlaveKwargs, +) +from backend.flow.utils.spider.spider_db_meta import SpiderDBMeta """ 定义一些TenDB cluster流程上可能会用到的子流程,以便于减少代码的重复率 @@ -135,9 +151,10 @@ def add_spider_slaves_sub_flow( { "source": tmp_spider.ip_port, "target": f"{spider['ip']}{AUTH_ADDRESS_DIVIDER}{tmp_spider.port}", + "machine_type": MachineType.SPIDER.value, "bk_cloud_id": cluster.bk_cloud_id, }, - ] + ], ) ), } @@ -193,10 +210,12 @@ def add_spider_masters_sub_flow( @param parent_global_data: 本次子流程的对应上层流程的全局只读上下文 @param is_add_spider_mnt: 表示这次添加spider 运维节点,如果是则True,不是则False """ + tag = "master" tdbctl_pass = get_random_string(length=10) # 获取到集群对应的spider端口,作为这次的安装 parent_global_data["spider_ports"] = [cluster.proxyinstance_set.first().port] + parent_global_data["ctl_port"] = cluster.proxyinstance_set.first().admin_port sub_pipeline = SubBuilder(root_id=root_id, data=parent_global_data) # 拼接执行原子任务活动节点需要的通用的私有参数结构体, 减少代码重复率,但引用时注意内部参数值传递的问题 @@ -213,7 +232,7 @@ def add_spider_masters_sub_flow( DownloadMediaKwargs( bk_cloud_id=cluster.bk_cloud_id, exec_ip=[ip_info["ip"] for ip_info in add_spider_masters], - file_list=GetFileList(db_type=DBType.MySQL).spider_slave_install_package( + file_list=GetFileList(db_type=DBType.MySQL).spider_master_install_package( spider_version=parent_global_data["spider_version"] ), ) @@ -223,6 +242,7 @@ def add_spider_masters_sub_flow( # 阶段2 初始化待安装机器 exec_act_kwargs.exec_ip = [ip_info["ip"] for ip_info in add_spider_masters] exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_sys_init_payload.__name__ + sub_pipeline.add_act( act_name=_("初始化机器"), act_component_code=ExecuteDBActuatorScriptComponent.code, @@ -258,6 +278,7 @@ def add_spider_masters_sub_flow( # 判断添加的角色来是否安装中控实例,spider-mnt不需要安装 if not is_add_spider_mnt: + acts_list = [] for ctl_ip in add_spider_masters: exec_act_kwargs.exec_ip = ctl_ip["ip"] exec_act_kwargs.cluster = {"immutable_domain": cluster.immute_domain} @@ -275,7 +296,6 @@ def add_spider_masters_sub_flow( acts_list = [] # 这里获取集群内running状态的spider节点作为这次克隆权限的依据 - # todo 这里目前spider节点克隆权限API接口出现异常,需要调整 tmp_spider = cluster.proxyinstance_set.filter(status=InstanceStatus.RUNNING)[0] for spider in add_spider_masters: @@ -289,9 +309,10 @@ def add_spider_masters_sub_flow( { "source": tmp_spider.ip_port, "target": f"{spider['ip']}{AUTH_ADDRESS_DIVIDER}{tmp_spider.port}", + "machine_type": MachineType.SPIDER.value, "bk_cloud_id": cluster.bk_cloud_id, }, - ] + ], ) ), } @@ -320,11 +341,12 @@ def add_spider_masters_sub_flow( if not is_add_spider_mnt: # 阶段8 待添加中控实例建立主从数据同步关系 sub_pipeline.add_sub_pipeline( - sub_flow=add_ctl_node_with_gtid( + sub_flow=build_ctl_replication_with_gtid( root_id=root_id, parent_global_data=parent_global_data, - cluster=cluster, - add_tdbctls=add_spider_masters, + bk_cloud_id=cluster.bk_cloud_id, + ctl_primary=cluster.tendbcluster_ctl_primary_address(), + ctl_secondary_list=add_spider_masters, ) ) # 阶段8 添加域名映射关系 @@ -340,8 +362,9 @@ def add_spider_masters_sub_flow( ) ), ) + tag = "mnt" - return sub_pipeline.build_sub_process(sub_name=_("集群[{}]添加spider master节点".format(cluster.name))) + return sub_pipeline.build_sub_process(sub_name=_("集群[{}]添加spider {}节点".format(cluster.name, tag))) def build_apps_for_spider_sub_flow( @@ -353,7 +376,6 @@ def build_apps_for_spider_sub_flow( ): """ 定义为spider机器部署周边组件的子流程 - todo 目前spider安装备份有异常,需要调整后再做联调 @param bk_cloud_id: 操作所属的云区域 @param spiders: 需要操作的spider机器列表信息 @param root_id: 整体flow流程的root_id @@ -363,7 +385,7 @@ def build_apps_for_spider_sub_flow( sub_pipeline = SubBuilder(root_id=root_id, data=parent_global_data) sub_pipeline.add_act( - act_name=_("下发MySQL周边程序介质"), + act_name=_("下发Spider周边程序介质"), act_component_code=TransFileComponent.code, kwargs=asdict( DownloadMediaKwargs( @@ -425,24 +447,38 @@ def build_apps_for_spider_sub_flow( ), }, ) + + acts_list.append( + { + "act_name": _("安装backup-client工具"), + "act_component_code": DownloadBackupClientComponent.code, + "kwargs": asdict( + DownloadBackupClientKwargs( + bk_cloud_id=bk_cloud_id, + download_host_list=list(filter(None, list(set(spiders)))), + ) + ), + } + ) + sub_pipeline.add_parallel_acts(acts_list=acts_list) return sub_pipeline.build_sub_process(sub_name=_("安装Spider周边程序")) -def add_ctl_node_with_gtid( +def build_ctl_replication_with_gtid( root_id: str, parent_global_data: dict, - cluster: Cluster, - add_tdbctls: list, + bk_cloud_id: int, + ctl_primary: str, + ctl_secondary_list: list, ): """ 添加新的定义ctl建立基于gtid的主从同步 """ - ctl_master = cluster.tendbcluster_ctl_primary_address() extend = { - "mysql_port": ctl_master.split(":")[1], - "master_ip": ctl_master.split(":")[0], - "slaves": [ip_info["ip"] for ip_info in add_tdbctls], + "mysql_port": int(ctl_primary.split(":")[1]), + "master_ip": ctl_primary.split(":")[0], + "slaves": [ip_info["ip"] for ip_info in ctl_secondary_list], } sub_pipeline = SubBuilder(root_id=root_id, data=parent_global_data) @@ -451,22 +487,22 @@ def add_ctl_node_with_gtid( act_component_code=ExecuteDBActuatorScriptComponent.code, kwargs=asdict( ExecActuatorKwargs( - bk_cloud_id=cluster.bk_cloud_id, - exec_ip=ctl_master.split(":")[0], + bk_cloud_id=bk_cloud_id, + exec_ip=ctl_primary.split(":")[0], get_mysql_payload_func=MysqlActPayload.get_grant_repl_for_ctl_payload.__name__, cluster=extend, ) ), ) acts_list = [] - for tdbctl in add_tdbctls: + for tdbctl in ctl_secondary_list: acts_list.append( { "act_name": _("建立主从关系"), "act_component_code": ExecuteDBActuatorScriptComponent.code, "kwargs": asdict( ExecActuatorKwargs( - bk_cloud_id=cluster.bk_cloud_id, + bk_cloud_id=bk_cloud_id, exec_ip=tdbctl["ip"], get_mysql_payload_func=MysqlActPayload.get_change_master_for_gitd_payload.__name__, cluster=extend, @@ -476,4 +512,286 @@ def add_ctl_node_with_gtid( ) sub_pipeline.add_parallel_acts(acts_list=acts_list) - return sub_pipeline.build_sub_process(sub_name=_("部署spider-ctl集群")) + return sub_pipeline.build_sub_process(sub_name=_("建立spider-ctl数据同步")) + + +def reduce_spider_slaves_flow( + cluster: Cluster, + reduce_spiders: list, + root_id: str, + parent_global_data: dict, + spider_role: TenDBClusterSpiderRole, +): + """ + 减少spider节点的子流程, 提供给集群缩容接入层或者替换类单据所用 + @param cluster: 待操作的集群 + @param reduce_spiders: 待卸载的spider节点机器信息 + @param root_id: flow流程的root_id + @param parent_global_data: 本次子流程的对应上层流程的全局只读上下文 + @param spider_role: 本次操作的spider角色 + """ + + sub_pipeline = SubBuilder(root_id=root_id, data=parent_global_data) + + # 拼接执行原子任务活动节点需要的通用的私有参数结构体, 减少代码重复率,但引用时注意内部参数值传递的问题 + exec_act_kwargs = ExecActuatorKwargs( + cluster_type=ClusterType.TenDBCluster, + bk_cloud_id=cluster.bk_cloud_id, + ) + + # 获取集群对应的spider端口 + spider_port = cluster.proxyinstance_set.first().port + spider_admin_port = cluster.proxyinstance_set.first().admin_port + + # 先回收集群所有服务实例内容,避免出现误报监控 + del_instance_list = [] + for spider in reduce_spiders: + del_instance_list.append({"ip": spider["ip"], "port": spider_port}) + + sub_pipeline.add_act( + act_name=_("删除注册CC系统的服务实例"), + act_component_code=DelCCServiceInstComponent.code, + kwargs=asdict( + DelServiceInstKwargs( + cluster_id=cluster.id, + del_instance_list=del_instance_list, + ) + ), + ) + + # 阶段1 下发spider安装介质包 + sub_pipeline.add_act( + act_name=_("下发db-actuator介质"), + act_component_code=TransFileComponent.code, + kwargs=asdict( + DownloadMediaKwargs( + bk_cloud_id=cluster.bk_cloud_id, + exec_ip=[ip_info["ip"] for ip_info in reduce_spiders], + file_list=GetFileList(db_type=DBType.MySQL).get_db_actuator_package(), + ) + ), + ) + + # 阶段2 卸载相关db组件 + acts_list = [] + for spider in reduce_spiders: + exec_act_kwargs.exec_ip = spider["ip"] + exec_act_kwargs.cluster = {"spider_port": spider_port} + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_uninstall_spider_payload.__name__ + acts_list.append( + { + "act_name": _("卸载spider实例"), + "act_component_code": ExecuteDBActuatorScriptComponent.code, + "kwargs": asdict(exec_act_kwargs), + } + ) + sub_pipeline.add_parallel_acts(acts_list=acts_list) + + # 阶段3 如果这次卸载的是spider-master,需要卸载对应的中控实例 + if spider_role == TenDBClusterSpiderRole.SPIDER_MASTER.value: + + # 回收对应ctl的路由信息,如果涉及到ctl primary,先切换,再回收 + reduce_ctls = cluster.proxyinstance_set.filter(machine__ip__in=[ip_info["ip"] for ip_info in reduce_spiders]) + sub_pipeline.add_sub_pipeline( + sub_flow=reduce_ctls_routing( + root_id=root_id, parent_global_data=parent_global_data, cluster=cluster, reduce_ctls=list(reduce_ctls) + ) + ) + + # 卸载ctl的进程 + acts_list = [] + for ctl in reduce_spiders: + exec_act_kwargs.exec_ip = ctl["ip"] + exec_act_kwargs.cluster = {"spider_ctl_port": spider_admin_port} + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_uninstall_spider_ctl_payload.__name__ + acts_list.append( + { + "act_name": _("卸载中控实例"), + "act_component_code": ExecuteDBActuatorScriptComponent.code, + "kwargs": asdict(exec_act_kwargs), + } + ) + sub_pipeline.add_parallel_acts(acts_list=acts_list) + + # 阶段4 清空相关集群元信息;相关的cmdb注册信息 + sub_pipeline.add_act( + act_name=_("清理db_meta元信息"), + act_component_code=SpiderDBMetaComponent.code, + kwargs=asdict( + DBMetaOPKwargs( + db_meta_class_func=SpiderDBMeta.reduce_spider_nodes_apply.__name__, + ) + ), + ) + + # 阶段5 清理机器配置,这里不需要做实例级别的配置清理,因为目前平台spider的单机单实例部署,专属一套集群 + exec_act_kwargs.exec_ip = [ip_info["ip"] for ip_info in reduce_spiders] + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_clear_machine_crontab.__name__ + sub_pipeline.add_act( + act_name=_("清理机器周边配置"), + act_component_code=MySQLClearMachineComponent.code, + kwargs=asdict(exec_act_kwargs), + ) + + return sub_pipeline.build_sub_process(sub_name=_("下架spider节点")) + + +def reduce_ctls_routing(root_id: str, parent_global_data: dict, cluster: Cluster, reduce_ctls: list): + """ + 根据回收spider-ctl,构建专属的中控实例路由删除的子流程 + """ + reduce_ctl_primary = None + reduce_ctl_secondary_list = [] + + # 计算每个待回收的ctl的角色,分配下架行为 + for ctl in reduce_ctls: + if f"{ctl.machine.ip}{IP_PORT_DIVIDER}{ctl.admin_port}" == cluster.tendbcluster_ctl_primary_address(): + # 本次回收事件涉及到ctl主节点回收,则加入这次回收流程 + reduce_ctl_primary = f"{ctl.machine.ip}{IP_PORT_DIVIDER}{ctl.admin_port}" + else: + reduce_ctl_secondary_list.append(f"{ctl.machine.ip}{IP_PORT_DIVIDER}{ctl.admin_port}") + + sub_pipeline = SubBuilder(root_id=root_id, data=parent_global_data) + + if reduce_ctl_primary: + # 选择新节点作为primary,过滤待回收的节点 + all_ctl = cluster.proxyinstance_set.filter( + tendbclusterspiderext__spider_role=TenDBClusterSpiderRole.SPIDER_MASTER + ) + + # 因为ctl集群是采用GTID+半同步数据同步,所以理论上选择任意一个从节点作为主,数据不会丢失 + new_ctl_primary = all_ctl.exclude(machine__ip__in=[ip_info["ip"] for ip_info in reduce_ctls]).first() + + sub_pipeline.add_act( + act_name=_("切换ctl中控集群"), + act_component_code=CtlSwitchToSlaveComponent.code, + kwargs=asdict( + CtlSwitchToSlaveKwargs( + cluster_id=cluster.id, + reduce_ctl_primary=reduce_ctl_primary, + new_ctl_primary=f"{new_ctl_primary.machine.ip}{IP_PORT_DIVIDER}{new_ctl_primary.admin_port}", + ) + ), + ) + + acts_list = [] + for ctl in reduce_ctl_secondary_list: + acts_list.append( + { + "act_name": _("卸载中控实例路由[{}]".format(ctl)), + "act_component_code": CtlDropRoutingComponent.code, + "kwargs": asdict( + CtlDropRoutingKwargs( + cluster_id=cluster.id, + reduce_ctl=ctl, + ) + ), + } + ) + sub_pipeline.add_parallel_acts(acts_list=acts_list) + + return sub_pipeline.build_sub_process(sub_name=_("删除中控的路由节点")) + + +def remote_migrate_switch_sub_flow( + uid: str, + root_id: str, + cluster: Cluster, + migrate_tuples: list, + created_by: str, +): + """ + 定义成对迁移的切换子流程 + @param uid: flow流程的uid + @param root_id: flow流程的root_id + @param cluster: 关联的cluster对象 + @param migrate_tuples: 成对迁移关联的实例信息,每个元素的结构体如下: + { + “old_master”: "ip:port", + “old_slave”: "ip:port", + “new_master”: "ip:port", + “new_slave”: "ip:port + } ... + """ + # 获取cluster对应的中控primary + ctl_primary = cluster.tendbcluster_ctl_primary_address() + + # 获取所有接入层正在running 状态的spider列表 + spiders = cluster.proxyinstance_set.filter(status=InstanceStatus.RUNNING) + + # 默认预检测连接情况、同步延时、checksum校验结果 + parent_global_data = { + "uid": uid, + "root_id": root_id, + "cluster_id": cluster.id, + "slave_delay_check": True, + "migrate_tuples": migrate_tuples, + "tdbctl_pass": get_random_string(length=10), + "bk_biz_id": cluster.bk_biz_id, + "created_by": created_by, + } + + sub_pipeline = SubBuilder(root_id=root_id, data=parent_global_data) + + sub_pipeline.add_act( + act_name=_("下发db-actuator介质"), + act_component_code=TransFileComponent.code, + kwargs=asdict( + DownloadMediaKwargs( + bk_cloud_id=cluster.bk_cloud_id, + exec_ip=ctl_primary.split(":")[0], + file_list=GetFileList(db_type=DBType.MySQL).get_db_actuator_package(), + ) + ), + ) + + # 切换前做预检测 + verify_checksum_tuples = [] + for m in migrate_tuples: + # old_master-> new_master ; new_master -> new_slave 都需要检测checksum结果 + verify_checksum_tuples.append({"master": m["old_master"], "slave": m["new_master"]}) + verify_checksum_tuples.append({"master": m["new_master"], "slave": m["new_slave"]}) + sub_pipeline.add_sub_pipeline( + sub_flow=check_sub_flow( + uid=uid, + root_id=root_id, + cluster=cluster, + is_check_client_conn=True, + is_verify_checksum=True, + check_client_conn_inst=[s.ip_port for s in spiders], + verify_checksum_tuples=verify_checksum_tuples, + ) + ) + + clone_data = [] + for tuple_info in migrate_tuples: + clone_data.append( + { + "source": f"{tuple_info['old_master']}", + "target": f"{tuple_info['new_master']}", + "machine_type": MachineType.REMOTE.value, + "bk_cloud_id": cluster.bk_cloud_id, + } + ) + clone_data.append( + { + "source": f"{tuple_info['old_slave']}", + "target": f"{tuple_info['new_slave']}", + "machine_type": MachineType.REMOTE.value, + "bk_cloud_id": cluster.bk_cloud_id, + } + ) + + sub_pipeline.add_act( + act_name=_("克隆权限"), + act_component_code=CloneUserComponent.code, + kwargs=asdict(InstanceUserCloneKwargs(clone_data=clone_data)), + ) + + sub_pipeline.add_act( + act_name=_("执行成对切换"), + act_component_code=RemoteMigrateCutOverComponent.code, + kwargs={}, + ) + + return sub_pipeline.build_sub_process(sub_name=_("[{}]成对切换".format(cluster.name))) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/spider/common/exceptions.py b/dbm-ui/backend/flow/engine/bamboo/scene/spider/common/exceptions.py index 75dd17e22a..b27e77bf1f 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/spider/common/exceptions.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/spider/common/exceptions.py @@ -33,3 +33,27 @@ class AddSpiderNodeFailedException(TenDBClusterFlowBaseException): ERROR_CODE = "003" MESSAGE = _("添加spider节点路由失败") MESSAGE_TPL = _("{message}") + + +class CtlSwitchToSlaveFailedException(TenDBClusterFlowBaseException): + ERROR_CODE = "004" + MESSAGE = _("中控集群切换失败") + MESSAGE_TPL = _("{message}") + + +class DropSpiderNodeFailedException(TenDBClusterFlowBaseException): + ERROR_CODE = "005" + MESSAGE = _("删除spider节点路由失败") + MESSAGE_TPL = _("{message}") + + +class TendbGetBackupInfoFailedException(TenDBClusterFlowBaseException): + ERROR_CODE = "006" + MESSAGE = _("获取备份失败") + MESSAGE_TPL = _("{message}") + + +class TendbGetBinlogFailedException(TenDBClusterFlowBaseException): + ERROR_CODE = "007" + MESSAGE = _("获取binlog失败") + MESSAGE_TPL = _("{message}") diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/spider/import_sqlfile_flow.py b/dbm-ui/backend/flow/engine/bamboo/scene/spider/import_sqlfile_flow.py index 51a2f72cb7..6fd33bb411 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/spider/import_sqlfile_flow.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/spider/import_sqlfile_flow.py @@ -31,6 +31,7 @@ from backend.flow.plugins.components.collections.mysql.trans_flies import TransFileComponent from backend.flow.utils.mysql.mysql_act_dataclass import DownloadMediaKwargs, ExecActuatorKwargs from backend.flow.utils.mysql.mysql_act_playload import MysqlActPayload +from backend.flow.utils.spider.spider_bk_config import get_spider_version_and_charset logger = logging.getLogger("flow") @@ -134,7 +135,11 @@ def sql_semantic_check_flow(self): cluster = self.__get_master_ctl_info(cluster_id) remotedb_version = self.__get_remotedb_version(cluster_id) spider_version = self.__get_spider_version(cluster_id) - + spider_charset = self.data["charset"] + if self.data["charset"] == "default": + spider_charset, config_spider_ver = get_spider_version_and_charset( + bk_biz_id=cluster["bk_biz_id"], db_module_id=cluster["db_module_id"] + ) semantic_check_pipeline.add_act( act_name=_("给模板集群下发db-actuator"), act_component_code=TransFileComponent.code, @@ -169,7 +174,7 @@ def sql_semantic_check_flow(self): "uid": self.data["uid"], "spider_version": spider_version, "mysql_version": remotedb_version, - "mysql_charset": self.data["charset"], + "mysql_charset": spider_charset, "path": BKREPO_SQLFILE_PATH, "task_id": self.root_id, "schema_sql_file": self.semantic_dump_schema_file_name, @@ -184,7 +189,7 @@ def sql_semantic_check_flow(self): semantic_check_pipeline.run_pipeline() - def __get_master_ctl_info(self, cluster_id: int) -> object: + def __get_master_ctl_info(self, cluster_id: int) -> dict: cluster = Cluster.objects.get(id=cluster_id) logger.info("get ") master_ctl_addr = cluster.tendbcluster_ctl_primary_address() @@ -192,6 +197,8 @@ def __get_master_ctl_info(self, cluster_id: int) -> object: master_ctl_port = master_ctl_addr.split(IP_PORT_DIVIDER)[1] return { "id": cluster_id, + "bk_biz_id": cluster.bk_biz_id, + "db_module_id": cluster.db_module_id, "bk_cloud_id": cluster.bk_cloud_id, "name": cluster.name, "port": int(master_ctl_port), diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/spider/remote_local_slave_recover.py b/dbm-ui/backend/flow/engine/bamboo/scene/spider/remote_local_slave_recover.py new file mode 100644 index 0000000000..38bcf5ad8d --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/spider/remote_local_slave_recover.py @@ -0,0 +1,197 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import copy +import logging +from dataclasses import asdict +from datetime import datetime +from typing import Dict, Optional + +from django.utils.translation import ugettext as _ + +from backend.configuration.constants import DBType +from backend.constants import IP_PORT_DIVIDER +from backend.db_meta.enums import ClusterType, InstanceStatus +from backend.db_services.mysql.fixpoint_rollback.handlers import FixPointRollbackHandler +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList +from backend.flow.engine.bamboo.scene.spider.common.exceptions import TendbGetBackupInfoFailedException +from backend.flow.engine.bamboo.scene.spider.spider_remote_node_migrate import remote_slave_recover_sub_flow +from backend.flow.plugins.components.collections.mysql.exec_actuator_script import ExecuteDBActuatorScriptComponent +from backend.flow.plugins.components.collections.mysql.trans_flies import TransFileComponent +from backend.flow.plugins.components.collections.spider.spider_db_meta import SpiderDBMetaComponent +from backend.flow.utils.mysql.common.mysql_cluster_info import get_version_and_charset +from backend.flow.utils.mysql.mysql_act_dataclass import DBMetaOPKwargs, DownloadMediaKwargs, ExecActuatorKwargs +from backend.flow.utils.mysql.mysql_act_playload import MysqlActPayload +from backend.flow.utils.mysql.mysql_context_dataclass import ClusterInfoContext +from backend.flow.utils.spider.spider_db_meta import SpiderDBMeta +from backend.flow.utils.spider.tendb_cluster_info import get_slave_local_recover_info + +logger = logging.getLogger("flow") + + +class TenDBRemoteSlaveLocalRecoverFlow(object): + """ + TenDB 后端从节点恢复: 迁移机器恢复,指定实例的本地恢复 + """ + + def __init__(self, root_id: str, ticket_data: Optional[Dict]): + """ + @param root_id : 任务流程定义的root_id + @param ticket_data : 单据传递参数 + """ + self.root_id = root_id + self.ticket_data = ticket_data + self.data = {} + + def tendb_remote_slave_local_recover(self): + """ + tendb cluster remote slave recover + """ + tendb_migrate_pipeline_all = Builder(root_id=self.root_id, data=copy.deepcopy(self.ticket_data)) + tendb_migrate_pipeline_all_list = [] + # 阶段1 获取集群所有信息。计算端口,构建数据。 + for info in self.ticket_data["infos"]: + self.data = copy.deepcopy(info) + self.data["bk_cloud_id"] = self.ticket_data["bk_cloud_id"] + self.data["root_id"] = self.root_id + self.data["uid"] = self.ticket_data["uid"] + self.data["ticket_type"] = self.ticket_data["ticket_type"] + self.data["bk_biz_id"] = self.ticket_data["bk_biz_id"] + self.data["created_by"] = self.ticket_data["created_by"] + # self.data["module"] = info["module"] + # 卸载流程时强制卸载 + self.data["force"] = True + # 先判断备份是否存在 + backup_handler = FixPointRollbackHandler(self.data["cluster_id"]) + restore_time = datetime.now() + # restore_time = datetime.strptime("2023-07-31 17:40:00", "%Y-%m-%d %H:%M:%S") + backup_info = backup_handler.query_latest_backup_log(restore_time) + if backup_info is None: + logger.error("cluster {} backup info not exists".format(self.data["cluster_id"])) + raise TendbGetBackupInfoFailedException(message=_("获取集群 {} 的备份信息失败".format(self.data["cluster_id"]))) + logger.debug(backup_info) + + cluster_info = get_slave_local_recover_info(self.data["cluster_id"], self.data["storage_id"]) + charset, db_version = get_version_and_charset( + bk_biz_id=cluster_info["bk_biz_id"], + db_module_id=cluster_info["db_module_id"], + cluster_type=cluster_info["cluster_type"], + ) + cluster_info["charset"] = charset + cluster_info["db_version"] = db_version + self.data["target_ip"] = cluster_info["target_ip"] + tendb_migrate_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(self.data)) + + cluster_info["ports"] = [] + for shard_id, shard in cluster_info["my_shards"].items(): + slave = { + "ip": self.data["target_ip"], + "port": shard["port"], + "bk_cloud_id": self.data["bk_cloud_id"], + "instance": "{}{}{}".format(self.data["target_ip"], IP_PORT_DIVIDER, shard["port"]), + } + cluster_info["my_shards"][shard_id]["new_slave"] = slave + cluster_info["ports"].append(shard["port"]) + + sync_data_sub_pipeline_list = [] + for shard_id, node in cluster_info["my_shards"].items(): + ins_cluster = copy.deepcopy(cluster_info["cluster"]) + ins_cluster["charset"] = cluster_info["charset"] + ins_cluster["new_slave_ip"] = node["new_slave"]["ip"] + ins_cluster["new_slave_port"] = node["new_slave"]["port"] + ins_cluster["master_ip"] = node["master"]["ip"] + ins_cluster["slave_ip"] = node["slave"]["ip"] + ins_cluster["master_port"] = node["master"]["port"] + ins_cluster["slave_port"] = node["slave"]["port"] + # 设置实例状态 + ins_cluster["storage_id"] = node["slave"]["id"] + ins_cluster["storage_status"] = InstanceStatus.RESTORING.value + # todo 正式环境放开file_target_path,需要备份接口支持自动创建目录 + # ins_cluster["file_target_path"] = "/data/dbbak/{}/{}"\ + # .format(self.root_id, ins_cluster["new_master_port"]) + ins_cluster["file_target_path"] = "/home/mysql/install" + ins_cluster["shard_id"] = shard_id + ins_cluster["change_master_force"] = False + + ins_cluster["backupinfo"] = backup_info["remote_node"].get(shard_id, {}) + # 判断 remote_node 下每个分片的备份信息是否正常 + if ( + len(ins_cluster["backupinfo"]) == 0 + or len(ins_cluster["backupinfo"].get("file_list_details", {})) == 0 + ): + logger.error( + "cluster {} shard {} backup info not exists".format(self.data["cluster_id"], shard_id) + ) + raise TendbGetBackupInfoFailedException( + message=_("获取集群分片 {} shard {} 的备份信息失败".format(self.data["cluster_id"], shard_id)) + ) + sync_data_sub_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(self.data)) + sync_data_sub_pipeline.add_act( + act_name=_("写入初始化实例的db_meta元信息"), + act_component_code=SpiderDBMetaComponent.code, + kwargs=asdict( + DBMetaOPKwargs( + db_meta_class_func=SpiderDBMeta.tendb_modify_storage_status.__name__, + cluster=copy.deepcopy(ins_cluster), + is_update_trans_data=False, + ) + ), + ) + exec_act_kwargs = ExecActuatorKwargs( + bk_cloud_id=int(ins_cluster["bk_cloud_id"]), + cluster_type=ClusterType.TenDBCluster, + ) + exec_act_kwargs.exec_ip = ins_cluster["new_slave_ip"] + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_clean_mysql_payload.__name__ + sync_data_sub_pipeline.add_act( + act_name=_("slave重建之清理从库{}").format(exec_act_kwargs.exec_ip), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(exec_act_kwargs), + ) + + sync_data_sub_pipeline.add_sub_pipeline( + sub_flow=remote_slave_recover_sub_flow( + root_id=self.root_id, ticket_data=copy.deepcopy(self.data), cluster_info=ins_cluster + ) + ) + ins_cluster["storage_status"] = InstanceStatus.RUNNING.value + sync_data_sub_pipeline.add_act( + act_name=_("写入初始化实例的db_meta元信息"), + act_component_code=SpiderDBMetaComponent.code, + kwargs=asdict( + DBMetaOPKwargs( + db_meta_class_func=SpiderDBMeta.tendb_modify_storage_status.__name__, + cluster=copy.deepcopy(ins_cluster), + is_update_trans_data=False, + ) + ), + ) + sync_data_sub_pipeline_list.append(sync_data_sub_pipeline.build_sub_process(sub_name=_("恢复实例数据"))) + + tendb_migrate_pipeline.add_act( + act_name=_("下发工具"), + act_component_code=TransFileComponent.code, + kwargs=asdict( + DownloadMediaKwargs( + bk_cloud_id=cluster_info["bk_cloud_id"], + exec_ip=self.data["target_ip"], + file_list=GetFileList(db_type=DBType.MySQL).get_db_actuator_package(), + ) + ), + ) + tendb_migrate_pipeline.add_parallel_sub_pipeline(sub_flow_list=sync_data_sub_pipeline_list) + tendb_migrate_pipeline_all_list.append( + tendb_migrate_pipeline.build_sub_process(_("集群迁移{}").format(self.data["cluster_id"])) + ) + + # 运行流程 + tendb_migrate_pipeline_all.add_parallel_sub_pipeline(tendb_migrate_pipeline_all_list) + tendb_migrate_pipeline_all.run_pipeline(init_trans_data_class=ClusterInfoContext()) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/spider/remote_master_fail_over.py b/dbm-ui/backend/flow/engine/bamboo/scene/spider/remote_master_fail_over.py new file mode 100644 index 0000000000..730fb5d1e4 --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/spider/remote_master_fail_over.py @@ -0,0 +1,19 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 backend.flow.engine.bamboo.scene.spider.remote_master_slave_swtich import RemoteMasterSlaveSwitchFlow + + +class RemoteMasterFailOverFlow(RemoteMasterSlaveSwitchFlow): + """ + 主故障切换继承主从互切流程类,执行构建 + """ + + def remote_fail_over(self): + super().remote_switch() diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/spider/remote_master_slave_swtich.py b/dbm-ui/backend/flow/engine/bamboo/scene/spider/remote_master_slave_swtich.py new file mode 100644 index 0000000000..1376cfd114 --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/spider/remote_master_slave_swtich.py @@ -0,0 +1,173 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import copy +import logging.config +from dataclasses import asdict +from typing import Dict, Optional + +from django.utils.translation import ugettext as _ + +from backend.configuration.constants import DBType +from backend.db_meta.enums import ClusterType, InstanceStatus +from backend.db_meta.exceptions import ClusterNotExistException +from backend.db_meta.models import Cluster, StorageInstanceTuple +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList +from backend.flow.engine.bamboo.scene.mysql.common.common_sub_flow import ( + build_surrounding_apps_sub_flow, + check_sub_flow, +) +from backend.flow.plugins.components.collections.mysql.exec_actuator_script import ExecuteDBActuatorScriptComponent +from backend.flow.plugins.components.collections.mysql.trans_flies import TransFileComponent +from backend.flow.plugins.components.collections.spider.spider_db_meta import SpiderDBMetaComponent +from backend.flow.utils.mysql.mysql_act_dataclass import DBMetaOPKwargs, DownloadMediaKwargs, ExecActuatorKwargs +from backend.flow.utils.mysql.mysql_act_playload import MysqlActPayload +from backend.flow.utils.spider.spider_db_meta import SpiderDBMeta + +logger = logging.getLogger("flow") + + +class RemoteMasterSlaveSwitchFlow(object): + """ + 构建TenDB Cluster集群remote存储对的互切流程,产品形态是整机切换,保证同一台机器的所有实例要不master角色,要不slave角色 + 目前集群维度的额互切流程如下: + 1:下发db-actuator介质到中控primary机器 + 2:下发中控执行互切逻辑命令,命令包括有: + 2.1:做前置检查 + 2.2:中控执行切换主分片 + 2.3:断开newMaster同步 + 2.4:授权repl账号给oldMaster + 2.3:建立新的复制关系 + 2.3:断开newMaster同步 + 3: 修改元数据 + """ + + def __init__(self, root_id: str, data: Optional[Dict]): + """ + @param root_id : 任务流程定义的root_id + @param data : 单据传递参数 + """ + self.root_id = root_id + self.data = data + + def remote_switch(self): + """ + 构建remote互切的流程 + """ + + switch_pipeline = Builder(root_id=self.root_id, data=self.data) + sub_pipelines = [] + + for info in self.data["infos"]: + # 拼接子流程需要全局参数 + sub_flow_context = copy.deepcopy(self.data) + sub_flow_context.pop("infos") + + # 拼接子流程的全局参数 + sub_flow_context.update(info) + + # 获取对应集群相关对象 + try: + cluster = Cluster.objects.get(id=info["cluster_id"], bk_biz_id=int(self.data["bk_biz_id"])) + except Cluster.DoesNotExist: + raise ClusterNotExistException( + cluster_id=info["cluster_id"], bk_biz_id=int(self.data["bk_biz_id"]), message=_("集群不存在") + ) + + # 获取所有接入层正在running 状态的spider列表 + spiders = cluster.proxyinstance_set.filter(status=InstanceStatus.RUNNING) + + # 获取中控primary + ctl_primary = cluster.tendbcluster_ctl_primary_address() + + # 计算预检测需要参数 + check_client_conn_inst = [] + verify_checksum_tuples = [] + if sub_flow_context["is_check_process"]: + # 需要做客户端连接检测,计算出需要做检查的实例 + check_client_conn_inst = [s.ip_port for s in spiders] + + if sub_flow_context["is_verify_checksum"]: + # 需要检测checksum结果,计算出需要做checksum结果的存储对 + # 需要做客户端连接检测,计算出需要做检查的实例 + for t in sub_flow_context["switch_tuples"]: + objs = cluster.storageinstance_set.filter(machine__ip=t["master"]["ip"]) + for master in objs: + slave = StorageInstanceTuple.objects.get(ejector=master).receiver + verify_checksum_tuples.append({"master": master.ip_port, "slave": slave.ip_port}) + + # 启动子流程 + sub_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(sub_flow_context)) + + sub_pipeline.add_act( + act_name=_("下发db-actuator介质"), + act_component_code=TransFileComponent.code, + kwargs=asdict( + DownloadMediaKwargs( + bk_cloud_id=cluster.bk_cloud_id, + exec_ip=ctl_primary.split(":")[0], + file_list=GetFileList(db_type=DBType.MySQL).get_db_actuator_package(), + ) + ), + ) + + # 切换前做预检测 + sub_flow = check_sub_flow( + uid=self.data["uid"], + root_id=self.root_id, + cluster=cluster, + is_check_client_conn=sub_flow_context["is_check_process"], + is_verify_checksum=sub_flow_context["is_verify_checksum"], + check_client_conn_inst=check_client_conn_inst, + verify_checksum_tuples=verify_checksum_tuples, + ) + if sub_flow: + sub_pipeline.add_sub_pipeline(sub_flow=sub_flow) + + sub_pipeline.add_act( + act_name=_("执行切换"), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict( + ExecActuatorKwargs( + bk_cloud_id=cluster.bk_cloud_id, + get_mysql_payload_func=MysqlActPayload.tendb_cluster_remote_switch.__name__, + exec_ip=ctl_primary.split(":")[0], + ) + ), + ) + + sub_pipeline.add_act( + act_name=_("变更db_meta元信息"), + act_component_code=SpiderDBMetaComponent.code, + kwargs=asdict( + DBMetaOPKwargs( + db_meta_class_func=SpiderDBMeta.remote_switch.__name__, + ) + ), + ) + + # 阶段7 切换后重建备份程序和数据校验程序 + # 如果旧master机器故障,则重建失败(主故障切换场景) + sub_pipeline.add_sub_pipeline( + sub_flow=build_surrounding_apps_sub_flow( + bk_cloud_id=cluster.bk_cloud_id, + master_ip_list=[info["slave"]["ip"] for info in info["switch_tuples"]], + slave_ip_list=[info["master"]["ip"] for info in info["switch_tuples"]], + root_id=self.root_id, + parent_global_data=copy.deepcopy(sub_flow_context), + is_init=False, + cluster_type=ClusterType.TenDBCluster.value, + ) + ) + + sub_pipelines.append(sub_pipeline.build_sub_process(sub_name=_("[{}]集群后端切换".format(cluster.name)))) + + switch_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) + switch_pipeline.run_pipeline() diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/spider/remote_slave_recover.py b/dbm-ui/backend/flow/engine/bamboo/scene/spider/remote_slave_recover.py new file mode 100644 index 0000000000..8b1a3b4aca --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/spider/remote_slave_recover.py @@ -0,0 +1,317 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import copy +import logging +from dataclasses import asdict +from datetime import datetime +from typing import Dict, Optional + +from django.utils.translation import ugettext as _ + +from backend.constants import IP_PORT_DIVIDER +from backend.db_meta.enums import ClusterType +from backend.db_meta.models import Cluster +from backend.db_services.mysql.fixpoint_rollback.handlers import FixPointRollbackHandler +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.engine.bamboo.scene.mysql.common.common_sub_flow import ( + build_surrounding_apps_sub_flow, + install_mysql_in_cluster_sub_flow, +) +from backend.flow.engine.bamboo.scene.spider.common.exceptions import TendbGetBackupInfoFailedException +from backend.flow.engine.bamboo.scene.spider.spider_remote_node_migrate import ( + remote_node_uninstall_sub_flow, + remote_slave_recover_sub_flow, +) +from backend.flow.plugins.components.collections.common.download_backup_client import DownloadBackupClientComponent +from backend.flow.plugins.components.collections.common.pause import PauseComponent +from backend.flow.plugins.components.collections.mysql.clear_machine import MySQLClearMachineComponent +from backend.flow.plugins.components.collections.mysql.exec_actuator_script import ExecuteDBActuatorScriptComponent +from backend.flow.plugins.components.collections.spider.spider_db_meta import SpiderDBMetaComponent +from backend.flow.utils.common_act_dataclass import DownloadBackupClientKwargs +from backend.flow.utils.mysql.common.mysql_cluster_info import get_version_and_charset +from backend.flow.utils.mysql.mysql_act_dataclass import ClearMachineKwargs, DBMetaOPKwargs, ExecActuatorKwargs +from backend.flow.utils.mysql.mysql_act_playload import MysqlActPayload +from backend.flow.utils.mysql.mysql_context_dataclass import ClusterInfoContext +from backend.flow.utils.spider.spider_db_meta import SpiderDBMeta +from backend.flow.utils.spider.tendb_cluster_info import get_slave_recover_info + +logger = logging.getLogger("flow") + + +class TenDBRemoteSlaveRecoverFlow(object): + """ + TenDB 后端从节点恢复: 迁移机器恢复,指定实例的本地恢复 + """ + + def __init__(self, root_id: str, ticket_data: Optional[Dict]): + """ + @param root_id : 任务流程定义的root_id + @param ticket_data : 单据传递参数 + """ + self.root_id = root_id + self.ticket_data = ticket_data + self.data = {} + + def tendb_remote_slave_recover(self): + """ + tendb cluster remote slave recover + """ + tendb_migrate_pipeline_all = Builder(root_id=self.root_id, data=copy.deepcopy(self.ticket_data)) + tendb_migrate_pipeline_all_list = [] + # 阶段1 获取集群所有信息。计算端口,构建数据。 + for info in self.ticket_data["infos"]: + self.data = copy.deepcopy(info) + self.data["bk_cloud_id"] = self.ticket_data["bk_cloud_id"] + self.data["root_id"] = self.root_id + self.data["start_port"] = 20000 + self.data["uid"] = self.ticket_data["uid"] + self.data["ticket_type"] = self.ticket_data["ticket_type"] + self.data["bk_biz_id"] = self.ticket_data["bk_biz_id"] + self.data["created_by"] = self.ticket_data["created_by"] + # self.data["module"] = info["module"] + self.data["source_ip"] = self.data["source_slave"]["ip"] + self.data["target_ip"] = self.data["target_slave"]["ip"] + # 卸载流程时强制卸载 + self.data["force"] = True + # 先判断备份是否存在 + backup_handler = FixPointRollbackHandler(self.data["cluster_id"]) + restore_time = datetime.now() + # restore_time = datetime.strptime("2023-07-31 17:40:00", "%Y-%m-%d %H:%M:%S") + backup_info = backup_handler.query_latest_backup_log(restore_time) + if backup_info is None: + logger.error("cluster {} backup info not exists".format(self.data["cluster_id"])) + raise TendbGetBackupInfoFailedException(message=_("获取集群 {} 的备份信息失败".format(self.data["cluster_id"]))) + logger.debug(backup_info) + tendb_migrate_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(self.data)) + + cluster_info = get_slave_recover_info(self.data["cluster_id"], self.data["target_ip"]) + charset, db_version = get_version_and_charset( + bk_biz_id=cluster_info["bk_biz_id"], + db_module_id=cluster_info["db_module_id"], + cluster_type=cluster_info["cluster_type"], + ) + cluster_info["charset"] = charset + cluster_info["db_version"] = db_version + cluster_class = Cluster.objects.get(id=self.data["cluster_id"]) + + # 构造从节点恢复 + cluster_info["ports"] = [] + for shard_id, shard in cluster_info["my_shards"].items(): + slave = { + "ip": self.data["target_ip"], + "port": shard["port"], + "bk_cloud_id": self.data["bk_cloud_id"], + "instance": "{}{}{}".format(self.data["target_ip"], IP_PORT_DIVIDER, shard["port"]), + } + cluster_info["my_shards"][shard_id]["new_slave"] = slave + cluster_info["ports"].append(shard["port"]) + + install_sub_pipeline_list = [] + install_sub_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(self.data)) + install_sub_pipeline.add_sub_pipeline( + sub_flow=install_mysql_in_cluster_sub_flow( + uid=self.data["uid"], + root_id=self.root_id, + cluster=cluster_class, + new_mysql_list=[self.data["target_ip"]], + install_ports=cluster_info["ports"], + ) + ) + cluster = { + "new_slave_ip": self.data["target_ip"], + "cluster_id": cluster_info["cluster_id"], + "bk_cloud_id": cluster_info["bk_cloud_id"], + "bk_biz_id": cluster_info["bk_biz_id"], + "ports": cluster_info["ports"], + "version": cluster_info["cluster"]["major_version"], + } + install_sub_pipeline.add_act( + act_name=_("写入初始化实例的db_meta元信息"), + act_component_code=SpiderDBMetaComponent.code, + kwargs=asdict( + DBMetaOPKwargs( + db_meta_class_func=SpiderDBMeta.tendb_slave_recover_add_nodes.__name__, + cluster=copy.deepcopy(cluster), + is_update_trans_data=False, + ) + ), + ) + + install_sub_pipeline.add_act( + act_name=_("安装backup-client工具"), + act_component_code=DownloadBackupClientComponent.code, + kwargs=asdict( + DownloadBackupClientKwargs( + bk_cloud_id=cluster_class.bk_cloud_id, + download_host_list=[cluster["new_master_ip"], cluster["new_slave_ip"]], + ) + ), + ) + + exec_act_kwargs = ExecActuatorKwargs( + cluster=cluster, + bk_cloud_id=cluster_class.bk_cloud_id, + cluster_type=cluster_class.cluster_type, + get_mysql_payload_func=MysqlActPayload.get_install_tmp_db_backup_payload.__name__, + ) + exec_act_kwargs.exec_ip = [cluster["new_master_ip"], cluster["new_slave_ip"]] + install_sub_pipeline.add_act( + act_name=_("安装临时备份程序"), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(exec_act_kwargs), + ) + + install_sub_pipeline_list.append(install_sub_pipeline.build_sub_process(sub_name=_("安装remote从节点"))) + sync_data_sub_pipeline_list = [] + for shard_id, node in cluster_info["my_shards"].items(): + ins_cluster = copy.deepcopy(cluster_info["cluster"]) + ins_cluster["charset"] = cluster_info["charset"] + ins_cluster["new_slave_ip"] = node["new_slave"]["ip"] + ins_cluster["new_slave_port"] = node["new_slave"]["port"] + ins_cluster["master_ip"] = node["master"]["ip"] + ins_cluster["slave_ip"] = node["slave"]["ip"] + ins_cluster["master_port"] = node["master"]["port"] + ins_cluster["slave_port"] = node["slave"]["port"] + # todo 正式环境放开file_target_path,需要备份接口支持自动创建目录 + # ins_cluster["file_target_path"] = "/data/dbbak/{}/{}"\ + # .format(self.root_id, ins_cluster["new_master_port"]) + ins_cluster["file_target_path"] = "/home/mysql/install" + ins_cluster["shard_id"] = shard_id + ins_cluster["change_master_force"] = False + + ins_cluster["backupinfo"] = backup_info["remote_node"].get(shard_id, {}) + # 判断 remote_node 下每个分片的备份信息是否正常 + if ( + len(ins_cluster["backupinfo"]) == 0 + or len(ins_cluster["backupinfo"].get("file_list_details", {})) == 0 + ): + logger.error( + "cluster {} shard {} backup info not exists".format(self.data["cluster_id"], shard_id) + ) + raise TendbGetBackupInfoFailedException( + message=_("获取集群分片 {} shard {} 的备份信息失败".format(self.data["cluster_id"], shard_id)) + ) + sync_data_sub_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(self.data)) + sync_data_sub_pipeline.add_sub_pipeline( + sub_flow=remote_slave_recover_sub_flow( + root_id=self.root_id, ticket_data=copy.deepcopy(self.data), cluster_info=ins_cluster + ) + ) + sync_data_sub_pipeline.add_act( + act_name=_("同步数据完毕,写入数据节点tuple相关元数据"), + act_component_code=SpiderDBMetaComponent.code, + kwargs=asdict( + DBMetaOPKwargs( + db_meta_class_func=SpiderDBMeta.tendb_slave_recover_add_tuple.__name__, + cluster=ins_cluster, + is_update_trans_data=True, + ) + ), + ) + sync_data_sub_pipeline_list.append(sync_data_sub_pipeline.build_sub_process(sub_name=_("恢复实例数据"))) + # 阶段4 切换 todo 等待从库切换接口 + switch_sub_pipeline_list = [] + # 切换后写入元数据 + switch_sub_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(self.data)) + switch_sub_pipeline.add_act( + act_name=_("SLAVE切换完毕后修改元数据指向"), + act_component_code=SpiderDBMetaComponent.code, + kwargs=asdict( + DBMetaOPKwargs( + db_meta_class_func=SpiderDBMeta.tendb_slave_recover_switch.__name__, + cluster=cluster_info, + is_update_trans_data=True, + ) + ), + ) + switch_sub_pipeline_list.append(switch_sub_pipeline.build_sub_process(sub_name=_("切换SLAVE节点"))) + + # 阶段5 安装实例周边组件 + surrounding_sub_pipeline_list = [] + surrounding_sub_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(self.data)) + surrounding_sub_pipeline.add_sub_pipeline( + sub_flow=build_surrounding_apps_sub_flow( + bk_cloud_id=cluster_class.bk_cloud_id, + master_ip_list=None, + slave_ip_list=[self.data["target_ip"]], + root_id=self.root_id, + parent_global_data=copy.deepcopy(self.data), + is_init=True, + cluster_type=ClusterType.TenDBCluster.value, + ) + ) + surrounding_sub_pipeline_list.append(surrounding_sub_pipeline.build_sub_process(sub_name=_("新机器安装周边组件"))) + + install_sub_pipeline.add_sub_pipeline( + sub_flow=build_surrounding_apps_sub_flow( + bk_cloud_id=cluster["bk_cloud_id"], + master_ip_list=None, + slave_ip_list=[self.data["target_ip"]], + root_id=self.root_id, + parent_global_data=copy.deepcopy(self.data), + is_init=True, + cluster_type=ClusterType.TenDBCluster.value, + ) + ) + + # 阶段6 卸载 + uninstall_svr_sub_pipeline_list = [] + uninstall_svr_sub_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(self.data)) + ins_cluster = {"uninstall_ip": self.data["target_ip"], "cluster_id": cluster_info["cluster_id"]} + uninstall_svr_sub_pipeline.add_sub_pipeline( + sub_flow=remote_node_uninstall_sub_flow( + root_id=self.root_id, ticket_data=copy.deepcopy(self.data), ip=self.data["target_ip"] + ) + ) + uninstall_svr_sub_pipeline.add_act( + act_name=_("整机卸载成功后删除元数据"), + act_component_code=SpiderDBMetaComponent.code, + kwargs=asdict( + DBMetaOPKwargs( + db_meta_class_func=SpiderDBMeta.remotedb_migrate_remove_storage.__name__, + cluster=ins_cluster, + is_update_trans_data=True, + ) + ), + ) + uninstall_svr_sub_pipeline.add_act( + act_name=_("清理机器配置"), + act_component_code=MySQLClearMachineComponent.code, + kwargs=asdict( + ClearMachineKwargs( + exec_ip=self.data["target_ip"], + bk_cloud_id=self.data["bk_cloud_id"], + ) + ), + ) + uninstall_svr_sub_pipeline_list.append( + uninstall_svr_sub_pipeline.build_sub_process(sub_name=_("卸载remote节点{}".format(self.data["target_ip"]))) + ) + # 安装实例 + tendb_migrate_pipeline.add_parallel_sub_pipeline(sub_flow_list=install_sub_pipeline_list) + # 数据同步 + tendb_migrate_pipeline.add_parallel_sub_pipeline(sub_flow_list=sync_data_sub_pipeline_list) + # 人工确认切换迁移实例 + tendb_migrate_pipeline.add_act(act_name=_("人工确认切换"), act_component_code=PauseComponent.code, kwargs={}) + # 切换迁移实例 + tendb_migrate_pipeline.add_parallel_sub_pipeline(sub_flow_list=switch_sub_pipeline_list) + # 安装周边组件 + tendb_migrate_pipeline.add_parallel_sub_pipeline(sub_flow_list=surrounding_sub_pipeline_list) + # 卸载流程人工确认 + tendb_migrate_pipeline.add_act(act_name=_("人工确认卸载实例"), act_component_code=PauseComponent.code, kwargs={}) + # 卸载remote节点 + tendb_migrate_pipeline.add_parallel_sub_pipeline(sub_flow_list=uninstall_svr_sub_pipeline_list) + tendb_migrate_pipeline_all_list.append( + tendb_migrate_pipeline.build_sub_process(_("集群迁移{}").format(self.data["cluster_id"])) + ) + # 运行流程 + tendb_migrate_pipeline_all.add_parallel_sub_pipeline(tendb_migrate_pipeline_all_list) + tendb_migrate_pipeline_all.run_pipeline(init_trans_data_class=ClusterInfoContext()) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_add_mnt.py b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_add_mnt.py new file mode 100644 index 0000000000..98f7d6082a --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_add_mnt.py @@ -0,0 +1,109 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +import copy +import logging.config +from dataclasses import asdict +from typing import Dict, Optional + +from django.utils.translation import ugettext as _ + +from backend.db_meta.enums import TenDBClusterSpiderRole +from backend.db_meta.exceptions import ClusterNotExistException +from backend.db_meta.models import Cluster +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.engine.bamboo.scene.spider.common.common_sub_flow import ( + add_spider_masters_sub_flow, + build_apps_for_spider_sub_flow, +) +from backend.flow.plugins.components.collections.spider.spider_db_meta import SpiderDBMetaComponent +from backend.flow.utils.mysql.mysql_act_dataclass import DBMetaOPKwargs +from backend.flow.utils.spider.spider_bk_config import get_spider_version_and_charset +from backend.flow.utils.spider.spider_db_meta import SpiderDBMeta + +logger = logging.getLogger("flow") + + +class TenDBClusterAddSpiderMNTFlow(object): + """ + tendb cluster添加运维节点 + """ + + def __init__(self, root_id: str, data: Optional[Dict]): + """ + @param root_id : 任务流程定义的root_id + @param data : 单据传递参数 + """ + self.root_id = root_id + self.data = data + + def add_spider_mnt(self): + """ + 上架spider节点,授予中控访问权限 + 加入路由 + """ + pipeline = Builder(root_id=self.root_id, data=self.data) + sub_pipelines = [] + for info in self.data["infos"]: + # 拼接子流程所需全局参数 + sub_flow_context = copy.deepcopy(self.data) + # 拷贝了字典类型的参数,并将键 infos 剔除 + sub_flow_context.pop("infos") + # 加入info,info中包含实例的私有信息 + sub_flow_context.update(info) + # 通过cluster_id获取对应集群对象 + try: + cluster = Cluster.objects.get(id=info["cluster_id"], bk_biz_id=int(self.data["bk_biz_id"])) + except Cluster.DoesNotExist: + raise ClusterNotExistException( + cluster_id=info["cluster_id"], bk_biz_id=int(self.data["bk_biz_id"]), message=_("集群不存在") + ) + # 通过bk—config获取版本号和字符集信息 + # 获取的是业务默认配置,不一定是集群当前配置 + spider_charset, spider_version = get_spider_version_and_charset( + bk_biz_id=cluster.bk_biz_id, db_module_id=cluster.db_module_id + ) + # 补充这次单据需要的隐形参数,spider版本以及字符集 + sub_flow_context["spider_charset"] = spider_charset + sub_flow_context["spider_version"] = spider_version + # 启动子流程 + sub_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(sub_flow_context)) + # 阶段1 根据场景执行添加spider-mnt子流程 + sub_pipeline.add_sub_pipeline( + sub_flow=add_spider_masters_sub_flow( + cluster=cluster, + add_spider_masters=sub_flow_context["spider_ip_list"], + root_id=self.root_id, + parent_global_data=sub_flow_context, + is_add_spider_mnt=True, + ) + ) + + # 阶段2 变更db_meta数据 + sub_pipeline.add_act( + act_name=_("更新DBMeta元信息"), + act_component_code=SpiderDBMetaComponent.code, + kwargs=asdict(DBMetaOPKwargs(db_meta_class_func=SpiderDBMeta.add_spider_mnt.__name__)), + ) + + # 阶段3 安装周边程序 + sub_pipeline.add_sub_pipeline( + sub_flow=build_apps_for_spider_sub_flow( + bk_cloud_id=cluster.bk_cloud_id, + spiders=[spider["ip"] for spider in sub_flow_context["spider_ip_list"]], + root_id=self.root_id, + parent_global_data=copy.deepcopy(sub_flow_context), + spider_role=TenDBClusterSpiderRole.SPIDER_MNT, + ) + ) + sub_pipelines.append(sub_pipeline.build_sub_process(sub_name=_("{}添加spider_mnt节点流程".format(cluster.name)))) + + pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) + pipeline.run_pipeline() diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_add_nodes.py b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_add_nodes.py index 9aa6f5cd70..51e4119843 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_add_nodes.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_add_nodes.py @@ -16,7 +16,8 @@ from django.utils.translation import ugettext as _ from backend.db_meta.enums import ClusterEntryRole, TenDBClusterSpiderRole -from backend.db_meta.models import Cluster +from backend.db_meta.exceptions import ClusterNotExistException +from backend.db_meta.models import Cluster, ClusterEntry from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder from backend.flow.engine.bamboo.scene.spider.common.common_sub_flow import ( add_spider_masters_sub_flow, @@ -58,17 +59,22 @@ def add_spider_nodes(self): sub_flow_context = copy.deepcopy(self.data) sub_flow_context.pop("infos") - # 拼接子流程的全局参数 - sub_flow_context.update(info) - # 获取对应集群相关对象 - cluster = Cluster.objects.get(id=info["cluster_id"]) + try: + cluster = Cluster.objects.get(id=info["cluster_id"], bk_biz_id=int(self.data["bk_biz_id"])) + except Cluster.DoesNotExist: + raise ClusterNotExistException( + cluster_id=info["cluster_id"], bk_biz_id=int(self.data["bk_biz_id"]), message=_("集群不存在") + ) # 根据集群去bk-config获取对应spider版本和字符集 spider_charset, spider_version = get_spider_version_and_charset( bk_biz_id=cluster.bk_biz_id, db_module_id=cluster.db_module_id ) + # 拼接子流程的全局参数 + sub_flow_context.update(info) + # 补充这次单据需要的隐形参数,spider版本以及字符集 sub_flow_context["spider_charset"] = spider_charset sub_flow_context["spider_version"] = spider_version @@ -76,25 +82,28 @@ def add_spider_nodes(self): if info["add_spider_role"] == TenDBClusterSpiderRole.SPIDER_MASTER: # 加入spider-master 子流程 + sub_flow_context["ctl_charset"] = spider_charset sub_pipelines.append(self.add_spider_master_notes(sub_flow_context, cluster)) elif info["add_spider_role"] == TenDBClusterSpiderRole.SPIDER_SLAVE: - # 先判断集群是否存在已添加从集群,如果没有则跳过这次扩容,判断依据是集群是存在有且只有一个的从域名 - cluster = Cluster.objects.get(id=info["cluster_id"]) - slave_dns = cluster.clusterentry_set.get(role=ClusterEntryRole.SLAVE_ENTRY) - if not slave_dns: + try: + # 先判断集群是否存在已添加从集群,如果没有则跳过这次扩容,判断依据是集群是存在有且只有一个的从域名 + slave_dns = cluster.clusterentry_set.get(role=ClusterEntryRole.SLAVE_ENTRY) + # 加入spider-slave 子流程 + sub_pipelines.append(self.add_spider_slave_notes(sub_flow_context, cluster, slave_dns.entry)) + + except ClusterEntry.DoesNotExist: logger.warning(_("[{}]The cluster has not added a slave cluster, skip".format(cluster.name))) continue - # 加入spider-slave 子流程 - sub_pipelines.append(self.add_spider_slave_notes(sub_flow_context, cluster, slave_dns.entry)) - else: # 理论上不会出现,出现就中断这次流程构造 raise NormalSpiderFlowException( message=_("[{}]This type of role addition is not supported".format(info["add_spider_role"])) ) + if not sub_pipelines: + raise NormalSpiderFlowException(message=_("build spider-add-nodes-pipeline failed")) pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) pipeline.run_pipeline() @@ -103,7 +112,6 @@ def add_spider_master_notes(self, sub_flow_context: dict, cluster: Cluster): """ 定义spider master集群部署子流程 目前产品形态 spider专属一套集群,所以流程只支持spider单机单实例安装 - todo 目前spider-master扩容功能中,当前中控版本需要调整,等最新版本做联调工作 """ # 启动子流程 diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_add_tmp_node.py b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_add_tmp_node.py deleted file mode 100644 index ee6f97f922..0000000000 --- a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_add_tmp_node.py +++ /dev/null @@ -1,157 +0,0 @@ -from dataclasses import asdict -from typing import Dict, Optional - -from django.utils.translation import ugettext as _ - -from backend.configuration.constants import DBType -from backend.db_meta.enums import ClusterType -from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder -from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList -from backend.flow.plugins.components.collections.mysql.exec_actuator_script import ExecuteDBActuatorScriptComponent -from backend.flow.plugins.components.collections.mysql.trans_flies import TransFileComponent -from backend.flow.plugins.components.collections.spider.add_system_user_in_cluster import ( - AddSystemUserInClusterComponent, -) -from backend.flow.utils.mysql.mysql_act_dataclass import ( - AddSpiderSystemUserKwargs, - DownloadMediaKwargs, - ExecActuatorKwargs, -) -from backend.flow.utils.mysql.mysql_act_playload import MysqlActPayload - -# from backend.flow.utils.mysql.mysql_context_dataclass import SpiderAddTmpNodeManualContext - - -class SpiderAddTmpNodeFlow(object): - """ - TODO 通过域名查找主库ip、端口、集群中spider数量 - TODO - """ - - def __init__(self, root_id: str, data: Optional[Dict]): - self.root_id = root_id - self.data = data - - def __get_dbctl_master_info(self): - """ - 根据域名获取中控主节点信息 - """ - # self.data["immutable_domain"] - dbctl_master = {"ip": "127.0.0.1", "port": 26000} - return dbctl_master - - def __get_spider_node_num(self): - return 10 - - def __get_spider_instance_info(self): - info = { - "spider_instances": [], - } - for ip_info in self.data["spider_ip_list"]: - info["spider_instances"].append({"host": ip_info["ip"], "port": self.data["spider_port"]}) - return info - - def spider_add_tmp_node_with_resource_pool(self): - """ - 机器通过资源池获取 - """ - pass - - def spider_add_tmp_node_with_manual_input(self): - """ - 手动输入ip - 上架spider节点,授予中控访问权限 - 需要根据域名查找到中控主节点,并加入路由 - """ - - pipeline = Builder(root_id=self.root_id, data=self.data) - # 子流程 - deploy_pipeline = SubBuilder(root_id=self.root_id, data=self.data) - - # 拼接执行原子任务活动节点需要的通用的私有参数结构体 - exec_act_kwargs = ExecActuatorKwargs( - bk_cloud_id=int(self.data["bk_cloud_id"]), - cluster_type=ClusterType.TenDBCluster, - ) - - # 1 分发安装文件 - deploy_pipeline.add_act( - act_name=_("下发Spider/tdbCtl介质包"), - act_component_code=TransFileComponent.code, - kwargs=asdict( - DownloadMediaKwargs( - bk_cloud_id=int(self.data["bk_cloud_id"]), - exec_ip=[ip_info["ip"] for ip_info in self.data["spider_ip_list"]], - file_list=GetFileList(db_type=DBType.MySQL).spider_master_install_package( - spider_version=self.data["spider_version"], - ), - ) - ), - ) - - # 2 初始化机器,安装crond进程 - # 获取spider ip - exec_act_kwargs.exec_ip = [ip_info["ip"] for ip_info in self.data["spider_ip_list"]] - exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_sys_init_payload.__name__ - # 此处会下发act命令,实际是调用act去执行机器初始化 - # ExecuteDBActuatorScriptComponent用来构成act命令,并通过job下发 - deploy_pipeline.add_act( - act_name=_("初始化机器"), - act_component_code=ExecuteDBActuatorScriptComponent.code, - kwargs=asdict(exec_act_kwargs), - ) - - acts_list = [] - for ip_info in self.data["spider_ip_list"]: - exec_act_kwargs.exec_ip = ip_info["ip"] - exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_deploy_mysql_crond_payload.__name__ - acts_list.append( - { - "act_name": _("部署mysql-crond"), - "act_component_code": ExecuteDBActuatorScriptComponent.code, - "kwargs": asdict(exec_act_kwargs), - } - ) - deploy_pipeline.add_parallel_acts(acts_list=acts_list) - - # 3 并发安装spider - acts_list = [] - auto_incr_value = self.__get_spider_node_num() + 1 - for spider_ip in self.data["spider_ip_list"]: - exec_act_kwargs.exec_ip = spider_ip["ip"] - exec_act_kwargs.cluster = { - "immutable_domain": self.data["immutable_domain"], - "auto_incr_value": auto_incr_value, - } - exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_install_spider_payload.__name__ - acts_list.append( - { - "act_name": _("安装Spider实例"), - "act_component_code": ExecuteDBActuatorScriptComponent.code, - "kwargs": asdict(exec_act_kwargs), - } - ) - auto_incr_value += 1 - deploy_pipeline.add_parallel_acts(acts_list=acts_list) - - # 4 spider上授权给中控 - dbctl_master_info = self.__get_dbctl_master_info() - deploy_pipeline.add_act( - act_name=_("spider上对中控主节点进行授权"), - act_component_code=AddSystemUserInClusterComponent.code, - kwargs=asdict(AddSpiderSystemUserKwargs(ctl_master_ip=dbctl_master_info["ip"])), - ) - - # 5 在中控主节点上生成临时spider节点的路由信息tdbctl create node ... with database; - exec_act_kwargs.cluster = self.__get_spider_instance_info() - exec_act_kwargs.exec_ip = dbctl_master_info - exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_add_tmp_spider_node_payload.__name__ - deploy_pipeline.add_act( - act_name=_("中控主节点注册临时spider节点路由信息"), - act_component_code=ExecuteDBActuatorScriptComponent.code, - kwargs=asdict(exec_act_kwargs), - ) - pipeline.add_sub_pipeline( - sub_flow=deploy_pipeline.build_sub_process(sub_name=_("{}添加临时spider节点".format(self.data["cluster_name"]))) - ) - pipeline.run_pipeline() diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_checksum.py b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_checksum.py index 386daae9b6..2d39347def 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_checksum.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_checksum.py @@ -50,7 +50,7 @@ class SpiderChecksumFlow(object): "root_id": 123, "created_by": "admin", "bk_biz_id": 9991001, - "ticket_type": "SPIDER_CHECKSUM", + "ticket_type": "TENDBCLUSTER_CHECKSUM", "timing": "2022-11-21 12:04:10", "is_sync_non_innodb": true, "runtime_hour": 48, diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_db_table_backup.py b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_db_table_backup.py index a058216e89..bb8bdd67ff 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_db_table_backup.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_db_table_backup.py @@ -20,7 +20,7 @@ from backend.configuration.constants import DBType from backend.constants import IP_PORT_DIVIDER -from backend.db_meta.enums import ClusterType, InstanceInnerRole +from backend.db_meta.enums import ClusterType, InstanceInnerRole, TenDBClusterSpiderRole from backend.db_meta.exceptions import ClusterNotExistException, DBMetaBaseException from backend.db_meta.models import Cluster, StorageInstanceTuple from backend.flow.consts import DBA_SYSTEM_USER @@ -57,7 +57,7 @@ def backup_flow(self): "uid": "2022051612120001", "created_by": "xxx", "bk_biz_id": "152", - "ticket_type": "SPIDER_DB_TABLE_BACKUP", + "ticket_type": "TENDBCLUSTER_DB_TABLE_BACKUP", "infos": [ { "cluster_id": int, @@ -153,6 +153,7 @@ def backup_on_spider_ctl(self, backup_id: uuid.UUID, job: dict, cluster_obj: Clu "backup_type": "logical", "backup_gsd": ["schema"], "custom_backup_dir": "backupDatabaseTable", + "role": TenDBClusterSpiderRole.SPIDER_MASTER, }, ) @@ -193,18 +194,6 @@ def backup_on_spider_ctl(self, backup_id: uuid.UUID, job: dict, cluster_obj: Clu ), ) - on_ctl_sub_pipe.add_act( - act_name=_("ctl 执行库表备份"), - act_component_code=ExecuteDBActuatorScriptComponent.code, - kwargs=asdict( - ExecActuatorKwargs( - bk_cloud_id=cluster_obj.bk_cloud_id, - run_as_system_user=DBA_SYSTEM_USER, - exec_ip=ctl_primary_ip, - get_mysql_payload_func=MysqlActPayload.mysql_backup_demand_payload_on_ctl.__name__, - ) - ), - ) return on_ctl_sub_pipe.build_sub_process(sub_name=_("spider/ctl备份库表结构")) def backup_on_remote(self, backup_id: uuid.UUID, job: dict, cluster_obj: Cluster) -> List[SubProcess]: @@ -220,18 +209,22 @@ def backup_on_remote(self, backup_id: uuid.UUID, job: dict, cluster_obj: Cluster receiver__is_stand_by=True, ): stand_by_slaves[tp.receiver.machine.ip].append( - {"port": tp.receiver.port, "shard_id": tp.tendbclusterstorageset.shard_id} + { + "port": tp.receiver.port, + "shard_id": tp.tendbclusterstorageset.shard_id, + "role": tp.receiver.instance_role, + } ) for ip, dtls in stand_by_slaves.items(): for dtl in dtls: on_slave_job = copy.deepcopy(job) on_slave_job["db_patterns"] = [ - ele if ele.endswith("%") else "{}_{}".format(ele, dtl["shard_id"]) + ele if ele.endswith("%") or ele == "*" else "{}_{}".format(ele, dtl["shard_id"]) for ele in on_slave_job["db_patterns"] ] on_slave_job["ignore_dbs"] = [ - ele if ele.endswith("%") else "{}_{}".format(ele, dtl["shard_id"]) + ele if ele.endswith("%") or ele == "*" else "{}_{}".format(ele, dtl["shard_id"]) for ele in on_slave_job["ignore_dbs"] ] @@ -249,6 +242,8 @@ def backup_on_remote(self, backup_id: uuid.UUID, job: dict, cluster_obj: Cluster "backup_type": "logical", "backup_gsd": ["schema", "data"], "custom_backup_dir": "backupDatabaseTable", + "role": dtl["role"], + "shard_id": dtl["shard_id"], }, ) @@ -304,6 +299,7 @@ def backup_on_spider_mnt(self, backup_id: uuid.UUID, job: dict, cluster_obj: Clu "backup_type": "logical", "backup_gsd": ["schema", "data"], "custom_backup_dir": "backupDatabaseTable", + "role": TenDBClusterSpiderRole.SPIDER_MNT, }, ) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_deploy.py b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_deploy.py index 78f4dd6152..a8389e16a1 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_deploy.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_deploy.py @@ -17,6 +17,7 @@ from django.utils.translation import ugettext as _ from backend.configuration.constants import DBType +from backend.constants import IP_PORT_DIVIDER from backend.db_meta.enums import ClusterType, TenDBClusterSpiderRole from backend.flow.consts import TDBCTL_USER from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder @@ -25,7 +26,10 @@ build_repl_by_manual_input_sub_flow, build_surrounding_apps_sub_flow, ) -from backend.flow.engine.bamboo.scene.spider.common.common_sub_flow import build_apps_for_spider_sub_flow +from backend.flow.engine.bamboo.scene.spider.common.common_sub_flow import ( + build_apps_for_spider_sub_flow, + build_ctl_replication_with_gtid, +) from backend.flow.plugins.components.collections.mysql.dns_manage import MySQLDnsManageComponent from backend.flow.plugins.components.collections.mysql.exec_actuator_script import ExecuteDBActuatorScriptComponent from backend.flow.plugins.components.collections.mysql.trans_flies import TransFileComponent @@ -74,14 +78,24 @@ def __init__(self, root_id: str, data: Optional[Dict]): # 集群所有组件统一字符集配置 self.data["ctl_charset"] = self.data["spider_charset"] = self.data["charset"] - # 一个单据自动生成同一份随机密码, 中控实例需要,不需要内部来维护 + # 一个单据自动生成同一份随机密码, 中控实例需要,不需要内部来维护,每次部署随机生成一次 self.tdbctl_pass = get_random_string(length=10) # 声明中控实例的端口 self.data["ctl_port"] = self.data["spider_port"] + 1000 - if len(self.data["mysql_ip_list"]) % 2 != 0: - raise Exception(_("存入的存储节点数量不是偶数,请检查!")) + # 是否升级成tokudb引擎 + if not self.data.__contains__("enable_tokudb"): + self.data["enable_tokudb"] = False + + if len(self.data["remote_group"]) * int(self.data["remote_shard_num"]) != int(self.data["cluster_shard_num"]): + raise Exception(_("传入参数有异常,请检查!len(remote_group)*remote_shard_num != cluster_shard_num")) + + # 获取所有的remote ip + self.data["mysql_ip_list"] = [] + for i in self.data["remote_group"]: + self.data["mysql_ip_list"].append(i["master"]) + self.data["mysql_ip_list"].append(i["slave"]) def __calc_install_ports(self, inst_sum: int) -> list: """ @@ -103,29 +117,21 @@ def __calc_install_ports(self, inst_sum: int) -> list: def __assign_shard_master_slave(self, install_port_list: list) -> Optional[List[ShardInfo]]: """ 根据需求场景,为集群每个分片组分配合适的主从机器 - todo 后续需要在自动分配时保持分片组的主从机器的反亲和性 - @param + 资源池获取的资源保持分片组的主从机器的具有反亲和性 + @param install_port_list: 单机部署的端口列表 """ shard_cluster_list = [] - master_ip = "" - slave_ip = "" - cycles = 0 - mysql_ip_list = copy.deepcopy(self.data["mysql_ip_list"]) - for key in range(1, self.data["cluster_shard_num"] + 1): - - if len(install_port_list) * cycles < key: - master_ip = mysql_ip_list.pop(0)["ip"] - slave_ip = mysql_ip_list.pop(0)["ip"] - cycles += 1 - - inst_tuple = InstanceTuple( - master_ip=master_ip, - slave_ip=slave_ip, - mysql_port=install_port_list[(key - 1) % len(install_port_list)], - ) - shard_info = ShardInfo(shard_key=key - 1, instance_tuple=inst_tuple) - shard_cluster_list.append(shard_info) - + start_index = 0 + for remote_tuple in self.data["remote_group"]: + for index, mysql_port in enumerate(install_port_list): + inst_tuple = InstanceTuple( + master_ip=remote_tuple["master"]["ip"], + slave_ip=remote_tuple["slave"]["ip"], + mysql_port=mysql_port, + ) + shard_info = ShardInfo(shard_key=index + start_index, instance_tuple=inst_tuple) + shard_cluster_list.append(shard_info) + start_index += len(install_port_list) return shard_cluster_list def __create_cluster_nodes_info(self, shard_infos: Optional[List[ShardInfo]]) -> dict: @@ -158,15 +164,11 @@ def deploy_cluster(self): """ 机器通过手动输入IP而触发的场景 todo 集群所有节点的时区是否需要对比?如果要对比,怎么对比 - todo 补充周边组件部署 todo 目前bamboo-engine存在bug,不能正常给trans_data初始化值,先用流程套子流程方式来避开这个问题 """ - # 根据集群总分片数、存储节点数量计算出每个节点需要部署的实例的数量, - # 比如总分片数是4,存储节点是4,那么每个存储需要部署2个实例(考虑主从) - inst_sum = int(self.data["cluster_shard_num"] / int((len(self.data["mysql_ip_list"]) / 2))) # 计算每个mysql机器需要部署的mysql端口信息 - self.data["mysql_ports"] = self.__calc_install_ports(inst_sum=inst_sum) + self.data["mysql_ports"] = self.__calc_install_ports(inst_sum=int(self.data["remote_shard_num"])) # 先确定谁是中控集群中谁是master,对后续做数据同步依赖和初始化集群路由信息依赖 ctl_master = self.data["spider_ip_list"][0] @@ -253,6 +255,21 @@ def deploy_cluster(self): ) deploy_pipeline.add_parallel_acts(acts_list=acts_list) + # 给安装好的mysql实例开启tokudb引擎 + if self.data["enable_tokudb"]: + acts_list = [] + for mysql_ip in self.data["mysql_ip_list"]: + exec_act_kwargs.exec_ip = mysql_ip["ip"] + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.enable_tokudb_payload.__name__ + acts_list.append( + { + "act_name": _("安装tokudb引擎"), + "act_component_code": ExecuteDBActuatorScriptComponent.code, + "kwargs": asdict(exec_act_kwargs), + } + ) + deploy_pipeline.add_parallel_acts(acts_list=acts_list) + acts_list = [] # 定义每个spider节点auto_incr_mode_value值,单调递增 auto_incr_value = 1 @@ -300,7 +317,8 @@ def deploy_cluster(self): parent_global_data=self.data, master_ip=info.instance_tuple.master_ip, slave_ip=info.instance_tuple.slave_ip, - mysql_port=info.instance_tuple.mysql_port, + master_port=info.instance_tuple.mysql_port, + slave_port=info.instance_tuple.mysql_port, sub_flow_name=f"Shard{info.shard_key}", ) ) @@ -308,7 +326,13 @@ def deploy_cluster(self): # 阶段5 构建spider中控集群 deploy_pipeline.add_sub_pipeline( - sub_flow=self.build_ctl_replication_with_gtid(ctl_master=ctl_master, ctl_slaves=ctl_slaves) + sub_flow=build_ctl_replication_with_gtid( + root_id=self.root_id, + parent_global_data=self.data, + bk_cloud_id=int(self.data["bk_cloud_id"]), + ctl_primary=f"{ctl_master['ip']}{IP_PORT_DIVIDER}{self.data['ctl_port']}", + ctl_secondary_list=ctl_slaves, + ) ) # 阶段6 内部集群节点之间授权 @@ -383,47 +407,3 @@ def deploy_cluster(self): sub_flow=deploy_pipeline.build_sub_process(sub_name=_("{}集群部署").format(self.data["cluster_name"])) ) pipeline.run_pipeline(init_trans_data_class=SpiderApplyManualContext()) - - def build_ctl_replication_with_gtid(self, ctl_master: dict, ctl_slaves: list): - """ - 定义ctl建立基于gtid的主从同步 - """ - - cluster = { - "mysql_port": self.data["ctl_port"], - "master_ip": ctl_master["ip"], - "slaves": [ip_info["ip"] for ip_info in ctl_slaves], - } - - sub_pipeline = SubBuilder(root_id=self.root_id, data=self.data) - sub_pipeline.add_act( - act_name=_("新增repl帐户"), - act_component_code=ExecuteDBActuatorScriptComponent.code, - kwargs=asdict( - ExecActuatorKwargs( - bk_cloud_id=self.data["bk_cloud_id"], - exec_ip=ctl_master["ip"], - get_mysql_payload_func=MysqlActPayload.get_grant_repl_for_ctl_payload.__name__, - cluster=cluster, - ) - ), - ) - acts_list = [] - for slave in ctl_slaves: - acts_list.append( - { - "act_name": _("建立主从关系"), - "act_component_code": ExecuteDBActuatorScriptComponent.code, - "kwargs": asdict( - ExecActuatorKwargs( - bk_cloud_id=self.data["bk_cloud_id"], - exec_ip=slave["ip"], - get_mysql_payload_func=MysqlActPayload.get_change_master_for_gitd_payload.__name__, - cluster=cluster, - ) - ), - } - ) - - sub_pipeline.add_parallel_acts(acts_list=acts_list) - return sub_pipeline.build_sub_process(sub_name=_("部署spider-ctl集群")) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_destroy.py b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_destroy.py index f994d0a31b..52961eb2e1 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_destroy.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_destroy.py @@ -15,6 +15,7 @@ from backend.configuration.constants import DBType from backend.db_meta.enums import TenDBClusterSpiderRole +from backend.db_meta.exceptions import ClusterNotExistException from backend.db_meta.models import Cluster from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList @@ -50,12 +51,17 @@ def __init__(self, root_id: str, data: Optional[Dict]): self.data = data @staticmethod - def __get_cluster_info(cluster_id: int) -> dict: + def __get_cluster_info(cluster_id: int, bk_biz_id: int) -> dict: """ 根据cluster_id 获取到单节点集群实例信息 @param cluster_id: 需要下架的集群id + @param bk_biz_id: 需要下架集群对应的业务id """ - cluster = Cluster.objects.get(id=cluster_id) + try: + cluster = Cluster.objects.get(id=cluster_id, bk_biz_id=bk_biz_id) + except Cluster.DoesNotExist: + raise ClusterNotExistException(cluster_id=cluster_id, bk_biz_id=bk_biz_id, message=_("集群不存在")) + remote_objs = cluster.storageinstance_set.all() spider_objs = cluster.proxyinstance_set.all() ctl_objs = cluster.proxyinstance_set.filter( @@ -68,8 +74,8 @@ def __get_cluster_info(cluster_id: int) -> dict: "name": cluster.name, "remote_objs": remote_objs, "spider_port": spider_objs[0].port, - "remote_ip_list": [s.machine.ip for s in remote_objs], - "spider_ip_list": [s.machine.ip for s in spider_objs], + "remote_ip_list": list(set([s.machine.ip for s in remote_objs])), + "spider_ip_list": list(set([s.machine.ip for s in spider_objs])), "spider_ctl_port": ctl_objs[0].admin_port, "spider_ctl_ip_list": [c.machine.ip for c in ctl_objs], } @@ -85,7 +91,7 @@ def destroy_cluster(self): for cluster_id in self.data["cluster_ids"]: # 获取集群的实例信息 - cluster = self.__get_cluster_info(cluster_id=cluster_id) + cluster = self.__get_cluster_info(cluster_id=cluster_id, bk_biz_id=int(self.data["bk_biz_id"])) sub_pipeline = SubBuilder(root_id=self.root_id, data=self.data) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_disable_deploy.py b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_disable_deploy.py index cb5f1a4c9e..2c4c11c926 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_disable_deploy.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_disable_deploy.py @@ -8,15 +8,16 @@ 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. """ -import logging.config +import copy from dataclasses import asdict -from typing import Dict, Optional +from typing import Optional from django.utils.translation import ugettext as _ from backend.configuration.constants import DBType -from backend.db_meta.enums import ClusterType, MachineType -from backend.db_meta.models import Cluster, Machine, ProxyInstance +from backend.db_meta.enums import ClusterEntryRole, ClusterEntryType, ClusterType +from backend.db_meta.exceptions import ClusterNotExistException +from backend.db_meta.models import Cluster, ClusterEntry from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList from backend.flow.plugins.components.collections.mysql.dns_manage import MySQLDnsManageComponent @@ -38,66 +39,81 @@ def __init__(self, root_id: str, data: Optional[dict]): self.root_id = root_id self.data = data - @staticmethod - def __get_spider_cluster_info(cluster_id: int) -> dict: + def __get_tendb_cluster_info(self, cluster_id: int, is_only_delete_slave_domain: bool): """ - 根据cluster_id获取集群信息 + 获取集群信息,主要获取代理层ip信息spider_ip_list spider_port """ - cluster = Cluster.objects.get(id=cluster_id) - # 需要咨询一下spider相关的表与字段 - # spider_info = SpiderInstance.objects.filter(cluster=cluster).all() - # return { - # "id": cluster_id, - # "bk_cloud_id": cluster.bk_cloud_id, - # "name": cluster.name, - # "spider_port": spider_info[0].port, - # "spider_ip_list": [s.machine.ip for s in spider_info] - # } - return { + try: + cluster = Cluster.objects.get(id=cluster_id, bk_biz_id=int(self.data["bk_biz_id"])) + except Cluster.DoesNotExist: + raise ClusterNotExistException( + cluster_id=cluster_id, bk_biz_id=int(self.data["bk_biz_id"]), message=_("集群不存在") + ) + + if is_only_delete_slave_domain: + entry = ClusterEntry.objects.filter( + cluster=cluster, cluster_entry_type=ClusterEntryType.DNS.value, role=ClusterEntryRole.SLAVE_ENTRY.value + ).all() + instance_list = entry[0].proxyinstance_set.all() + spider_port = instance_list[0].port + spider_ip_list = [instance.machine.ip for instance in instance_list] + else: + # 从域名端口是否和主域名是一致的? + spider_port = cluster.proxyinstance_set.first().port + spider_ip_list = [instance.machine.ip for instance in cluster.proxyinstance_set.all()] + + cluster_info = { "id": cluster_id, "bk_cloud_id": cluster.bk_cloud_id, "name": cluster.name, - "spider_port": cluster.proxyinstance_set.first().port, - "spider_ip_list": [ele.machine.ip for ele in cluster.proxyinstance_set.all()], + "spider_port": spider_port, + "spider_ip_list": spider_ip_list, } + return cluster_info def disable_spider_cluster_flow(self): """ 定义spider集群禁用流程 """ - spider_cluster_disable_pipleline = Builder(root_id=self.root_id, data=self.data) + pipeline = Builder(root_id=self.root_id, data=self.data) sub_pipelines = [] # 多集群禁用时,循环加入禁用子流程 for cluster_id in self.data["cluster_ids"]: - # 获取集群实例信息 - cluster = self.__get_spider_cluster_info(cluster_id=cluster_id) - sub_pipeline = SubBuilder(root_id=self.root_id, data=self.data) + sub_flow_context = copy.deepcopy(self.data) + sub_flow_context.pop("cluster_ids") + cluster_info = self.__get_tendb_cluster_info(cluster_id, self.data["is_only_delete_slave_domain"]) + sub_flow_context.update(cluster_info) + # 开始流程编排 + sub_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(sub_flow_context)) sub_pipeline.add_act( act_name=_("删除集群域名"), act_component_code=MySQLDnsManageComponent.code, kwargs=asdict( DeleteClusterDnsKwargs( - bk_cloud_id=cluster["bk_cloud_id"], + bk_cloud_id=cluster_info["bk_cloud_id"], delete_cluster_id=cluster_id, + is_only_delete_slave_domain=self.data["is_only_delete_slave_domain"], ), ), ) + # 传入需要下发的ip列表 sub_pipeline.add_act( act_name=_("下发db-actuator介质"), act_component_code=TransFileComponent.code, kwargs=asdict( DownloadMediaKwargs( - bk_cloud_id=cluster["bk_cloud_id"], - exec_ip=cluster["spider_ip_list"], + bk_cloud_id=cluster_info["bk_cloud_id"], + exec_ip=cluster_info["spider_ip_list"], file_list=GetFileList(db_type=DBType.MySQL).get_db_actuator_package(), ) ), ) + # 以目标ip为单位,分别下发命令,执行重启spider命令 acts_list = [] - for spider_ip in cluster["spider_ip_list"]: + for spider_ip in cluster_info["spider_ip_list"]: acts_list.append( { "act_name": _("重启spider实例"), @@ -106,29 +122,28 @@ def disable_spider_cluster_flow(self): ExecActuatorKwargs( exec_ip=spider_ip, cluster_type=ClusterType.TenDBCluster, - bk_cloud_id=cluster["bk_cloud_id"], - cluster=cluster, + bk_cloud_id=cluster_info["bk_cloud_id"], + cluster=cluster_info, get_mysql_payload_func=MysqlActPayload.get_restart_spider_payload.__name__, ) ), } ) sub_pipeline.add_parallel_acts(acts_list=acts_list) - - sub_pipeline.add_act( - act_name=_("集群变更OFFLINE状态"), - act_component_code=MySQLDBMetaComponent.code, - kwargs=asdict( - DBMetaOPKwargs( - db_meta_class_func=MySQLDBMeta.mysql_cluster_offline.__name__, - cluster=cluster, - ) - ), - ) - + if not self.data["is_only_delete_slave_domain"]: + sub_pipeline.add_act( + act_name=_("集群变更OFFLINE状态"), + act_component_code=MySQLDBMetaComponent.code, + kwargs=asdict( + DBMetaOPKwargs( + db_meta_class_func=MySQLDBMeta.mysql_cluster_offline.__name__, + cluster=cluster_info, + ) + ), + ) sub_pipelines.append( - sub_pipeline.build_sub_process(sub_name=_("禁用MySQL高可用集群[{}]").format(cluster["name"])) + sub_pipeline.build_sub_process(sub_name=_("禁用MySQL高可用集群[{}]").format(cluster_info["name"])) ) - spider_cluster_disable_pipleline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) - spider_cluster_disable_pipleline.run_pipeline() + pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) + pipeline.run_pipeline() diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_enable_deploy.py b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_enable_deploy.py index 75cedac889..a71700a6a8 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_enable_deploy.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_enable_deploy.py @@ -8,14 +8,15 @@ 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. """ -import logging.config +import copy from dataclasses import asdict from typing import Optional from django.utils.translation import ugettext as _ -from backend.db_meta.enums import ClusterEntryType, InstanceInnerRole -from backend.db_meta.models import Cluster, StorageInstance +from backend.db_meta.enums import ClusterEntryRole +from backend.db_meta.exceptions import ClusterNotExistException +from backend.db_meta.models import Cluster, ClusterEntry from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder from backend.flow.plugins.components.collections.mysql.dns_manage import MySQLDnsManageComponent from backend.flow.plugins.components.collections.mysql.mysql_db_meta import MySQLDBMetaComponent @@ -28,100 +29,111 @@ def __init__(self, root_id: str, data: Optional[dict]): self.root_id = root_id self.data = data - @staticmethod - def __get_spider_cluster_info(cluster_id: int) -> dict: + def __get_tendb_cluster_info(self, cluster_id: int, is_only_add_slave_domain: bool): """ - 根据cluster_id获取集群信息 + 获取集群信息,主要获取代理层ip信息spider_ip_list spider_port """ - cluster = Cluster.objects.get(id=cluster_id) - # spider_info = SpiderInstance.objects.filter(cluster=cluster).all() - # - # # spider集群的从域名有自己的spider节点,区别于mysql ha集群,咨询下spider集群相关表是哪些 - # slave_info = StorageInstance.objects.filter( - # cluster=cluster, instance_inner_role=InstanceInnerRole.SLAVE.value - # ).all() - # - # spider_dns_list = spider_info[0].bind_entry.filter(cluster_entry_type=ClusterEntryType.DNS.value).all() - # - # slave_dns_list = [] - # for slave in slave_info: - # slave_dns_infos = slave.bind_entry.filter(cluster_entry_type=ClusterEntryType.DNS.value).all() - # slave_dns_list.append( - # { - # "slave_ip": slave.machine.ip, - # "slave_port": slave.port, - # "dns_list": [i.entry for i in slave_dns_infos], - # } - # ) - - # return { - # "id": cluster_id, - # "bk_cloud_id": cluster.bk_cloud_id, - # "name": cluster.name, - # "spider_port": spider_info[0].port, - # "spider_ip_list": [s.machine.ip for s in spider_info], - # "spider_dns_list": [i.entry for i in spider_dns_list], - # "slave_dns_list": slave_dns_list, - # } - return { + try: + cluster = Cluster.objects.get(id=cluster_id, bk_biz_id=int(self.data["bk_biz_id"])) + except Cluster.DoesNotExist: + raise ClusterNotExistException( + cluster_id=cluster_id, bk_biz_id=int(self.data["bk_biz_id"]), message=_("集群不存在") + ) + + master_domain_list = [] + spider_master_port = 25000 + spider_master_ip_list = [] + if not is_only_add_slave_domain: + entry_list = ClusterEntry.objects.filter(cluster=cluster, role=ClusterEntryRole.MASTER_ENTRY.value).all() + master_domain_list = master_domain_list + [entry.entry for entry in entry_list] + instance_list = entry_list[0].proxyinstance_set.all() + spider_master_port = instance_list[0].port + spider_master_ip_list = spider_master_ip_list + [instance.machine.ip for instance in instance_list] + + cluster_info = { "id": cluster_id, "bk_cloud_id": cluster.bk_cloud_id, "name": cluster.name, - "spider_port": 25000, - "spider_ip_list": ["127.0.0.1"], - "spider_dns_list": ["xxxx.xxxx.xxxx"], - "slave_dns_list": [{"slave_ip": "127.0.0.1", "slave_port": 20000, "dns_list": ["xxxx.xxxx.xxxx"]}], + "master_domain_list": master_domain_list, + "spider_master_port": spider_master_port, + "spider_master_ip_list": spider_master_ip_list, } + entry_list = ClusterEntry.objects.filter(cluster=cluster, role=ClusterEntryRole.SLAVE_ENTRY.value) + if not entry_list.exists(): + return cluster_info + + # 如果从域名存在 则补充从域名的相关信息 + slave_domain_list = [entry.entry for entry in entry_list] + instance_list = entry_list[0].proxyinstance_set.all() + spider_slave_port = instance_list[0].port + spider_slave_ip_list = [instance.machine.ip for instance in instance_list] + cluster_info.update( + { + "slave_domain_list": slave_domain_list, + "spider_slave_port": spider_slave_port, + "spider_slave_ip_list": spider_slave_ip_list, + } + ) + return cluster_info + def enable_spider_cluster_flow(self): """ 定义spider集群启用流程 """ - spider_cluster_enable_pipleline = Builder(root_id=self.root_id, data=self.data) + spider_cluster_enable_pipeline = Builder(root_id=self.root_id, data=self.data) sub_pipelines = [] # 多集群禁用时,循环加入禁用子流程 for cluster_id in self.data["cluster_ids"]: # 获取集群实例信息 - cluster = self.__get_spider_cluster_info(cluster_id=cluster_id) - sub_pipeline = SubBuilder(root_id=self.root_id, data=self.data) + cluster = Cluster.objects.get(id=cluster_id) + instance_set = cluster.proxyinstance_set.all() + # 这里问下,一套集群对应多个域名? + cluster_info = self.__get_tendb_cluster_info(cluster_id, self.data["is_only_add_slave_domain"]) + sub_flow_context = copy.deepcopy(self.data) + sub_flow_context.pop("cluster_ids") + sub_flow_context.update(cluster_info) - acts_list = [] - for spider_dns_name in cluster["spider_dns_list"]: - acts_list.append( - { - "act_name": _("添加主集群域名"), - "act_component_code": MySQLDnsManageComponent.code, - "kwargs": asdict( - CreateDnsKwargs( - bk_cloud_id=cluster["bk_cloud_id"], - add_domain_name=spider_dns_name, - dns_op_exec_port=cluster["spider_port"], - exec_ip=cluster["spider_ip_list"], - ) - ), - } - ) - sub_pipeline.add_parallel_acts(acts_list=acts_list) + sub_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(sub_flow_context)) - acts_list = [] - for slave_dns_info in cluster["slave_dns_list"]: - for dns_name in slave_dns_info["dns_list"]: + if not self.data["is_only_add_slave_domain"]: + acts_list = [] + for master_domain in cluster_info["master_domain_list"]: acts_list.append( { - "act_name": _("添加从集群域名"), + "act_name": _("添加集群域名"), "act_component_code": MySQLDnsManageComponent.code, "kwargs": asdict( CreateDnsKwargs( - bk_cloud_id=cluster["bk_cloud_id"], - add_domain_name=dns_name, - dns_op_exec_port=slave_dns_info["slave_port"], - exec_ip=slave_dns_info["slave_ip"], + bk_cloud_id=cluster_info["bk_cloud_id"], + add_domain_name=master_domain, + dns_op_exec_port=cluster_info["spider_master_port"], + exec_ip=cluster_info["spider_master_ip_list"], ) ), } ) - sub_pipeline.add_parallel_acts(acts_list=acts_list) + sub_pipeline.add_parallel_acts(acts_list=acts_list) + + acts_list = [] + for slave_domain in cluster_info.get("slave_domain_list", []): + acts_list.append( + { + "act_name": _("添加从集群域名"), + "act_component_code": MySQLDnsManageComponent.code, + "kwargs": asdict( + CreateDnsKwargs( + bk_cloud_id=cluster_info["bk_cloud_id"], + add_domain_name=slave_domain, + dns_op_exec_port=cluster_info["spider_slave_port"], + exec_ip=cluster_info["spider_slave_ip_list"], + ) + ), + } + ) + if acts_list: + sub_pipeline.add_parallel_acts(acts_list=acts_list) sub_pipeline.add_act( act_name=_("集群变更ONLINE状态"), @@ -129,12 +141,14 @@ def enable_spider_cluster_flow(self): kwargs=asdict( DBMetaOPKwargs( db_meta_class_func=MySQLDBMeta.mysql_cluster_online.__name__, - cluster=cluster, + cluster=cluster_info, ) ), ) - sub_pipelines.append(sub_pipeline.build_sub_process(sub_name=_("启用spider集群[{}]").format(cluster["name"]))) + sub_pipelines.append( + sub_pipeline.build_sub_process(sub_name=_("启用spider集群[{}]").format(cluster_info["name"])) + ) - spider_cluster_enable_pipleline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) - spider_cluster_enable_pipleline.run_pipeline() + spider_cluster_enable_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) + spider_cluster_enable_pipeline.run_pipeline() diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_flashback.py b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_flashback.py new file mode 100644 index 0000000000..838fbf3b68 --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_flashback.py @@ -0,0 +1,193 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +import logging +from collections import defaultdict +from copy import deepcopy +from dataclasses import asdict +from typing import Dict, Optional + +from django.core.exceptions import ObjectDoesNotExist +from django.utils.translation import ugettext as _ + +from backend.configuration.constants import DBType +from backend.db_meta.enums import InstanceInnerRole, TenDBClusterSpiderRole +from backend.db_meta.exceptions import ClusterNotExistException +from backend.db_meta.models import Cluster, StorageInstanceTuple +from backend.flow.consts import TruncateDataTypeEnum +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList +from backend.flow.plugins.components.collections.mysql.exec_actuator_script import ExecuteDBActuatorScriptComponent +from backend.flow.plugins.components.collections.mysql.filter_database_table_by_flashback_input import ( + FilterDatabaseTableFromFlashbackInputComponent, +) +from backend.flow.plugins.components.collections.mysql.trans_flies import TransFileComponent +from backend.flow.plugins.components.collections.spider.check_cluster_table_using_sub import ( + build_check_cluster_table_using_sub_flow, +) +from backend.flow.utils.mysql.mysql_act_dataclass import BKCloudIdKwargs, DownloadMediaKwargs, ExecActuatorKwargs +from backend.flow.utils.mysql.mysql_act_playload import MysqlActPayload +from backend.flow.utils.mysql.mysql_context_dataclass import MySQLFlashBackContext + +logger = logging.getLogger("flow") + + +class TenDBClusterFlashbackFlow(object): + """ + tendbcluster flashback + 在各remote instance 并发执行 + """ + + def __init__(self, root_id: str, cluster_type: str, data: Optional[Dict]): + self.root_id = root_id + self.data = data + self.cluster_type = cluster_type + + def flashback(self): + """ + { + "uid": "{}".format(datetime.now().strftime("%Y%m%d%H%M%f")), + "created_by": "xxx", + "bk_biz_id": "3", + "module": 1, + "ticket_type": constants.TicketType.TENDB_CLUSTER_FLASHBACK.value, + "infos": [ + { + "cluster_id": cluster_id, + "start_time": "2023-07-03 15:06:00", + "end_time": "2023-07-03 15:28:00", + "databases": ["db_ob1"], + "databases_ignore": [], + "tables": [], + "tables_ignore": [], + "force": bool, + } + ] + } + 目前库表输入和库表选择器的逻辑不一样 + 这里 tables 为空时指代所有表 + end_time >= start_time + 回档时从 end_time 向 start_time 反演 binlog + 所以 + start_time 是平时说的需要回档到的时间点 + """ + flashback_pipeline = Builder(root_id=self.root_id, data=self.data) + cluster_pipes = [] + for job in self.data["infos"]: + try: + cluster_obj = Cluster.objects.get( + pk=job["cluster_id"], bk_biz_id=self.data["bk_biz_id"], cluster_type=self.cluster_type + ) + except ObjectDoesNotExist: + raise ClusterNotExistException(cluster_type=self.cluster_type, cluster_id=job["cluster_id"]) + + cluster_pipe = SubBuilder( + root_id=self.root_id, + data={ + **deepcopy(job), + "uid": self.data["uid"], + "created_by": self.data["created_by"], + "bk_biz_id": self.data["bk_biz_id"], + "ip": cluster_obj.proxyinstance_set.filter( + tendbclusterspiderext__spider_role=TenDBClusterSpiderRole.SPIDER_MASTER + ) + .first() + .machine.ip, + "port": cluster_obj.proxyinstance_set.first().port, + "truncate_data_type": TruncateDataTypeEnum.TRUNCATE_TABLE.value, # 不是真的要删表, 只是复用打开检查必须 + }, + ) + + cluster_pipe.add_act( + act_name=_("获取回档库表"), + act_component_code=FilterDatabaseTableFromFlashbackInputComponent.code, + kwargs=asdict(BKCloudIdKwargs(bk_cloud_id=cluster_obj.bk_cloud_id)), + ) + + # 在所有 spider 做库表打开检查 + # 不做连接检查, 因为意义不大 + # 有个问题是, 如何减少因为监控导致的误报呢? 可以考虑多检查一次 + if not self.data["force"]: + cluster_pipe.add_sub_pipeline( + build_check_cluster_table_using_sub_flow( + root_id=self.root_id, cluster_obj=cluster_obj, parent_global_data=self.data + ) + ) + + ip_port_map = defaultdict(list) + for remote_master_instance in cluster_obj.storageinstance_set.filter( + instance_inner_role=InstanceInnerRole.MASTER.value + ): + ip_port_map[remote_master_instance.machine.ip].append(remote_master_instance.port) + + on_remote_pipes = [] + for ip, port_list in ip_port_map.items(): + on_remote_pipe = SubBuilder( + root_id=self.root_id, + data={ + **deepcopy(job), + "uid": self.data["uid"], + "created_by": self.data["created_by"], + "bk_biz_id": self.data["bk_biz_id"], + }, + ) + + on_remote_pipe.add_act( + act_name=_("下发actuator介质"), + act_component_code=TransFileComponent.code, + kwargs=asdict( + DownloadMediaKwargs( + bk_cloud_id=cluster_obj.bk_cloud_id, + exec_ip=ip, + file_list=GetFileList(db_type=DBType.MySQL).get_db_actuator_package(), + ) + ), + ) + + port_acts = [] + for port in port_list: + shard_id = ( + StorageInstanceTuple.objects.filter(ejector__machine__ip=ip, ejector__port=port) + .first() + .tendbclusterstorageset.shard_id + ) + port_job = deepcopy(job) + port_job["shard_id"] = shard_id + port_job["master_port"] = port + port_job["work_dir"] = "/data/dbbak/{}/{}".format(self.root_id, port) + + port_acts.append( + { + "act_name": _("端口 {} 执行闪回".format(port)), + "act_component_code": ExecuteDBActuatorScriptComponent.code, + "kwargs": asdict( + ExecActuatorKwargs( + exec_ip=ip, + bk_cloud_id=cluster_obj.bk_cloud_id, + cluster=port_job, + get_mysql_payload_func=MysqlActPayload.get_spider_flashback_payload.__name__, + ) + ), + } + ) + + on_remote_pipe.add_parallel_acts(port_acts) + + on_remote_pipes.append(on_remote_pipe.build_sub_process(sub_name=_("{} 闪回".format(ip)))) + + cluster_pipe.add_parallel_sub_pipeline(sub_flow_list=on_remote_pipes) + cluster_pipes.append( + cluster_pipe.build_sub_process(sub_name=_("{} flashback".format(cluster_obj.immute_domain))) + ) + + flashback_pipeline.add_parallel_sub_pipeline(sub_flow_list=cluster_pipes) + logger.info(_("构造flashback流程成功")) + flashback_pipeline.run_pipeline(init_trans_data_class=MySQLFlashBackContext()) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_full_backup.py b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_full_backup.py index 634b65ae0c..f46841bcbc 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_full_backup.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_full_backup.py @@ -20,7 +20,7 @@ from backend.configuration.constants import DBType from backend.constants import IP_PORT_DIVIDER -from backend.db_meta.enums import ClusterType, InstanceInnerRole +from backend.db_meta.enums import ClusterType, InstanceInnerRole, TenDBClusterSpiderRole from backend.db_meta.exceptions import ClusterNotExistException from backend.db_meta.models import Cluster, StorageInstanceTuple from backend.flow.consts import DBA_SYSTEM_USER @@ -50,13 +50,13 @@ def full_backup_flow(self): "uid": "398346234", "created_type": "xxx", "bk_biz_id": "152", - "ticket_type": "SPIDER_FULL_BACKUP", + "ticket_type": "TENDBCLUSTER_FULL_BACKUP", "infos": { "backup_type": enum of backend.flow.consts.MySQLBackupTypeEnum "file_tag": enum of backend.flow.consts.MySQLBackupFileTagEnum “clusters": [ { - "id": int, + "cluster_id": int, "backup_local": enum TenDBBackupLocation::[REMOTE, SPIDER_MNT], "spider_mnt_address": "x.x.x.x:y" # 如果 backup_local 是 spider_mnt }, @@ -79,13 +79,13 @@ def full_backup_flow(self): try: cluster_obj = Cluster.objects.get( - pk=cluster["id"], + pk=cluster["cluster_id"], bk_biz_id=self.data["bk_biz_id"], cluster_type=ClusterType.TenDBCluster.value, ) except ObjectDoesNotExist: raise ClusterNotExistException( - cluster_type=ClusterType.TenDBCluster.value, cluster_id=cluster["id"], immute_domain="" + cluster_type=ClusterType.TenDBCluster.value, cluster_id=cluster["cluster_id"], immute_domain="" ) backup_id = uuid.uuid1() @@ -148,6 +148,7 @@ def backup_on_spider_ctl(self, backup_id: uuid.UUID, cluster_obj: Cluster) -> Su "backup_type": "logical", "backup_gsd": ["schema"], "file_tag": self.data["infos"]["file_tag"], + "role": TenDBClusterSpiderRole.SPIDER_MASTER, }, ) @@ -176,18 +177,6 @@ def backup_on_spider_ctl(self, backup_id: uuid.UUID, cluster_obj: Cluster) -> Su ), ) - on_ctl_sub_pipe.add_act( - act_name=_("ctl 执行全库备份"), - act_component_code=ExecuteDBActuatorScriptComponent.code, - kwargs=asdict( - ExecActuatorKwargs( - bk_cloud_id=cluster_obj.bk_cloud_id, - run_as_system_user=DBA_SYSTEM_USER, - exec_ip=ctl_primary_ip, - get_mysql_payload_func=MysqlActPayload.mysql_backup_demand_payload_on_ctl.__name__, - ) - ), - ) return on_ctl_sub_pipe.build_sub_process(sub_name=_("spider/ctl备份库表结构")) def backup_on_remote(self, backup_id: uuid.UUID, cluster_obj: Cluster) -> List[SubProcess]: @@ -201,7 +190,11 @@ def backup_on_remote(self, backup_id: uuid.UUID, cluster_obj: Cluster) -> List[S receiver__is_stand_by=True, ): stand_by_slaves[tp.receiver.machine.ip].append( - {"port": tp.receiver.port, "shard_id": tp.tendbclusterstorageset.shard_id} + { + "port": tp.receiver.port, + "shard_id": tp.tendbclusterstorageset.shard_id, + "role": tp.receiver.instance_role, + } ) for ip, dtls in stand_by_slaves.items(): @@ -219,6 +212,8 @@ def backup_on_remote(self, backup_id: uuid.UUID, cluster_obj: Cluster) -> List[S "ip": ip, "port": dtl["port"], "backup_gsd": ["schema", "data"], + "role": dtl["role"], + "shard_id": dtl["shard_id"], }, ) @@ -266,6 +261,7 @@ def backup_on_spider_mnt(self, backup_id: uuid.UUID, cluster_obj: Cluster, spide "backup_id": backup_id, "backup_type": "logical", "backup_gsd": ["schema", "data"], + "role": TenDBClusterSpiderRole.SPIDER_MNT, }, ) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_rollback_flow.py b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_rollback_flow.py new file mode 100644 index 0000000000..cb2ae58cb4 --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_rollback_flow.py @@ -0,0 +1,151 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import copy +import logging +from dataclasses import asdict +from typing import Dict, Optional + +from django.utils.translation import ugettext as _ + +from backend.configuration.constants import DBType +from backend.db_services.mysql.fixpoint_rollback.handlers import FixPointRollbackHandler +from backend.flow.consts import RollbackType +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList +from backend.flow.engine.bamboo.scene.spider.common.exceptions import TendbGetBackupInfoFailedException +from backend.flow.engine.bamboo.scene.spider.spider_recover import remote_node_rollback, spider_recover_sub_flow +from backend.flow.plugins.components.collections.mysql.trans_flies import TransFileComponent +from backend.flow.utils.mysql.common.mysql_cluster_info import get_version_and_charset +from backend.flow.utils.mysql.mysql_act_dataclass import DownloadMediaKwargs +from backend.flow.utils.spider.tendb_cluster_info import get_rollback_clusters_info +from backend.utils import time + +logger = logging.getLogger("flow") + + +class TenDBRollBackDataFlow(object): + """ + TenDB 后端节点主从成对迁移 + """ + + def __init__(self, root_id: str, data: Optional[Dict]): + """ + @param root_id : 任务流程定义的root_id + @param data : 单据传递参数 + """ + self.root_id = root_id + self.data = data + + def tendb_rollback_data(self): + """ + tendb rollback data + """ + tendb_rollback_pipeline = Builder(root_id=self.root_id, data=copy.deepcopy(self.data)) + clusters_info = get_rollback_clusters_info( + source_cluster_id=self.data["source_cluster_id"], target_cluster_id=self.data["target_cluster_id"] + ) + charset, db_version = get_version_and_charset( + bk_biz_id=clusters_info["source"]["bk_biz_id"], + db_module_id=clusters_info["source"]["db_module_id"], + cluster_type=clusters_info["source"]["cluster_type"], + ) + # 先查询恢复介质 + if self.data["rollback_type"] == RollbackType.REMOTE_AND_BACKUPID.value: + backup_info = self.data["backupinfo"] + else: + rollback_handler = FixPointRollbackHandler(self.data["source_cluster_id"]) + rollback_time = time.strptime(self.data["rollback_time"], "%Y-%m-%d %H:%M:%S") + backup_info = rollback_handler.query_latest_backup_log(rollback_time) + if backup_info is None: + logger.error("cluster {} backup info not exists".format(self.data["source_cluster_id"])) + raise TendbGetBackupInfoFailedException( + message=_("获取实例 {} 的备份信息失败".format(self.data["source_cluster_id"])) + ) + + # 下发 actuator + tendb_rollback_pipeline.add_act( + act_name=_("下发actuator工具 {}".format(clusters_info["ip_list"])), + act_component_code=TransFileComponent.code, + kwargs=asdict( + DownloadMediaKwargs( + bk_cloud_id=clusters_info["bk_cloud_id"], + exec_ip=clusters_info["ip_list"], + file_list=GetFileList(DBType.MySQL).get_db_actuator_package(), + ) + ), + ) + + ins_sub_pipeline_list = [] + for spider_node in clusters_info["target_spiders"]: + spd_cluster = { + "charset": charset, + "backupinfo": backup_info["spider_node"], + # "file_target_path": "/data/dbbak/{}/{}".format(self.root_id, spider_node["port"]), + "file_target_path": "/home/mysql/install", + "rollback_ip": spider_node["ip"], + "rollback_port": spider_node["port"], + "instance": spider_node["instance"], + "bk_cloud_id": clusters_info["bk_cloud_id"], + "cluster_id": self.data["source_cluster_id"], + "rollback_time": self.data["rollback_time"], + "rollback_type": self.data["rollback_type"], + "databases": self.data["databases"], + "tables": self.data["tables"], + "databases_ignore": self.data["databases_ignore"], + "tables_ignore": self.data["tables_ignore"], + "change_master": False, + } + spd_sub_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(self.data)) + spd_sub_pipeline.add_sub_pipeline( + sub_flow=spider_recover_sub_flow( + root_id=self.root_id, ticket_data=copy.deepcopy(self.data), cluster=spd_cluster + ) + ) + ins_sub_pipeline_list.append(spd_sub_pipeline.build_sub_process(sub_name=_("恢复spider节点数据"))) + for shard_id, remote_node in clusters_info["shards"].items(): + shd_cluster = { + "charset": charset, + "shard_id": shard_id, + "new_master_ip": remote_node["new_master"]["ip"], + "new_master_port": remote_node["new_master"]["port"], + "new_slave_ip": remote_node["new_slave"]["ip"], + "new_master": remote_node["new_master"], + "new_slave": remote_node["new_slave"], + "new_slave_port": remote_node["new_slave"]["port"], + "master_ip": remote_node["master"]["ip"], + "master_port": remote_node["master"]["port"], + "slave_ip": remote_node["slave"]["ip"], + "slave_port": remote_node["slave"]["port"], + "master": remote_node["master"], + "slave": remote_node["slave"], + # "file_target_path": "/data/dbbak/{}/{}".format(self.root_id, remote_node["new_master"]["port"]), + "file_target_path": "/home/mysql/install", + "cluster_id": self.data["source_cluster_id"], + "bk_cloud_id": clusters_info["bk_cloud_id"], + "backupinfo": backup_info["remote_node"][str(shard_id)], + "rollback_time": self.data["rollback_time"], + "rollback_type": self.data["rollback_type"], + "databases": self.data["databases"], + "tables": self.data["tables"], + "databases_ignore": self.data["databases_ignore"], + "tables_ignore": self.data["tables_ignore"], + "change_master": False, + } + + ins_sub_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(self.data)) + ins_sub_pipeline.add_sub_pipeline( + sub_flow=remote_node_rollback( + root_id=self.root_id, ticket_data=copy.deepcopy(self.data), cluster=shd_cluster + ) + ) + ins_sub_pipeline_list.append(ins_sub_pipeline.build_sub_process(sub_name=_("恢复remote节点数据"))) + tendb_rollback_pipeline.add_parallel_sub_pipeline(sub_flow_list=ins_sub_pipeline_list) + tendb_rollback_pipeline.run_pipeline() diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_truncate_database.py b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_truncate_database.py index 15688d040c..bc540d1bfb 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_truncate_database.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_truncate_database.py @@ -18,7 +18,7 @@ from django.utils.translation import ugettext as _ from backend.configuration.constants import DBType -from backend.db_meta.enums import InstanceInnerRole +from backend.db_meta.enums import InstanceInnerRole, TenDBClusterSpiderRole from backend.db_meta.exceptions import ClusterNotExistException, DBMetaException from backend.db_meta.models import Cluster, StorageInstanceTuple from backend.flow.consts import DBA_SYSTEM_USER @@ -87,8 +87,8 @@ def truncate_database(self): "uid": "2022051612120001", "created_by": "xxx", "bk_biz_id": "152", - "ticket_type": "SPIDER_TRUNCATE_DATABASE", - "truncate_data_infos": [ + "ticket_type": "TENDBCLUSTER_TRUNCATE_DATABASE", + "infos": [ { "cluster_id": int, "db_patterns": ["db1%", "db2%"], @@ -127,7 +127,11 @@ def truncate_database(self): "created_by": self.data["created_by"], "bk_biz_id": self.data["bk_biz_id"], "ticket_type": self.data["ticket_type"], - "ip": cluster_obj.immute_domain, + "ip": cluster_obj.proxyinstance_set.filter( + tendbclusterspiderext__spider_role=TenDBClusterSpiderRole.SPIDER_MASTER + ) + .first() + .machine.ip, "port": cluster_obj.proxyinstance_set.first().port, "ctl_primary": cluster_obj.tendbcluster_ctl_primary_address(), }, @@ -200,12 +204,14 @@ def truncate_database(self): logger.info("shard_id: {}".format(shard_id)) on_remote_job = copy.deepcopy(job) - # 库正则模式不以 % 结尾时, 需要拼接 shard_id + # 库正则模式不以 % 结尾, 或者不是 "*" 时, 需要拼接 shard_id on_remote_job["db_patterns"] = [ - ele if ele.endswith("%") else "{}_{}".format(ele, shard_id) for ele in on_remote_job["db_patterns"] + ele if ele.endswith("%") or ele == "*" else "{}_{}".format(ele, shard_id) + for ele in on_remote_job["db_patterns"] ] on_remote_job["ignore_dbs"] = [ - ele if ele.endswith("%") else "{}_{}".format(ele, shard_id) for ele in on_remote_job["ignore_dbs"] + ele if ele.endswith("%") or ele == "*" else "{}_{}".format(ele, shard_id) + for ele in on_remote_job["ignore_dbs"] ] logger.info("on_remote_job: {}".format(on_remote_job)) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_partition.py b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_partition.py index 178c69596a..24e62b197f 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_partition.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_partition.py @@ -5,15 +5,11 @@ from dataclasses import asdict from typing import Dict, Optional -from django.utils.crypto import get_random_string from django.utils.translation import ugettext as _ from backend.configuration.constants import DBType from backend.core.consts import BK_PKG_INSTALL_PATH -from backend.db_meta.enums import ClusterType, InstanceRole -from backend.db_meta.exceptions import ClusterNotExistException, MasterInstanceNotExistException -from backend.db_meta.models import Cluster, StorageInstance -from backend.flow.consts import DBA_SYSTEM_USER +from backend.flow.consts import DBA_ROOT_USER from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList from backend.flow.plugins.components.collections.mysql.exec_actuator_script import ExecuteDBActuatorScriptComponent @@ -35,7 +31,7 @@ class SpiderPartitionFlow(object): "root_id": 123, "created_by": "xxx", "bk_biz_id": "xxx", - "ticket_type": "SPIDER_PARTITION", + "ticket_type": "TENDBCLUSTER_PARTITION", "infos": [ { "config_id": 1, @@ -139,7 +135,7 @@ def spider_partition_flow(self): "ip": ip, "port": port, "shard_name": shard, - "file_path": os.path.join(BK_PKG_INSTALL_PATH, filename), + "file_path": os.path.join(BK_PKG_INSTALL_PATH, "partition", filename), } exec_info = dict() exec_info["act_name"] = _("{}: {}".format(_("actuator执行partition"), address_tip)) @@ -148,7 +144,7 @@ def spider_partition_flow(self): ExecActuatorKwargs( exec_ip=ip, bk_cloud_id=bk_cloud_id, - run_as_system_user=DBA_SYSTEM_USER, + run_as_system_user=DBA_ROOT_USER, get_mysql_payload_func=MysqlActPayload.get_partition_payload.__name__, cluster=cluster, ) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_recover.py b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_recover.py new file mode 100644 index 0000000000..2bdfdd7320 --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_recover.py @@ -0,0 +1,203 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import copy +from dataclasses import asdict + +from django.utils.translation import ugettext as _ + +from backend.db_meta.enums import ClusterType +from backend.db_services.mysql.fixpoint_rollback.handlers import FixPointRollbackHandler +from backend.flow.consts import RollbackType +from backend.flow.engine.bamboo.scene.common.builder import SubBuilder +from backend.flow.engine.bamboo.scene.spider.common.exceptions import TendbGetBinlogFailedException +from backend.flow.plugins.components.collections.mysql.exec_actuator_script import ExecuteDBActuatorScriptComponent +from backend.flow.plugins.components.collections.mysql.mysql_download_backupfile import ( + MySQLDownloadBackupfileComponent, +) +from backend.flow.utils.mysql.common.compare_time import compare_time +from backend.flow.utils.mysql.mysql_act_dataclass import DownloadBackupFileKwargs, ExecActuatorKwargs +from backend.flow.utils.mysql.mysql_act_playload import MysqlActPayload +from backend.utils import time + + +def spider_recover_sub_flow(root_id: str, ticket_data: dict, cluster: dict): + """ + spider 恢复表结构 + 从指定spider列表获取备份介质恢复至指定的spider + 1 获取介质>判断介质来源>恢复数据 + @param root_id: flow流程root_id + @param ticket_data: 单据 data + @param cluster_info: 关联的cluster对象 + """ + sub_pipeline = SubBuilder(root_id=root_id, data=ticket_data) + exec_act_kwargs = ExecActuatorKwargs( + bk_cloud_id=int(cluster["bk_cloud_id"]), cluster_type=ClusterType.TenDBCluster, cluster=cluster + ) + # spider 没有主从节点.指定备份的ip:port为主节点。 + cluster["master_ip"], cluster["master_port"] = "", 0 + cluster["change_master"] = False + backup_info = cluster["backupinfo"] + task_ids = [i["task_id"] for i in backup_info["file_list_details"]] + download_kwargs = DownloadBackupFileKwargs( + bk_cloud_id=cluster["bk_cloud_id"], + task_ids=task_ids, + dest_ip=cluster["rollback_ip"], + desc_dir=cluster["file_target_path"], + reason="spider node rollback data", + ) + sub_pipeline.add_act( + act_name=_("下载定点恢复的全库备份介质到{}:{}").format(cluster["rollback_ip"], cluster["rollback_port"]), + act_component_code=MySQLDownloadBackupfileComponent.code, + kwargs=asdict(download_kwargs), + ) + exec_act_kwargs.exec_ip = cluster["rollback_ip"] + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_rollback_data_restore_payload.__name__ + sub_pipeline.add_act( + act_name=_("定点恢复之恢复数据{}:{}").format(exec_act_kwargs.exec_ip, cluster["rollback_port"]), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(exec_act_kwargs), + ) + if cluster["rollback_type"] == RollbackType.REMOTE_AND_TIME.value: + # rollback_time = time.strptime(cluster["rollback_time"], "%Y-%m-%d %H:%M:%S") + # backup_time = time.strptime(backup_info["backup_time"], "%Y-%m-%d %H:%M:%S") + if compare_time(backup_info["backup_time"], cluster["rollback_time"]): + raise TendbGetBinlogFailedException(message=_("{} 备份时间点大于回滚时间点".format(cluster["master_ip"]))) + rollback_handler = FixPointRollbackHandler(cluster["cluster_id"]) + backup_binlog = rollback_handler.query_binlog_from_bklog( + backup_info["backup_time"], + cluster["rollback_time"], + minute_range=30, + host_ip=cluster["master_ip"], + port=cluster["master_port"], + ) + if backup_binlog is None: + raise TendbGetBinlogFailedException(message=_("获取实例 {} binlog失败".format(cluster["master_ip"]))) + + task_ids = [i["task_id"] for i in backup_binlog["file_list_details"]] + binlog_files = [i["file_name"] for i in backup_binlog["file_list_details"]] + cluster["binlog_files"] = ",".join(binlog_files) + download_kwargs = DownloadBackupFileKwargs( + bk_cloud_id=cluster["bk_cloud_id"], + task_ids=task_ids, + dest_ip=cluster["rollback_ip"], + desc_dir=cluster["file_target_path"], + reason="spider node rollback binlog", + ) + sub_pipeline.add_act( + act_name=_("下载定点恢复的binlog到{}:{}").format(cluster["rollback_ip"], cluster["rollback_port"]), + act_component_code=MySQLDownloadBackupfileComponent.code, + kwargs=asdict(download_kwargs), + ) + + exec_act_kwargs.exec_ip = cluster["rollback_ip"] + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.tendb_recover_binlog_payload.__name__ + sub_pipeline.add_act( + act_name=_("定点恢复之前滚binlog{}:{}").format(exec_act_kwargs.exec_ip, cluster["rollback_port"]), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(exec_act_kwargs), + ) + return sub_pipeline.build_sub_process(sub_name=_("spider恢复:{}".format(cluster["instance"]))) + + +def remote_node_rollback(root_id: str, ticket_data: dict, cluster: dict): + """ + remote node 主从节点数据恢复+binlog前滚 备份类型 rollback_type 分为2种: + REMOTE_AND_TIME:指定时间点恢复。恢复备份文件+binlog前滚 + REMOTE_AND_BACKUPID:指定备份id恢复。只需恢复备份文件 + @param root_id: flow 流程 root_id + @param ticket_data: 关联单据 ticket对象 + @param cluster_info: 关联的cluster对象 + """ + # cluster = copy.deepcopy(cluster_info) + sub_pipeline_all = SubBuilder(root_id=root_id, data=ticket_data) + sub_pipeline_all_list = [] + for node in [cluster["new_master"], cluster["new_slave"]]: + cluster["rollback_ip"] = node["ip"] + cluster["rollback_port"] = node["port"] + backup_info = cluster["backupinfo"] + + sub_pipeline = SubBuilder(root_id=root_id, data=ticket_data) + exec_act_kwargs = ExecActuatorKwargs( + bk_cloud_id=int(cluster["bk_cloud_id"]), cluster_type=ClusterType.TenDBCluster, cluster=cluster + ) + + task_ids = [i["task_id"] for i in backup_info["file_list_details"]] + # 是否回档从库? + download_kwargs = DownloadBackupFileKwargs( + bk_cloud_id=cluster["bk_cloud_id"], + task_ids=task_ids, + dest_ip=cluster["rollback_ip"], + desc_dir=cluster["file_target_path"], + reason="spider remote node rollback data", + ) + sub_pipeline.add_act( + act_name=_("下载定点恢复的全库备份介质到{}:{}".format(cluster["rollback_ip"], cluster["rollback_port"])), + act_component_code=MySQLDownloadBackupfileComponent.code, + kwargs=asdict(download_kwargs), + ) + exec_act_kwargs.exec_ip = cluster["rollback_ip"] + + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_rollback_data_restore_payload.__name__ + sub_pipeline.add_act( + act_name=_("定点恢复之恢复数据{}:{}".format(exec_act_kwargs.exec_ip, cluster["rollback_port"])), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(exec_act_kwargs), + ) + # 指定时间点的定点回档则需要执行binlog前滚。滚动到指定的时间点。 + if cluster["rollback_type"] == RollbackType.REMOTE_AND_TIME.value: + # rollback_time = time.strptime(cluster["rollback_time"], "%Y-%m-%d %H:%M:%S") + # backup_time = time.strptime(backup_info["backup_time"], "%Y-%m-%d %H:%M:%S") + if compare_time(backup_info["backup_time"], cluster["rollback_time"]): + raise TendbGetBinlogFailedException(message=_("{} 备份时间点大于回滚时间点".format(cluster["master_ip"]))) + rollback_handler = FixPointRollbackHandler(cluster["cluster_id"]) + backup_binlog = rollback_handler.query_binlog_from_bklog( + start_time=backup_info["backup_time"], + end_time=cluster["rollback_time"], + minute_range=30, + host_ip=cluster["master_ip"], + port=cluster["master_port"], + ) + if backup_binlog is None: + raise TendbGetBinlogFailedException(message=_("获取实例 {} 的备份信息失败".format(cluster["master_ip"]))) + + task_ids = [i["task_id"] for i in backup_binlog["file_list_details"]] + binlog_files = [i["file_name"] for i in backup_binlog["file_list_details"]] + cluster["binlog_files"] = ",".join(binlog_files) + download_kwargs = DownloadBackupFileKwargs( + bk_cloud_id=cluster["bk_cloud_id"], + task_ids=task_ids, + dest_ip=cluster["rollback_ip"], + desc_dir=cluster["file_target_path"], + reason="tenDB rollback binlog", + ) + sub_pipeline.add_act( + act_name=_("下载定点恢复的binlog到{}:{}".format(cluster["rollback_ip"], cluster["rollback_port"])), + act_component_code=MySQLDownloadBackupfileComponent.code, + kwargs=asdict(download_kwargs), + ) + exec_act_kwargs.exec_ip = cluster["rollback_ip"] + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.tendb_recover_binlog_payload.__name__ + sub_pipeline.add_act( + act_name=_("定点恢复之前滚binlog{}:{}".format(exec_act_kwargs.exec_ip, cluster["rollback_port"])), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(exec_act_kwargs), + ) + sub_pipeline_all_list.append( + sub_pipeline.build_sub_process(sub_name=_("定点恢复 {}:{}".format(node["ip"], node["port"]))) + ) + sub_pipeline_all.add_parallel_sub_pipeline(sub_pipeline_all_list) + return sub_pipeline_all.build_sub_process( + sub_name=_( + "Remote node {} 恢复: 主 {} 从 {} ".format( + cluster["shard_id"], cluster["new_master"]["instance"], cluster["new_slave"]["instance"] + ) + ) + ) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_reduce_mnt.py b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_reduce_mnt.py new file mode 100644 index 0000000000..f3da929361 --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_reduce_mnt.py @@ -0,0 +1,105 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import copy +import logging +from dataclasses import asdict +from typing import Dict, Optional + +from django.utils.translation import ugettext as _ + +from backend.db_meta.enums import TenDBClusterSpiderRole +from backend.db_meta.models import Cluster +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.engine.bamboo.scene.spider.common.common_sub_flow import reduce_spider_slaves_flow +from backend.flow.plugins.components.collections.common.pause import PauseComponent +from backend.flow.plugins.components.collections.mysql.dns_manage import MySQLDnsManageComponent +from backend.flow.plugins.components.collections.spider.drop_spider_ronting import DropSpiderRoutingComponent +from backend.flow.utils.mysql.mysql_act_dataclass import RecycleDnsRecordKwargs +from backend.flow.utils.spider.spider_act_dataclass import DropSpiderRoutingKwargs + +logger = logging.getLogger("flow") + + +class TenDBClusterReduceMNTFlow(object): + """ + 减少运维节点(临时节点下架) + """ + + def __init__(self, root_id: str, data: Optional[Dict]): + """ + @param root_id: 任务流程定义的root_id + @param data: 单据传递参数 + """ + self.root_id = root_id + self.data = data + + def reduce_spider_mnt(self): + """ + 定义运维节点下架后端流程 + """ + pipeline = Builder(root_id=self.root_id, data=self.data) + sub_pipelines = [] + for info in self.data["infos"]: + sub_flow_context = copy.deepcopy(self.data) + sub_flow_context.pop("infos") + sub_flow_context.update(info) + + # 这里后面看下reduce_spider_nodes怎么改 + sub_flow_context["force"] = True + + cluster = Cluster.objects.get(id=info["cluster_id"]) + reduce_spiders = info["spider_ip_list"] + sub_flow_context["reduce_spiders"] = reduce_spiders + + sub_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(sub_flow_context)) + + # 删除spider的路由关系 + sub_pipeline.add_act( + act_name=_("删除spider的路由关系"), + act_component_code=DropSpiderRoutingComponent.code, + kwargs=asdict( + DropSpiderRoutingKwargs( + cluster_id=cluster.id, + is_safe=self.data["is_safe"], + reduce_spiders=reduce_spiders, + ) + ), + ) + + # 回收对应的域名关系 + sub_pipeline.add_act( + act_name=_("回收对应spider集群映射"), + act_component_code=MySQLDnsManageComponent.code, + kwargs=asdict( + RecycleDnsRecordKwargs( + bk_cloud_id=cluster.bk_cloud_id, + dns_op_exec_port=cluster.proxyinstance_set.first().port, + exec_ip=[info["ip"] for info in reduce_spiders], + ), + ), + ) + + # 后续流程需要在这里加一个暂停节点,让用户在合适的时间执行下架 + sub_pipeline.add_act(act_name=_("人工确认"), act_component_code=PauseComponent.code, kwargs={}) + + # 根据场景执行下架spider子流程 + sub_pipeline.add_sub_pipeline( + sub_flow=reduce_spider_slaves_flow( + cluster=cluster, + reduce_spiders=reduce_spiders, + root_id=self.root_id, + parent_global_data=sub_flow_context, + spider_role=TenDBClusterSpiderRole.SPIDER_MNT.value, + ) + ) + sub_pipelines.append(sub_pipeline.build_sub_process(sub_name=_("[{}]下架spider运维节点流程".format(cluster.name)))) + + pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) + pipeline.run_pipeline() diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_reduce_nodes.py b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_reduce_nodes.py new file mode 100644 index 0000000000..aba23b8397 --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_reduce_nodes.py @@ -0,0 +1,175 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +import copy +import logging.config +from dataclasses import asdict +from typing import Dict, Optional + +from django.utils.translation import ugettext as _ + +from backend.db_meta.enums import TenDBClusterSpiderRole +from backend.db_meta.exceptions import ClusterNotExistException +from backend.db_meta.models import Cluster +from backend.flow.consts import MIN_SPIDER_MASTER_COUNT, MIN_SPIDER_SLAVE_COUNT +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.engine.bamboo.scene.spider.common.common_sub_flow import reduce_spider_slaves_flow +from backend.flow.engine.bamboo.scene.spider.common.exceptions import NormalSpiderFlowException +from backend.flow.plugins.components.collections.common.pause import PauseComponent +from backend.flow.plugins.components.collections.mysql.dns_manage import MySQLDnsManageComponent +from backend.flow.plugins.components.collections.spider.drop_spider_ronting import DropSpiderRoutingComponent +from backend.flow.utils.mysql.mysql_act_dataclass import RecycleDnsRecordKwargs +from backend.flow.utils.spider.spider_act_dataclass import DropSpiderRoutingKwargs + +logger = logging.getLogger("flow") + + +class TenDBClusterReduceNodesFlow(object): + """ + 构建TenDB Cluster 减少 spider 节点;添加不同角色的spider,处理方式不一样 + 目前只支持spider_master/spider_slave 角色的减少 + 节点减少不是无脑操作,应该有数量上限制:spider_master至少需要保留2台;spider_slave至少需要保留1台 + 支持不同云区域的合并操作 + """ + + def __init__(self, root_id: str, data: Optional[Dict]): + """ + @param root_id : 任务流程定义的root_id + @param data : 单据传递参数 + """ + self.root_id = root_id + self.data = data + self.mix_spider_master_count = MIN_SPIDER_MASTER_COUNT + self.mix_spider_slave_count = MIN_SPIDER_SLAVE_COUNT + + def __calc_reduce_spiders( + self, cluster: Cluster, reduce_spider_role: TenDBClusterSpiderRole, spider_reduced_to_count: int + ): + """ + 根据每个子单据的操作spider角色和缩容剩余数量,来计算出合理的待回收spider节点列表 + @param cluster: 集群对象 + @param reduce_spider_role: 待回收角色 + @param spider_reduced_to_count: 缩容至数量 + """ + # 检测 + spiders_count = cluster.proxyinstance_set.filter(tendbclusterspiderext__spider_role=reduce_spider_role).count() + if reduce_spider_role == TenDBClusterSpiderRole.SPIDER_MASTER.value and ( + spider_reduced_to_count < self.mix_spider_master_count or spider_reduced_to_count >= spiders_count + ): + + raise NormalSpiderFlowException( + message=_( + "集群最后不能少于{}个spider_master实例,或者不能大于集群存量[{}]".format(self.mix_spider_master_count, spiders_count) + ) + ) + + if reduce_spider_role == TenDBClusterSpiderRole.SPIDER_SLAVE.value and ( + spider_reduced_to_count < self.mix_spider_slave_count or spider_reduced_to_count >= spiders_count + ): + + raise NormalSpiderFlowException( + message=_( + "集群最后不能少于{}个spider_slave实例,或者不能大于集群存量[{}]".format(self.mix_spider_slave_count, spiders_count) + ) + ) + + # 计算合理的待下架的spider节点列表 + + ctl_primary = cluster.tendbcluster_ctl_primary_address() + + # 选择上尽量避开ctl_primary的选择, 避免做一次切换逻辑 + reduce_spiders = cluster.proxyinstance_set.filter( + tendbclusterspiderext__spider_role=reduce_spider_role + ).exclude(machine__ip=ctl_primary.split(":"[0]))[: spiders_count - spider_reduced_to_count] + + return [{"ip": s.machine.ip} for s in reduce_spiders] + + def reduce_spider_nodes(self): + """ + 定义TenDB Cluster缩容接入层的后端流程 + todo 目前spider-master缩容功能开发中,当前中控版本需要调整,等最新版本做联调工作 + """ + + pipeline = Builder(root_id=self.root_id, data=self.data) + sub_pipelines = [] + for info in self.data["infos"]: + # 拼接子流程需要全局参数 + sub_flow_context = copy.deepcopy(self.data) + sub_flow_context.pop("infos") + + # 拼接子流程的全局参数 + sub_flow_context.update(info) + + # 卸载spider实例级别默认先为True, 看看是否后续让用户自行选择? + sub_flow_context["force"] = True + + # 获取对应集群相关对象 + try: + cluster = Cluster.objects.get(id=info["cluster_id"], bk_biz_id=int(self.data["bk_biz_id"])) + except Cluster.DoesNotExist: + raise ClusterNotExistException( + cluster_id=info["cluster_id"], bk_biz_id=int(self.data["bk_biz_id"]), message=_("集群不存在") + ) + + # 计算待下架的spider节点列表,转化成全局参数 + reduce_spiders = self.__calc_reduce_spiders( + cluster=cluster, + reduce_spider_role=info["reduce_spider_role"], + spider_reduced_to_count=int(info["spider_reduced_to_count"]), + ) + sub_flow_context["reduce_spiders"] = reduce_spiders + + # 启动子流程 + sub_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(sub_flow_context)) + + # 删除spider的路由关系 + sub_pipeline.add_act( + act_name=_("删除spider的路由关系"), + act_component_code=DropSpiderRoutingComponent.code, + kwargs=asdict( + DropSpiderRoutingKwargs( + cluster_id=cluster.id, + is_safe=self.data["is_safe"], + reduce_spiders=reduce_spiders, + ) + ), + ) + + # 回收对应的域名关系 + sub_pipeline.add_act( + act_name=_("回收对应spider集群映射"), + act_component_code=MySQLDnsManageComponent.code, + kwargs=asdict( + RecycleDnsRecordKwargs( + bk_cloud_id=cluster.bk_cloud_id, + dns_op_exec_port=cluster.proxyinstance_set.first().port, + exec_ip=[info["ip"] for info in reduce_spiders], + ), + ), + ) + + # 后续流程需要在这里加一个暂停节点,让用户在合适的时间执行下架 + sub_pipeline.add_act(act_name=_("人工确认"), act_component_code=PauseComponent.code, kwargs={}) + + # 根据场景执行下架spider子流程 + sub_pipeline.add_sub_pipeline( + sub_flow=reduce_spider_slaves_flow( + cluster=cluster, + reduce_spiders=reduce_spiders, + root_id=self.root_id, + parent_global_data=sub_flow_context, + spider_role=info["reduce_spider_role"], + ) + ) + + sub_pipelines.append(sub_pipeline.build_sub_process(sub_name=_("[{}]减少spider节点流程".format(cluster.name)))) + + pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) + pipeline.run_pipeline() diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_remote_node_migrate.py b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_remote_node_migrate.py new file mode 100644 index 0000000000..8a487eae86 --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_remote_node_migrate.py @@ -0,0 +1,432 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import copy +from dataclasses import asdict + +from django.utils.translation import ugettext as _ + +from backend.configuration.constants import DBType +from backend.db_meta.enums import ClusterType +from backend.flow.consts import MysqlChangeMasterType +from backend.flow.engine.bamboo.scene.common.builder import SubBuilder +from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList +from backend.flow.plugins.components.collections.mysql.exec_actuator_script import ExecuteDBActuatorScriptComponent +from backend.flow.plugins.components.collections.mysql.mysql_download_backupfile import ( + MySQLDownloadBackupfileComponent, +) +from backend.flow.plugins.components.collections.mysql.slave_trans_flies import SlaveTransFileComponent +from backend.flow.plugins.components.collections.mysql.trans_flies import TransFileComponent +from backend.flow.utils.mysql.mysql_act_dataclass import ( + DownloadBackupFileKwargs, + DownloadMediaKwargs, + ExecActuatorKwargs, + P2PFileKwargs, +) +from backend.flow.utils.mysql.mysql_act_playload import MysqlActPayload +from backend.flow.utils.spider.tendb_cluster_info import get_remotedb_info + + +def remote_node_migrate_sub_flow(root_id: str, ticket_data: dict, cluster_info: dict): + """ + 主从成对迁移子流程。(只做流程,元数据请在主流程控制) + @param root_id: flow 流程的root_id + @param ticket_data: 单据传输过来的data数据 + @param cluster_info: 关联集群的信息 + """ + + sub_pipeline = SubBuilder(root_id=root_id, data=ticket_data) + # 已经安装好的2个ip,需要导入同步数据 + # 下发dbactor》通过master/slave 获取备份的文件》判断备份文件》恢复数据》change master + cluster = { + "master_ip": cluster_info["master_ip"], + "slave_ip": cluster_info["slave_ip"], + "master_port": cluster_info["master_port"], + "new_master_ip": cluster_info["new_master_ip"], + "new_slave_ip": cluster_info["new_slave_ip"], + "new_master_port": cluster_info["new_master_port"], + "bk_cloud_id": cluster_info["bk_cloud_id"], + "backup_target_path": cluster_info["backup_target_path"], + } + exec_act_kwargs = ExecActuatorKwargs( + bk_cloud_id=int(cluster["bk_cloud_id"]), + cluster_type=ClusterType.TenDBCluster, + ) + # 阶段1 传输工具 + exec_ip = [cluster["master_ip"], cluster["slave_ip"], cluster["new_master_ip"], cluster["new_slave_ip"]] + sub_pipeline.add_act( + act_name=_("下发db-actor: {}".format(exec_ip)), + act_component_code=TransFileComponent.code, + kwargs=asdict( + DownloadMediaKwargs( + bk_cloud_id=cluster["bk_cloud_id"], + exec_ip=exec_ip, + file_list=GetFileList(db_type=DBType.MySQL).get_db_actuator_package(), + ) + ), + ) + # 阶段2 + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_find_local_backup_payload.__name__ + exec_act_kwargs.exec_ip = cluster["master_ip"] + sub_pipeline.add_act( + act_name=_("获取master节点备份介质{}").format(exec_act_kwargs.exec_ip), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(exec_act_kwargs), + write_payload_var="master_backup_file", + ) + exec_act_kwargs.exec_ip = cluster["slave_ip"] + sub_pipeline.add_act( + act_name=_("获取slave节点备份介质{}").format(exec_act_kwargs.exec_ip), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(exec_act_kwargs), + write_payload_var="slave_backup_file", + ) + # 阶段3 判断备份介质,并传输备份介质 + sub_pipeline.add_act( + act_name=_("判断备份文件来源,并传输备份文件新机器"), + act_component_code=SlaveTransFileComponent.code, + kwargs=asdict( + P2PFileKwargs( + bk_cloud_id=cluster["bk_cloud_id"], + file_list=[], + file_target_path=cluster["backup_target_path"], + source_ip_list=[], + exec_ip=[cluster["new_slave_ip"], cluster["new_master_ip"]], + ) + ), + ) + # 阶段4 恢复数据 payload 需要注意新实例端口可能与旧实例不一样 + restore_list = [] + exec_act_kwargs.exec_ip = cluster["new_master_ip"] + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_mysql_restore_slave_payload.__name__ + restore_list.append( + { + "act_name": _("恢复新主节点数据{}:{}").format(exec_act_kwargs.exec_ip, cluster["new_master_port"]), + "act_component_code": ExecuteDBActuatorScriptComponent.code, + "kwargs": asdict(exec_act_kwargs), + "write_payload_var": "change_master_info", + } + ) + + exec_act_kwargs.exec_ip = cluster["new_slave_ip"] + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_mysql_restore_slave_payload.__name__ + restore_list.append( + { + "act_name": _("恢复新从节点数据{}:{}").format(exec_act_kwargs.exec_ip, cluster["new_master_port"]), + "act_component_code": ExecuteDBActuatorScriptComponent.code, + "kwargs": asdict(exec_act_kwargs), + } + ) + sub_pipeline.add_parallel_acts(acts_list=restore_list) + + # 阶段5 change master: 新从库指向新主库 注意端口 + exec_act_kwargs.exec_ip = cluster["new_master_ip"] + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_grant_mysql_repl_user_payload.__name__ + sub_pipeline.add_act( + act_name=_("新增repl帐户{}").format(exec_act_kwargs.exec_ip), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(exec_act_kwargs), + write_payload_var="master_ip_sync_info", + ) + + exec_act_kwargs.exec_ip = cluster["new_slave_ip"] + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_change_master_payload.__name__ + sub_pipeline.add_act( + act_name=_("建立主从关系:新从库指向新主库 {}").format(exec_act_kwargs.exec_ip), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(exec_act_kwargs), + ) + + # 阶段6 change master: 新主库指向旧主库 todo 注意端口 + exec_act_kwargs.exec_ip = cluster["master_ip"] + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_grant_repl_for_migrate_cluster.__name__ + sub_pipeline.add_act( + act_name=_("新增repl帐户{}").format(exec_act_kwargs.exec_ip), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(exec_act_kwargs), + ) + exec_act_kwargs.exec_ip = cluster["new_master_ip"] + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.get_change_master_payload_for_migrate_cluster.__name__ + sub_pipeline.add_act( + act_name=_("建立主从关系:新主库指向旧主库 {}").format(exec_act_kwargs.exec_ip), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(exec_act_kwargs), + ) + + return sub_pipeline.build_sub_process(sub_name=_("RemoteDB主从节点成对迁移子流程{}".format(exec_ip))) + + +def remote_instance_migrate_sub_flow(root_id: str, ticket_data: dict, cluster_info: dict): + """ + tendb remote 节点 扩容缩容流程。实例级别迁移。(只做流程,元数据请在主流程控制) + @param root_id: flow流程的root_id + @param ticket_data: 关联单据 ticket对象 + @param cluster_info: 关联的cluster对象 + """ + + sub_pipeline = SubBuilder(root_id=root_id, data=ticket_data) + # 已经安装好的2个ip,需要导入同步数据 + # 下发dbactor》通过master/slave 获取备份的文件》判断备份文件》恢复数据》change master + cluster = { + "cluster_id": cluster_info["cluster_id"], + "master_ip": cluster_info["master_ip"], + "slave_ip": cluster_info["slave_ip"], + "master_port": cluster_info["master_port"], + "new_master_ip": cluster_info["new_master_ip"], + "new_slave_ip": cluster_info["new_slave_ip"], + "new_slave_port": cluster_info["new_slave_port"], + "new_master_port": cluster_info["new_master_port"], + "bk_cloud_id": cluster_info["bk_cloud_id"], + "file_target_path": cluster_info["file_target_path"], + "change_master_force": cluster_info["change_master_force"], + "backupinfo": cluster_info["backupinfo"], + "charset": cluster_info["charset"], + } + exec_act_kwargs = ExecActuatorKwargs( + bk_cloud_id=int(cluster["bk_cloud_id"]), + cluster_type=ClusterType.TenDBCluster, + ) + backup_info = cluster["backupinfo"] + # 主从并发下载备份介质 下载为异步下载,定时调起接口扫描下载结果 + task_ids = [i["task_id"] for i in backup_info["file_list_details"]] + download_sub_pipeline_list = [] + download_kwargs = DownloadBackupFileKwargs( + bk_cloud_id=cluster["bk_cloud_id"], + task_ids=task_ids, + dest_ip=cluster["new_master_ip"], + desc_dir=cluster["file_target_path"], + reason="spider remote node sync data", + ) + download_sub_pipeline_list.append( + { + "act_name": _("下载全库备份介质到 {}".format(cluster["new_master_ip"])), + "act_component_code": MySQLDownloadBackupfileComponent.code, + "kwargs": asdict(download_kwargs), + } + ) + download_kwargs.dest_ip = cluster["new_slave_ip"] + download_sub_pipeline_list.append( + { + "act_name": _("下载全库备份介质到 {}".format(cluster["new_slave_ip"])), + "act_component_code": MySQLDownloadBackupfileComponent.code, + "kwargs": asdict(download_kwargs), + } + ) + sub_pipeline.add_parallel_acts(download_sub_pipeline_list) + + # 阶段4 恢复数据remote主从节点的数据 + restore_list = [] + cluster["restore_ip"] = cluster["new_master_ip"] + cluster["restore_port"] = cluster["new_master_port"] + cluster["source_ip"] = cluster["master_ip"] + cluster["source_port"] = cluster["master_port"] + cluster["change_master"] = False + exec_act_kwargs.exec_ip = cluster["new_master_ip"] + exec_act_kwargs.cluster = copy.deepcopy(cluster) + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.tendb_restore_remotedb_payload.__name__ + restore_list.append( + { + "act_name": _("恢复新主节点数据 {}:{}".format(exec_act_kwargs.exec_ip, cluster["restore_port"])), + "act_component_code": ExecuteDBActuatorScriptComponent.code, + "kwargs": asdict(exec_act_kwargs), + "write_payload_var": "change_master_info", + } + ) + + cluster["restore_ip"] = cluster["new_slave_ip"] + cluster["restore_port"] = cluster["new_slave_port"] + cluster["source_ip"] = cluster["master_ip"] + cluster["source_port"] = cluster["master_port"] + cluster["change_master"] = False + exec_act_kwargs.cluster = copy.deepcopy(cluster) + exec_act_kwargs.exec_ip = cluster["new_slave_ip"] + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.tendb_restore_remotedb_payload.__name__ + restore_list.append( + { + "act_name": _("恢复新从节点数据 {}:{}".format(exec_act_kwargs.exec_ip, cluster["restore_port"])), + "act_component_code": ExecuteDBActuatorScriptComponent.code, + "kwargs": asdict(exec_act_kwargs), + } + ) + sub_pipeline.add_parallel_acts(acts_list=restore_list) + + # 阶段5 change master: 新从库指向新主库 + cluster["target_ip"] = cluster["new_master_ip"] + cluster["target_port"] = cluster["new_master_port"] + cluster["repl_ip"] = cluster["new_slave_ip"] + exec_act_kwargs.cluster = copy.deepcopy(cluster) + exec_act_kwargs.exec_ip = cluster["new_master_ip"] + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.tendb_grant_remotedb_repl_user.__name__ + sub_pipeline.add_act( + act_name=_("新增repl帐户{}".format(exec_act_kwargs.exec_ip)), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(exec_act_kwargs), + write_payload_var="show_master_status_info", + ) + + cluster["repl_ip"] = cluster["new_slave_ip"] + cluster["repl_port"] = cluster["new_slave_port"] + cluster["target_ip"] = cluster["new_master_ip"] + cluster["target_port"] = cluster["new_master_port"] + cluster["change_master_type"] = MysqlChangeMasterType.MASTERSTATUS.value + exec_act_kwargs.cluster = copy.deepcopy(cluster) + exec_act_kwargs.exec_ip = cluster["new_slave_ip"] + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.tendb_remotedb_change_master.__name__ + sub_pipeline.add_act( + act_name=_("建立主从关系:新从库指向新主库 {} {}:".format(exec_act_kwargs.exec_ip, cluster["repl_port"])), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(exec_act_kwargs), + ) + + # 阶段6 change master: 新主库指向旧主库 + cluster["target_ip"] = cluster["master_ip"] + cluster["target_port"] = cluster["master_port"] + cluster["repl_ip"] = cluster["new_master_ip"] + exec_act_kwargs.cluster = copy.deepcopy(cluster) + exec_act_kwargs.exec_ip = cluster["master_ip"] + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.tendb_grant_remotedb_repl_user.__name__ + sub_pipeline.add_act( + act_name=_("新增repl帐户{}".format(exec_act_kwargs.exec_ip)), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(exec_act_kwargs), + ) + + cluster["repl_ip"] = cluster["new_master_ip"] + cluster["repl_port"] = cluster["new_master_port"] + cluster["target_ip"] = cluster["master_ip"] + cluster["target_port"] = cluster["master_port"] + cluster["change_master_type"] = MysqlChangeMasterType.BACKUPFILE.value + exec_act_kwargs.cluster = copy.deepcopy(cluster) + exec_act_kwargs.exec_ip = cluster["new_master_ip"] + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.tendb_remotedb_change_master.__name__ + sub_pipeline.add_act( + act_name=_("建立主从关系:新主库指向旧主库 {}:{}".format(exec_act_kwargs.exec_ip, cluster["repl_port"])), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(exec_act_kwargs), + ) + return sub_pipeline.build_sub_process(sub_name=_("RemoteDB主从节点成对迁移子流程{}".format(exec_act_kwargs.exec_ip))) + + +def remote_node_uninstall_sub_flow(root_id: str, ticket_data: dict, ip: str): + """ + 卸载remotedb 指定ip节点下的所有实例 + @param root_id: flow流程的root_id + @param ticket_data: 单据 data 对象 + @param ip: 指定卸载的ip + """ + sub_pipeline = SubBuilder(root_id=root_id, data=ticket_data) + cluster = {"uninstall_ip": ip, "bk_cloud_id": ticket_data["bk_cloud_id"]} + instances = get_remotedb_info(cluster["uninstall_ip"], cluster["bk_cloud_id"]) + sub_pipeline_list = [] + for instance in instances: + cluster["backend_port"] = instance["port"] + sub_pipeline_list.append( + { + "act_name": _("卸载MySQL实例:{}:{}".format(cluster["uninstall_ip"], cluster["backend_port"])), + "act_component_code": ExecuteDBActuatorScriptComponent.code, + "kwargs": asdict( + ExecActuatorKwargs( + exec_ip=cluster["uninstall_ip"], + bk_cloud_id=cluster["bk_cloud_id"], + cluster=cluster, + get_mysql_payload_func=MysqlActPayload.get_uninstall_mysql_payload.__name__, + ) + ), + } + ) + sub_pipeline.add_parallel_acts(sub_pipeline_list) + return sub_pipeline.build_sub_process(sub_name=_("Remote node {} 卸载整机实例".format(cluster["uninstall_ip"]))) + + +def remote_slave_recover_sub_flow(root_id: str, ticket_data: dict, cluster_info: dict): + """ + tendb remote slave 节点 恢复。(只做流程,元数据请在主流程控制) + @param root_id: flow流程的root_id + @param ticket_data: 关联单据 ticket对象 + @param cluster_info: 关联的cluster对象 + """ + + sub_pipeline = SubBuilder(root_id=root_id, data=ticket_data) + # 下发dbactor》通过master/slave 获取备份的文件》判断备份文件》恢复数据》change master + cluster = { + "cluster_id": cluster_info["cluster_id"], + "master_ip": cluster_info["master_ip"], + "slave_ip": cluster_info["slave_ip"], + "master_port": cluster_info["master_port"], + "new_slave_ip": cluster_info["new_slave_ip"], + "new_slave_port": cluster_info["new_slave_port"], + "bk_cloud_id": cluster_info["bk_cloud_id"], + "file_target_path": cluster_info["file_target_path"], + "change_master_force": cluster_info["change_master_force"], + "backupinfo": cluster_info["backupinfo"], + "charset": cluster_info["charset"], + } + exec_act_kwargs = ExecActuatorKwargs( + bk_cloud_id=int(cluster["bk_cloud_id"]), + cluster_type=ClusterType.TenDBCluster, + ) + backup_info = cluster["backupinfo"] + # 新从库下载备份介质 下载为异步下载,定时调起接口扫描下载结果 + task_ids = [i["task_id"] for i in backup_info["file_list_details"]] + download_kwargs = DownloadBackupFileKwargs( + bk_cloud_id=cluster["bk_cloud_id"], + task_ids=task_ids, + dest_ip=cluster["new_slave_ip"], + desc_dir=cluster["file_target_path"], + reason="spider remote node sync data", + ) + sub_pipeline.add_act( + act_name=_("下载全库备份介质到 {}".format(cluster["new_slave_ip"])), + act_component_code=MySQLDownloadBackupfileComponent.code, + kwargs=asdict(download_kwargs), + ) + + # 阶段4 恢复数据remote主从节点的数据 + cluster["restore_ip"] = cluster["new_slave_ip"] + cluster["restore_port"] = cluster["new_slave_port"] + cluster["source_ip"] = cluster["master_ip"] + cluster["source_port"] = cluster["master_port"] + cluster["change_master"] = False + exec_act_kwargs.cluster = copy.deepcopy(cluster) + exec_act_kwargs.exec_ip = cluster["new_slave_ip"] + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.tendb_restore_remotedb_payload.__name__ + sub_pipeline.add_act( + act_name=_("恢复新从节点数据 {}:{}".format(exec_act_kwargs.exec_ip, cluster["restore_port"])), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(exec_act_kwargs), + ) + + # 阶段5 change master: 新从库指向旧主库 + cluster["target_ip"] = cluster["master_ip"] + cluster["target_port"] = cluster["master_port"] + cluster["repl_ip"] = cluster["new_slave_ip"] + exec_act_kwargs.cluster = copy.deepcopy(cluster) + exec_act_kwargs.exec_ip = cluster["master_ip"] + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.tendb_grant_remotedb_repl_user.__name__ + sub_pipeline.add_act( + act_name=_("新增repl帐户{}".format(exec_act_kwargs.exec_ip)), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(exec_act_kwargs), + ) + + cluster["repl_ip"] = cluster["new_slave_ip"] + cluster["repl_port"] = cluster["new_slave_port"] + cluster["target_ip"] = cluster["master_ip"] + cluster["target_port"] = cluster["master_port"] + cluster["change_master_type"] = MysqlChangeMasterType.BACKUPFILE.value + exec_act_kwargs.cluster = copy.deepcopy(cluster) + exec_act_kwargs.exec_ip = cluster["new_slave_ip"] + exec_act_kwargs.get_mysql_payload_func = MysqlActPayload.tendb_remotedb_change_master.__name__ + sub_pipeline.add_act( + act_name=_("建立主从关系:新主库指向旧主库 {}:{}".format(exec_act_kwargs.exec_ip, cluster["repl_port"])), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(exec_act_kwargs), + ) + return sub_pipeline.build_sub_process(sub_name=_("RemoteDB从节点重建子流程{}".format(exec_act_kwargs.exec_ip))) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_remotedb_migrate_flow.py b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_remotedb_migrate_flow.py new file mode 100644 index 0000000000..df0a89d5d2 --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_remotedb_migrate_flow.py @@ -0,0 +1,194 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import copy +import logging +from dataclasses import asdict +from typing import Dict, List, Optional + +from django.utils.translation import ugettext as _ + +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.engine.bamboo.scene.spider.spider_remote_node_migrate import ( + remote_node_migrate_sub_flow, + remote_node_uninstall_sub_flow, +) +from backend.flow.plugins.components.collections.common.pause import PauseComponent +from backend.flow.plugins.components.collections.mysql.clear_machine import MySQLClearMachineComponent +from backend.flow.plugins.components.collections.mysql.mysql_db_meta import MySQLDBMetaComponent +from backend.flow.utils.mysql.mysql_act_dataclass import ClearMachineKwargs, DBMetaOPKwargs +from backend.flow.utils.spider.spider_db_meta import SpiderDBMeta +from backend.flow.utils.spider.tendb_cluster_info import get_remotedb_info + +logger = logging.getLogger("flow") + + +class TenDBMigrateFlow(object): + """ + TenDB 后端节点主从成对迁移 + """ + + def __init__(self, root_id: str, data: Optional[Dict]): + """ + @param root_id : 任务流程定义的root_id + @param data : 单据传递参数 + """ + self.root_id = root_id + self.data = data + # todo 初始化数据 + + def tendb_migrate(self): + """ + tendb 迁移 + """ + # 根据已有的实例计算出端口。nodes 中的每一个ip对应一个流程。 + # 根据集群获取版本。 + tendb_migrate_pipeline = Builder(root_id=self.root_id, data=copy.deepcopy(self.data)) + svr_sub_pipeline_list = [] + for node in self.data["nodes"]: + svr_sub_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(self.data)) + node_cluster = { + "cluster_id": node["cluster_id"], + "bk_cloud_id": self.data["bk_cloud_id"], + "bk_biz_id": self.data["bk_biz_id"], + "master_ip": node["master"]["ip"], + "slave_ip": node["slave"]["ip"], + "new_master_ip": node["new_master"]["ip"], + "new_slave_ip": node["new_slave"]["ip"], + } + instances = get_remotedb_info(node["master"]["ip"], node["master"]["bk_cloud_id"]) + ports = [one["port"] for one in instances] + node_cluster["ports"] = ports + # todo 是使用实例的版本,还是集群的版本 + node_cluster["version"] = instances[0]["version"] + install_sub_pipeline_list = [] + install_sub_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(self.data)) + # 阶段1 安装主从库 + # todo 并发调起安装子流程 + install_sub_pipeline.add_act( + act_name=_("写入初始化实例的db_meta元信息"), + act_component_code=MySQLDBMetaComponent.code, + kwargs=asdict( + DBMetaOPKwargs( + db_meta_class_func=SpiderDBMeta.remotedb_migrate_add_install_nodes.__name__, + cluster=node_cluster, + is_update_trans_data=True, + ) + ), + ) + install_sub_pipeline_list.append(install_sub_pipeline.build_sub_process(sub_name=_("恢复实例数据 "))) + + # 阶段2 同步数据到新主从库 + sync_data_sub_pipeline_list = [] + for cluster_info in instances: + ins_cluster = copy.deepcopy(cluster_info) + ins_cluster["new_master_ip"] = node["new_master"]["ip"] + ins_cluster["new_slave_ip"] = node["new_slave"]["ip"] + ins_cluster["new_master_port"] = ins_cluster["port"] + ins_cluster["new_slave_port"] = ins_cluster["port"] + ins_cluster["master_ip"] = node["master"]["ip"] + ins_cluster["slave_ip"] = node["slave"]["ip"] + ins_cluster["master_port"] = ins_cluster["port"] + ins_cluster["slave_port"] = ins_cluster["port"] + ins_cluster["backup_target_path"] = "/data/dbbak/{}/{}".format(self.root_id, ins_cluster["port"]) + ins_cluster["cluster_id"] = node["cluster_id"] + ins_cluster["bk_cloud_id"] = self.data["bk_cloud_id"] + + sync_data_sub_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(self.data)) + sync_data_sub_pipeline.add_sub_pipeline( + sub_flow=remote_node_migrate_sub_flow( + root_id=self.root_id, ticket_data=copy.deepcopy(self.data), cluster_info=ins_cluster + ) + ) + sync_data_sub_pipeline.add_act( + act_name=_("同步数据完毕,写入数据节点的主从关系相关元数据"), + act_component_code=MySQLDBMetaComponent.code, + kwargs=asdict( + DBMetaOPKwargs( + db_meta_class_func=SpiderDBMeta.remotedb_migrate_add_storage_tuple.__name__, + cluster=node_cluster, + is_update_trans_data=True, + ) + ), + ) + sync_data_sub_pipeline_list.append(sync_data_sub_pipeline.build_sub_process(sub_name=_("恢复实例数据 "))) + + # 阶段3 todo 整机切换实例 + switch_sub_pipeline_list = [] + switch_sub_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(self.data)) + switch_sub_pipeline.add_sub_pipeline(sub_flow=_("切换子流程")) + switch_sub_pipeline.add_act( + act_name=_("整机切换完毕后修改元数据指向"), + act_component_code=MySQLDBMetaComponent.code, + kwargs=asdict( + DBMetaOPKwargs( + db_meta_class_func=SpiderDBMeta.remotedb_migrate_switch.__name__, + cluster=node_cluster, + is_update_trans_data=True, + ) + ), + ) + switch_sub_pipeline_list.append(switch_sub_pipeline.build_sub_process(sub_name=_("切换remote node 节点"))) + + # 阶段4 主机级别卸载实例,卸载指定ip下的所有实例 + uninstall_db_sub_pipeline_list = [] + for ip in [node["master_ip"], node["slave_ip"]]: + node_cluster["uninstall_ip"] = ip + uninstall_sub_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(self.data)) + uninstall_sub_pipeline.add_sub_pipeline( + sub_flow=remote_node_uninstall_sub_flow( + root_id=self.root_id, ticket_data=copy.deepcopy(self.data), ip=ip + ) + ) + # 卸载完毕后修改删除实例信息(整机) 删除元数据 + uninstall_sub_pipeline.add_act( + act_name=_("整机卸载成功后删除元数据"), + act_component_code=MySQLDBMetaComponent.code, + kwargs=asdict( + DBMetaOPKwargs( + db_meta_class_func=SpiderDBMeta.remotedb_migrate_remove_storage.__name__, + cluster=node_cluster, + is_update_trans_data=True, + ) + ), + ) + # 下线机器 + uninstall_sub_pipeline.add_act( + act_name=_("清理机器配置"), + act_component_code=MySQLClearMachineComponent.code, + kwargs=asdict( + ClearMachineKwargs( + exec_ip=ip, + bk_cloud_id=self.data["bk_cloud_id"], + ) + ), + ) + uninstall_db_sub_pipeline_list.append( + uninstall_sub_pipeline.build_sub_process(sub_name=_("卸载节点实例{}".format(ip))) + ) + + # 安装实例 + svr_sub_pipeline.add_parallel_acts(install_sub_pipeline_list) + # 同步数据 + svr_sub_pipeline.add_parallel_acts(sync_data_sub_pipeline_list) + # 人工确认切换 + svr_sub_pipeline.add_act(act_name=_("人工确认切换remote节点"), act_component_code=PauseComponent.code, kwargs={}) + svr_sub_pipeline.add_parallel_acts(switch_sub_pipeline_list) + # 人工确认卸载实例 + svr_sub_pipeline.add_act(act_name=_("人工确认卸载实例"), act_component_code=PauseComponent.code, kwargs={}) + svr_sub_pipeline.add_parallel_acts(uninstall_db_sub_pipeline_list) + # 加入并发列表 + svr_sub_pipeline_list.append( + svr_sub_pipeline.build_sub_process( + sub_name=_("成对迁移remote 节点主从实例 {} {}".format(node["master"]["ip"], node["slave"]["ip"])) + ) + ) + tendb_migrate_pipeline.add_parallel_sub_pipeline(sub_flow_list=svr_sub_pipeline_list) + tendb_migrate_pipeline.run_pipeline() diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_remotedb_rebalance_flow.py b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_remotedb_rebalance_flow.py new file mode 100644 index 0000000000..eed4e1f8c0 --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_remotedb_rebalance_flow.py @@ -0,0 +1,349 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import copy +import logging +from dataclasses import asdict +from datetime import datetime +from typing import Dict, Optional + +from django.utils.translation import ugettext as _ + +from backend.constants import IP_PORT_DIVIDER +from backend.db_meta.enums import ClusterType +from backend.db_meta.models import Cluster +from backend.db_services.mysql.fixpoint_rollback.handlers import FixPointRollbackHandler +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.engine.bamboo.scene.mysql.common.common_sub_flow import ( + build_surrounding_apps_sub_flow, + install_mysql_in_cluster_sub_flow, +) +from backend.flow.engine.bamboo.scene.spider.common.common_sub_flow import remote_migrate_switch_sub_flow +from backend.flow.engine.bamboo.scene.spider.common.exceptions import TendbGetBackupInfoFailedException +from backend.flow.engine.bamboo.scene.spider.spider_remote_node_migrate import ( + remote_instance_migrate_sub_flow, + remote_node_uninstall_sub_flow, +) +from backend.flow.plugins.components.collections.common.download_backup_client import DownloadBackupClientComponent +from backend.flow.plugins.components.collections.common.pause import PauseComponent +from backend.flow.plugins.components.collections.mysql.clear_machine import MySQLClearMachineComponent +from backend.flow.plugins.components.collections.mysql.exec_actuator_script import ExecuteDBActuatorScriptComponent +from backend.flow.plugins.components.collections.spider.spider_db_meta import SpiderDBMetaComponent +from backend.flow.utils.common_act_dataclass import DownloadBackupClientKwargs +from backend.flow.utils.mysql.common.mysql_cluster_info import get_version_and_charset +from backend.flow.utils.mysql.mysql_act_dataclass import ClearMachineKwargs, DBMetaOPKwargs, ExecActuatorKwargs +from backend.flow.utils.mysql.mysql_act_playload import MysqlActPayload +from backend.flow.utils.mysql.mysql_context_dataclass import ClusterInfoContext +from backend.flow.utils.spider.spider_db_meta import SpiderDBMeta +from backend.flow.utils.spider.tendb_cluster_info import get_cluster_info + +logger = logging.getLogger("flow") + + +class TenDBRemoteRebalanceFlow(object): + """ + TenDB 后端节点主从成对迁移 + """ + + def __init__(self, root_id: str, ticket_data: Optional[Dict]): + """ + @param root_id : 任务流程定义的root_id + @param ticket_data : 单据传递参数 + """ + self.root_id = root_id + self.ticket_data = ticket_data + self.data = {} + + def tendb_migrate(self): + """ + tendb 迁移 + """ + # 根据已有的实例计算出端口。nodes 中的每一个ip对应一个流程。 + tendb_migrate_pipeline_all = Builder(root_id=self.root_id, data=copy.deepcopy(self.ticket_data)) + # 阶段1 获取集群所有信息。计算端口,构建数据。 + tendb_migrate_pipeline_all_list = [] + for info in self.ticket_data["infos"]: + cluster_info = get_cluster_info(info["cluster_id"]) + + self.data = {} + self.data = copy.deepcopy(info) + self.data["bk_cloud_id"] = cluster_info["bk_cloud_id"] + self.data["root_id"] = self.root_id + self.data["start_port"] = 20000 + self.data["uid"] = self.ticket_data["uid"] + self.data["ticket_type"] = self.ticket_data["ticket_type"] + self.data["bk_biz_id"] = self.ticket_data["bk_biz_id"] + self.data["created_by"] = self.ticket_data["created_by"] + self.data["module"] = info["db_module_id"] + # 卸载流程时强制卸载 + self.data["force"] = True + + # 先查询备份,如果备份不存在则退出,不安装实例 + # restore_time = datetime.strptime("2023-07-31 17:40:00", "%Y-%m-%d %H:%M:%S") + backup_handler = FixPointRollbackHandler(self.data["cluster_id"]) + restore_time = datetime.now() + backup_info = backup_handler.query_latest_backup_log(restore_time) + if backup_info is None: + logger.error("cluster {} backup info not exists".format(self.data["cluster_id"])) + raise TendbGetBackupInfoFailedException(message=_("获取集群 {} 的备份信息失败".format(self.data["cluster_id"]))) + logger.debug(backup_info) + tendb_migrate_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(self.data)) + charset, db_version = get_version_and_charset( + bk_biz_id=cluster_info["bk_biz_id"], + db_module_id=cluster_info["db_module_id"], + cluster_type=cluster_info["cluster_type"], + ) + cluster_info["charset"] = charset + cluster_info["db_version"] = db_version + cluster_class = Cluster.objects.get(id=self.data["cluster_id"]) + + shards = len(cluster_info["shards"]) + if self.data["remote_shard_num"] * len(self.data["remote_group"]) != shards: + return + cluster_info["ports"] = [] + for port in range(self.data["start_port"], self.data["start_port"] + self.data["remote_shard_num"]): + cluster_info["ports"].append(port) + + shard_ids = copy.deepcopy(cluster_info["shard_ids"]) + for node in self.data["remote_group"]: + for port in cluster_info["ports"]: + master = { + "ip": node["master"]["ip"], + "port": port, + "bk_cloud_id": self.data["bk_cloud_id"], + "instance": "{}{}{}".format(node["master"]["ip"], IP_PORT_DIVIDER, port), + } + slave = { + "ip": node["slave"]["ip"], + "port": port, + "bk_cloud_id": self.data["bk_cloud_id"], + "instance": "{}{}{}".format(node["slave"]["ip"], IP_PORT_DIVIDER, port), + } + shard_id = shard_ids.pop(0) + cluster_info["shards"][shard_id]["new_master"] = master + cluster_info["shards"][shard_id]["new_slave"] = slave + + # 阶段2 安装实例并写入数据 + install_sub_pipeline_list = [] + for node in self.data["remote_group"]: + install_sub_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(self.data)) + install_sub_pipeline.add_sub_pipeline( + sub_flow=install_mysql_in_cluster_sub_flow( + uid=self.data["uid"], + root_id=self.root_id, + cluster=cluster_class, + new_mysql_list=[node["master"]["ip"], node["slave"]["ip"]], + install_ports=cluster_info["ports"], + ) + ) + cluster = { + "new_master_ip": node["master"]["ip"], + "new_slave_ip": node["slave"]["ip"], + "cluster_id": cluster_info["cluster_id"], + "bk_cloud_id": cluster_info["bk_cloud_id"], + "bk_biz_id": cluster_info["bk_biz_id"], + "ports": cluster_info["ports"], + "version": cluster_info["cluster"]["major_version"], + } + install_sub_pipeline.add_act( + act_name=_("写入初始化实例的db_meta元信息"), + act_component_code=SpiderDBMetaComponent.code, + kwargs=asdict( + DBMetaOPKwargs( + db_meta_class_func=SpiderDBMeta.remotedb_migrate_add_install_nodes.__name__, + cluster=copy.deepcopy(cluster), + is_update_trans_data=False, + ) + ), + ) + install_sub_pipeline.add_act( + act_name=_("安装backup-client工具"), + act_component_code=DownloadBackupClientComponent.code, + kwargs=asdict( + DownloadBackupClientKwargs( + bk_cloud_id=cluster_class.bk_cloud_id, + download_host_list=[cluster["new_master_ip"], cluster["new_slave_ip"]], + ) + ), + ) + # 安装临时备份程序 + exec_act_kwargs = ExecActuatorKwargs( + cluster=cluster, + bk_cloud_id=cluster_class.bk_cloud_id, + cluster_type=cluster_class.cluster_type, + get_mysql_payload_func=MysqlActPayload.get_install_tmp_db_backup_payload.__name__, + ) + exec_act_kwargs.exec_ip = [cluster["new_master_ip"], cluster["new_slave_ip"]] + install_sub_pipeline.add_act( + act_name=_("安装临时备份程序"), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict(exec_act_kwargs), + ) + install_sub_pipeline_list.append(install_sub_pipeline.build_sub_process(sub_name=_("安装remote主从节点"))) + + # 阶段3 逐个实例同步数据到新主从库 + sync_data_sub_pipeline_list = [] + for shard_id, node in cluster_info["shards"].items(): + ins_cluster = copy.deepcopy(cluster_info["cluster"]) + ins_cluster["charset"] = cluster_info["charset"] + ins_cluster["new_master_ip"] = node["new_master"]["ip"] + ins_cluster["new_slave_ip"] = node["new_slave"]["ip"] + ins_cluster["new_master_port"] = node["new_master"]["port"] + ins_cluster["new_slave_port"] = node["new_slave"]["port"] + ins_cluster["master_ip"] = node["master"]["ip"] + ins_cluster["slave_ip"] = node["slave"]["ip"] + ins_cluster["master_port"] = node["master"]["port"] + ins_cluster["slave_port"] = node["slave"]["port"] + # todo 正式环境放开file_target_path,需要备份接口支持自动创建目录 + # ins_cluster["file_target_path"] = "/data/dbbak/{}/{}"\ + # .format(self.root_id, ins_cluster["new_master_port"]) + ins_cluster["file_target_path"] = "/home/mysql/install" + ins_cluster["shard_id"] = shard_id + ins_cluster["change_master_force"] = False + + ins_cluster["backupinfo"] = backup_info["remote_node"].get(shard_id, {}) + # 判断 remote_node 下每个分片的备份信息是否正常 + if ( + len(ins_cluster["backupinfo"]) == 0 + or len(ins_cluster["backupinfo"].get("file_list_details", {})) == 0 + ): + logger.error( + "cluster {} shard {} backup info not exists".format(self.data["cluster_id"], shard_id) + ) + raise TendbGetBackupInfoFailedException( + message=_("获取集群分片 {} shard {} 的备份信息失败".format(self.data["cluster_id"], shard_id)) + ) + sync_data_sub_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(self.data)) + sync_data_sub_pipeline.add_sub_pipeline( + sub_flow=remote_instance_migrate_sub_flow( + root_id=self.root_id, ticket_data=copy.deepcopy(self.data), cluster_info=ins_cluster + ) + ) + sync_data_sub_pipeline.add_act( + act_name=_("同步数据完毕,写入数据节点的主从关系相关元数据"), + act_component_code=SpiderDBMetaComponent.code, + kwargs=asdict( + DBMetaOPKwargs( + db_meta_class_func=SpiderDBMeta.remotedb_migrate_add_storage_tuple.__name__, + cluster=ins_cluster, + is_update_trans_data=True, + ) + ), + ) + sync_data_sub_pipeline_list.append(sync_data_sub_pipeline.build_sub_process(sub_name=_("恢复实例数据"))) + + # 阶段4 切换 + switch_sub_pipeline_list = [] + shard_list = [] + for shard_id, node in cluster_info["shards"].items(): + shard_cluster = { + "old_master": node["master"]["instance"], + "old_slave": node["slave"]["instance"], + "new_master": node["new_master"]["instance"], + "new_slave": node["new_slave"]["instance"], + } + shard_list.append(shard_cluster) + switch_sub_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(self.data)) + switch_sub_pipeline.add_sub_pipeline( + sub_flow=remote_migrate_switch_sub_flow( + uid=self.data["uid"], + root_id=self.root_id, + cluster=cluster_class, + migrate_tuples=shard_list, + created_by=self.data["created_by"], + ) + ) + switch_sub_pipeline.add_act( + act_name=_("整集群切换完毕后修改元数据指向"), + act_component_code=SpiderDBMetaComponent.code, + kwargs=asdict( + DBMetaOPKwargs( + db_meta_class_func=SpiderDBMeta.tendb_remotedb_rebalance_switch.__name__, + cluster=cluster_info, + is_update_trans_data=True, + ) + ), + ) + switch_sub_pipeline_list.append(switch_sub_pipeline.build_sub_process(sub_name=_("切换remote node 节点"))) + + # 阶段5: 新机器安装周边组件 + surrounding_sub_pipeline_list = [] + for node in self.data["remote_group"]: + surrounding_sub_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(self.data)) + surrounding_sub_pipeline.add_sub_pipeline( + sub_flow=build_surrounding_apps_sub_flow( + bk_cloud_id=cluster_class.bk_cloud_id, + master_ip_list=[node["master"]["ip"]], + slave_ip_list=[node["slave"]["ip"]], + root_id=self.root_id, + parent_global_data=copy.deepcopy(self.data), + is_init=True, + cluster_type=ClusterType.TenDBCluster.value, + ) + ) + surrounding_sub_pipeline_list.append( + surrounding_sub_pipeline.build_sub_process(sub_name=_("新机器安装周边组件")) + ) + + # 阶段6: 主机级别卸载实例,卸载指定ip下的所有实例 + uninstall_svr_sub_pipeline_list = [] + machines = cluster_info["masters"] + cluster_info["slaves"] + for ip in machines: + uninstall_svr_sub_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(self.data)) + ins_cluster = {"uninstall_ip": ip, "cluster_id": cluster_info["cluster_id"]} + uninstall_svr_sub_pipeline.add_sub_pipeline( + sub_flow=remote_node_uninstall_sub_flow( + root_id=self.root_id, ticket_data=copy.deepcopy(self.data), ip=ip + ) + ) + uninstall_svr_sub_pipeline.add_act( + act_name=_("整机卸载成功后删除元数据"), + act_component_code=SpiderDBMetaComponent.code, + kwargs=asdict( + DBMetaOPKwargs( + db_meta_class_func=SpiderDBMeta.remotedb_migrate_remove_storage.__name__, + cluster=ins_cluster, + is_update_trans_data=True, + ) + ), + ) + uninstall_svr_sub_pipeline.add_act( + act_name=_("清理机器配置"), + act_component_code=MySQLClearMachineComponent.code, + kwargs=asdict( + ClearMachineKwargs( + exec_ip=ip, + bk_cloud_id=self.data["bk_cloud_id"], + ) + ), + ) + uninstall_svr_sub_pipeline_list.append( + uninstall_svr_sub_pipeline.build_sub_process(sub_name=_("卸载remote节点{}".format(ip))) + ) + # 安装实例 + tendb_migrate_pipeline.add_parallel_sub_pipeline(sub_flow_list=install_sub_pipeline_list) + # 数据同步 + tendb_migrate_pipeline.add_parallel_sub_pipeline(sub_flow_list=sync_data_sub_pipeline_list) + # 人工确认切换迁移实例 + tendb_migrate_pipeline.add_act(act_name=_("人工确认切换"), act_component_code=PauseComponent.code, kwargs={}) + # 切换迁移实例 + tendb_migrate_pipeline.add_parallel_sub_pipeline(sub_flow_list=switch_sub_pipeline_list) + # 新机器安装周边组件 + tendb_migrate_pipeline.add_parallel_sub_pipeline(sub_flow_list=surrounding_sub_pipeline_list) + # 卸载流程人工确认 + tendb_migrate_pipeline.add_act(act_name=_("人工确认卸载实例"), act_component_code=PauseComponent.code, kwargs={}) + # # 卸载remote节点 + tendb_migrate_pipeline.add_parallel_sub_pipeline(sub_flow_list=uninstall_svr_sub_pipeline_list) + tendb_migrate_pipeline_all_list.append( + tendb_migrate_pipeline.build_sub_process(_("集群迁移{}").format(self.data["cluster_id"])) + ) + # 运行流程 + tendb_migrate_pipeline_all.add_parallel_sub_pipeline(tendb_migrate_pipeline_all_list) + tendb_migrate_pipeline_all.run_pipeline(init_trans_data_class=ClusterInfoContext()) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_rename_database_flow.py b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_rename_database_flow.py index b78d12a936..8e3cee4a9f 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_rename_database_flow.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_rename_database_flow.py @@ -18,7 +18,7 @@ from django.utils.translation import ugettext as _ from backend.configuration.constants import DBType -from backend.db_meta.enums import InstanceInnerRole +from backend.db_meta.enums import InstanceInnerRole, TenDBClusterSpiderRole from backend.db_meta.exceptions import ClusterNotExistException from backend.db_meta.models import Cluster, StorageInstanceTuple from backend.flow.consts import DBA_SYSTEM_USER, TruncateDataTypeEnum @@ -83,7 +83,7 @@ def rename_database(self): "uid": "2022051612120001", "created_by": "xxx", "bk_biz_id": "152", - "ticket_type": "SPIDER_RENAME_DATABASE", + "ticket_type": "TENDBCLUSTER_RENAME_DATABASE", "infos": [ { "cluster_id": int, @@ -124,7 +124,11 @@ def rename_database(self): "created_by": self.data["created_by"], "bk_biz_id": self.data["bk_biz_id"], "ticket_type": self.data["ticket_type"], - "ip": cluster_obj.immute_domain, + "ip": cluster_obj.proxyinstance_set.filter( + tendbclusterspiderext__spider_role=TenDBClusterSpiderRole.SPIDER_MASTER + ) + .first() + .machine.ip, "port": cluster_obj.proxyinstance_set.first().port, "ctl_primary": cluster_obj.tendbcluster_ctl_primary_address(), "truncate_data_type": TruncateDataTypeEnum.DROP_DATABASE.value, # 为了复用 truncate data 的 service @@ -317,7 +321,9 @@ def rename_database(self): kwargs=asdict(BKCloudIdKwargs(bk_cloud_id=cluster_obj.bk_cloud_id)), ) - cluster_pipes.append(cluster_pipe.build_sub_process(sub_name="")) + cluster_pipes.append( + cluster_pipe.build_sub_process(sub_name=_("{} 库表重命名".format(cluster_obj.immute_domain))) + ) rename_pipeline.add_parallel_sub_pipeline(sub_flow_list=cluster_pipes) logger.info(_("构造数据库重命名流程成功")) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_slave_cluster_deploy.py b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_slave_cluster_deploy.py index 6a1e0e1f64..9b8a1d2dd1 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_slave_cluster_deploy.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_slave_cluster_deploy.py @@ -15,6 +15,7 @@ from django.utils.translation import ugettext as _ from backend.db_meta.enums import TenDBClusterSpiderRole +from backend.db_meta.exceptions import ClusterNotExistException from backend.db_meta.models import Cluster from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder from backend.flow.engine.bamboo.scene.spider.common.common_sub_flow import ( @@ -61,7 +62,12 @@ def deploy_slave_cluster(self): sub_flow_context.update(info) # 获取对应集群相关对象 - cluster = Cluster.objects.get(id=info["cluster_id"]) + try: + cluster = Cluster.objects.get(id=info["cluster_id"], bk_biz_id=int(self.data["bk_biz_id"])) + except Cluster.DoesNotExist: + raise ClusterNotExistException( + cluster_id=info["cluster_id"], bk_biz_id=int(self.data["bk_biz_id"]), message=_("集群不存在") + ) # 根据集群去bk-config获取对应spider版本和字符集 spider_charset, spider_version = get_spider_version_and_charset( diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_slave_cluster_destroy.py b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_slave_cluster_destroy.py new file mode 100644 index 0000000000..bec5416b78 --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_slave_cluster_destroy.py @@ -0,0 +1,138 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import copy +import logging +from dataclasses import asdict +from typing import Dict, Optional + +from django.utils.translation import ugettext as _ + +from backend.db_meta.enums import ClusterEntryRole, ClusterEntryType, TenDBClusterSpiderRole +from backend.db_meta.models import Cluster, ClusterEntry, ProxyInstance +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.engine.bamboo.scene.spider.common.common_sub_flow import reduce_spider_slaves_flow +from backend.flow.plugins.components.collections.common.pause import PauseComponent +from backend.flow.plugins.components.collections.mysql.dns_manage import MySQLDnsManageComponent +from backend.flow.plugins.components.collections.spider.drop_spider_ronting import DropSpiderRoutingComponent +from backend.flow.plugins.components.collections.spider.spider_db_meta import SpiderDBMetaComponent +from backend.flow.utils.mysql.mysql_act_dataclass import DBMetaOPKwargs, DeleteClusterDnsKwargs +from backend.flow.utils.spider.spider_act_dataclass import DropSpiderRoutingKwargs +from backend.flow.utils.spider.spider_db_meta import SpiderDBMeta + +logger = logging.getLogger("flow") + + +class TenDBSlaveClusterDestroyFlow(object): + """ + 构建TenDB Cluster只读接入层的下架流程抽象类 + """ + + def __init__(self, root_id: str, data: Optional[Dict]): + """ + @param root_id : 任务流程定义的root_id + @param data : 单据传递参数 + """ + self.root_id = root_id + self.data = data + + @staticmethod + def __get_slave_cluster_info(cluster_id: int) -> dict: + """ + 根据主机群id获取只读接入层相关信息 + @param cluster_id: 主机群的id + """ + cluster = Cluster.objects.get(id=cluster_id) + clusterentry = cluster.clusterentry_set.filter( + cluster_entry_type=ClusterEntryType.DNS.value, role=ClusterEntryRole.SLAVE_ENTRY.value + ).first() + proxy_instances = ProxyInstance.objects.filter(bind_entry=clusterentry).all() + + return { + "cluster_id": cluster_id, + "bk_cloud_id": cluster.bk_cloud_id, + "slave_domain": clusterentry.entry, + "spider_ip_list": list(set([instance.machine.ip for instance in proxy_instances])), + "port": proxy_instances[0].port, + } + + def destroy_slave_cluster(self): + """ + 定义spider只读接入层的下架流程 + 支持多集群下架 + """ + spider_slave_destroy_pipeline = Builder(root_id=self.root_id, data=self.data) + sub_pipelines = [] + for cluster_id in self.data["cluster_ids"]: + # 拼接子流程参数 + sub_flow_context = copy.deepcopy(self.data) + sub_flow_context.pop("cluster_ids") + + slave_cluster = self.__get_slave_cluster_info(cluster_id) + sub_flow_context.update(slave_cluster) + sub_flow_context["force"] = True + sub_flow_context["reduce_spiders"] = [{"ip": inst} for inst in slave_cluster["spider_ip_list"]] + # 子流程 + sub_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(sub_flow_context)) + + # 删除spider路由关系 + sub_pipeline.add_act( + act_name=_("删除spider路由关系"), + act_component_code=DropSpiderRoutingComponent.code, + kwargs=asdict( + DropSpiderRoutingKwargs( + cluster_id=slave_cluster["cluster_id"], + is_safe=sub_flow_context["is_safe"], + reduce_spiders=sub_flow_context["reduce_spiders"], + ) + ), + ) + + # 删除对应的域名关系 + sub_pipeline.add_act( + act_name=_("删除集群域名"), + act_component_code=MySQLDnsManageComponent.code, + kwargs=asdict( + DeleteClusterDnsKwargs( + bk_cloud_id=slave_cluster["bk_cloud_id"], + delete_cluster_id=slave_cluster["cluster_id"], + is_only_delete_slave_domain=True, + ), + ), + ) + + # 暂停节点,让用户在合适的时间执行下架 + sub_pipeline.add_act(act_name=_("人工确认"), act_component_code=PauseComponent.code, kwargs={}) + + # 根据场景执行下架spider子流程 + sub_pipeline.add_sub_pipeline( + sub_flow=reduce_spider_slaves_flow( + cluster=Cluster.objects.get(id=slave_cluster["cluster_id"]), + reduce_spiders=sub_flow_context["reduce_spiders"], + root_id=self.root_id, + parent_global_data=sub_flow_context, + spider_role=TenDBClusterSpiderRole.SPIDER_SLAVE.value, + ) + ) + + sub_pipeline.add_act( + act_name=_("清理db_meta元信息"), + act_component_code=SpiderDBMetaComponent.code, + kwargs=asdict( + DBMetaOPKwargs( + db_meta_class_func=SpiderDBMeta.tendb_cluster_slave_destroy.__name__, + ) + ), + ) + + sub_pipelines.append( + sub_pipeline.build_sub_process(sub_name=_("只读接入层[{}]下架".format(slave_cluster["slave_domain"]))) + ) + spider_slave_destroy_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) + spider_slave_destroy_pipeline.run_pipeline() diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/tbinlogdumper/add_nodes.py b/dbm-ui/backend/flow/engine/bamboo/scene/tbinlogdumper/add_nodes.py new file mode 100644 index 0000000000..3a7e568924 --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/tbinlogdumper/add_nodes.py @@ -0,0 +1,133 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +import copy +import logging.config +from dataclasses import asdict +from typing import Dict, Optional + +from django.utils.translation import ugettext as _ + +from backend.db_meta.enums import InstanceRole +from backend.flow.consts import TBinlogDumperAddType +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.engine.bamboo.scene.mysql.common.common_sub_flow import build_repl_by_manual_input_sub_flow +from backend.flow.engine.bamboo.scene.tbinlogdumper.common.common_sub_flow import ( + add_tbinlogdumper_sub_flow, + full_sync_sub_flow, + incr_sync_sub_flow, +) +from backend.flow.engine.bamboo.scene.tbinlogdumper.common.exceptions import NormalTBinlogDumperFlowException +from backend.flow.engine.bamboo.scene.tbinlogdumper.common.util import get_cluster, get_tbinlogdumper_install_port +from backend.flow.plugins.components.collections.mysql.mysql_db_meta import MySQLDBMetaComponent +from backend.flow.utils.mysql.mysql_act_dataclass import DBMetaOPKwargs +from backend.flow.utils.mysql.mysql_db_meta import MySQLDBMeta +from backend.flow.utils.tbinlogdumper.context_dataclass import TBinlogDumperAddContext + +logger = logging.getLogger("flow") + + +class TBinlogDumperAddNodesFlow(object): + """ + 构建 tbinlogdumper节点添加;目前尽量在tendb-ha的master进行部署,作为附属进程,可追加部署,多实例部署 + 目前仅支持 tendb-ha 架构 + 支持不同云区域的合并操作 + """ + + def __init__(self, root_id: str, data: Optional[Dict]): + """ + @param root_id : 任务流程定义的root_id + @param data : 单据传递参数 + """ + self.root_id = root_id + self.data = data + + def add_nodes(self): + pipeline = Builder(root_id=self.root_id, data=self.data) + sub_pipelines = [] + for info in self.data["infos"]: + + # 获取对应集群相关对象 + cluster = get_cluster(cluster_id=int(info["cluster_id"]), bk_biz_id=int(self.data["bk_biz_id"])) + + # 获取最新的master实例 + master = cluster.storageinstance_set.get(instance_role=InstanceRole.BACKEND_MASTER) + + # 获取安装端口 + install_ports = get_tbinlogdumper_install_port(machine=master.machine, install_num=len(info["add_confs"])) + # 将端口分配到每个add_conf + for key, conf in enumerate(info["add_confs"]): + conf["port"] = install_ports[key] + + # 启动子流程 + sub_flow_context = copy.deepcopy(self.data) + sub_flow_context.pop("infos") + # 拼接子流程的全局参数 + sub_flow_context.update(info) + + sub_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(sub_flow_context)) + + sub_pipeline.add_sub_pipeline( + sub_flow=add_tbinlogdumper_sub_flow( + cluster=cluster, + root_id=self.root_id, + uid=self.data["uid"], + add_conf_list=info["add_confs"], + created_by=self.data["created_by"], + ) + ) + + for add_conf in info["add_confs"]: + # 根据不同的添加类型,来确定TBinlogDumper数据同步的行为 + if add_conf["add_type"] == TBinlogDumperAddType.INCR_SYNC.value: + sub_pipeline.add_sub_pipeline( + sub_flow=incr_sync_sub_flow( + cluster=cluster, + root_id=self.root_id, + uid=self.data["uid"], + add_tbinlogdumper_conf=add_conf, + created_by=self.data["created_by"], + ) + ) + elif add_conf["add_type"] == TBinlogDumperAddType.FULL_SYNC.value: + sub_pipeline.add_sub_pipeline( + sub_flow=full_sync_sub_flow( + cluster=cluster, + root_id=self.root_id, + uid=self.data["uid"], + add_tbinlogdumper_conf=add_conf, + created_by=self.data["created_by"], + ) + ) + else: + raise NormalTBinlogDumperFlowException( + message=_("非法上架特性,请联系系统管理员:add_type:{}".format(add_conf["add_type"])) + ) + + # 写元数据 + sub_pipeline.add_act( + act_name=_("写入实例元信息"), + act_component_code=MySQLDBMetaComponent.code, + kwargs=asdict( + DBMetaOPKwargs( + db_meta_class_func=MySQLDBMeta.add_tbinlogdumper.__name__, + ) + ), + ) + + sub_pipelines.append( + sub_pipeline.build_sub_process(sub_name=_("[{}]集群添加TBinlogDumper实例".format(cluster.name))) + ) + + if not sub_pipelines: + raise NormalTBinlogDumperFlowException(message=_("计算不到需要上架的实例,拼装TBinlogDumper上架流程失败")) + + pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) + pipeline.run_pipeline(init_trans_data_class=TBinlogDumperAddContext()) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/tbinlogdumper/common/common_sub_flow.py b/dbm-ui/backend/flow/engine/bamboo/scene/tbinlogdumper/common/common_sub_flow.py new file mode 100644 index 0000000000..ddabe39277 --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/tbinlogdumper/common/common_sub_flow.py @@ -0,0 +1,437 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import uuid +from dataclasses import asdict + +from django.utils.translation import ugettext as _ + +from backend.configuration.constants import DBType +from backend.db_meta.enums import InstanceRole +from backend.db_meta.models import Cluster +from backend.db_meta.models.extra_process import ExtraProcessInstance +from backend.flow.consts import DBA_SYSTEM_USER +from backend.flow.engine.bamboo.scene.common.builder import SubBuilder +from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList +from backend.flow.engine.bamboo.scene.mysql.common.common_sub_flow import build_repl_by_manual_input_sub_flow +from backend.flow.engine.bamboo.scene.tbinlogdumper.common.exceptions import NormalTBinlogDumperFlowException +from backend.flow.engine.bamboo.scene.tbinlogdumper.common.util import get_tbinlogdumper_charset +from backend.flow.plugins.components.collections.mysql.exec_actuator_script import ExecuteDBActuatorScriptComponent +from backend.flow.plugins.components.collections.mysql.trans_flies import TransFileComponent +from backend.flow.plugins.components.collections.tbinlogdumper.dumper_data import TBinlogDumperFullSyncDataComponent +from backend.flow.plugins.components.collections.tbinlogdumper.stop_slave import TBinlogDumperStopSlaveComponent +from backend.flow.plugins.components.collections.tbinlogdumper.trans_backup_file import TBinlogDumperTransFileComponent +from backend.flow.utils.mysql.mysql_act_dataclass import DownloadMediaKwargs, ExecActuatorKwargs, P2PFileKwargs +from backend.flow.utils.mysql.mysql_act_playload import MysqlActPayload +from backend.flow.utils.tbinlogdumper.context_dataclass import StopSlaveKwargs, TBinlogDumperFullSyncDataKwargs +from backend.flow.utils.tbinlogdumper.tbinlogdumper_act_payload import TBinlogDumperActPayload + +""" +定义一些TBinlogDumper流程上可能会用到的子流程,以便于减少代码的重复率 +""" + + +def add_tbinlogdumper_sub_flow( + cluster: Cluster, + root_id: str, + uid: str, + add_conf_list: list, + created_by: str = "", +): + """ + 定义添加TBinlogdumper实例的公共子流程 + @param cluster: 待操作的集群 + @param uid: 单据uid + @param root_id: flow流程的root_id + @param add_conf_list: 本次上架的配置列表,每个的元素的格式为:{"module_id":x,"area_name":x,add_type:x} + @param created_by: 单据发起者 + """ + # 查找集群的当前master实例, tendb-ha架构无论什么时候只有一个master角色 + master = cluster.storageinstance_set.get(instance_role=InstanceRole.BACKEND_MASTER) + + # 获取TBinlogDumper的字符集配置,以mysql数据源的为准 + charset = get_tbinlogdumper_charset(ip=master.machine.ip, port=master.port, bk_cloud_id=cluster.bk_cloud_id) + + # 拼接子流程的只读全局参数 + parent_global_data = { + "uid": uid, + "add_conf_list": add_conf_list, + "bk_biz_id": cluster.bk_biz_id, + "created_by": created_by, + "charset": charset, + } + # 声明子流程 + sub_pipeline = SubBuilder(root_id=root_id, data=parent_global_data) + + # 阶段1 并行分发安装文件 + sub_pipeline.add_act( + act_name=_("下发TBinlogDumper介质包"), + act_component_code=TransFileComponent.code, + kwargs=asdict( + DownloadMediaKwargs( + bk_cloud_id=cluster.bk_cloud_id, + exec_ip=master.machine.ip, + file_list=GetFileList(db_type=DBType.MySQL).get_tbinlogdumper_package(), + ) + ), + ) + + # 阶段2 并发安装TBinlogDumper实例 + sub_pipeline.add_act( + act_name=_("安装TBinlogDumper实例"), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict( + ExecActuatorKwargs( + bk_cloud_id=cluster.bk_cloud_id, + cluster_type=cluster.cluster_type, + exec_ip=master.machine.ip, + get_mysql_payload_func=TBinlogDumperActPayload.install_tbinlogdumper_payload.__name__, + ) + ), + ) + # 返回子流程 + return sub_pipeline.build_sub_process(sub_name=_("安装TBinlogDumper实例flow")) + + +def reduce_tbinlogdumper_sub_flow( + cluster: Cluster, + root_id: str, + uid: str, + reduce_ids: list, + created_by: str = "", +): + """ + 定义针对集群维度卸载TBinlogdumper实例的公共子流程 + @param cluster: 关联的cluster信息 + @param uid: 单据uid + @param root_id: flow流程的root_id + @param reduce_ids: 本次卸载的实例id列表 + @param created_by: 单据发起者 + """ + + parent_global_data = { + "uid": uid, + "bk_biz_id": cluster.bk_biz_id, + "created_by": created_by, + } + sub_pipeline = SubBuilder(root_id=root_id, data=parent_global_data) + + # 阶段1 下发db-actuator介质包 + tbinlogdumpers = ExtraProcessInstance.objects.filter(id__in=reduce_ids) + if len(tbinlogdumpers) == 0: + # 如果根据下架的id list 获取的元信息为空,则作为异常处理 + raise NormalTBinlogDumperFlowException(message=_("传入的TBinlogDumper进程信息已不存在[{}],请联系系统管理员".format(reduce_ids))) + + # 聚合并行下发dbactor, 避免出现下发异常 + sub_pipeline.add_act( + act_name=_("下发db-actuator介质"), + act_component_code=TransFileComponent.code, + kwargs=asdict( + DownloadMediaKwargs( + bk_cloud_id=cluster.bk_cloud_id, + exec_ip=list(set([t.ip for t in tbinlogdumpers])), + file_list=GetFileList(db_type=DBType.MySQL).get_db_actuator_package(), + ) + ), + ) + + # 阶段2 按照实例并发卸载 + acts_list = [] + for inst in tbinlogdumpers: + acts_list.append( + { + "act_name": _("卸载TBinlogDumper实例[{}:{}]".format(inst.ip, inst.listen_port)), + "act_component_code": ExecuteDBActuatorScriptComponent.code, + "kwargs": asdict( + ExecActuatorKwargs( + bk_cloud_id=inst.bk_cloud_id, + exec_ip=inst.ip, + get_mysql_payload_func=TBinlogDumperActPayload.uninstall_tbinlogdumper_payload.__name__, + cluster={"listen_ports": [inst.listen_port]}, + ) + ), + } + ) + sub_pipeline.add_parallel_acts(acts_list=acts_list) + + # 返回子流程 + return sub_pipeline.build_sub_process(sub_name=_("集群[{}]卸载TBinlogDumper实例flow".format(cluster.name))) + + +def switch_sub_flow( + cluster: Cluster, + root_id: str, + uid: str, + is_safe: bool, + switch_instances: list, + created_by: str = "", +): + """ + 定义TBinlogDumper切换的子流程 + @param cluster: 操作的云区域id + @param root_id: flow流程的root_id + @param uid: 单据uid + @param is_safe: 是否做安全切换 + @param switch_instances: 待切换的TBinlogDumper实例对列表 + @param created_by: 单据发起者 + """ + + # 先获取集群的最新的master对象 + master = cluster.storageinstance_set.get(instance_role=InstanceRole.BACKEND_MASTER) + + # 拼接子流程的全局只读参数 + parent_global_data = { + "uid": uid, + "bk_biz_id": cluster.bk_biz_id, + "created_by": created_by, + } + + sub_pipeline = SubBuilder(root_id=root_id, data=parent_global_data) + sub_sub_pipelines = [] + + # 根据传入的待切换部署 TBinlogDumper id 列表变量,做切换处理 + for inst in switch_instances: + old_dumper = ExtraProcessInstance.objects.get(id=inst["reduce_id"]) + + # 按照实例维度声明子流程 + sub_sub_pipeline = SubBuilder(root_id=root_id, data=parent_global_data) + + # 旧实例断开同步 + sub_sub_pipeline.add_act( + act_name=_("中断同步"), + act_component_code=TBinlogDumperStopSlaveComponent.code, + kwargs=asdict( + StopSlaveKwargs( + bk_cloud_id=cluster.bk_cloud_id, + is_safe=is_safe, + tbinlogdumper_ip=old_dumper.ip, + tbinlogdumper_port=old_dumper.listen_port, + ) + ), + ) + + # 增加同步账号 + sub_sub_pipeline.add_act( + act_name=_("新增repl帐户"), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict( + ExecActuatorKwargs( + bk_cloud_id=cluster.bk_cloud_id, + exec_ip=master.machine.ip, + get_mysql_payload_func=MysqlActPayload.get_grant_mysql_repl_user_payload.__name__, + cluster={"new_slave_ip": master.machine.ip, "mysql_port": inst["port"]}, + run_as_system_user=DBA_SYSTEM_USER, + ) + ), + ) + + # 根据传入的位点信息建立数据同步关系 + sub_sub_pipeline.add_act( + act_name=_("建立主从关系"), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict( + ExecActuatorKwargs( + bk_cloud_id=cluster.bk_cloud_id, + exec_ip=master.machine.ip, + get_mysql_payload_func=TBinlogDumperActPayload.tbinlogdumper_sync_data_payload.__name__, + cluster={ + "master_ip": master.machine.ip, + "master_port": master.port, + "listen_port": inst["port"], + "bin_file": inst["repl_binlog_file"], + "bin_position": inst["repl_binlog_pos"], + }, + run_as_system_user=DBA_SYSTEM_USER, + ) + ), + ) + sub_sub_pipelines.append( + sub_sub_pipeline.build_sub_process(sub_name=_("切换到新实例[{}:{}]".format(master.machine.ip, inst["port"]))) + ) + + # 在将实例子流程聚合到上层 + sub_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_sub_pipelines) + + return sub_pipeline.build_sub_process(sub_name=_("集群[{}]切换TBinlogDumper".format(cluster.name))) + + +def incr_sync_sub_flow( + cluster: Cluster, + root_id: str, + uid: str, + add_tbinlogdumper_conf: dict, + created_by: str = "", +): + """ + 定义TBinlogDumper增量同步的子流程 + @param cluster: 操作的云区域id + @param root_id: flow流程的root_id + @param uid: 单据uid + @param add_tbinlogdumper_conf: 待添加TBinlogdumper的实例配置 + @param created_by: 单据发起者 + """ + # 先获取集群的最新的master对象 + master = cluster.storageinstance_set.get(instance_role=InstanceRole.BACKEND_MASTER) + + # 拼接子流程的全局只读参数 + parent_global_data = { + "uid": uid, + "bk_biz_id": cluster.bk_biz_id, + "created_by": created_by, + "cluster_id": cluster.id, + "add_tbinlogdumper_conf": add_tbinlogdumper_conf, + } + + # 声明子流程 + sub_pipeline = SubBuilder(root_id=root_id, data=parent_global_data) + + # 阶段1 对新TBinlogDumper做全表结构导入 + sub_pipeline.add_act( + act_name=_("导入相关表结构"), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict( + ExecActuatorKwargs( + bk_cloud_id=cluster.bk_cloud_id, + exec_ip=master.machine.ip, + get_mysql_payload_func=TBinlogDumperActPayload.tbinlogdumper_load_schema_payload.__name__, + run_as_system_user=DBA_SYSTEM_USER, + ) + ), + ) + + # 阶段2 对新TBinlogDumper跟数据源做数据同步 + sub_pipeline.add_sub_pipeline( + build_repl_by_manual_input_sub_flow( + bk_cloud_id=cluster.bk_cloud_id, + root_id=root_id, + parent_global_data=parent_global_data, + master_ip=master.machine.ip, + slave_ip=master.machine.ip, + master_port=master.port, + slave_port=add_tbinlogdumper_conf["port"], + ) + ) + # 返回子流程 + return sub_pipeline.build_sub_process( + sub_name=_("实例TBinlogDumper[{}:{}]做增量同步".format(master.machine.ip, add_tbinlogdumper_conf["port"])) + ) + + +def full_sync_sub_flow( + cluster: Cluster, + root_id: str, + uid: str, + add_tbinlogdumper_conf: dict, + created_by: str = "", +): + """ + 定义TBinlogDumper全量同步的子流程 + @param cluster: 操作的云区域id + @param root_id: flow流程的root_id + @param uid: 单据uid + @param add_tbinlogdumper_conf: 待添加TBinlogdumper的实例配置 + @param created_by: 单据发起者 + """ + # 先获取集群的最新的master、backup对象 + master = cluster.storageinstance_set.get(instance_role=InstanceRole.BACKEND_MASTER) + backup = cluster.storageinstance_set.get(instance_role=InstanceRole.BACKEND_SLAVE, is_stand_by=True) + + # 拼接子流程的全局只读参数 + parent_global_data = { + "uid": uid, + "bk_biz_id": cluster.bk_biz_id, + "created_by": created_by, + "cluster_id": cluster.id, + "add_tbinlogdumper_conf": add_tbinlogdumper_conf, + "backup_id": uuid.uuid1(), + } + + # 声明子流程 + sub_pipeline = SubBuilder(root_id=root_id, data=parent_global_data) + + # 阶段1 对新TBinlogDumper做全表结构导入 + sub_pipeline.add_act( + act_name=_("导入相关表结构"), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict( + ExecActuatorKwargs( + bk_cloud_id=cluster.bk_cloud_id, + exec_ip=master.machine.ip, + get_mysql_payload_func=TBinlogDumperActPayload.tbinlogdumper_load_schema_payload.__name__, + run_as_system_user=DBA_SYSTEM_USER, + ) + ), + ) + + # 阶段2 添加同步账号 + sub_pipeline.add_act( + act_name=_("新增repl帐户"), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict( + ExecActuatorKwargs( + bk_cloud_id=cluster.bk_cloud_id, + exec_ip=master.machine.ip, + get_mysql_payload_func=MysqlActPayload.get_grant_mysql_repl_user_payload.__name__, + cluster={"new_slave_ip": master.machine.ip, "mysql_port": master.port}, + run_as_system_user=DBA_SYSTEM_USER, + ) + ), + write_payload_var="master_ip_sync_info", + ) + + # 阶段3 根据TBinlogDumper的同步数据配置,在从节点触发一次逻辑备份请求 + sub_pipeline.add_act( + act_name=_("在slave[{}:{}]备份数据".format(backup.machine.ip, backup.port)), + act_component_code=TBinlogDumperFullSyncDataComponent.code, + kwargs=asdict( + TBinlogDumperFullSyncDataKwargs( + bk_cloud_id=cluster.bk_cloud_id, + backup_ip=backup.machine.ip, + backup_port=backup.port, + backup_role=backup.instance_role, + module_id=add_tbinlogdumper_conf["module_id"], + ) + ), + write_payload_var="backup_info", + ) + + # 阶段4 备份文件传输到TBinlogDumper机器,做导入数据准备 + sub_pipeline.add_act( + act_name=_("传输备份文件到TBinlogDumper[{}]".format(master.machine.ip)), + act_component_code=TBinlogDumperTransFileComponent.code, + kwargs=asdict( + P2PFileKwargs( + bk_cloud_id=cluster.bk_cloud_id, + file_list=[], + file_target_path="", + source_ip_list=[backup.machine.ip], + exec_ip=master.machine.ip, + run_as_system_user=DBA_SYSTEM_USER, + ) + ), + ) + + # 阶段5 发起导入备份数据,同步与数据源建立同步 + sub_pipeline.add_act( + act_name=_("导入备份数据"), + act_component_code=ExecuteDBActuatorScriptComponent.code, + kwargs=asdict( + ExecActuatorKwargs( + bk_cloud_id=cluster.bk_cloud_id, + exec_ip=master.machine.ip, + get_mysql_payload_func=TBinlogDumperActPayload.tbinlogdumper_restore_payload.__name__, + run_as_system_user=DBA_SYSTEM_USER, + ) + ), + ) + + # 返回子流程 + return sub_pipeline.build_sub_process( + sub_name=_("实例TBinlogDumper[{}:{}]做全量同步".format(master.machine.ip, add_tbinlogdumper_conf["port"])) + ) diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/tbinlogdumper/common/exceptions.py b/dbm-ui/backend/flow/engine/bamboo/scene/tbinlogdumper/common/exceptions.py new file mode 100644 index 0000000000..7c02b60c5d --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/tbinlogdumper/common/exceptions.py @@ -0,0 +1,23 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 as _ + +from backend.exceptions import AppBaseException, ErrorCode + + +class TBinlogDumperFlowBaseException(AppBaseException): + MODULE_CODE = ErrorCode.FLOW_CODE + MESSAGE = _("Flow模块TBinlogDumper异常") + + +class NormalTBinlogDumperFlowException(TBinlogDumperFlowBaseException): + ERROR_CODE = "001" + MESSAGE = _("通用异常") + MESSAGE_TPL = _("{message}") diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/tbinlogdumper/common/util.py b/dbm-ui/backend/flow/engine/bamboo/scene/tbinlogdumper/common/util.py new file mode 100644 index 0000000000..435ca251ea --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/tbinlogdumper/common/util.py @@ -0,0 +1,88 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 as _ + +from backend.components import DRSApi +from backend.constants import IP_PORT_DIVIDER +from backend.db_meta.enums import ClusterType +from backend.db_meta.exceptions import ClusterNotExistException +from backend.db_meta.models import Cluster, Machine, StorageInstance +from backend.db_meta.models.extra_process import ExtraProcessInstance +from backend.flow.consts import TBINLOGDUMPER_PORT +from backend.flow.engine.bamboo.scene.tbinlogdumper.common.exceptions import NormalTBinlogDumperFlowException + + +def get_tbinlogdumper_install_port(machine: Machine, install_num: int) -> list: + """ + 根据ip以及当前部署的实例数量,返回预安装的端口列表 + """ + install_ports = [] + max_port = 40000 + default_port = TBINLOGDUMPER_PORT + for i in range(0, install_num): + while default_port <= max_port: + if ( + ExtraProcessInstance.objects.filter( + bk_cloud_id=machine.bk_cloud_id, ip=machine.ip, listen_port=default_port + ).exists() + or StorageInstance.objects.filter(machine=machine, port=default_port).exists() + ): + + # 如果端口在元信息记录部署,则往上添加100 + default_port += 100 + + else: + break + + if default_port <= max_port: + install_ports.append(default_port) + default_port += 100 + + if len(install_ports) != install_num: + raise NormalTBinlogDumperFlowException( + message=_("获取预安装端口失败, 期望生成端口数:{},实际生成端口数:{}".format(install_num, len(install_ports))) + ) + + return list(set(install_ports)) + + +def get_tbinlogdumper_charset(ip: str, port: int, bk_cloud_id: int) -> str: + """ + 根据传进来的ip:port, 查询实例对应字符集配置 + """ + + res = DRSApi.rpc( + { + "addresses": [f"{ip}{IP_PORT_DIVIDER}{port}"], + "cmds": ["show global variables like 'character_set_server'"], + "force": False, + "bk_cloud_id": bk_cloud_id, + } + ) + if res[0]["error_msg"]: + raise NormalTBinlogDumperFlowException(message=_("get charset failed: {}".format(res[0]["error_msg"]))) + + table_data = res[0]["cmd_results"][0]["table_data"] + return table_data[0]["Value"] + + +def get_cluster(cluster_id: int, bk_biz_id: int) -> Cluster: + """ + 根据传入的clsuter_id 和 业务id,判断是否是HA类型集群 + 如果是则返回cluster对象 + """ + try: + cluster = Cluster.objects.get(id=cluster_id, bk_biz_id=bk_biz_id) + except Cluster.DoesNotExist: + raise ClusterNotExistException(cluster_id=cluster_id, bk_biz_id=bk_biz_id, message=_("集群不存在")) + if cluster.cluster_type != ClusterType.TenDBHA: + raise NormalTBinlogDumperFlowException(message=_("非TenDB-HA架构不支持添加TBinlogDumper实例")) + + return cluster diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/tbinlogdumper/reduce_node.py b/dbm-ui/backend/flow/engine/bamboo/scene/tbinlogdumper/reduce_node.py new file mode 100644 index 0000000000..488a63eb17 --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/tbinlogdumper/reduce_node.py @@ -0,0 +1,90 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +import copy +import logging.config +from dataclasses import asdict +from typing import Dict, Optional + +from django.utils.translation import ugettext as _ + +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.engine.bamboo.scene.tbinlogdumper.common.common_sub_flow import reduce_tbinlogdumper_sub_flow +from backend.flow.engine.bamboo.scene.tbinlogdumper.common.exceptions import NormalTBinlogDumperFlowException +from backend.flow.engine.bamboo.scene.tbinlogdumper.common.util import get_cluster +from backend.flow.plugins.components.collections.mysql.mysql_db_meta import MySQLDBMetaComponent +from backend.flow.utils.mysql.mysql_act_dataclass import DBMetaOPKwargs +from backend.flow.utils.mysql.mysql_db_meta import MySQLDBMeta + +logger = logging.getLogger("flow") + + +class TBinlogDumperReduceNodesFlow(object): + """ + 构建 tbinlogdumper节点删除, 按集群维度去聚合卸载 + 目前仅支持 tendb-ha 架构 + 支持不同云区域的合并操作 + """ + + def __init__(self, root_id: str, data: Optional[Dict]): + """ + @param root_id : 任务流程定义的root_id + @param data : 单据传递参数 + """ + self.root_id = root_id + self.data = data + + def reduce_nodes(self): + + pipeline = Builder(root_id=self.root_id, data=self.data) + sub_pipelines = [] + for info in self.data["infos"]: + + # 获取对应集群相关对象 + cluster = get_cluster(cluster_id=int(info["cluster_id"]), bk_biz_id=int(self.data["bk_biz_id"])) + + # 启动子流程 + sub_flow_context = copy.deepcopy(self.data) + sub_flow_context.pop("infos") + # 拼接子流程的全局参数 + sub_flow_context.update(info) + sub_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(sub_flow_context)) + + # 按集群维度卸载TBinlogDumper实例 + sub_pipeline.add_sub_pipeline( + sub_flow=reduce_tbinlogdumper_sub_flow( + cluster=cluster, + root_id=self.root_id, + uid=self.data["uid"], + reduce_ids=info["reduce_ids"], + created_by=self.data["created_by"], + ) + ) + + # 删除元数据 + sub_pipeline.add_act( + act_name=_("删除实例元信息"), + act_component_code=MySQLDBMetaComponent.code, + kwargs=asdict( + DBMetaOPKwargs( + db_meta_class_func=MySQLDBMeta.reduce_tbinlogdumper.__name__, + ) + ), + ) + + sub_pipelines.append( + sub_pipeline.build_sub_process(sub_name=_("[{}]下架TBinlogDumper实例".format(cluster.name))) + ) + + if not sub_pipelines: + raise NormalTBinlogDumperFlowException(message=_("找不到需要下架的实例,拼装TBinlogDumper下架流程失败")) + + pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) + pipeline.run_pipeline() diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/tbinlogdumper/switch_nodes.py b/dbm-ui/backend/flow/engine/bamboo/scene/tbinlogdumper/switch_nodes.py new file mode 100644 index 0000000000..d40b705f09 --- /dev/null +++ b/dbm-ui/backend/flow/engine/bamboo/scene/tbinlogdumper/switch_nodes.py @@ -0,0 +1,180 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +import copy +import logging.config +from dataclasses import asdict +from typing import Dict, Optional + +from django.utils.translation import ugettext as _ + +from backend.db_meta.enums import InstanceRole +from backend.db_meta.enums.extra_process_type import ExtraProcessType +from backend.db_meta.models import Cluster +from backend.db_meta.models.extra_process import ExtraProcessInstance +from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder +from backend.flow.engine.bamboo.scene.tbinlogdumper.common.common_sub_flow import ( + add_tbinlogdumper_sub_flow, + reduce_tbinlogdumper_sub_flow, + switch_sub_flow, +) +from backend.flow.engine.bamboo.scene.tbinlogdumper.common.exceptions import NormalTBinlogDumperFlowException +from backend.flow.engine.bamboo.scene.tbinlogdumper.common.util import get_cluster +from backend.flow.plugins.components.collections.mysql.mysql_db_meta import MySQLDBMetaComponent +from backend.flow.utils.mysql.mysql_act_dataclass import DBMetaOPKwargs +from backend.flow.utils.mysql.mysql_db_meta import MySQLDBMeta + +logger = logging.getLogger("flow") + + +class TBinlogDumperSwitchNodesFlow(object): + """ + 构建 tbinlogdumper 节点迁移部署,目的是为了保证节点在最新的master机器上部署 + 目前仅支持 tendb-ha 架构 + 支持不同云区域的合并操作 + """ + + def __init__(self, root_id: str, data: Optional[Dict]): + """ + @param root_id : 任务流程定义的root_id + @param data : 单据传递参数 + """ + self.root_id = root_id + self.data = data + + @staticmethod + def _get_real_switch_inst_for_cluster(cluster: Cluster, switch_instances: list) -> list: + """ + 根据传入的cluster对象以及待切换的TBinlogDumper实例列表 + @param cluster: 集群model + @param switch_instances: 待切换TBinlogDumper实例列表 + """ + real_switch_instances = [] + master = cluster.storageinstance_set.get(instance_role=InstanceRole.BACKEND_MASTER) + + for instance in switch_instances: + # 判断节点是否在元信息记录 + try: + binlogdumper = ExtraProcessInstance.objects.get( + ip=instance["host"], + bk_cloud_id=cluster.bk_cloud_id, + listen_port=instance["port"], + proc_type=ExtraProcessType.TBINLOGDUMPER, + ) + except ExtraProcessInstance.DoesNotExist: + logger.warning( + f"TBinlogDumper node [{instance['host']}:{instance['port']}] " + f"does not exist in cluster [{cluster.name}]" + ) + continue + + # 判断当前每个TBinlogDumper实例是否和当前的master一致,如果一致,跳过这次的迁移 + if master.machine.ip == instance["host"]: + logger.warning( + f"The current TBinlogDumper instance " + f"[{instance['host']}:instance{'port'}] is on master [{master.machine.ip},skip]" + ) + continue + + tmp = { + "module_id": binlogdumper.extra_config["module_id"], + "area_name": binlogdumper.extra_config["area_name"], + "port": binlogdumper.listen_port, + "repl_binlog_file": instance["repl_binlog_file"], + "repl_binlog_pos": instance["repl_binlog_pos"], + "reduce_id": binlogdumper.id, + } + real_switch_instances.append(tmp) + + return real_switch_instances + + def switch_nodes(self): + """ + 定义TBinlogDumper切换过程 + """ + pipeline = Builder(root_id=self.root_id, data=self.data) + sub_pipelines = [] + for info in self.data["infos"]: + # 获取对应集群相关对象 + cluster = get_cluster(cluster_id=int(info["cluster_id"]), bk_biz_id=int(self.data["bk_biz_id"])) + + # 获取真正需要迁移的实例对象 + real_switch_instances = self._get_real_switch_inst_for_cluster(cluster, info["switch_instances"]) + + if len(real_switch_instances) == 0: + logger.warning( + f"There is no TBinlogdumper that needs to be " + f"migrated and deployed in this cluster [{cluster.name}]" + ) + continue + + # 启动子流程 + sub_flow_context = copy.deepcopy(self.data) + sub_flow_context.pop("infos") + # 拼接子流程的全局参数 + sub_flow_context.update(info) + + sub_pipeline = SubBuilder(root_id=self.root_id, data=copy.deepcopy(sub_flow_context)) + + # 阶段1 安装新实例 + sub_pipeline.add_sub_pipeline( + sub_flow=add_tbinlogdumper_sub_flow( + cluster=cluster, + root_id=self.root_id, + uid=self.data["uid"], + add_conf_list=real_switch_instances, + created_by=self.data["created_by"], + ) + ) + + # 阶段2 关闭旧实例的同步,同时新实例同步位点数据 + sub_pipeline.add_sub_pipeline( + sub_flow=switch_sub_flow( + cluster=cluster, + root_id=self.root_id, + uid=self.data["uid"], + is_safe=self.data["is_safe"], + switch_instances=real_switch_instances, + created_by=self.data["created_by"], + ) + ) + + # 阶段3 卸载旧实例 + sub_pipeline.add_sub_pipeline( + sub_flow=reduce_tbinlogdumper_sub_flow( + cluster=cluster, + root_id=self.root_id, + uid=self.data["uid"], + reduce_ids=[i["reduce_id"] for i in real_switch_instances], + created_by=self.data["created_by"], + ) + ) + + # 阶段4 修改元数据 + sub_pipeline.add_act( + act_name=_("变更实例元信息"), + act_component_code=MySQLDBMetaComponent.code, + kwargs=asdict( + DBMetaOPKwargs( + db_meta_class_func=MySQLDBMeta.switch_tbinlogdumper.__name__, + cluster={"switch_ids": [i["reduce_id"] for i in real_switch_instances]}, + ) + ), + ) + + sub_pipelines.append( + sub_pipeline.build_sub_process(sub_name=_("[{}]集群迁移TBinlogDumper实例".format(cluster.name))) + ) + + if not sub_pipelines: + raise NormalTBinlogDumperFlowException(message=_("没检测到需要迁移的实例,拼装TBinlogDumper迁移部署流程失败")) + + pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines) + pipeline.run_pipeline() diff --git a/dbm-ui/backend/flow/engine/controller/mysql.py b/dbm-ui/backend/flow/engine/controller/mysql.py index 130b3f67ef..14e546927f 100644 --- a/dbm-ui/backend/flow/engine/controller/mysql.py +++ b/dbm-ui/backend/flow/engine/controller/mysql.py @@ -23,9 +23,11 @@ from backend.flow.engine.bamboo.scene.mysql.mysql_ha_disable_flow import MySQLHADisableFlow from backend.flow.engine.bamboo.scene.mysql.mysql_ha_enable_flow import MySQLHAEnableFlow from backend.flow.engine.bamboo.scene.mysql.mysql_ha_full_backup_flow import MySQLHAFullBackupFlow +from backend.flow.engine.bamboo.scene.mysql.mysql_ha_standardize_flow import MySQLHAStandardizeFlow from backend.flow.engine.bamboo.scene.mysql.mysql_master_fail_over import MySQLMasterFailOverFlow from backend.flow.engine.bamboo.scene.mysql.mysql_master_slave_switch import MySQLMasterSlaveSwitchFlow from backend.flow.engine.bamboo.scene.mysql.mysql_migrate_cluster_flow import MySQLMigrateClusterFlow +from backend.flow.engine.bamboo.scene.mysql.mysql_open_area_flow import MysqlOpenAreaFlow from backend.flow.engine.bamboo.scene.mysql.mysql_partition import MysqlPartitionFlow from backend.flow.engine.bamboo.scene.mysql.mysql_proxy_cluster_add import MySQLProxyClusterAddFlow from backend.flow.engine.bamboo.scene.mysql.mysql_proxy_cluster_switch import MySQLProxyClusterSwitchFlow @@ -224,7 +226,7 @@ def mysql_ha_truncate_data_scene(self): "created_by": "xxx", "bk_biz_id": "152", "ticket_type": "MYSQL_HA_TRUNCATE_DATA", - "truncate_data_infos": [ + "infos": [ { "cluster_id": str, "db_patterns": ["db1%", "db2%"], @@ -443,7 +445,7 @@ def mysql_single_truncate_data_scene(self): "created_by": "xxx", "bk_biz_id": "152", "ticket_type": "MYSQL_SINGLE_TRUNCATE_DATA", - "truncate_data_infos": [ + "infos": [ { "cluster_id": str, "db_patterns": ["db1%", "db2%"], @@ -496,3 +498,11 @@ def mysql_single_rename_database_scene(self): root_id=self.root_id, data=self.ticket_data, cluster_type=ClusterType.TenDBSingle.value ) flow.rename_database() + + def mysql_ha_standardize_scene(self): + flow = MySQLHAStandardizeFlow(root_id=self.root_id, data=self.ticket_data) + flow.standardize() + + def mysql_open_area_scene(self): + flow = MysqlOpenAreaFlow(root_id=self.root_id, data=self.ticket_data) + flow.mysql_open_area_flow() diff --git a/dbm-ui/backend/flow/engine/controller/name_service.py b/dbm-ui/backend/flow/engine/controller/name_service.py index af89bd6549..ae8d6725ae 100644 --- a/dbm-ui/backend/flow/engine/controller/name_service.py +++ b/dbm-ui/backend/flow/engine/controller/name_service.py @@ -22,25 +22,39 @@ def clb_create(self): 创建clb """ flow = NameServiceFlow(root_id=self.root_id, data=self.ticket_data) - flow.name_service_clb_create_flow() + flow.clb_create_flow() def clb_delete(self): """ 删除clb """ flow = NameServiceFlow(root_id=self.root_id, data=self.ticket_data) - flow.name_service_clb_delete_flow() + flow.clb_delete_flow() + + def immute_domain_bind_clb_ip(self): + """ + 主域名绑定clb ip + """ + flow = NameServiceFlow(root_id=self.root_id, data=self.ticket_data) + flow.immute_domain_bind_clb_ip() + + def immute_domain_unbind_clb_ip(self): + """ + 主域名解绑clb ip + """ + flow = NameServiceFlow(root_id=self.root_id, data=self.ticket_data) + flow.immute_domain_unbind_clb_ip() def polaris_create(self): """ 创建polaris """ flow = NameServiceFlow(root_id=self.root_id, data=self.ticket_data) - flow.name_service_polaris_create_flow() + flow.polaris_create_flow() def polaris_delete(self): """ 删除polaris """ flow = NameServiceFlow(root_id=self.root_id, data=self.ticket_data) - flow.name_service_polaris_delete_flow() + flow.polaris_delete_flow() diff --git a/dbm-ui/backend/flow/engine/controller/redis.py b/dbm-ui/backend/flow/engine/controller/redis.py index c9d0669225..14ac5a8c0b 100644 --- a/dbm-ui/backend/flow/engine/controller/redis.py +++ b/dbm-ui/backend/flow/engine/controller/redis.py @@ -8,18 +8,29 @@ 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 backend.flow.engine.bamboo.scene.redis.redis_add_dts_server import RedisAddDtsServerFlow +from backend.flow.engine.bamboo.scene.redis.redis_backend_scale import RedisBackendScaleFlow +from backend.flow.engine.bamboo.scene.redis.redis_cluster_add_slave import RedisClusterAddSlaveFlow from backend.flow.engine.bamboo.scene.redis.redis_cluster_apply_flow import RedisClusterApplyFlow from backend.flow.engine.bamboo.scene.redis.redis_cluster_backup import RedisClusterBackupFlow -from backend.flow.engine.bamboo.scene.redis.redis_cluster_dts import RedisClusterDtsFlow +from backend.flow.engine.bamboo.scene.redis.redis_cluster_data_check_repair import RedisClusterDataCheckRepairFlow +from backend.flow.engine.bamboo.scene.redis.redis_cluster_data_copy import RedisClusterDataCopyFlow +from backend.flow.engine.bamboo.scene.redis.redis_cluster_instance_shutdown import ( + RedisClusterInstanceShutdownSceneFlow, +) from backend.flow.engine.bamboo.scene.redis.redis_cluster_open_close import RedisClusterOpenCloseFlow -from backend.flow.engine.bamboo.scene.redis.redis_cluster_scene_master import RedisClusterMasterSceneFlow -from backend.flow.engine.bamboo.scene.redis.redis_cluster_scene_slave import RedisClusterSlaveSceneFlow +from backend.flow.engine.bamboo.scene.redis.redis_cluster_scene_auotfix import RedisClusterAutoFixSceneFlow +from backend.flow.engine.bamboo.scene.redis.redis_cluster_scene_cmr import RedisClusterCMRSceneFlow +from backend.flow.engine.bamboo.scene.redis.redis_cluster_scene_mss import RedisClusterMSSSceneFlow from backend.flow.engine.bamboo.scene.redis.redis_cluster_shutdown import RedisClusterShutdownFlow +from backend.flow.engine.bamboo.scene.redis.redis_data_structure import RedisDataStructureFlow +from backend.flow.engine.bamboo.scene.redis.redis_data_structure_task_delete import RedisDataStructureTaskDeleteFlow from backend.flow.engine.bamboo.scene.redis.redis_dbmon import RedisDbmonSceneFlow from backend.flow.engine.bamboo.scene.redis.redis_flush_data import RedisFlushDataFlow from backend.flow.engine.bamboo.scene.redis.redis_keys_delete import RedisKeysDeleteFlow from backend.flow.engine.bamboo.scene.redis.redis_keys_extract import RedisKeysExtractFlow from backend.flow.engine.bamboo.scene.redis.redis_proxy_scale import RedisProxyScaleFlow +from backend.flow.engine.bamboo.scene.redis.redis_remove_dts_server import RedisRemoveDtsServerFlow from backend.flow.engine.bamboo.scene.redis.singele_redis_shutdown import SingleRedisShutdownFlow from backend.flow.engine.bamboo.scene.redis.single_proxy_shutdown import SingleProxyShutdownFlow from backend.flow.engine.bamboo.scene.redis.tendis_plus_apply_flow import TendisPlusApplyFlow @@ -103,24 +114,45 @@ def redis_flush_data(self): def redis_proxy_scale(self): """ - proxy 新增、删除、替换 + proxy 新增、删除 """ flow = RedisProxyScaleFlow(root_id=self.root_id, data=self.ticket_data) flow.redis_proxy_scale_flow() - def redis_cluster_slave_cutoff_scene(self): + def redis_backend_scale(self): """ - tendis 集群版, slave 裁撤、迁移场景 + redis后端扩缩容 """ - flow = RedisClusterSlaveSceneFlow(root_id=self.root_id, data=self.ticket_data) - flow.work_4_replace() + flow = RedisBackendScaleFlow(root_id=self.root_id, data=self.ticket_data) + flow.redis_backend_scale_flow() - def redis_cluster_master_cutoff_scene(self): + def redis_cluster_cutoff_scene(self): """ - tendis 集群版, master 裁撤、迁移场景 (成对: master & slave) + tendis 集群版, master/slave/proxy 裁撤、迁移场景 """ - flow = RedisClusterMasterSceneFlow(root_id=self.root_id, data=self.ticket_data) - flow.work_4_auotfix() + flow = RedisClusterCMRSceneFlow(root_id=self.root_id, data=self.ticket_data) + flow.complete_machine_replace() + + def redis_cluster_auotfix_scene(self): + """ + tendis 集群版, slave/proxy 故障自愈 + """ + flow = RedisClusterAutoFixSceneFlow(root_id=self.root_id, data=self.ticket_data) + flow.start_redis_auotfix() + + def redis_cluster_instance_shutdown(self): + """ + 提交实例下架单据 + """ + flow = RedisClusterInstanceShutdownSceneFlow(root_id=self.root_id, data=self.ticket_data) + flow.start_instance_shutdown() + + def redis_cluster_failover_scene(self): + """ + tendis 集群版, master slave 故障切换 + """ + flow = RedisClusterMSSSceneFlow(root_id=self.root_id, data=self.ticket_data) + flow.redis_ms_switch() def redis_install_dbmon_scene(self): """ @@ -129,9 +161,72 @@ def redis_install_dbmon_scene(self): flow = RedisDbmonSceneFlow(root_id=self.root_id, data=self.ticket_data) flow.batch_update_dbmon() - def redis_dts(self): + def redis_cluster_data_copy(self): + """ + redis 数据复制 + """ + flow = RedisClusterDataCopyFlow(root_id=self.root_id, data=self.ticket_data) + flow.redis_cluster_data_copy_flow() + + def redis_cluster_shard_num_update(self): + """ + redis 集群分片变更 + """ + flow = RedisClusterDataCopyFlow(root_id=self.root_id, data=self.ticket_data) + flow.shard_num_or_cluster_type_update_flow() + + def redis_cluster_type_update(self): + """ + redis 集群类型变更 + """ + flow = RedisClusterDataCopyFlow(root_id=self.root_id, data=self.ticket_data) + flow.shard_num_or_cluster_type_update_flow() + + def redis_dts_online_switch(self): + """ + redis dts在线切换 + """ + flow = RedisClusterDataCopyFlow(root_id=self.root_id, data=self.ticket_data) + flow.online_switch_flow() + + def redis_cluster_data_check_repair(self): + """ + redis 数据校验与修复 + """ + flow = RedisClusterDataCheckRepairFlow(root_id=self.root_id, data=self.ticket_data) + flow.redis_cluster_data_check_repair_flow() + + def redis_add_dts_server(self): + """ + redis add dts server + """ + flow = RedisAddDtsServerFlow(root_id=self.root_id, data=self.ticket_data) + flow.redis_add_dts_server_flow() + + def redis_remove_dts_server(self): + """ + redis remove dts server + """ + flow = RedisRemoveDtsServerFlow(root_id=self.root_id, data=self.ticket_data) + flow.redis_remove_dts_server_flow() + + def redis_data_structure(self): + """ + redis 数据构造 + """ + flow = RedisDataStructureFlow(root_id=self.root_id, data=self.ticket_data) + flow.redis_data_structure_flow() + + def redis_data_structure_task_delete(self): + """ + redis 数据构造 + """ + flow = RedisDataStructureTaskDeleteFlow(root_id=self.root_id, data=self.ticket_data) + flow.redis_rollback_task_delete_flow() + + def redis_cluster_add_slave(self): """ - redis 数据迁移 + redis 新建从库 """ - flow = RedisClusterDtsFlow(root_id=self.root_id, data=self.ticket_data) - flow.redis_cluster_dts_flow() + flow = RedisClusterAddSlaveFlow(root_id=self.root_id, data=self.ticket_data) + flow.add_slave_flow() diff --git a/dbm-ui/backend/flow/engine/controller/riak.py b/dbm-ui/backend/flow/engine/controller/riak.py index d42d3c38e9..2a59b8f771 100644 --- a/dbm-ui/backend/flow/engine/controller/riak.py +++ b/dbm-ui/backend/flow/engine/controller/riak.py @@ -9,6 +9,11 @@ specific language governing permissions and limitations under the License. """ from backend.flow.engine.bamboo.scene.riak.riak_cluster_apply_flow import RiakClusterApplyFlow +from backend.flow.engine.bamboo.scene.riak.riak_cluster_destroy_flow import RiakClusterDestroyFlow +from backend.flow.engine.bamboo.scene.riak.riak_cluster_disable_flow import RiakClusterDisableFlow +from backend.flow.engine.bamboo.scene.riak.riak_cluster_enable_flow import RiakClusterEnableFlow +from backend.flow.engine.bamboo.scene.riak.riak_cluster_scale_in_flow import RiakClusterScaleInFlow +from backend.flow.engine.bamboo.scene.riak.riak_cluster_scale_out_flow import RiakClusterScaleOutFlow from backend.flow.engine.controller.base import BaseController @@ -23,3 +28,38 @@ def riak_cluster_apply_scene(self): """ flow = RiakClusterApplyFlow(root_id=self.root_id, data=self.ticket_data) flow.deploy_riak_cluster_flow() + + def riak_cluster_scale_out_scene(self): + """ + riak集群扩容场景 + """ + flow = RiakClusterScaleOutFlow(root_id=self.root_id, data=self.ticket_data) + flow.riak_cluster_scale_out_flow() + + def riak_cluster_scale_in_scene(self): + """ + riak集群缩容场景 + """ + flow = RiakClusterScaleInFlow(root_id=self.root_id, data=self.ticket_data) + flow.riak_cluster_scale_in_flow() + + def riak_cluster_destroy_scene(self): + """ + riak集群下架 + """ + flow = RiakClusterDestroyFlow(root_id=self.root_id, data=self.ticket_data) + flow.riak_cluster_destroy_flow() + + def riak_cluster_disable_scene(self): + """ + riak集群下架 + """ + flow = RiakClusterDisableFlow(root_id=self.root_id, data=self.ticket_data) + flow.riak_cluster_disable_flow() + + def riak_cluster_enable_scene(self): + """ + riak集群下架 + """ + flow = RiakClusterEnableFlow(root_id=self.root_id, data=self.ticket_data) + flow.riak_cluster_enable_flow() diff --git a/dbm-ui/backend/flow/engine/controller/spider.py b/dbm-ui/backend/flow/engine/controller/spider.py index 2bbecfe994..cc5d95bdcf 100644 --- a/dbm-ui/backend/flow/engine/controller/spider.py +++ b/dbm-ui/backend/flow/engine/controller/spider.py @@ -10,19 +10,28 @@ from backend.db_meta.enums import ClusterType from backend.flow.engine.bamboo.scene.spider.import_sqlfile_flow import ImportSQLFlow +from backend.flow.engine.bamboo.scene.spider.remote_master_fail_over import RemoteMasterFailOverFlow +from backend.flow.engine.bamboo.scene.spider.remote_master_slave_swtich import RemoteMasterSlaveSwitchFlow +from backend.flow.engine.bamboo.scene.spider.spider_add_mnt import TenDBClusterAddSpiderMNTFlow from backend.flow.engine.bamboo.scene.spider.spider_add_nodes import TenDBClusterAddNodesFlow -from backend.flow.engine.bamboo.scene.spider.spider_add_tmp_node import SpiderAddTmpNodeFlow from backend.flow.engine.bamboo.scene.spider.spider_checksum import SpiderChecksumFlow from backend.flow.engine.bamboo.scene.spider.spider_cluster_db_table_backup import TenDBClusterDBTableBackupFlow from backend.flow.engine.bamboo.scene.spider.spider_cluster_deploy import TenDBClusterApplyFlow from backend.flow.engine.bamboo.scene.spider.spider_cluster_destroy import TenDBClusterDestroyFlow from backend.flow.engine.bamboo.scene.spider.spider_cluster_disable_deploy import SpiderClusterDisableFlow from backend.flow.engine.bamboo.scene.spider.spider_cluster_enable_deploy import SpiderClusterEnableFlow +from backend.flow.engine.bamboo.scene.spider.spider_cluster_flashback import TenDBClusterFlashbackFlow from backend.flow.engine.bamboo.scene.spider.spider_cluster_full_backup import TenDBClusterFullBackupFlow +from backend.flow.engine.bamboo.scene.spider.spider_cluster_rollback_flow import TenDBRollBackDataFlow from backend.flow.engine.bamboo.scene.spider.spider_cluster_truncate_database import SpiderTruncateDatabaseFlow from backend.flow.engine.bamboo.scene.spider.spider_partition import SpiderPartitionFlow +from backend.flow.engine.bamboo.scene.spider.spider_reduce_mnt import TenDBClusterReduceMNTFlow +from backend.flow.engine.bamboo.scene.spider.spider_reduce_nodes import TenDBClusterReduceNodesFlow +from backend.flow.engine.bamboo.scene.spider.spider_remotedb_migrate_flow import TenDBMigrateFlow +from backend.flow.engine.bamboo.scene.spider.spider_remotedb_rebalance_flow import TenDBRemoteRebalanceFlow from backend.flow.engine.bamboo.scene.spider.spider_rename_database_flow import SpiderRenameDatabaseFlow from backend.flow.engine.bamboo.scene.spider.spider_slave_cluster_deploy import TenDBSlaveClusterApplyFlow +from backend.flow.engine.bamboo.scene.spider.spider_slave_cluster_destroy import TenDBSlaveClusterDestroyFlow from backend.flow.engine.controller.base import BaseController @@ -45,9 +54,9 @@ def spider_cluster_destroy_scene(self): flow = TenDBClusterDestroyFlow(root_id=self.root_id, data=self.ticket_data) flow.destroy_cluster() - def spider_add_tmp_node_scene(self): - flow = SpiderAddTmpNodeFlow(root_id=self.root_id, data=self.ticket_data) - flow.spider_add_tmp_node_with_manual_input() + def add_spider_mnt_scene(self): + flow = TenDBClusterAddSpiderMNTFlow(root_id=self.root_id, data=self.ticket_data) + flow.add_spider_mnt() def spider_checksum(self): """ @@ -121,3 +130,65 @@ def add_spider_nodes_scene(self): def full_backup(self): flow = TenDBClusterFullBackupFlow(root_id=self.root_id, data=self.ticket_data) flow.full_backup_flow() + + def reduce_spider_nodes_scene(self): + """ + 缩容接入层的场景 + """ + flow = TenDBClusterReduceNodesFlow(root_id=self.root_id, data=self.ticket_data) + flow.reduce_spider_nodes() + + def flashback(self): + flow = TenDBClusterFlashbackFlow( + root_id=self.root_id, data=self.ticket_data, cluster_type=ClusterType.TenDBCluster.value + ) + flow.flashback() + + def tendb_cluster_remote_switch_scene(self): + """ + remote端互切的场景 + """ + flow = RemoteMasterSlaveSwitchFlow(root_id=self.root_id, data=self.ticket_data) + flow.remote_switch() + + def tendbcluster_remote_fail_over_scene(self): + """ + remote端主故障切换的场景 + """ + flow = RemoteMasterFailOverFlow(root_id=self.root_id, data=self.ticket_data) + flow.remote_fail_over() + + def migrate_remotedb(self): + """ + remote 节点1:1迁移 + """ + flow = TenDBMigrateFlow(root_id=self.root_id, data=self.ticket_data) + flow.tendb_migrate() + + def tendb_cluster_remote_rebalance(self): + """ + remote 节点扩缩容同步数据(重均衡) + """ + flow = TenDBRemoteRebalanceFlow(root_id=self.root_id, ticket_data=self.ticket_data) + flow.tendb_migrate() + + def tendb_cluster_rollback_data(self): + """ + tendb cluster 定点回档 + """ + flow = TenDBRollBackDataFlow(root_id=self.root_id, data=self.ticket_data) + flow.tendb_rollback_data() + + def destroy_tendb_slave_cluster(self): + """ + tendb cluster 只读接入层下架 + """ + flow = TenDBSlaveClusterDestroyFlow(root_id=self.root_id, data=self.ticket_data) + flow.destroy_slave_cluster() + + def reduce_spider_mnt_scene(self): + """ + tendb cluster 运维节点下架 + """ + flow = TenDBClusterReduceMNTFlow(root_id=self.root_id, data=self.ticket_data) + flow.reduce_spider_mnt() diff --git a/dbm-ui/backend/flow/engine/controller/tbinlogdumper.py b/dbm-ui/backend/flow/engine/controller/tbinlogdumper.py new file mode 100644 index 0000000000..c833ab5cf9 --- /dev/null +++ b/dbm-ui/backend/flow/engine/controller/tbinlogdumper.py @@ -0,0 +1,31 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 backend.flow.engine.bamboo.scene.tbinlogdumper.add_nodes import TBinlogDumperAddNodesFlow +from backend.flow.engine.bamboo.scene.tbinlogdumper.reduce_node import TBinlogDumperReduceNodesFlow +from backend.flow.engine.bamboo.scene.tbinlogdumper.switch_nodes import TBinlogDumperSwitchNodesFlow +from backend.flow.engine.controller.base import BaseController + + +class TBinlogDumperController(BaseController): + """ + TBinlogDumper相关调用 + """ + + def add_nodes_scene(self): + flow = TBinlogDumperAddNodesFlow(root_id=self.root_id, data=self.ticket_data) + flow.add_nodes() + + def reduce_nodes_scene(self): + flow = TBinlogDumperReduceNodesFlow(root_id=self.root_id, data=self.ticket_data) + flow.reduce_nodes() + + def switch_nodes_scene(self): + flow = TBinlogDumperSwitchNodesFlow(root_id=self.root_id, data=self.ticket_data) + flow.switch_nodes() diff --git a/dbm-ui/backend/flow/plugins/components/collections/common/base_service.py b/dbm-ui/backend/flow/plugins/components/collections/common/base_service.py index 7ed646866d..9277e87889 100644 --- a/dbm-ui/backend/flow/plugins/components/collections/common/base_service.py +++ b/dbm-ui/backend/flow/plugins/components/collections/common/base_service.py @@ -186,12 +186,10 @@ def __get_target_ip_context( 对单个节点获取执行后log,并赋值给定义好流程上下文的trans_data write_op 控制写入变量的方式,rewrite是默认值,代表覆盖写入;append代表以{"ip":xxx} 形式追加里面变量里面 """ - resp = self.__log__(job_instance_id, step_instance_id, ip_dict) if not resp["result"]: # 结果返回异常,则异常退出 return False - try: # 以dict形式追加写入 result = json.loads(re.search(cpl, resp["data"]["log_content"]).group("context")) diff --git a/dbm-ui/backend/flow/plugins/components/collections/common/cc_service.py b/dbm-ui/backend/flow/plugins/components/collections/common/cc_service.py deleted file mode 100644 index 15adf3bb56..0000000000 --- a/dbm-ui/backend/flow/plugins/components/collections/common/cc_service.py +++ /dev/null @@ -1,139 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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. -""" -import logging -from typing import List - -from pipeline.component_framework.component import Component -from pipeline.core.flow.activity import Service - -from backend.db_meta import api -from backend.db_meta.models import BKModule, Cluster, DBModule -from backend.flow.plugins.components.collections.common.base_service import BaseService -from backend.flow.utils.cc_manage import CcManage - -logger = logging.getLogger("flow") - - -class TransferHostCreateClusterService(BaseService): - """ - 集群创建时,挪动机器模块 - """ - - def _execute(self, data, parent_data) -> bool: - kwargs = data.get_one_of_inputs("kwargs") - global_data = data.get_one_of_inputs("global_data") - trans_data = data.get_one_of_inputs("trans_data") - - bk_biz_id = global_data["bk_biz_id"] - - # 注意: 原子入参要求提供cluster_id - cluster_id = global_data["cluster_id"] - cluster = Cluster.objects.get(id=cluster_id) - - # 更新DBMeta和创建cc目录,这里如果已经存在,则会报错 - machine_topo = api.db_module.get_or_create( - bk_biz_id=bk_biz_id, - cluster_id=cluster_id, - cluster_type=cluster.cluster_type, - cluster_name=cluster.name, - cluster_domain=cluster.immute_domain, - ) - - ip_modules = [] - for info in kwargs["cluster"]["machine_list"]: - ips = getattr(trans_data, info["ips_var_name"]) - # 将哪批机器转移到哪个模块 - ip_modules.append({"ips": ips, "bk_module_id": machine_topo[info["machine_type"]]}) - - CcManage.transfer_machines(bk_biz_id, cluster.bk_cloud_id, ip_modules) - - return True - - def inputs_format(self) -> List: - return [ - Service.InputItem(name="kwargs", key="kwargs", type="dict", required=True), - Service.InputItem(name="global_data", key="global_data", type="dict", required=True), - Service.InputItem(name="trans_data", key="trans_data", type="dict", required=True), - ] - - -class TransferHostCreateClusterComponent(Component): - name = __name__ - code = "transfer_host" - bound_service = TransferHostCreateClusterService - - -class TransferHostDestroyClusterService(BaseService): - """ - 集群销毁时,挪动机器模块 - """ - - def _execute(self, data, parent_data) -> bool: - kwargs = data.get_one_of_inputs("kwargs") - ips = kwargs["cluster"]["ips"] - CcManage.transfer_machines_idle(ips) - return True - - def inputs_format(self) -> List: - return [ - Service.InputItem(name="kwargs", key="kwargs", type="dict", required=True), - Service.InputItem(name="global_data", key="global_data", type="dict", required=True), - Service.InputItem(name="trans_data", key="trans_data", type="dict", required=True), - ] - - -class TransferHostDestroyClusterComponent(Component): - name = __name__ - code = "transfer_host_destroy" - bound_service = TransferHostDestroyClusterService - - -class TransferHostScaleService(BaseService): - """ - 集群扩缩容,移动模块 - """ - - def _execute(self, data, parent_data) -> bool: - kwargs = data.get_one_of_inputs("kwargs") - global_data = data.get_one_of_inputs("global_data") - - bk_biz_id = global_data["bk_biz_id"] - cluster_type = kwargs["cluster"]["cluster_type"] - module_name = kwargs["cluster"]["cluster_name"] - - db_module_id = DBModule.objects.get( - bk_biz_id=bk_biz_id, cluster_type=cluster_type, db_module_name=module_name - ).db_module_id - - machine_list = kwargs["cluster"]["machine_list"] - ip_modules = [] - for info in machine_list: - machine_type = info["machine_type"] - ips = info["ips"] - bk_module_id = BKModule.objects.get(db_module_id=db_module_id, machine_type=machine_type).bk_module_id - # 将哪批机器转移到哪个模块 - ip_modules.append({"ips": ips, "bk_module_id": bk_module_id}) - - CcManage.transfer_machines(bk_biz_id, kwargs["bk_cloud_id"], ip_modules) - return True - - def inputs_format(self) -> List: - return [ - Service.InputItem(name="kwargs", key="kwargs", type="dict", required=True), - Service.InputItem(name="global_data", key="global_data", type="dict", required=True), - Service.InputItem(name="trans_data", key="trans_data", type="dict", required=True), - ] - - -class TransferHostScaleComponent(Component): - name = __name__ - code = "transfer_host_scale" - bound_service = TransferHostScaleService diff --git a/dbm-ui/backend/flow/plugins/components/collections/common/clb_manage.py b/dbm-ui/backend/flow/plugins/components/collections/common/clb_manage.py new file mode 100644 index 0000000000..2d4312c0f5 --- /dev/null +++ b/dbm-ui/backend/flow/plugins/components/collections/common/clb_manage.py @@ -0,0 +1,97 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging +from typing import List + +from django.utils.translation import ugettext as _ +from pipeline.component_framework.component import Component +from pipeline.core.flow.activity import Service + +from backend.flow.consts import DnsOpType +from backend.flow.plugins.components.collections.common.base_service import BaseService +from backend.flow.utils.clb_manage import GetClbByIp + +logger = logging.getLogger("flow") + + +class RedisClbManageService(BaseService): + """ + 定义集群CLB管理的活动节点 + """ + + def __get_exec_ips(self, kwargs, trans_data) -> list: + """ + 获取需要执行的ip list + """ + # 拼接节点执行ip所需要的信息,ip信息统一用list处理拼接 + if kwargs["get_trans_data_ip_var"]: + exec_ips = self.splice_exec_ips_list( + ticket_ips=kwargs["exec_ip"], pool_ips=getattr(trans_data, kwargs["get_trans_data_ip_var"]) + ) + else: + exec_ips = self.splice_exec_ips_list(ticket_ips=kwargs["exec_ip"]) + + if not exec_ips: + self.log_error(_("该节点获取到执行ip信息为空,请联系系统管理员")) + + return exec_ips + + def _execute(self, data, parent_data) -> bool: + kwargs = data.get_one_of_inputs("kwargs") + global_data = data.get_one_of_inputs("global_data") + trans_data = data.get_one_of_inputs("trans_data") + + # 传入调用结果 + dns_op_type = kwargs["clb_op_type"] + clb_manager = GetClbByIp(kwargs["clb_ip"]) + + if dns_op_type == DnsOpType.CREATE: + # 添加CLB映射,proxy扩容场景 + exec_ips = self.__get_exec_ips(kwargs=kwargs, trans_data=trans_data) + if not exec_ips: + self.log_error(_("该节点获取到执行ip信息为空,请联系系统管理员")) + return False + + add_instance_list = [f"{ip}:{kwargs['clb_op_exec_port']}" for ip in exec_ips] + result = clb_manager.add_clb_rs(instance_list=add_instance_list) + elif dns_op_type == DnsOpType.RECYCLE_RECORD: + # 删除CLB映射,proxy缩容场景 + exec_ips = self.__get_exec_ips(kwargs=kwargs, trans_data=trans_data) + if not exec_ips: + self.log_error(_("该节点获取到执行ip信息为空,请联系系统管理员")) + return False + + delete_instance_list = [f"{ip}:{kwargs['clb_op_exec_port']}" for ip in exec_ips] + result = clb_manager.del_clb_rs(instance_list=delete_instance_list) + elif dns_op_type == DnsOpType.CLUSTER_DELETE: + # 删除CLB,集群下架场景 + result = clb_manager.deregiste_clb() + else: + self.log_error(_("无法适配到传入的域名处理类型,请联系系统管理员:{}").format(dns_op_type)) + return False + + self.log_info("successfully") + return result + + def inputs_format(self) -> List: + return [ + Service.InputItem(name="kwargs", key="kwargs", type="dict", required=True), + Service.InputItem(name="global_data", key="global_data", type="dict", required=True), + ] + + def outputs_format(self) -> List: + return [Service.OutputItem(name="command result", key="result", type="str")] + + +class RedisClbManageComponent(Component): + name = __name__ + code = "clb_dns_manage" + bound_service = RedisClbManageService diff --git a/dbm-ui/backend/flow/plugins/components/collections/common/delete_cc_service_instance.py b/dbm-ui/backend/flow/plugins/components/collections/common/delete_cc_service_instance.py index b5af6c1731..6f58498744 100644 --- a/dbm-ui/backend/flow/plugins/components/collections/common/delete_cc_service_instance.py +++ b/dbm-ui/backend/flow/plugins/components/collections/common/delete_cc_service_instance.py @@ -9,9 +9,9 @@ """ from pipeline.component_framework.component import Component -from backend.db_meta.api.common import del_service_instance from backend.db_meta.models import Cluster from backend.flow.plugins.components.collections.common.base_service import BaseService +from backend.flow.utils.cc_manage import CcManage class DelCCServiceInstService(BaseService): @@ -28,12 +28,12 @@ def _execute(self, data, parent_data) -> bool: if cluster.proxyinstance_set.filter(machine__ip=instance["ip"], port=instance["port"]).exists(): # 一个集群有却只有一个proxy类型实例 proxy = cluster.proxyinstance_set.get(machine__ip=instance["ip"], port=instance["port"]) - del_service_instance(bk_instance_id=proxy.bk_instance_id) + CcManage(proxy.bk_biz_id).delete_service_instance(bk_instance_ids=[proxy.bk_instance_id]) if cluster.storageinstance_set.filter(machine__ip=instance["ip"], port=instance["port"]).exists(): # 一个集群有却只有一个storage类型实例 storage = cluster.storageinstance_set.get(machine__ip=instance["ip"], port=instance["port"]) - del_service_instance(bk_instance_id=storage.bk_instance_id) + CcManage(storage.bk_biz_id).delete_service_instance(bk_instance_ids=[storage.bk_instance_id]) return True diff --git a/dbm-ui/backend/flow/plugins/components/collections/common/download_backup_client.py b/dbm-ui/backend/flow/plugins/components/collections/common/download_backup_client.py new file mode 100644 index 0000000000..3dda6c300e --- /dev/null +++ b/dbm-ui/backend/flow/plugins/components/collections/common/download_backup_client.py @@ -0,0 +1,59 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +import logging + +from pipeline.component_framework.component import Component + +from backend import env +from backend.components.mysql_backup.client import MysqlBackupApi +from backend.flow.consts import BACKUP_TAG +from backend.flow.plugins.components.collections.common.base_service import BaseService + +logger = logging.getLogger("flow") + + +class DownloadBackupClientService(BaseService): + """ + 下载并安装backup_client,kwargs参数结构如下: + kwargs:{ + "bk_cloud_id":0, + "download_host_list": [ip,ip.ip] + } + """ + + def _execute(self, data, parent_data) -> bool: + if not env.BACKUP_SYSTEM_ENABLED: + logger.warning("backup system disable, skip") + return True + + kwargs = data.get_one_of_inputs("kwargs") + + params = { + "host_list": [ + {"bk_cloud_id": int(kwargs["bk_cloud_id"]), "ip": ip} for ip in kwargs["download_host_list"] + ], + "file_tag": BACKUP_TAG, + "cos_info_render": { + "auth_path": f"/home/{kwargs['backup_os_user']}/.cosinfo.toml", + "os_user": kwargs["backup_os_user"], + "auth_path_overwrite": True, + }, + } + + MysqlBackupApi.download_backup_client(params=params) + logger.info(f"Download and install backup_client successfully {params['host_list']}") + return True + + +class DownloadBackupClientComponent(Component): + name = __name__ + code = "download_backup_client" + bound_service = DownloadBackupClientService diff --git a/dbm-ui/backend/flow/plugins/components/collections/common/external_service.py b/dbm-ui/backend/flow/plugins/components/collections/common/external_service.py index 6cd28886e5..ca7a3a9ccf 100644 --- a/dbm-ui/backend/flow/plugins/components/collections/common/external_service.py +++ b/dbm-ui/backend/flow/plugins/components/collections/common/external_service.py @@ -36,8 +36,10 @@ def _execute(self, data, parent_data): self.log_info(_("第三方接口: {} 请求成功! 返回参数为: {}").format(f"{import_module}.{call_func}", resp)) except (ApiResultError, ApiRequestError) as e: self.log_info(_("第三方接口:{} 调用失败!错误信息为: {}").format(f"{import_module}.{call_func}", e)) + return False except Exception as e: # pylint: disable=broad-except self.log_info(_("请求遇到未知错误!错误信息为: {}").format(e)) + return False # TODO: 考虑对第三方接口请求后对回调函数的支持 return True diff --git a/dbm-ui/backend/flow/plugins/components/collections/common/pause.py b/dbm-ui/backend/flow/plugins/components/collections/common/pause.py index 30ba615796..55e157e496 100644 --- a/dbm-ui/backend/flow/plugins/components/collections/common/pause.py +++ b/dbm-ui/backend/flow/plugins/components/collections/common/pause.py @@ -41,7 +41,7 @@ def _execute(self, data, parent_data): flow = ticket.current_flow() Todo.objects.create( - name=_("【{}】自动化流程待确认,是否继续?").format(ticket.get_ticket_type_display()), + name=_("【{}】流程待确认,是否继续?").format(ticket.get_ticket_type_display()), flow=flow, ticket=ticket, type=TodoType.INNER_APPROVE, diff --git a/dbm-ui/backend/flow/plugins/components/collections/common/polaris_manage.py b/dbm-ui/backend/flow/plugins/components/collections/common/polaris_manage.py new file mode 100644 index 0000000000..89a9728c72 --- /dev/null +++ b/dbm-ui/backend/flow/plugins/components/collections/common/polaris_manage.py @@ -0,0 +1,97 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging +from typing import List + +from django.utils.translation import ugettext as _ +from pipeline.component_framework.component import Component +from pipeline.core.flow.activity import Service + +from backend.flow.consts import DnsOpType +from backend.flow.plugins.components.collections.common.base_service import BaseService +from backend.flow.utils.polaris_manage import GetPolarisManageByName + +logger = logging.getLogger("flow") + + +class RedisPolarisManageService(BaseService): + """ + 定义集群Polaris管理的活动节点 + """ + + def __get_exec_ips(self, kwargs, trans_data) -> list: + """ + 获取需要执行的ip list + """ + # 拼接节点执行ip所需要的信息,ip信息统一用list处理拼接 + if kwargs["get_trans_data_ip_var"]: + exec_ips = self.splice_exec_ips_list( + ticket_ips=kwargs["exec_ip"], pool_ips=getattr(trans_data, kwargs["get_trans_data_ip_var"]) + ) + else: + exec_ips = self.splice_exec_ips_list(ticket_ips=kwargs["exec_ip"]) + + if not exec_ips: + self.log_error(_("该节点获取到执行ip信息为空,请联系系统管理员")) + + return exec_ips + + def _execute(self, data, parent_data) -> bool: + kwargs = data.get_one_of_inputs("kwargs") + global_data = data.get_one_of_inputs("global_data") + trans_data = data.get_one_of_inputs("trans_data") + + # 传入调用结果 + dns_op_type = kwargs["polaris_op_type"] + polaris_manager = GetPolarisManageByName(kwargs["servicename"]) + + result = False + if dns_op_type == DnsOpType.CREATE: + # 添加polaris映射,proxy扩容场景 + exec_ips = self.__get_exec_ips(kwargs=kwargs, trans_data=trans_data) + if not exec_ips: + return False + + add_instance_list = [f"{ip}:{kwargs['polaris_op_exec_port']}" for ip in exec_ips] + result = polaris_manager.add_polaris_rs(instance_list=add_instance_list) + elif dns_op_type == DnsOpType.RECYCLE_RECORD: + # 删除polaris映射,proxy缩容场景 + exec_ips = self.__get_exec_ips(kwargs=kwargs, trans_data=trans_data) + if not exec_ips: + self.log_error(_("该节点获取到执行ip信息为空,请联系系统管理员")) + return False + + delete_instance_list = [f"{ip}:{kwargs['polaris_op_exec_port']}" for ip in exec_ips] + result = polaris_manager.del_polaris_rs(instance_list=delete_instance_list) + elif dns_op_type == DnsOpType.CLUSTER_DELETE: + # 删除polaris, 集群下架场景 + result = polaris_manager.deregiste_polaris() + else: + self.log_error(_("无法适配到传入的域名处理类型,请联系系统管理员:{}").format(dns_op_type)) + return False + + self.log_info("successfully") + return result + + def inputs_format(self) -> List: + return [ + Service.InputItem(name="kwargs", key="kwargs", type="dict", required=True), + Service.InputItem(name="global_data", key="global_data", type="dict", required=True), + ] + + def outputs_format(self) -> List: + return [Service.OutputItem(name="command result", key="result", type="str")] + + +class RedisPolarisManageComponent(Component): + name = __name__ + code = "polaris_dns_manage" + bound_service = RedisPolarisManageService diff --git a/dbm-ui/backend/flow/plugins/components/collections/common/transfer_host_service.py b/dbm-ui/backend/flow/plugins/components/collections/common/transfer_host_service.py new file mode 100644 index 0000000000..da340679c7 --- /dev/null +++ b/dbm-ui/backend/flow/plugins/components/collections/common/transfer_host_service.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +import logging + +from django.utils.translation import ugettext as _ +from pipeline.component_framework.component import Component + +from backend.flow.plugins.components.collections.common.base_service import BaseService +from backend.flow.utils.cc_manage import CcManage + +logger = logging.getLogger("flow") + + +class TransferHostService(BaseService): + """将主机转移对应的模块下""" + + def _execute(self, data, parent_data): + kwargs = data.get_one_of_inputs("kwargs") + bk_biz_id = kwargs["bk_biz_id"] + bk_module_ids = kwargs["bk_module_ids"] + bk_host_ids = kwargs["bk_host_ids"] + try: + CcManage(bk_biz_id=bk_biz_id).transfer_host_module( + bk_host_ids=bk_host_ids, target_module_ids=bk_module_ids + ) + return True + except Exception as e: # pylint: disable=broad-except + logger.error(_("主机{}转移目标模块{}失败").format(bk_host_ids, bk_module_ids)) + return False + + +class TransferHostServiceComponent(Component): + name = __name__ + code = "transfer_host_service" + bound_service = TransferHostService diff --git a/dbm-ui/backend/flow/plugins/components/collections/es/get_es_resource.py b/dbm-ui/backend/flow/plugins/components/collections/es/get_es_resource.py index 88f4b44900..c44ba82731 100644 --- a/dbm-ui/backend/flow/plugins/components/collections/es/get_es_resource.py +++ b/dbm-ui/backend/flow/plugins/components/collections/es/get_es_resource.py @@ -37,22 +37,20 @@ def _execute(self, data, parent_data) -> bool: trans_data = getattr(flow_context, kwargs["set_trans_data_dataclass"])() # 页面输入 - if global_data["ip_source"] == "manual_input": - trans_data.new_hot_ips = [] - trans_data.new_cold_ips = [] - trans_data.new_master_ips = [] - trans_data.new_client_ips = [] - for role in global_data["nodes"]: - if role == ESRoleEnum.HOT: - trans_data.new_hot_ips = [node["ip"] for node in global_data["nodes"][role]] - elif role == ESRoleEnum.COLD: - trans_data.new_cold_ips = [node["ip"] for node in global_data["nodes"][role]] - elif role == ESRoleEnum.MASTER: - trans_data.new_master_ips = [node["ip"] for node in global_data["nodes"][role]] - elif role == ESRoleEnum.CLIENT: - trans_data.new_client_ips = [node["ip"] for node in global_data["nodes"][role]] - elif self.data["ip_source"] == "resource_pool": - pass + # 由于flow传参基本不变,无需判断 IP来源 属于 手动选择/资源池 + trans_data.new_hot_ips = [] + trans_data.new_cold_ips = [] + trans_data.new_master_ips = [] + trans_data.new_client_ips = [] + for role in global_data["nodes"]: + if role == ESRoleEnum.HOT: + trans_data.new_hot_ips = [node["ip"] for node in global_data["nodes"][role]] + elif role == ESRoleEnum.COLD: + trans_data.new_cold_ips = [node["ip"] for node in global_data["nodes"][role]] + elif role == ESRoleEnum.MASTER: + trans_data.new_master_ips = [node["ip"] for node in global_data["nodes"][role]] + elif role == ESRoleEnum.CLIENT: + trans_data.new_client_ips = [node["ip"] for node in global_data["nodes"][role]] self.log_info(_("获取机器资源成功成功。 {}").format(trans_data)) data.outputs["trans_data"] = trans_data diff --git a/dbm-ui/backend/flow/plugins/components/collections/kafka/kafka_config.py b/dbm-ui/backend/flow/plugins/components/collections/kafka/kafka_config.py index b787865c28..d6f227300f 100644 --- a/dbm-ui/backend/flow/plugins/components/collections/kafka/kafka_config.py +++ b/dbm-ui/backend/flow/plugins/components/collections/kafka/kafka_config.py @@ -38,6 +38,7 @@ def _execute(self, data, parent_data) -> bool: "username": global_data["username"], "password": global_data["password"], "zookeeper_conf": global_data["zookeeper_conf"], + "no_security": str(global_data["no_security"]), } conf_items = [] diff --git a/dbm-ui/backend/flow/plugins/components/collections/mysql/check_client_connections.py b/dbm-ui/backend/flow/plugins/components/collections/mysql/check_client_connections.py new file mode 100644 index 0000000000..e269030dfe --- /dev/null +++ b/dbm-ui/backend/flow/plugins/components/collections/mysql/check_client_connections.py @@ -0,0 +1,49 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 backend.flow.engine.bamboo.scene.spider.common.exceptions import NormalSpiderFlowException +from backend.flow.plugins.components.collections.common.base_service import BaseService +from backend.flow.utils.mysql.check_client_connections import check_client_connection + + +class CheckClientConnService(BaseService): + """ + 定义检测实例是否存储用户连接的活动节点(系统账号和内置账号会过滤) + 本节点只支持mysql/spider实例,不支持中控实例的检测 + """ + + def _execute(self, data, parent_data): + kwargs = data.get_one_of_inputs("kwargs") + + results = check_client_connection(kwargs["bk_cloud_id"], kwargs["check_instances"]) + + for res in results: + # 检查返回的每个实例的结果 + + if res["error_msg"]: + self.log_error(f"select processlist failed: {res[0]['error_msg']}") + return False + + if res["cmd_results"][0]["table_data"]: + self.log_error(f"There are also {len(res['cmd_results'][0]['table_data'])} not-system threads") + return False + + else: + self.log_info(f"This node [{res['address']}] passed the checkpoint [check-client-conn]!") + + return True + + +class CheckClientConnComponent(Component): + name = __name__ + code = "check_client_connections" + bound_service = CheckClientConnService diff --git a/dbm-ui/backend/flow/plugins/components/collections/mysql/clone_rules.py b/dbm-ui/backend/flow/plugins/components/collections/mysql/clone_rules.py index a97c53875b..9df7e1c7ad 100644 --- a/dbm-ui/backend/flow/plugins/components/collections/mysql/clone_rules.py +++ b/dbm-ui/backend/flow/plugins/components/collections/mysql/clone_rules.py @@ -35,14 +35,15 @@ def _execute(self, data, parent_data, callback=None) -> bool: bk_biz_id = kwargs["bk_biz_id"] operator = kwargs["created_by"] clone_type = kwargs["clone_type"] + clone_cluster_type = kwargs["clone_cluster_type"] clone_data_list = kwargs["clone_data"] clone_success_count = 0 # 如果是实例克隆,则提前获得ip:port与机器信息的字典 if clone_type == CloneType.INSTANCE: - address_machine_map = CloneHandler(bk_biz_id, operator, clone_type).get_address__machine_map( - clone_data_list - ) + address_machine_map = CloneHandler( + bk_biz_id=bk_biz_id, operator=operator, clone_type=clone_type, clone_cluster_type=clone_cluster_type + ).get_address__machine_map(clone_data_list) for clone_data in clone_data_list: # 实例化权限克隆记录,后续存到数据库中 @@ -56,15 +57,19 @@ def _execute(self, data, parent_data, callback=None) -> bool: if clone_type == CloneType.CLIENT.value: record.target = "\n".join(record.target) - # 权限克隆 + # 权限克隆全局参数准备 params = { "bk_biz_id": bk_biz_id, "operator": operator, "bk_cloud_id": clone_data["bk_cloud_id"], + "cluster_type": clone_cluster_type, } try: + # 调用客户端克隆/实例克隆 if clone_type == CloneType.CLIENT.value: params.update({"source_ip": clone_data["source"], "target_ip": clone_data["target"]}) + if "user" in clone_data and "target_instances" in clone_data: + params.update({"user": clone_data["user"], "target_instances": clone_data["target_instances"]}) resp = MySQLPrivManagerApi.clone_client(params=params, raw=True) else: params.update( @@ -81,20 +86,15 @@ def _execute(self, data, parent_data, callback=None) -> bool: ) resp = MySQLPrivManagerApi.clone_instance(params=params, raw=True) + # 填充克隆的结果和信息 record.status = int(resp["code"]) == 0 clone_success_count += record.status - - self.log_info(f"{resp['message']}\n") record.error = resp["message"] + self.log_info(f"{resp['message']}\n") except Exception as e: # pylint: disable=broad-except - if isinstance(e, ApiResultError): - error_message = _("「权限克隆返回结果异常」{}").format(e.message) - else: - error_message = _("「权限克隆调用异常」{}").format(e) - - record.status = False - record.error = error_message + error_message = _("「权限克隆异常」{}").format(getattr(e, "message", e)) + record.status, record.error = False, error_message self.log_error(_("权限克隆失败,错误信息: {}\n").format(error_message)) record.save() @@ -112,7 +112,6 @@ def _execute(self, data, parent_data, callback=None) -> bool: "get_clone_info_excel/?ticket_id={}&clone_type={}'>excel 下载" ).format(env.BK_SAAS_HOST, bk_biz_id, ticket_id, clone_type) ) - return overall_result def inputs_format(self) -> List: diff --git a/dbm-ui/backend/flow/plugins/components/collections/mysql/clone_user.py b/dbm-ui/backend/flow/plugins/components/collections/mysql/clone_user.py index 6c664934c8..a1cd78b69d 100644 --- a/dbm-ui/backend/flow/plugins/components/collections/mysql/clone_user.py +++ b/dbm-ui/backend/flow/plugins/components/collections/mysql/clone_user.py @@ -17,7 +17,7 @@ from backend.components.mysql_priv_manager.client import MySQLPrivManagerApi from backend.db_services.mysql.permission.clone.handlers import CloneHandler -from backend.db_services.mysql.permission.constants import CloneType +from backend.db_services.mysql.permission.constants import CloneClusterType, CloneType from backend.exceptions import ApiResultError from backend.flow.plugins.components.collections.common.base_service import BaseService @@ -27,6 +27,8 @@ class CloneUserService(BaseService): """ slave 克隆账号 + 优化即使实例不在db_meta记录,都能克隆权限(针对添加实例场景) + 如果传入不存machine_type, 则db_meta为准,兼容旧版本代表 """ def _execute(self, data, parent_data, callback=None) -> bool: @@ -35,22 +37,30 @@ def _execute(self, data, parent_data, callback=None) -> bool: clone_data_list = kwargs["clone_data"] address__machine_map = CloneHandler( - bk_biz_id=global_data["bk_biz_id"], operator=global_data["created_by"], clone_type=CloneType.INSTANCE + bk_biz_id=global_data["bk_biz_id"], + operator=global_data["created_by"], + clone_type=CloneType.INSTANCE, + # 克隆账户是实例间克隆,无需克隆类型,默认mysql即可 + clone_cluster_type=CloneClusterType.MYSQL, ).get_address__machine_map(clone_data_list) + try: for clone_data in clone_data_list: - params = {"bk_biz_id": global_data["bk_biz_id"], "operator": global_data["created_by"]} + if clone_data.get("machine_type"): + target_machine_type = source_machine_type = clone_data["machine_type"] + else: + source_machine_type = address__machine_map[clone_data["source"]].machine_type + target_machine_type = address__machine_map[clone_data["target"]].machine_type + + params = {"bk_biz_id": int(global_data["bk_biz_id"]), "operator": global_data["created_by"]} params.update( { "source": { "address": clone_data["source"], - "machine_type": address__machine_map[clone_data["source"]].machine_type, - }, - "target": { - "address": clone_data["target"], - "machine_type": address__machine_map[clone_data["target"]].machine_type, + "machine_type": source_machine_type, }, + "target": {"address": clone_data["target"], "machine_type": target_machine_type}, "bk_cloud_id": clone_data["bk_cloud_id"], } ) diff --git a/dbm-ui/backend/flow/plugins/components/collections/mysql/cluster_standardize_trans_module.py b/dbm-ui/backend/flow/plugins/components/collections/mysql/cluster_standardize_trans_module.py new file mode 100644 index 0000000000..d71bec0374 --- /dev/null +++ b/dbm-ui/backend/flow/plugins/components/collections/mysql/cluster_standardize_trans_module.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 as _ +from pipeline.component_framework.component import Component + +from backend.db_meta.models import Cluster +from backend.flow.plugins.components.collections.common.base_service import BaseService +from backend.flow.utils.mysql.mysql_module_operate import MysqlCCTopoOperator + + +class ClusterStandardizeTransModuleService(BaseService): + def _execute(self, data, parent_data) -> bool: + kwargs = data.get_one_of_inputs("kwargs") + global_data = data.get_one_of_inputs("global_data") + + cluster_id = global_data["cluster_id"] + cluster_obj = Cluster.objects.get(pk=cluster_id) + + MysqlCCTopoOperator(cluster_obj).transfer_instances_to_cluster_module(cluster_obj.storageinstance_set.all()) + MysqlCCTopoOperator(cluster_obj).transfer_instances_to_cluster_module(cluster_obj.proxyinstance_set.all()) + self.log_info(_("[{}] CC 模块标准化完成".format(kwargs["node_name"]))) + return True + + +class ClusterStandardizeTransModuleComponent(Component): + name = __name__ + code = "cluster_standardize_trans_module" + bound_service = ClusterStandardizeTransModuleService diff --git a/dbm-ui/backend/flow/plugins/components/collections/mysql/dns_manage.py b/dbm-ui/backend/flow/plugins/components/collections/mysql/dns_manage.py index 9fdd5f5155..4606d21b5a 100644 --- a/dbm-ui/backend/flow/plugins/components/collections/mysql/dns_manage.py +++ b/dbm-ui/backend/flow/plugins/components/collections/mysql/dns_manage.py @@ -74,7 +74,10 @@ def _execute(self, data, parent_data) -> bool: elif dns_op_type == DnsOpType.CLUSTER_DELETE: # 回收集群所有的域名映射,适配集群回收场景 - result = dns_manage.delete_domain(cluster_id=kwargs["delete_cluster_id"]) + result = dns_manage.delete_domain( + cluster_id=kwargs["delete_cluster_id"], + is_only_delete_slave_domain=kwargs["is_only_delete_slave_domain"], + ) elif dns_op_type == DnsOpType.UPDATE: # 更新域名方法 diff --git a/dbm-ui/backend/flow/plugins/components/collections/mysql/exec_actuator_script.py b/dbm-ui/backend/flow/plugins/components/collections/mysql/exec_actuator_script.py index 28d55bdd35..27bdfe2b3a 100644 --- a/dbm-ui/backend/flow/plugins/components/collections/mysql/exec_actuator_script.py +++ b/dbm-ui/backend/flow/plugins/components/collections/mysql/exec_actuator_script.py @@ -21,13 +21,10 @@ from backend import env from backend.components import JobApi -from backend.core.encrypt.constants import RSAConfigType -from backend.core.encrypt.handlers import RSAHandler -from backend.db_proxy.constants import ExtensionType -from backend.db_proxy.models import DBExtension from backend.flow.consts import DBA_ROOT_USER from backend.flow.models import FlowNode from backend.flow.plugins.components.collections.common.base_service import BkJobService +from backend.flow.utils.mysql.get_mysql_sys_user import get_mysql_sys_users from backend.flow.utils.mysql.mysql_act_playload import MysqlActPayload from backend.flow.utils.script_template import actuator_template, fast_execute_script_common_kwargs @@ -54,25 +51,6 @@ def __get_exec_ips(self, kwargs, trans_data) -> list: return exec_ips - @staticmethod - def __get_mysql_sys_users(bk_cloud_id) -> list: - """ - 增加方法:收集SaaS内mysql/spider的系统账号列表,作为固定参数传入待执行Actuator指令 - """ - sys_users_map = { - ExtensionType.DRS: env.DRS_USERNAME, - ExtensionType.DBHA: env.DBHA_USERNAME, - } - sys_users = [] - for key, value in sys_users_map.items(): - if value: - sys_users.append(value) - else: - rsa = RSAHandler.get_or_generate_rsa_in_db(RSAConfigType.get_rsa_cloud_name(bk_cloud_id)) - info = DBExtension.get_latest_extension(bk_cloud_id=bk_cloud_id, extension_type=key) - sys_users.append(RSAHandler.decrypt_password(rsa.rsa_private_key.content, info.details["user"])) - return sys_users - def _execute(self, data, parent_data) -> bool: """ 执行fast_execute_script脚本 @@ -126,7 +104,7 @@ def _execute(self, data, parent_data) -> bool: # 拼接mysql系统账号固定参数 if "general" in db_act_template["payload"]: db_act_template["payload"]["general"].update( - {"runtime_extend": {"mysql_sys_users": self.__get_mysql_sys_users(kwargs["bk_cloud_id"])}} + {"runtime_extend": {"mysql_sys_users": get_mysql_sys_users(kwargs["bk_cloud_id"])}} ) # payload参数转换base64格式 diff --git a/dbm-ui/backend/flow/plugins/components/collections/mysql/filter_database_table_by_flashback_input.py b/dbm-ui/backend/flow/plugins/components/collections/mysql/filter_database_table_by_flashback_input.py new file mode 100644 index 0000000000..bfdc3ccad5 --- /dev/null +++ b/dbm-ui/backend/flow/plugins/components/collections/mysql/filter_database_table_by_flashback_input.py @@ -0,0 +1,108 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 collections import defaultdict + +from pipeline.component_framework.component import Component + +from backend.components.db_remote_service.client import DRSApi +from backend.constants import IP_PORT_DIVIDER +from backend.flow.consts import SYSTEM_DBS +from backend.flow.plugins.components.collections.common.base_service import BaseService + + +class FilterDatabaseTableFromFlashbackInputService(BaseService): + def _execute(self, data, parent_data) -> bool: + kwargs = data.get_one_of_inputs("kwargs") + trans_data = data.get_one_of_inputs("trans_data") + global_data = data.get_one_of_inputs("global_data") + + ip = global_data["ip"] + port = global_data["port"] + + databases = global_data["databases"] + databases_ignore = global_data["databases_ignore"] + tables = global_data["tables"] + tables_ignore = global_data["tables_ignore"] + + db_filter_sql = "SELECT SCHEMA_NAME FROM information_schema.SCHEMATA WHERE ({}) AND ({})".format( + " OR ".join(["SCHEMA_NAME LIKE '{}'".format(ele) for ele in databases]), + " AND ".join(["SCHEMA_NAME NOT LIKE '{}'".format(ele) for ele in databases_ignore + SYSTEM_DBS]), + ) + self.log_info("[{}] db_filter_sql: {}".format(kwargs["node_name"], db_filter_sql)) + db_filter_res = DRSApi.rpc( + { + "addresses": ["{}{}{}".format(ip, IP_PORT_DIVIDER, port)], + "cmds": [db_filter_sql], + "force": False, + "bk_cloud_id": kwargs["bk_cloud_id"], + } + ) + self.log_info("[{}] filter database response: {}".format(kwargs["node_name"], db_filter_res)) + if db_filter_res[0]["error_msg"]: + self.log_error( + "[{}] filter databases on {}{}{} failed: {}".format( + kwargs["node_name"], ip, IP_PORT_DIVIDER, port, db_filter_res[0]["error_msg"] + ) + ) + return False + + databases_filtered = [ele["SCHEMA_NAME"] for ele in db_filter_res[0]["cmd_results"][0]["table_data"]] + + # tables 和 tables_ignore 使用三元操作符返回一个恒真表达式, 简化代码 + table_filter_sql = ( + "SELECT TABLE_SCHEMA, TABLE_NAME FROM information_schema.TABLES " + "WHERE TABLE_TYPE='BASE TABLE' AND {} AND ({}) AND ({})" + ).format( + "TABLE_SCHEMA IN ({})".format(",".join(["'{}'".format(ele) for ele in databases_filtered])), + " OR ".join(["TABLE_NAME LIKE '{}'".format(ele) for ele in tables]) if tables else "1=1", + " AND ".join(["TABLE_NAME NOT LIKE '{}'".format(ele) for ele in tables_ignore]) + if tables_ignore + else "1=1", + ) + self.log_info("[{}] table_filter_sql: {}".format(kwargs["node_name"], table_filter_sql)) + table_filter_res = DRSApi.rpc( + { + "addresses": ["{}{}{}".format(ip, IP_PORT_DIVIDER, port)], + "cmds": [table_filter_sql], + "force": False, + "bk_cloud_id": kwargs["bk_cloud_id"], + } + ) + self.log_info("[{}] filter table response: {}".format(kwargs["node_name"], table_filter_res)) + if table_filter_res[0]["error_msg"]: + self.log_error( + "[{}] filter table on {}{}{} failed: {}".format( + kwargs["node_name"], ip, IP_PORT_DIVIDER, port, table_filter_res[0]["error_msg"] + ) + ) + return False + + targets = defaultdict(dict) + for row in table_filter_res[0]["cmd_results"][0]["table_data"]: + db = row["TABLE_SCHEMA"] + tbl = row["TABLE_NAME"] + targets[db][tbl] = False + + self.log_info("[{}] filtered table: {}".format(kwargs["node_name"], targets)) + + if not targets: + self.log_error("[{}] match nothing".format(kwargs["node_name"])) + return False + + trans_data.targets = dict(targets) + data.outputs["trans_data"] = trans_data + return True + + +class FilterDatabaseTableFromFlashbackInputComponent(Component): + name = __name__ + code = "filter_database_table_from_flashback_input" + bound_service = FilterDatabaseTableFromFlashbackInputService diff --git a/dbm-ui/backend/flow/plugins/components/collections/mysql/filter_database_table_from_regex.py b/dbm-ui/backend/flow/plugins/components/collections/mysql/filter_database_table_from_regex.py index 2474cdfc1b..d5df5fa3d6 100644 --- a/dbm-ui/backend/flow/plugins/components/collections/mysql/filter_database_table_from_regex.py +++ b/dbm-ui/backend/flow/plugins/components/collections/mysql/filter_database_table_from_regex.py @@ -91,7 +91,7 @@ def _execute(self, data, parent_data) -> bool: self.log_info(_("[{}] 过滤所得库表: {}").format(kwargs["node_name"], targets)) if not targets: - self.log_error(_("[{}] 未匹配到任何库")) + self.log_error(_("[{}] 未匹配到任何库".format(kwargs["node_name"]))) return False trans_data.targets = dict(targets) diff --git a/dbm-ui/backend/flow/plugins/components/collections/mysql/general_check_db_in_using.py b/dbm-ui/backend/flow/plugins/components/collections/mysql/general_check_db_in_using.py index 0812ea1c65..f2c1a0b141 100644 --- a/dbm-ui/backend/flow/plugins/components/collections/mysql/general_check_db_in_using.py +++ b/dbm-ui/backend/flow/plugins/components/collections/mysql/general_check_db_in_using.py @@ -36,100 +36,90 @@ class GeneralCheckDBInUsingService(BaseService): """ __need_schedule__ = True - interval = StaticIntervalGenerator(60) + interval = StaticIntervalGenerator(10) def _execute(self, data, parent_data) -> bool: """ bk_cloud_id 统一由私有变量kwargs传入 """ - kwargs = data.get_one_of_inputs("kwargs") - trans_data = data.get_one_of_inputs("trans_data") - global_data = data.get_one_of_inputs("global_data") - - ip = global_data["ip"] - port = global_data["port"] - - flush_table_res = DRSApi.rpc( - { - "addresses": ["{}{}{}".format(ip, IP_PORT_DIVIDER, port)], - "cmds": ["flush tables"], - "force": False, - "bk_cloud_id": kwargs["bk_cloud_id"], - } - ) - - self.log_info("[{}] flush tables response: {}".format(kwargs["node_name"], flush_table_res)) - if flush_table_res[0]["error_msg"]: - self.log_error( - "[{}] flush tables on {}{}{} failed: {}".format( - kwargs["node_name"], ip, IP_PORT_DIVIDER, port, flush_table_res[0]["error_msg"] - ) - ) - return False - - trans_data.show_open_fence = True - data.outputs["trans_data"] = trans_data + data.outputs.counter = 6 return True def _schedule(self, data, parent_data, callback_data=None): + counter = data.get_one_of_outputs("counter") kwargs = data.get_one_of_inputs("kwargs") trans_data = data.get_one_of_inputs("trans_data") global_data = data.get_one_of_inputs("global_data") - # schedule 模式马上会被调度, 这个栅栏立即返回 - # 达到 sleep 60 后才真的做检查的目的 - if trans_data.show_open_fence: - self.log_info("[{}] close fence will be sleep 60s".format(kwargs["node_name"])) - trans_data.show_open_fence = False - return True - ip = global_data["ip"] port = global_data["port"] targets = trans_data.targets truncate_data_type = global_data["truncate_data_type"] - show_open_table_res = DRSApi.rpc( - { - "addresses": ["{}{}{}".format(ip, IP_PORT_DIVIDER, port)], - "cmds": ["show open tables"], - "force": False, - "bk_cloud_id": kwargs["bk_cloud_id"], - } - ) - self.log_info("[{}] show open tables response: {}".format(kwargs["node_name"], show_open_table_res)) - if show_open_table_res[0]["error_msg"]: - self.log_error( - "[{}] show open tables on {}{}{} failed: {}".format( - kwargs["node_name"], ip, IP_PORT_DIVIDER, port, show_open_table_res[0]["error_msg"] - ) + if counter > 0: + counter -= 1 + self.log_info("[{}] {} round flush tables left".format(kwargs["node_name"], counter)) + + flush_table_res = DRSApi.rpc( + { + "addresses": ["{}{}{}".format(ip, IP_PORT_DIVIDER, port)], + "cmds": ["flush tables"], + "force": False, + "bk_cloud_id": kwargs["bk_cloud_id"], + } ) - data.outputs.ext_result = False - self.finish_schedule() - return False - - # 检查 targets dbs 是否在 open tables 中 - reopened_targets = [] - for row in show_open_table_res[0]["cmd_results"][0]["table_data"]: - db = row["Database"] - tbl = row["Table"] - # 如果是删库需求, 就不检查表了 - if truncate_data_type == TruncateDataTypeEnum.DROP_DATABASE.value: - if db in targets: - reopened_targets.append(db) - else: - if db in targets and tbl in targets[db]: - reopened_targets.append("{}.{}".format(db, tbl)) - - data.outputs["trans_data"] = trans_data + self.log_info("[{}] flush tables response: {}".format(kwargs["node_name"], flush_table_res)) + if flush_table_res[0]["error_msg"]: + self.log_error( + "[{}] flush tables on {}{}{} failed: {}".format( + kwargs["node_name"], ip, IP_PORT_DIVIDER, port, flush_table_res[0]["error_msg"] + ) + ) + self.finish_schedule() + return False - if reopened_targets: - self.log_info("[{}] check db using failed: {}".format(kwargs["node_name"], reopened_targets)) - self.finish_schedule() - return False - else: - self.log_info("[{}] check db using pass".format(kwargs["node_name"])) - self.finish_schedule() + data.outputs.counter = counter return True + else: + show_open_table_res = DRSApi.rpc( + { + "addresses": ["{}{}{}".format(ip, IP_PORT_DIVIDER, port)], + "cmds": ["show open tables"], + "force": False, + "bk_cloud_id": kwargs["bk_cloud_id"], + } + ) + self.log_info("[{}] show open tables response: {}".format(kwargs["node_name"], show_open_table_res)) + if show_open_table_res[0]["error_msg"]: + self.log_error( + "[{}] show open tables on {}{}{} failed: {}".format( + kwargs["node_name"], ip, IP_PORT_DIVIDER, port, show_open_table_res[0]["error_msg"] + ) + ) + data.outputs.ext_result = False + self.finish_schedule() + return False + + reopened_targets = [] + for row in show_open_table_res[0]["cmd_results"][0]["table_data"]: + db = row["Database"] + tbl = row["Table"] + + if truncate_data_type == TruncateDataTypeEnum.DROP_DATABASE.value: + if db in targets: + reopened_targets.append(db) + else: + if db in targets and tbl in targets[db]: + reopened_targets.append("{}.{}".format(db, tbl)) + + if reopened_targets: + self.log_info("[{}] check db/table using failed: {}".format(kwargs["node_name"], reopened_targets)) + self.finish_schedule() + return False + else: + self.log_info("[{}] check db/table using pass".format(kwargs["node_name"])) + self.finish_schedule() + return True class GeneralCheckDBInUsingComponent(Component): diff --git a/dbm-ui/backend/flow/plugins/components/collections/mysql/mysql_download_backupfile.py b/dbm-ui/backend/flow/plugins/components/collections/mysql/mysql_download_backupfile.py index 8288dc4293..a19bae6532 100644 --- a/dbm-ui/backend/flow/plugins/components/collections/mysql/mysql_download_backupfile.py +++ b/dbm-ui/backend/flow/plugins/components/collections/mysql/mysql_download_backupfile.py @@ -10,16 +10,14 @@ """ import logging +from django.utils.translation import ugettext as _ from pipeline.component_framework.component import Component -from pipeline.core.flow.activity import Service, StaticIntervalGenerator +from pipeline.core.flow.activity import StaticIntervalGenerator from backend.components.mysql_backup.client import MysqlBackupApi from backend.flow.plugins.components.collections.common.base_service import BaseService -from backend.ticket.constants import TicketType logger = logging.getLogger("flow") -single_ticket_type = [TicketType.MYSQL_SINGLE_APPLY.name] -ha_ticket_type = [TicketType.MYSQL_HA_APPLY.name] class MySQLDownloadBackupfile(BaseService): @@ -32,43 +30,50 @@ class MySQLDownloadBackupfile(BaseService): def _execute(self, data, parent_data) -> bool: kwargs = data.get_one_of_inputs("kwargs") - global_data = data.get_one_of_inputs("global_data") - trans_data = data.get_one_of_inputs("trans_data") params = { - "BkCloudID": kwargs["bk_cloud_id"], - "TaskidList": kwargs["task_ids"], - "DestIP": kwargs["dest_ip"], - "LoginUser": kwargs["user"], - "DestDir": kwargs["file_target_path"], - "Reason": kwargs["reason"], + "bk_cloud_id": kwargs["bk_cloud_id"], + "taskid_list": kwargs["task_ids"], + "dest_ip": kwargs["dest_ip"], + "login_user": kwargs["login_user"], + "dest_dir": kwargs["desc_dir"], + "reason": kwargs["reason"], } - logger.info(params) + logger.debug(params) response = MysqlBackupApi.download(params=params) - if response["code"] == "0": - bill_id = response["data"]["bill_id"] - data.outputs.bill_id = bill_id + backup_bill_id = response.get("bill_id", -1) + if backup_bill_id > 0: + logger.debug(_("调起下载 {}").format(backup_bill_id)) + data.outputs.backup_bill_id = backup_bill_id return True else: return False def _schedule(self, data, parent_data, callback_data=None): # 定义异步扫描 - bill_id = data.get_one_of_outputs("bill_id") - result_response = MysqlBackupApi.download_result({"bill_id": bill_id}) - if result_response["code"] == "0": - if result_response["data"]["total"]["todo"] == 0 and result_response["data"]["total"]["fail"] == 0: + backup_bill_id = data.get_one_of_outputs("backup_bill_id") + logger.info(_("下载单据ID {}").format(backup_bill_id)) + result_response = MysqlBackupApi.download_result({"bill_id": backup_bill_id}) + # 如何判断 + if result_response is not None and "total" in result_response: + if result_response["total"]["todo"] == 0 and result_response["total"]["fail"] == 0: + logger.info(_("{} 下载成功").format(backup_bill_id)) self.finish_schedule() return True - elif result_response["data"]["total"]["fail"] > 0: + elif result_response["total"]["fail"] > 0: + logger.error(_("{} 下载失败").format(backup_bill_id)) + logger.debug(str(result_response)) self.finish_schedule() return False + else: + logger.debug(_("{} 下载中: todo {}").format(backup_bill_id, result_response["total"]["todo"])) else: + logger.debug("result response fail") self.finish_schedule() return False class MySQLDownloadBackupfileComponent(Component): name = __name__ - code = "mysql_download_backupfile" + code = "mysql_download_backup_file" bound_service = MySQLDownloadBackupfile diff --git a/dbm-ui/backend/flow/plugins/components/collections/mysql/mysql_partition_check.py b/dbm-ui/backend/flow/plugins/components/collections/mysql/mysql_partition_check.py deleted file mode 100644 index 1bd231f50c..0000000000 --- a/dbm-ui/backend/flow/plugins/components/collections/mysql/mysql_partition_check.py +++ /dev/null @@ -1,53 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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 as _ -from pipeline.component_framework.component import Component - -from backend.components.mysql_partition.client import DBPartitionApi -from backend.flow.plugins.components.collections.common.base_service import BaseService - - -class MysqlPartitionDryRunService(BaseService): - """ - 获取分区语句 - """ - - def _execute(self, data, parent_data) -> bool: - global_data = data.get_one_of_inputs("global_data") or {} - trans_data = data.get_one_of_inputs("trans_data") - try: - resp = DBPartitionApi.dry_run( - { - "cluster_type": global_data["cluster_type"], - "bk_biz_id": global_data["bk_biz_id"], - "config_ids": [global_data["config_id"]], - "cluster_id": global_data["cluster_id"], - "ip": global_data["ip"], - "port": global_data["port"], - "operator": global_data["created_by"], - "bk_cloud_id": global_data["bk_cloud_id"], - } - ) - except Exception as e: - self.log_error(_("分区管理服务api异常,相关信息: {}").format(e)) - return False - self.log_info(resp) - trans_data.execute_objects = resp - data.outputs["trans_data"] = trans_data - self.log_info(_("单据id{}".format(global_data["uid"]))) - self.log_info(_("获取分区语句成功")) - return True - - -class MysqlPartitionDryRunServiceComponent(Component): - name = __name__ - code = "mysql_partition_dry_run" - bound_service = MysqlPartitionDryRunService diff --git a/dbm-ui/backend/flow/plugins/components/collections/mysql/mysql_rollback_data_download_binlog.py b/dbm-ui/backend/flow/plugins/components/collections/mysql/mysql_rollback_data_download_binlog.py new file mode 100644 index 0000000000..b92044e67c --- /dev/null +++ b/dbm-ui/backend/flow/plugins/components/collections/mysql/mysql_rollback_data_download_binlog.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging + +from django.utils.translation import ugettext as _ +from pipeline.component_framework.component import Component +from pipeline.core.flow.activity import StaticIntervalGenerator + +from backend.db_services.mysql.fixpoint_rollback.handlers import FixPointRollbackHandler +from backend.flow.engine.bamboo.scene.mysql.common.exceptions import TenDBGetBackupInfoFailedException +from backend.flow.plugins.components.collections.mysql.mysql_download_backupfile import MySQLDownloadBackupfile + +logger = logging.getLogger("flow") + + +class MySQLRollbackDownloadBinlog(MySQLDownloadBackupfile): + """ + MySQL下载备份文件 + """ + + __need_schedule__ = True + interval = StaticIntervalGenerator(60) + + def _execute(self, data, parent_data) -> bool: + kwargs = data.get_one_of_inputs("kwargs") + trans_data = data.get_one_of_inputs("trans_data") + cluster = kwargs["cluster"] + backup_time = trans_data["backupinfo"]["backup_time"] + rollback_time = cluster["rollback_time"] + logger.info("backup time is {}".format(backup_time)) + logger.info("rollback time is {}".format(rollback_time)) + # 查询binlog + rollback_handler = FixPointRollbackHandler(cluster["cluster_id"]) + backup_binlog = rollback_handler.query_binlog_from_bklog( + start_time=backup_time, + end_time=rollback_time, + minute_range=30, + host_ip=cluster["master_ip"], + port=cluster["master_port"], + ) + + if backup_binlog is None: + raise TenDBGetBackupInfoFailedException(message=_("获取实例 {} 的备份信息失败".format(cluster["master_ip"]))) + + binlog_files_list = [i["file_name"] for i in backup_binlog["file_list_details"]] + kwargs["task_ids"] = [i["task_id"] for i in backup_binlog["file_list_details"]] + trans_data.binlog_files_list = binlog_files_list + trans_data.binlog_files = ",".join(binlog_files_list) + trans_data.backup_time = backup_time + # trans_data["binlog_files_list"] = binlog_files_list + # trans_data["binlog_files"] = ",".join(binlog_files_list) + data.outputs["kwargs"] = kwargs + data.outputs["trans_data"] = trans_data + return super()._execute(data, parent_data) + + +class MySQLRollbackDownloadBinlogComponent(Component): + name = __name__ + code = "rollback_download_binlog" + bound_service = MySQLDownloadBackupfile diff --git a/dbm-ui/backend/flow/plugins/components/collections/mysql/semantic_check.py b/dbm-ui/backend/flow/plugins/components/collections/mysql/semantic_check.py index 4e3e4b2c8e..ce1fe04194 100644 --- a/dbm-ui/backend/flow/plugins/components/collections/mysql/semantic_check.py +++ b/dbm-ui/backend/flow/plugins/components/collections/mysql/semantic_check.py @@ -50,9 +50,9 @@ def _execute(self, data, parent_data) -> bool: payload["version_id"] = self._runtime_attrs.get("version") try: if cluster_type == ClusterType.TenDBCluster: - resp = SQLSimulation.mysql_simulation(payload, raw=True) - else: resp = SQLSimulation.spider_simulation(payload, raw=True) + else: + resp = SQLSimulation.mysql_simulation(payload, raw=True) self.log_info(_("创建模拟执行任务resp{}").format(resp)) code = resp["code"] if code != 0: @@ -73,6 +73,7 @@ def _execute(self, data, parent_data) -> bool: def _schedule(self, data, parent_data, callback_data=None) -> bool: kwargs = data.get_one_of_inputs("kwargs") payload = kwargs["payload"] + cluster_type = kwargs["cluster_type"] try: # code:0 成功 code:1 失败 code:2 running # - @@ -82,7 +83,7 @@ def _schedule(self, data, parent_data, callback_data=None) -> bool: if code == 0: self.log_info("run task success~ ") - self._prepare_for_ticket(data=data) + self._prepare_for_ticket(data=data, cluster_type=cluster_type) self.finish_schedule() return True if code == 1: @@ -99,12 +100,13 @@ def _schedule(self, data, parent_data, callback_data=None) -> bool: self.finish_schedule() return False - def _prepare_for_ticket(self, data): + def _prepare_for_ticket(self, data, cluster_type): """为创建单据做参数准备""" root_id = data.get_one_of_inputs("kwargs").get("root_id") bk_biz_id = data.get_one_of_inputs("global_data").get("bk_biz_id") auto_commit_key = CACHE_SEMANTIC_AUTO_COMMIT_FIELD.format(bk_biz_id=bk_biz_id, root_id=root_id) skip_pause_key = CACHE_SEMANTIC_SKIP_PAUSE_FILED.format(bk_biz_id=bk_biz_id, root_id=root_id) + # 默认自动创建单据 is_auto_commit = cache.get(auto_commit_key) or True is_skip_pause = cache.get(skip_pause_key) or False @@ -112,12 +114,16 @@ def _prepare_for_ticket(self, data): f"-----------auto_commit_key: {auto_commit_key}-{is_auto_commit}, " f"skip_pause_key: {skip_pause_key}-{is_skip_pause}-------------" ) + ticket_type = TicketType.MYSQL_IMPORT_SQLFILE + if cluster_type == ClusterType.TenDBCluster: + ticket_type = TicketType.TENDBCLUSTER_IMPORT_SQLFILE + # 构造单据信息 trans_data = { "is_auto_commit": is_auto_commit, "ticket_data": { "remark": _("语义检查出发的自动创建单据"), - "ticket_type": TicketType.MYSQL_IMPORT_SQLFILE, + "ticket_type": ticket_type, "details": {"root_id": root_id, "skip_pause": is_skip_pause}, }, } diff --git a/dbm-ui/backend/flow/plugins/components/collections/mysql/trans_flies.py b/dbm-ui/backend/flow/plugins/components/collections/mysql/trans_flies.py index f28b051454..a846b35521 100644 --- a/dbm-ui/backend/flow/plugins/components/collections/mysql/trans_flies.py +++ b/dbm-ui/backend/flow/plugins/components/collections/mysql/trans_flies.py @@ -17,7 +17,7 @@ from backend import env from backend.components import JobApi from backend.core import consts -from backend.flow.consts import MediumFileTypeEnum +from backend.flow.consts import DBA_ROOT_USER, MediumFileTypeEnum from backend.flow.models import FlowNode from backend.flow.plugins.components.collections.common.base_service import BkJobService @@ -91,6 +91,13 @@ def _execute(self, data, parent_data) -> bool: payload["file_source_list"].append(file_source) payload["target_server"]["ip_list"] = target_ip_info + # 选择什么用户来传输文件 + if kwargs.get("run_as_system_user"): + payload["account_alias"] = kwargs["run_as_system_user"] + else: + # 现在默认使用root账号来执行 + payload["account_alias"] = DBA_ROOT_USER + if kwargs.get("file_target_path"): kwargs["file_target_path"] = str(kwargs["file_target_path"]).strip() if kwargs["file_target_path"] is not None and kwargs["file_target_path"] != "": diff --git a/dbm-ui/backend/flow/plugins/components/collections/mysql/verify_checksum.py b/dbm-ui/backend/flow/plugins/components/collections/mysql/verify_checksum.py new file mode 100644 index 0000000000..71e150654f --- /dev/null +++ b/dbm-ui/backend/flow/plugins/components/collections/mysql/verify_checksum.py @@ -0,0 +1,102 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 pipeline.component_framework.component import Component + +from backend.components import DRSApi +from backend.flow.consts import INFODBA_SCHEMA +from backend.flow.plugins.components.collections.common.base_service import BaseService +from backend.ticket.builders.common.constants import MYSQL_CHECKSUM_TABLE + + +class VerifyChecksumService(BaseService): + """ + 定义检测实例checksum结果的活动节点 + """ + + def _execute(self, data, parent_data): + + # 每次检测checksum结果目前是保留一个轮次的检测结果,故优先检测checksum表 + # 如果checksum记录没有判断,且checksum没有test记录,说明一轮数据刚刚检验完且清空checksum表, + # 则减少checksum_history最近14天的不一致记录 + allow_diff_cnt = 0 + kwargs = data.get_one_of_inputs("kwargs") + + checksum_instance_tuples = kwargs["checksum_instance_tuples"] + error_message_list = [] + + for t in checksum_instance_tuples: + master_ip = t["master"].split(":")[0] + master_port = t["master"].split(":")[1] + slave_address = t["slave"] + + check_cmds = [ + ( + f"select count(0) as cnt from {INFODBA_SCHEMA}.checksum" + " where ts > date_sub(now(), interval 14 day)" + ), + ( + f"select count(0) as cnt from {INFODBA_SCHEMA}.{MYSQL_CHECKSUM_TABLE}" + " where ts > date_sub(now(), interval 14 day)" + ), + ( + f"select count(distinct db, tbl,chunk) as cnt from {INFODBA_SCHEMA}.checksum" + " where (this_crc <> master_crc or this_cnt <> master_cnt)" + f" and master_ip = '{master_ip}' and master_port = {master_port}" + ), + ( + f"select count(distinct db, tbl,chunk) as cnt from {INFODBA_SCHEMA}.{MYSQL_CHECKSUM_TABLE}" + " where (this_crc <> master_crc or this_cnt <> master_cnt)" + " and ts > date_sub(now(), interval 14 day)" + f" and master_ip = '{master_ip}' and master_port = {master_port}" + ), + ] + + res = DRSApi.rpc( + { + "addresses": [slave_address], + "cmds": check_cmds, + "force": False, + "bk_cloud_id": kwargs["bk_cloud_id"], + } + ) + + if res[0]["error_msg"]: + error_message_list.append(f"This node [{slave_address}] verify checksum failed: {res[0]['error_msg']}") + + checksum_cnt = int(res[0]["cmd_results"][0]["table_data"][0]["cnt"]) + checksum_history_cnt = int(res[0]["cmd_results"][1]["table_data"][0]["cnt"]) + checksum_diff_cnt = int(res[0]["cmd_results"][2]["table_data"][0]["cnt"]) + checksum_history_diff_cnt = int(res[0]["cmd_results"][3]["table_data"][0]["cnt"]) + + if not checksum_cnt and not checksum_history_cnt: + error_message_list.append( + f"This node [{slave_address}] has not queried the verification records of the last 14 days" + ) + + elif checksum_diff_cnt > allow_diff_cnt or ( + checksum_cnt == 0 and checksum_history_diff_cnt > allow_diff_cnt + ): + error_message_list.append(f"This node [{slave_address}] has diff chuck in the last 14 days") + else: + self.log_info(f"The node [{slave_address}] passed the checkpoint [verify-checksum] !") + + if error_message_list: + for error in error_message_list: + self.log_error(error) + return False + + return True + + +class VerifyChecksumComponent(Component): + name = __name__ + code = "verify_checksum" + bound_service = VerifyChecksumService diff --git a/dbm-ui/backend/flow/plugins/components/collections/name_service/name_service.py b/dbm-ui/backend/flow/plugins/components/collections/name_service/name_service.py index 809ee526c6..b4eceebf0f 100644 --- a/dbm-ui/backend/flow/plugins/components/collections/name_service/name_service.py +++ b/dbm-ui/backend/flow/plugins/components/collections/name_service/name_service.py @@ -14,13 +14,14 @@ from pipeline.component_framework.component import Component from pipeline.core.flow.activity import Service +import backend.flow.utils.name_service.name_service_dataclass as flow_context from backend.db_services.plugin.nameservice import clb, polaris from backend.flow.plugins.components.collections.common.base_service import BaseService logger = logging.getLogger("json") -class ExecNameServiceCreateService(BaseService): +class ExecNameServiceOperation(BaseService): """ NameServiceCreate服务 """ @@ -34,80 +35,64 @@ def _execute(self, data, parent_data) -> bool: # 从流程节点中获取变量 kwargs = data.get_one_of_inputs("kwargs") - name_service_type = kwargs["name_service_type"] + name_service_operation_type = kwargs["name_service_operation_type"] + trans_data = data.get_one_of_inputs("trans_data") + creator = kwargs["creator"] + cluster_id = kwargs["cluster_id"] - # 执行功能 - if name_service_type == "clb": - res = clb.create_lb_and_register_target(kwargs["cluster_id"], kwargs["created_by"]) - elif name_service_type == "polaris": - res = polaris.create_service_alias_bind_targets(kwargs["cluster_id"], kwargs["created_by"]) - else: - self.log_error("name_service_type %s not support error!" % name_service_type) - return False - - # 定义流程节点输出参数值 - data.outputs.result = res - if res["status"] == 0: - self.log_info("create %s successfully" % name_service_type) - return True - - self.log_error("create %s fail, error:%s" % (name_service_type, res["message"])) - return False - - # 流程节点输入参数 - def inputs_format(self) -> List: - return [ - Service.InputItem(name="kwargs", key="kwargs", type="dict", required=True), - Service.InputItem(name="global_data", key="global_data", type="dict", required=True), - ] - - # 流程节点输出参数 - def outputs_format(self) -> List: - return [Service.OutputItem(name="result", key="result", type="dict")] - - -class ExecNameServiceCreateComponent(Component): - """ - ExecNameServiceCreate组件 - """ - - name = __name__ - code = "name_service_create" - bound_service = ExecNameServiceCreateService - - -class ExecNameServiceDeleteService(BaseService): - """ - NameServiceDelete服务 - """ - - def _execute(self, data, parent_data) -> bool: - """ - 执行删除名字服务功能的函数 - global_data 单据全局变量,格式字典 - kwargs 私有变量 - """ - - # 从流程节点中获取变量 - kwargs = data.get_one_of_inputs("kwargs") - name_service_type = kwargs["name_service_type"] + if trans_data is None or trans_data == "${trans_data}": + # 表示没有加载上下文内容,则在此添加 + trans_data = getattr(flow_context, kwargs["set_trans_data_dataclass"])() # 执行功能 - if name_service_type == "clb": - res = clb.deregister_target_and_delete_lb(kwargs["cluster_id"]) - elif name_service_type == "polaris": + # clb创建 + if name_service_operation_type == "create_clb": + res = clb.create_lb_and_register_target(cluster_id=cluster_id) + # polaris创建 + elif name_service_operation_type == "create_polaris": + res = polaris.create_service_alias_bind_targets(cluster_id=cluster_id) + # clb删除 + elif name_service_operation_type == "delete_clb": + res = clb.deregister_target_and_delete_lb(cluster_id=cluster_id) + # polaris删除 + elif name_service_operation_type == "delete_polaris": res = polaris.unbind_targets_delete_alias_service(kwargs["cluster_id"]) + # clb信息写入meta + elif name_service_operation_type == "add_clb_info_to_meta": + res = clb.add_clb_info_to_meta(output=trans_data, cluster_id=cluster_id, creator=creator) + # 从meta删除clb信息 + elif name_service_operation_type == "delete_clb_info_from_meta": + res = clb.delete_clb_info_from_meta(output=trans_data, cluster_id=cluster_id) + # polaris信息写入meta + elif name_service_operation_type == "add_polaris_info_to_meta": + res = polaris.add_polaris_info_to_meta(output=trans_data, cluster_id=cluster_id, creator=creator) + # 从meta删除polaris信息 + elif name_service_operation_type == "delete_polaris_info_from_meta": + res = polaris.delete_polaris_info_from_meta(output=trans_data, cluster_id=cluster_id) + # 添加clb域名到dns,clb域名信息写入meta + elif name_service_operation_type == "add_clb_domain_to_dns": + res = clb.add_clb_domain_to_dns(cluster_id=cluster_id, creator=creator) + # 从dns删除clb域名,从meta中删除clb域名信息 + elif name_service_operation_type == "delete_clb_domain_from_dns": + res = clb.delete_clb_domain_from_dns(cluster_id=cluster_id) + # 主域名绑定clb ip + elif name_service_operation_type == "domain_bind_clb_ip": + res = clb.immute_domain_clb_ip(cluster_id=cluster_id, creator=creator, bind=True) + # 主域名解绑clb ip + elif name_service_operation_type == "domain_unbind_clb_ip": + res = clb.immute_domain_clb_ip(cluster_id=cluster_id, creator=creator, bind=False) else: - self.log_error("name_service_type %s not support error!" % name_service_type) + self.log_error("{} does not support error!".format(name_service_operation_type)) return False # 定义流程节点输出参数值 - data.outputs.result = res - if res["status"] == 0: - self.log_info("delete %s successfully" % name_service_type) + trans_data = res + if res["code"] == 0: + self.log_info("task:{} execute successfully".format(name_service_operation_type)) + data.outputs["trans_data"] = trans_data return True - self.log_error("delete %s fail, error:%s" % (name_service_type, res["message"])) + self.log_error("task:{} execute fail, error:{}".format(name_service_operation_type, res["message"])) return False # 流程节点输入参数 @@ -117,16 +102,12 @@ def inputs_format(self) -> List: Service.InputItem(name="global_data", key="global_data", type="dict", required=True), ] - # 流程节点输出参数 - def outputs_format(self) -> List: - return [Service.OutputItem(name="result", key="result", type="dict")] - -class ExecNameServiceDeleteComponent(Component): +class ExecNameServiceOperationComponent(Component): """ - ExecNameServiceDelete组件 + ExecNameServiceOperation组件 """ name = __name__ - code = "name_service_delete" - bound_service = ExecNameServiceDeleteService + code = "name_service_operation" + bound_service = ExecNameServiceOperation diff --git a/dbm-ui/backend/flow/plugins/components/collections/redis/exec_actuator_script.py b/dbm-ui/backend/flow/plugins/components/collections/redis/exec_actuator_script.py index cd9e80a77b..f65b516a97 100644 --- a/dbm-ui/backend/flow/plugins/components/collections/redis/exec_actuator_script.py +++ b/dbm-ui/backend/flow/plugins/components/collections/redis/exec_actuator_script.py @@ -73,8 +73,6 @@ def _execute(self, data, parent_data) -> bool: exec_ips = self.splice_exec_ips_list( ticket_ips=kwargs["exec_ip"], pool_ips=getattr(trans_data, kwargs["get_trans_data_ip_var"]) ) - if kwargs["ip_index"] is not None: - exec_ips = [exec_ips[kwargs["ip_index"]]] else: exec_ips = self.splice_exec_ips_list(ticket_ips=kwargs["exec_ip"]) @@ -99,6 +97,9 @@ def _execute(self, data, parent_data) -> bool: if kwargs["is_update_trans_data"]: self.log_info(_("[{}] kwargs['payload'] 是不完整,需要将{}内容加到payload中").format(node_name, kwargs["cluster"])) db_act_template["payload"].update(kwargs["cluster"]) + + db_act_template["payload"]["backup_tasks"] = trans_data.tendis_backup_info + db_act_template["payload"] = str( base64.b64encode(json.dumps(db_act_template["payload"]).encode("utf-8")), "utf-8" ) diff --git a/dbm-ui/backend/flow/plugins/components/collections/redis/redis_dts.py b/dbm-ui/backend/flow/plugins/components/collections/redis/redis_dts.py index 8887168f80..4a3579282b 100644 --- a/dbm-ui/backend/flow/plugins/components/collections/redis/redis_dts.py +++ b/dbm-ui/backend/flow/plugins/components/collections/redis/redis_dts.py @@ -14,31 +14,43 @@ import logging import re import traceback -from typing import Dict, List, Tuple +import uuid +from typing import List, Tuple from django.db import transaction +from django.db.models import Q from django.utils.translation import ugettext as _ from pipeline.component_framework.component import Component from pipeline.core.flow.activity import Service, StaticIntervalGenerator import backend.flow.utils.redis.redis_context_dataclass as flow_context -from backend.components import DBConfigApi, DRSApi -from backend.components.dbconfig.constants import FormatType, LevelName -from backend.db_meta.enums import ClusterType, InstanceRole, InstanceStatus +from backend.components import DRSApi +from backend.db_meta.enums import ClusterType, InstanceStatus from backend.db_meta.models import Cluster -from backend.db_services.redis_dts.constants import DtsCopyType, DtsTaskType -from backend.db_services.redis_dts.models.tb_tendis_dts_job import TbTendisDTSJob -from backend.db_services.redis_dts.models.tb_tendis_dts_task import TbTendisDtsTask -from backend.db_services.redis_dts.util import ( - get_redis_type_by_cluster_type, - is_predixy_proxy_type, - is_redis_cluster_protocal, - is_redis_instance_type, - is_tendisplus_instance_type, - is_tendisssd_instance_type, - is_twemproxy_proxy_type, +from backend.db_services.redis.redis_dts.constants import DtsOperateType, DtsTaskType +from backend.db_services.redis.redis_dts.enums import ( + DtsBillType, + DtsCopyType, + DtsDataCheckFreq, + DtsDataCheckType, + DtsDataRepairMode, + DtsSyncDisconnReminderFreq, + DtsSyncDisconnType, + DtsWriteMode, + ExecuteMode, + TimeoutVars, ) -from backend.flow.consts import DEFAULT_TENDISPLUS_KVSTORECOUNT, GB, MB, ConfigTypeEnum +from backend.db_services.redis.redis_dts.models import TbTendisDTSJob, TbTendisDtsTask +from backend.db_services.redis.redis_dts.util import get_safe_regex_pattern +from backend.db_services.redis.util import is_redis_cluster_protocal +from backend.flow.consts import GB, MB, StateType +from backend.flow.engine.bamboo.scene.redis.redis_cluster_apply_flow import RedisClusterApplyFlow +from backend.flow.engine.bamboo.scene.redis.redis_cluster_data_check_repair import RedisClusterDataCheckRepairFlow +from backend.flow.engine.bamboo.scene.redis.redis_cluster_open_close import RedisClusterOpenCloseFlow +from backend.flow.engine.bamboo.scene.redis.redis_cluster_shutdown import RedisClusterShutdownFlow +from backend.flow.engine.bamboo.scene.redis.redis_flush_data import RedisFlushDataFlow +from backend.flow.engine.bamboo.scene.redis.tendis_plus_apply_flow import TendisPlusApplyFlow +from backend.flow.models import FlowTree from backend.flow.plugins.components.collections.common.base_service import BaseService from backend.flow.utils.redis.redis_cluster_nodes import ( ClusterNodeData, @@ -46,299 +58,19 @@ get_masters_with_slots, group_slaves_by_master_id, ) -from backend.flow.utils.redis.redis_context_dataclass import RedisDtsContext -from backend.flow.utils.redis.redis_proxy_util import decode_predixy_info_servers, decode_twemproxy_backends -from backend.flow.utils.redis.redis_util import domain_without_port +from backend.flow.utils.redis.redis_context_dataclass import ActKwargs, RedisDtsContext +from backend.ticket.constants import TicketType logger = logging.getLogger("flow") -class GetRedisDtsDataService(BaseService): - """ - 获取redis dts 相关数据,存入到上下文中 - """ - - def _execute(self, data, parent_data) -> bool: - kwargs = data.get_one_of_inputs("kwargs") - global_data = data.get_one_of_inputs("global_data") - trans_data = data.get_one_of_inputs("trans_data") - - if trans_data is None or trans_data == "${trans_data}": - # 表示没有加载上下文内容,则在此添加 - trans_data = getattr(flow_context, kwargs["set_trans_data_dataclass"])() - - input_cluster = kwargs["cluster"] - self.log_info("redis dts get data start,input_cluster....") - self.log_info(input_cluster) - dts_copy_type = global_data.get("dts_copy_type") - trans_data.dts_copy_type = dts_copy_type - trans_data.dts_bill_type = global_data.get("dts_bill_type") - trans_data.bk_biz_id = global_data["bk_biz_id"] - - try: - # 获取源集群信息 - if dts_copy_type == DtsCopyType.USER_BUILT_TO_DBM: - trans_data.src_cluster_addr = input_cluster.get("src_cluster") - trans_data.src_cluster_password = input_cluster.get("src_cluster_password") - trans_data.src_cluster_type = input_cluster.get("src_cluster_type") - - # 无法确定源集群的bk_cloud_id,此时以目的集群的为准 - dst_cluster_data = self.get_cluster_info_by_domain( - global_data["bk_biz_id"], input_cluster["dst_cluster"] - ) - trans_data.bk_cloud_id = dst_cluster_data["bk_cloud_id"] - trans_data.src_cluster_region = dst_cluster_data["cluster_region"] - if dts_copy_type == DtsCopyType.COPY_FROM_ROLLBACK_TEMP: - pass - else: - src_cluster_data = self.get_cluster_info_by_domain( - global_data["bk_biz_id"], input_cluster["src_cluster"] - ) - trans_data.bk_cloud_id = src_cluster_data["bk_cloud_id"] - trans_data.src_cluster_id = src_cluster_data["cluster_id"] - trans_data.src_cluster_addr = ( - src_cluster_data["cluster_domain"] + ":" + str(src_cluster_data["cluster_port"]) - ) - trans_data.src_cluster_password = src_cluster_data["cluster_password"] - trans_data.src_cluster_type = src_cluster_data["cluster_type"] - trans_data.src_cluster_region = src_cluster_data["cluster_region"] - trans_data.src_redis_password = src_cluster_data["redis_password"] - self.log_info("src_cluster_data....{}".format(src_cluster_data)) - - src_redis_data = self.get_cluster_slaves_data( - global_data["bk_biz_id"], src_cluster_data["cluster_domain"] - ) - trans_data.src_cluster_running_master = src_redis_data[0] - trans_data.src_slave_instances = src_redis_data[1] - trans_data.src_slave_hosts = src_redis_data[2] - trans_data.src_slave_instances = self.get_redis_slaves_data_size(trans_data) - - # 获取目的集群信息 - if dts_copy_type == DtsCopyType.COPY_TO_OTHER_SYSTEM: - trans_data.dst_cluster_addr = input_cluster.get("dst_cluster") - trans_data.dst_cluster_password = input_cluster.get("dst_cluster_password") - else: - dst_cluster_data = self.get_cluster_info_by_domain( - input_cluster["dst_bk_biz_id"], input_cluster["dst_cluster"] - ) - trans_data.dst_cluster_id = dst_cluster_data["cluster_id"] - trans_data.dst_cluster_addr = ( - dst_cluster_data["cluster_domain"] + ":" + str(dst_cluster_data["cluster_port"]) - ) - trans_data.dst_cluster_password = dst_cluster_data["cluster_password"] - trans_data.dst_cluster_type = dst_cluster_data["cluster_type"] - trans_data.dst_redis_password = dst_cluster_data["redis_password"] - except Exception as e: - traceback.print_exc() - self.log_error("get redis dts data failed:{}".format(e)) - return False - - # 迁移正则 - trans_data.key_white_regex = input_cluster.get("key_white_regex") - trans_data.key_black_regex = input_cluster.get("key_black_regex") - - self.log_info("redis dts get data successfully") - data.outputs["trans_data"] = trans_data - return True - - def inputs_format(self) -> List: - return [ - Service.InputItem(name="kwargs", key="kwargs", type="dict", requiredc=True), - Service.InputItem(name="global_data", key="global_data", type="dict", required=True), - ] - - @staticmethod - def get_cluster_info_by_domain( - bk_biz_id: int, - cluster_domain: str, - ) -> dict: - """ - 根据域名获取集群信息(id,类型等) - """ - try: - cluster_domain = domain_without_port(cluster_domain) - cluster = Cluster.objects.get(bk_biz_id=bk_biz_id, immute_domain=cluster_domain) - one_master = cluster.storageinstance_set.filter( - instance_role=InstanceRole.REDIS_MASTER.value, status=InstanceStatus.RUNNING - ).first() - proxy_conf = DBConfigApi.query_conf_item( - params={ - "bk_biz_id": str(cluster.bk_biz_id), - "level_name": LevelName.CLUSTER.value, - "level_value": cluster.immute_domain, - "level_info": {"module": str(cluster.db_module_id)}, - "conf_file": cluster.proxy_version, - "conf_type": ConfigTypeEnum.ProxyConf, - "namespace": cluster.cluster_type, - "format": FormatType.MAP, - } - ) - proxy_content = proxy_conf.get("content", {}) - - redis_conf = DBConfigApi.query_conf_item( - params={ - "bk_biz_id": str(cluster.bk_biz_id), - "level_name": LevelName.CLUSTER.value, - "level_value": cluster.immute_domain, - "level_info": {"module": str(cluster.db_module_id)}, - "conf_file": cluster.major_version, - "conf_type": ConfigTypeEnum.DBConf, - "namespace": cluster.cluster_type, - "format": FormatType.MAP, - } - ) - redis_content = redis_conf.get("content", {}) - - return { - "cluster_id": cluster.id, - "bk_cloud_id": cluster.bk_cloud_id, - "cluster_domain": cluster.immute_domain, - "cluster_type": cluster.cluster_type, - "cluster_password": proxy_content.get("password"), - "cluster_port": proxy_content.get("port"), - "cluster_version": cluster.major_version, - "cluster_name": cluster.name, - "cluster_region": one_master.machine.bk_city.bk_idc_city_name, - "redis_password": redis_content.get("requirepass"), - } - except Exception as e: - traceback.print_exc() - logger.error(f"get cluster info by domain failed {e}, cluster_domain: {cluster_domain}") - raise Exception(f"get cluster info by domain failed {e}, cluster_domain: {cluster_domain}") - - @staticmethod - def get_cluster_slaves_data(bk_biz_id: int, cluster_domain: str) -> Tuple[dict, list, list]: - """ - 获取集群下的slaves实例信息 - """ - try: - cluster_domain = domain_without_port(cluster_domain) - cluster = Cluster.objects.get(bk_biz_id=bk_biz_id, immute_domain=cluster_domain) - one_master_instance = cluster.storageinstance_set.filter( - instance_role=InstanceRole.REDIS_MASTER.value, status=InstanceStatus.RUNNING - ).first() - running_master = { - "ip": one_master_instance.machine.ip, - "port": one_master_instance.port, - "status": one_master_instance.status, - "instance_role": one_master_instance.instance_role, - } - slave_instances = [] - uniq_hosts = set() - slave_hosts = [] - kvstore: int = 0 - for slave in cluster.storageinstance_set.filter(instance_role=InstanceRole.REDIS_SLAVE.value): - if is_tendisplus_instance_type(cluster.cluster_type): - kvstore = DEFAULT_TENDISPLUS_KVSTORECOUNT - else: - kvstore = 1 - slave_instances.append( - { - "ip": slave.machine.ip, - "port": slave.port, - "db_type": get_redis_type_by_cluster_type(cluster.cluster_type), - "status": slave.status, - "data_size": 1 * 1024 * 1024 * 1024, - "kvstorecount": kvstore, - "segment_start": -1, - "segment_end": -1, - } - ) - if slave.machine.ip in uniq_hosts: - continue - uniq_hosts.add(slave.machine.ip) - slave_hosts.append( - { - "ip": slave.machine.ip, - "mem_info": {}, - "disk_info": {}, - } - ) - return running_master, slave_instances, slave_hosts - except Exception as e: - logger.error(f"get cluster redis instances data failed {e}, cluster_domain: {cluster_domain}") - raise Exception(f"get cluster redis instances data failed {e}, cluster_domain: {cluster_domain}") - - @staticmethod - def decode_info_cmd(info_str: str) -> Dict: - info_ret: Dict[str, dict] = {} - info_list: List = info_str.split("\n") - for info_item in info_list: - info_item = info_item.strip() - if info_item.startswith("#"): - continue - if len(info_item) == 0: - continue - tmp_list = info_item.split(":", 1) - if len(tmp_list) < 2: - continue - tmp_list[0] = tmp_list[0].strip() - tmp_list[1] = tmp_list[1].strip() - info_ret[tmp_list[0]] = tmp_list[1] - return info_ret - - def get_redis_slaves_data_size(self, trans_data: RedisDtsContext) -> list: - """ - 获取redis slave的数据大小 - """ - info_cmd: str = "" - if is_redis_instance_type(trans_data.src_cluster_type): - info_cmd = "info memory" - elif is_tendisplus_instance_type(trans_data.src_cluster_type): - info_cmd = "info Dataset" - elif is_tendisssd_instance_type(trans_data.src_cluster_type): - info_cmd = "info" - slave_addrs = [slave["ip"] + ":" + str(slave["port"]) for slave in trans_data.src_slave_instances] - resp = DRSApi.redis_rpc( - { - "addresses": slave_addrs, - "db_num": 0, - "password": trans_data.src_redis_password, - "command": info_cmd, - "bk_cloud_id": trans_data.bk_cloud_id, - } - ) - info_ret: Dict[str, Dict] = {} - for item in resp: - info_ret[item["address"]] = self.decode_info_cmd(item["result"]) - new_slave_instances: List = [] - for slave in trans_data.src_slave_instances: - slave_addr = slave["ip"] + ":" + str(slave["port"]) - if slave["db_type"] == ClusterType.TendisRedisInstance.value: - slave["data_size"] = int(info_ret[slave_addr]["used_memory"]) - elif slave["db_type"] == ClusterType.TendisTendisplusInsance.value: - slave["data_size"] = int(info_ret[slave_addr]["rocksdb.total-sst-files-size"]) - elif slave["db_type"] == ClusterType.TendisTendisSSDInstance.value: - rockdb_size = 0 - level_head_reg = re.compile(r"^level-\d+$") - level_data_reg = re.compile(r"^bytes=(\d+),num_entries=(\d+),num_deletions=(\d+)") - for k, v in info_ret[slave_addr].items(): - if level_head_reg.match(k): - tmp_list = level_data_reg.findall(v) - if len(tmp_list) != 1: - err = f"redis:{slave_addr} info 'RocksDB Level stats' format not correct,{k}:{v}" - self.log_error(err) - raise Exception(err) - size01 = int(tmp_list[0][0]) - rockdb_size += size01 - slave["data_size"] = rockdb_size - new_slave_instances.append(slave) - return new_slave_instances - - -class GetRedisDtsDataComponent(Component): - name = __name__ - code = "get_redis_dts_data" - bound_service = GetRedisDtsDataService - - class RedisDtsPrecheckService(BaseService): """ redis dts前置检查 """ def _execute(self, data, parent_data): - kwargs = data.get_one_of_inputs("kwargs") + kwargs: ActKwargs = data.get_one_of_inputs("kwargs") global_data = data.get_one_of_inputs("global_data") trans_data: RedisDtsContext = data.get_one_of_inputs("trans_data") @@ -348,23 +80,31 @@ def _execute(self, data, parent_data): # 打印信息 self.log_info(" RedisDtsPrecheckService start") + self.log_info("kwargs: {}".format(kwargs)) try: + # 检查key正则是否合法 + if not self.check_key_regex(kwargs): + return False # 所有slave必须是running状态且可连接 - if not self.check_all_src_slaves_running(trans_data): + if not self.check_all_src_slaves_running(kwargs["cluster"]["src"]): return False # 源slave磁盘空间是否足够 - if not self.check_src_redis_host_disk(trans_data): + if not self.check_src_redis_host_disk(trans_data, kwargs): return False # 源集群如果是cluster协议,则检查集群状态是否ok - if not self.check_src_cluster_state_ok(trans_data): + if not self.check_src_cluster_state_ok(kwargs["cluster"]["src"]): return False # 源集群如果是cluster协议,则检查集群节点是否正常 self.log_info("start check_src_cluster_nodes_ok") - cluster_nice_slaves = self.check_src_cluster_nodes_ok(trans_data) - if not cluster_nice_slaves and len(cluster_nice_slaves) > 0: - trans_data.src_slave_instances = cluster_nice_slaves + self.check_src_cluster_nodes_ok(kwargs["cluster"]["dts_copy_type"], kwargs["cluster"]["src"]) + # cluster_nice_slaves = self.check_src_cluster_nodes_ok(trans_data) + # if not cluster_nice_slaves and len(cluster_nice_slaves) > 0: + # trans_data.src_slave_instances = cluster_nice_slaves + # 目的集群可连接性 - if not self.check_dst_cluster_connected(trans_data): + if not self.check_dst_cluster_connected( + global_data["bk_biz_id"], kwargs["cluster"]["dts_copy_type"], kwargs["cluster"]["dst"] + ): return False except Exception as e: traceback.print_exc() @@ -380,27 +120,55 @@ def inputs_format(self) -> List: Service.InputItem(name="global_data", key="global_data", type="dict", required=True), ] - def check_all_src_slaves_running(self, trans_data: RedisDtsContext) -> bool: + def check_key_regex(self, kwargs: dict) -> bool: + """ + 检查key的正则表达式是否合法 + """ + white_regex = get_safe_regex_pattern(kwargs["cluster"]["info"]["key_white_regex"]) + black_regex = get_safe_regex_pattern(kwargs["cluster"]["info"]["key_black_regex"]) + + if white_regex == "": + self.log_error(_("包含key正则:{} 不合法".format(kwargs["cluster"]["info"]["key_white_regex"]))) + return False + + if black_regex == ".*": + self.log_error(_("排除key正则:{} 不合法".format(kwargs["cluster"]["info"]["key_black_regex"]))) + return False + + if white_regex != ".*" and white_regex != "": + try: + re.compile(white_regex) + except Exception as e: + self.log_error(_("包含key正则:{} 不合法,err:{}".format(kwargs["cluster"]["info"]["key_white_regex"], e))) + return False + + if black_regex != "": + try: + re.compile(black_regex) + except Exception as e: + self.log_error(_("排除key正则:{} 不合法,err:{}".format(kwargs["cluster"]["info"]["key_black_regex"], e))) + return False + return True + + def check_all_src_slaves_running(self, src_data: dict) -> bool: """ 检查所有源redis slave是否都是running状态 """ unrunning_slave_cnt = 0 - for slave in trans_data.src_slave_instances: + for slave in src_data["slave_instances"]: if slave["status"] != InstanceStatus.RUNNING.value: unrunning_slave_cnt += 1 if unrunning_slave_cnt > 0: - self.log_error( - _("源redis集群{}存在{}个非running状态的slave".format(trans_data.src_cluster_addr, unrunning_slave_cnt)) - ) + self.log_error(_("源redis集群{}存在{}个非running状态的slave".format(src_data["cluster_addr"], unrunning_slave_cnt))) return False - slaves_addr = [slave["ip"] + ":" + str(slave["port"]) for slave in trans_data.src_slave_instances] + slaves_addr = [slave["ip"] + ":" + str(slave["port"]) for slave in src_data["slave_instances"]] DRSApi.redis_rpc( { "addresses": slaves_addr, "db_num": 0, - "password": trans_data.src_redis_password, + "password": src_data["redis_password"], "command": "ping", - "bk_cloud_id": trans_data.bk_cloud_id, + "bk_cloud_id": src_data["bk_cloud_id"], } ) return True @@ -421,12 +189,15 @@ def decode_slave_host_disk_info(disk_line: str) -> dict: ret["mount_on"] = l01[5] return ret - def check_src_redis_host_disk(self, trans_data: RedisDtsContext) -> bool: + def check_src_redis_host_disk(self, trans_data: RedisDtsContext, kwargs: dict) -> bool: """ 检查源redis磁盘空间是否足够 """ + if kwargs["cluster"]["dts_copy_type"] == DtsCopyType.USER_BUILT_TO_DBM.value: + return True disk_used = trans_data.disk_used - for src_slave in trans_data.src_slave_instances: + self.log_info("check_src_redis_host_disk disk_used:{}".format(disk_used)) + for src_slave in kwargs["cluster"]["src"]["slave_instances"]: disk_str = disk_used.get(src_slave["ip"])["data"] disk_info = self.decode_slave_host_disk_info(disk_str) if disk_info["used_ratio"] > 90: @@ -446,60 +217,61 @@ def check_src_redis_host_disk(self, trans_data: RedisDtsContext) -> bool: ) ) return False - slave_hosts = set(host["ip"] for host in trans_data.src_slave_instances) + slave_hosts = set(host["ip"] for host in kwargs["cluster"]["src"]["slave_instances"]) self.log_info(_("所有源redis slave机器:{} 磁盘空间检查通过").format(slave_hosts)) return True - def check_src_cluster_nodes_ok(self, trans_data: RedisDtsContext) -> List[dict]: + def check_src_cluster_nodes_ok(self, dts_copy_type: str, src_data: dict) -> List[dict]: """ 如果源集群是redis cluster协议,检查源集群节点是否正常: 1. 每个负责slot的master都至少有一个running的slave 2. 如果有多个running slave,则选择其中一个作为迁移节点 """ - if not is_redis_cluster_protocal(trans_data.src_cluster_type): + if not is_redis_cluster_protocal(src_data["cluster_type"]): self.log_info( _("src_cluster:{} type:{} 无需检查cluster nodes是否ok").format( - trans_data.src_cluster_addr, trans_data.src_cluster_type + src_data["cluster_addr"], src_data["cluster_type"] ) ) - return True + return [] + if dts_copy_type == DtsCopyType.USER_BUILT_TO_DBM.value: + # 用户自建集群,cluster nodes无需再次获取 + return [] + # 获取集群cluster nodes信息 - running_master = trans_data.src_cluster_running_master + running_master = src_data["one_running_master"] master_addr = running_master["ip"] + ":" + str(running_master["port"]) resp = DRSApi.redis_rpc( { "addresses": [master_addr], "db_num": 0, - "password": trans_data.src_redis_password, + "password": src_data["redis_password"], "command": "cluster nodes", - "bk_cloud_id": trans_data.bk_cloud_id, + "bk_cloud_id": src_data["bk_cloud_id"], } ) cluster_nodes_str = resp[0]["result"] - self.log_info("src_cluster:{} cluster_nodes_str:\n {}".format(trans_data.src_cluster_addr, cluster_nodes_str)) + self.log_info("src_cluster:{} cluster_nodes_str:\n {}".format(src_data["cluster_addr"], cluster_nodes_str)) # 确保所有负责slots的master都至少有一个running的slave # 如果有多个running slave,则选择其中一个保存到nice_slaves中 masters_with_slots = get_masters_with_slots(cluster_nodes_str) if len(masters_with_slots) == 0: self.log_error( - "src_cluster:{} not found masters(with_slots),master:{}".format( - trans_data.src_cluster_addr, master_addr - ) + "src_cluster:{} not found masters(with_slots),master:{}".format(src_data["cluster_addr"], master_addr) ) raise Exception( - "src_cluster:{} not found masters(with_slots),master:{}".format( - trans_data.src_cluster_addr, master_addr - ) + "src_cluster:{} not found masters(with_slots),master:{}".format(src_data["cluster_addr"], master_addr) ) slaves_by_masterid = group_slaves_by_master_id(cluster_nodes_str) meta_slaves = {} - for src_slave in trans_data.src_slave_instances: + for src_slave in src_data["slave_instances"]: addr = src_slave["ip"] + ":" + str(src_slave["port"]) meta_slaves[addr] = src_slave nice_slaves = [] for master in masters_with_slots: if master.node_id not in slaves_by_masterid: - raise Exception("master {} has no slave".format(master.addr)) + self.log_error("src_cluster({}) master {} has no slave".format(src_data["cluster_addr"], master.addr)) + raise Exception("src_cluster({}) master {} has no slave".format(src_data["cluster_addr"], master.addr)) slaves = slaves_by_masterid[master.node_id] one_running_slave: ClusterNodeData = None for slave in slaves: @@ -508,33 +280,34 @@ def check_src_cluster_nodes_ok(self, trans_data: RedisDtsContext) -> List[dict]: one_running_slave = slave break if one_running_slave is None: - raise Exception("master {} has no running slave".format(master.addr)) + self.log_error( + "src_cluster({}) master {} has no running slave".format(src_data["cluster_addr"], master.addr) + ) + raise Exception( + "src_cluster({}) master {} has no running slave".format(src_data["cluster_addr"], master.addr) + ) nice_slaves.append(meta_slaves[one_running_slave.addr]) self.log_info("check_src_cluster_nodes_ok nice_slaves:{}".format(nice_slaves)) return nice_slaves - def check_src_cluster_state_ok(self, trans_data: RedisDtsContext) -> bool: + def check_src_cluster_state_ok(self, src_data: dict) -> bool: """ 如果源集群是redis cluster协议,检查源集群cluster_state是ok的 """ - if not is_redis_cluster_protocal(trans_data.src_cluster_type): + if not is_redis_cluster_protocal(src_data["cluster_type"]): self.log_info( - _( - "src_cluster:{} 类型是:{} 无需检查cluster state".format( - trans_data.src_cluster_addr, trans_data.src_cluster_type - ) - ) + _("src_cluster:{} 类型是:{} 无需检查cluster state".format(src_data["cluster_addr"], src_data["cluster_type"])) ) return True - running_master = trans_data.src_cluster_running_master + running_master = src_data["one_running_master"] master_addr = running_master["ip"] + ":" + str(running_master["port"]) resp = DRSApi.redis_rpc( { "addresses": [master_addr], "db_num": 0, - "password": trans_data.src_redis_password, + "password": src_data["redis_password"], "command": "cluster info", - "bk_cloud_id": trans_data.bk_cloud_id, + "bk_cloud_id": src_data["bk_cloud_id"], } ) cluster_info_str = resp[0]["result"] @@ -542,7 +315,7 @@ def check_src_cluster_state_ok(self, trans_data: RedisDtsContext) -> bool: if cluster_info.cluster_state != "ok": self.log_error( "src cluster:{} cluster_state:{} is not ok".format( - trans_data.src_cluster_addr, cluster_info.cluster_state + src_data["cluster_addr"], cluster_info.cluster_state ) ) # raise Exception( @@ -551,30 +324,43 @@ def check_src_cluster_state_ok(self, trans_data: RedisDtsContext) -> bool: # ) # ) return False - self.log_info( - "src_cluster:{} cluster_state:{}".format(trans_data.src_cluster_addr, cluster_info.cluster_state) - ) + self.log_info("src_cluster:{} cluster_state:{}".format(src_data["cluster_addr"], cluster_info.cluster_state)) return True - def check_dst_cluster_connected(self, trans_data: RedisDtsContext) -> bool: + def check_dst_cluster_connected(self, bk_biz_id: int, dts_copy_type: str, dst_data: dict) -> bool: """ 检查目的集群是否可连接 """ - # TODO 现在域名无法使用,目的集群先用 proxy_ip:proxy_port 代替 - cluster = Cluster.objects.get(bk_biz_id=trans_data.bk_biz_id, id=trans_data.dst_cluster_id) dst_proxy_addrs = [] - for proxy in cluster.proxyinstance_set.all(): - dst_proxy_addrs.append(proxy.machine.ip + ":" + str(proxy.port)) + if dts_copy_type == DtsCopyType.COPY_TO_OTHER_SYSTEM: + dst_proxy_addrs.append(dst_data["cluster_addr"]) + else: + cluster: Cluster = None + if ( + dts_copy_type + in [DtsBillType.REDIS_CLUSTER_SHARD_NUM_UPDATE.value, DtsBillType.REDIS_CLUSTER_TYPE_UPDATE.value] + and int(dst_data["cluster_id"]) == 0 + ): + """ + 如果是集群分片数变更/集群类型变更,目的集群id为0,代表目的集群是新创建的集群 + """ + dst_addr_pair = dst_data["cluster_addr"].split(":") + dst_domain = dst_addr_pair[0] + cluster = Cluster.objects.get(bk_biz_id=bk_biz_id, immute_domain=dst_domain) + else: + cluster = Cluster.objects.get(id=dst_data["cluster_id"]) + for proxy in cluster.proxyinstance_set.all(): + dst_proxy_addrs.append(proxy.machine.ip + ":" + str(proxy.port)) DRSApi.redis_rpc( { "addresses": dst_proxy_addrs, "db_num": 0, - "password": trans_data.dst_cluster_password, + "password": dst_data["cluster_password"], "command": "get a", - "bk_cloud_id": trans_data.bk_cloud_id, + "bk_cloud_id": dst_data["bk_cloud_id"], } ) - self.log_info("dst_cluster:{} connect success".format(trans_data.dst_cluster_addr)) + self.log_info("dst_cluster:{} connect success".format(dst_data["cluster_addr"])) return True @@ -590,10 +376,10 @@ class RedisDtsExecuteService(BaseService): """ __need_schedule__ = True - interval = StaticIntervalGenerator(10) + interval = StaticIntervalGenerator(30) def _execute(self, data, parent_data): - kwargs = data.get_one_of_inputs("kwargs") + kwargs: ActKwargs = data.get_one_of_inputs("kwargs") global_data = data.get_one_of_inputs("global_data") trans_data: RedisDtsContext = data.get_one_of_inputs("trans_data") @@ -604,44 +390,80 @@ def _execute(self, data, parent_data): if trans_data.job_id and trans_data.task_ids: """如果job_id和task_ids已经存在,则表示已经执行过了,无需重复插入""" return True - input_cluster = kwargs["cluster"] try: job_id: int = 0 task_ids: list = [] - # root_id = kwargs["root_id"] uid = int(global_data["uid"]) + bk_biz_id = int(global_data["bk_biz_id"]) + dts_copy_type = kwargs["cluster"]["dts_copy_type"] + dst_cluster_id = int(kwargs["cluster"]["dst"]["cluster_id"]) + if ( + dts_copy_type + in [DtsBillType.REDIS_CLUSTER_SHARD_NUM_UPDATE.value, DtsBillType.REDIS_CLUSTER_TYPE_UPDATE.value] + and int(kwargs["cluster"]["dst"]["cluster_id"]) == 0 + ): + """ + 如果是集群分片数变更/集群类型变更,目的集群id为0,代表目的集群是新创建的集群 + """ + dst_addr_pair = kwargs["cluster"]["dst"]["cluster_addr"].split(":") + dst_domain = dst_addr_pair[0] + cluster = Cluster.objects.get(bk_biz_id=bk_biz_id, immute_domain=dst_domain) + dst_cluster_id = cluster.id with transaction.atomic(): job = TbTendisDTSJob() job.bill_id = uid - job.app = trans_data.bk_biz_id - job.bk_cloud_id = trans_data.bk_cloud_id + job.app = str(global_data["bk_biz_id"]) + job.bk_cloud_id = kwargs["cluster"]["src"]["bk_cloud_id"] job.user = global_data["created_by"] - job.dts_bill_type = trans_data.dts_bill_type - job.dts_copy_type = trans_data.dts_copy_type - job.online_switch_type = global_data["online_switch_type"] - job.datacheck = int(global_data["datacheck"]) - job.datarepair = int(global_data["datarepair"]) - job.datarepair_mode = global_data["datarepair_mode"] - job.src_cluster = trans_data.src_cluster_addr - job.src_cluster_type = trans_data.src_cluster_type - - job.src_rollback_bill_id = input_cluster["src_rollback_bill_id"] - - job.dst_bk_biz_id = input_cluster["dst_bk_biz_id"] - job.dst_cluster = trans_data.dst_cluster_addr - job.dst_cluster_type = trans_data.dst_cluster_type - job.key_white_regex = trans_data.key_white_regex.encode("utf-8") - job.key_black_regex = trans_data.key_black_regex.encode("utf-8") + job.dts_bill_type = kwargs["cluster"]["dts_bill_type"] + job.dts_copy_type = kwargs["cluster"]["dts_copy_type"] + job.write_mode = global_data.get("write_mode", DtsWriteMode.FLUSHALL_AND_WRITE_TO_REDIS.value) + job.online_switch_type = ( + global_data["online_switch_type"] if global_data.get("online_switch_type") else "" + ) + + job.sync_disconnect_type = global_data["sync_disconnect_setting"]["type"] + job.sync_disconnect_reminder_frequency = global_data["sync_disconnect_setting"].get( + "reminder_frequency", DtsSyncDisconnReminderFreq.ONCE_DAILY.value + ) + + job.data_check_repair_type = global_data["data_check_repair_setting"]["type"] + job.data_check_repair_execution_frequency = global_data["data_check_repair_setting"][ + "execution_frequency" + ] + + job.src_cluster = kwargs["cluster"]["src"]["cluster_addr"] + job.src_cluster_id = kwargs["cluster"]["src"]["cluster_id"] + job.src_cluster_type = kwargs["cluster"]["src"]["cluster_type"] + + job.src_rollback_bill_id = 0 + job.src_rollback_instances = "" + + job.dst_bk_biz_id = ( + kwargs["cluster"]["info"].get("dst_bk_biz_id") + if kwargs["cluster"]["info"].get("dst_bk_biz_id") + else global_data["bk_biz_id"] + ) + job.dst_cluster = kwargs["cluster"]["dst"]["cluster_addr"] + job.dst_cluster_id = dst_cluster_id + job.dst_cluster_type = kwargs["cluster"]["dst"]["cluster_type"] + job.key_white_regex = kwargs["cluster"]["info"]["key_white_regex"] + job.key_black_regex = kwargs["cluster"]["info"]["key_black_regex"] job.create_time = datetime.datetime.now() job.save() job_id = job.id - src_password_base64 = base64.b64encode(trans_data.src_redis_password.encode("utf-8")).decode("utf-8") - dst_passsword_base64 = base64.b64encode(trans_data.dst_cluster_password.encode("utf-8")).decode( - "utf-8" - ) - cuncurrency_limit = self.get_src_redis_host_concurrency(trans_data) - for slave in trans_data.src_slave_instances: + src_password_base64 = base64.b64encode( + kwargs["cluster"]["src"]["redis_password"].encode("utf-8") + ).decode("utf-8") + dst_passsword_base64 = base64.b64encode( + kwargs["cluster"]["dst"]["cluster_password"].encode("utf-8") + ).decode("utf-8") + task_white_regex = get_safe_regex_pattern(kwargs["cluster"]["info"]["key_white_regex"]) + task_black_regex = get_safe_regex_pattern(kwargs["cluster"]["info"]["key_black_regex"]) + + cuncurrency_limit = self.get_src_redis_host_concurrency(trans_data, kwargs) + for slave in kwargs["cluster"]["src"]["slave_instances"]: addr = slave["ip"] + ":" + str(slave["port"]) for kvstoreid in range(slave["kvstorecount"]): task = TbTendisDtsTask() @@ -649,6 +471,7 @@ def _execute(self, data, parent_data): task.user = job.user task.app = job.app task.bk_cloud_id = job.bk_cloud_id + task.write_mode = job.write_mode task.dts_server = "1.1.1.1" task.src_cluster = job.src_cluster task.src_cluster_priority = 0 @@ -661,11 +484,11 @@ def _execute(self, data, parent_data): task.src_seg_end = slave["segment_end"] task.src_weight = int(slave["port"] % 8) task.src_ip_concurrency_limit = cuncurrency_limit - task.src_ip_zonename = trans_data.src_cluster_region + task.src_ip_zonename = kwargs["cluster"]["src"]["cluster_city_name"] task.src_kvstore_id = kvstoreid - task.key_white_regex = trans_data.key_white_regex.encode("utf-8") - task.key_black_regex = trans_data.key_black_regex.encode("utf-8") - task.dst_cluster = job.dst_cluster + task.key_white_regex = task_white_regex + task.key_black_regex = task_black_regex + task.dst_cluster = kwargs["cluster"]["dst"]["cluster_addr"] task.dst_password = dst_passsword_base64 task.create_time = datetime.datetime.now() task.save() @@ -690,25 +513,32 @@ def _schedule(self, data, parent_data, callback_data=None) -> bool: trans_data = getattr(flow_context, kwargs["set_trans_data_dataclass"])() task_ids = trans_data.task_ids tasks_rows = TbTendisDtsTask.objects.filter(id__in=task_ids) - if self.__is_any_task_fail(tasks_rows): - self.log_error( - _( - "bill_id:{} src_cluster:{} dst_cluster:{} 某些tasks迁移失败".format( - tasks_rows[0].bill_id, tasks_rows[0].src_cluster, tasks_rows[0].dst_cluster + job_row = TbTendisDTSJob.objects.get(id=trans_data.job_id) + all_done, all_success = self.__is_all_tasks_done(tasks_rows) + if all_done: + # 所有的task都已经完成了 + if all_success: + # 所有的task都成功了 + self.log_info( + _( + "bill_id:{} src_cluster:{} dst_cluster:{} 所有tasks都成功且终止了同步task".format( + tasks_rows[0].bill_id, tasks_rows[0].src_cluster, tasks_rows[0].dst_cluster + ) ) ) - ) - return False - if self.__is_all_tasks_success(tasks_rows): - self.log_info( - _( - "bill_id:{} src_cluster:{} dst_cluster:{} 所有tasks都成功且终止了迁移进程".format( - tasks_rows[0].bill_id, tasks_rows[0].src_cluster, tasks_rows[0].dst_cluster + self.finish_schedule() + return True + else: + # 有task失败了 + self.log_error( + _( + "bill_id:{} src_cluster:{} dst_cluster:{} 某些tasks迁移失败".format( + tasks_rows[0].bill_id, tasks_rows[0].src_cluster, tasks_rows[0].dst_cluster + ) ) ) - ) - self.finish_schedule() - return True + self.finish_schedule() + return False if self.__is_all_tasks_incr_sync(tasks_rows): self.log_info( _( @@ -717,8 +547,90 @@ def _schedule(self, data, parent_data, callback_data=None) -> bool: ) ) ) - self.finish_schedule() - return True + if self.__is_able_to_new_check_repair(global_data, job_row): + self.log_info( + _( + "bill_id:{} src_cluster:{} dst_cluster:{} 开始新的校验修复".format( + tasks_rows[0].bill_id, tasks_rows[0].src_cluster, tasks_rows[0].dst_cluster + ) + ) + ) + self.__new_data_check_repair_job(global_data, job_row) + return True + if job_row.last_data_check_repair_flow_id != "": + stat = FlowTree.objects.get(root_id=job_row.last_data_check_repair_flow_id).status + if stat == StateType.RUNNING.value: + self.log_info( + _( + "bill_id:{} src_cluster:{} dst_cluster:{} 上次校验修复正在进行中...,flow_id:{}".format( + tasks_rows[0].bill_id, + tasks_rows[0].src_cluster, + tasks_rows[0].dst_cluster, + job_row.last_data_check_repair_flow_id, + ) + ) + ) + return True + if stat == StateType.FAILED.value: + self.log_error( + _( + "bill_id:{} src_cluster:{} dst_cluster:{} 上次校验修复失败,flow_id:{},请处理该失败信息而后再到当前页面重试".format( + tasks_rows[0].bill_id, + tasks_rows[0].src_cluster, + tasks_rows[0].dst_cluster, + job_row.last_data_check_repair_flow_id, + ) + ) + ) + return False + if stat == StateType.FINISHED.value: + self.log_info( + _( + "bill_id:{} src_cluster:{} dst_cluster:{} 上次校验修复已完成,flow_id:{}".format( + tasks_rows[0].bill_id, + tasks_rows[0].src_cluster, + tasks_rows[0].dst_cluster, + job_row.last_data_check_repair_flow_id, + ) + ) + ) + # 回档实例数据回写 直接断开同步关系 + if global_data["dts_copy_type"] == DtsCopyType.COPY_FROM_ROLLBACK_INSTANCE.value: + self.log_info( + _( + "bill_id:{} src_cluster:{} dst_cluster:{} 数据同步已 ok,后续将断开同步关系...".format( + job_row.bill_id, job_row.src_cluster, job_row.dst_cluster + ) + ) + ) + self.finish_schedule() + return True + # 如果是 集群分片数变更/集群类型变更,且数据校验与修复已经完成,则不断开同步关系,直接返回 + if global_data["ticket_type"] in [ + DtsBillType.REDIS_CLUSTER_SHARD_NUM_UPDATE.value, + DtsBillType.REDIS_CLUSTER_TYPE_UPDATE.value, + ]: + self.log_info( + _( + "bill_id:{} src_cluster:{} dst_cluster:{} 数据同步已 ok,校验修复已完成,将继续执行等待切换步骤...".format( + tasks_rows[0].bill_id, tasks_rows[0].src_cluster, tasks_rows[0].dst_cluster + ) + ) + ) + self.finish_schedule() + return True + + if self.__is_able_to_disconnect(global_data, job_row): + self.log_info( + _( + "bill_id:{} src_cluster:{} dst_cluster:{} 所有tasks开始断开同步关系".format( + tasks_rows[0].bill_id, tasks_rows[0].src_cluster, tasks_rows[0].dst_cluster + ) + ) + ) + self.__disconnect_all_tasks_sync(tasks_rows) + return True + # 继续下次循环 return True @@ -728,18 +640,18 @@ def inputs_format(self) -> List: Service.InputItem(name="global_data", key="global_data", type="dict", required=True), ] - def get_src_redis_host_concurrency(self, trans_data: RedisDtsContext) -> int: + def get_src_redis_host_concurrency(self, trans_data: RedisDtsContext, kwargs: ActKwargs) -> int: """ - 获取源redis host上可支持的并发度,对tendisSSD尤为重要,否则容易把磁盘打爆 + 获取源redis host上可支持的并发度,对tendisSSD尤为重要,否则容易把磁盘搞满 """ max_datasize_instance: dict = None - for slave in trans_data.src_slave_instances: + for slave in kwargs["cluster"]["src"]["slave_instances"]: if max_datasize_instance is None: max_datasize_instance = slave continue if max_datasize_instance["data_size"] < slave["data_size"]: max_datasize_instance = slave - if trans_data.dts_copy_type == DtsCopyType.USER_BUILT_TO_DBM: + if kwargs["cluster"]["dts_copy_type"] == DtsCopyType.USER_BUILT_TO_DBM: return 5 if max_datasize_instance["db_type"] == ClusterType.TendisTendisplusInsance: return 10 @@ -788,24 +700,110 @@ def get_src_redis_host_concurrency(self, trans_data: RedisDtsContext) -> int: return 5 return concurrency - def __is_any_task_fail(self, tasks: List[TbTendisDtsTask]) -> bool: + def __is_all_tasks_done(self, tasks: List[TbTendisDtsTask]) -> Tuple[bool, bool]: """ - 判断是否有任务失败 + 判断是否所有任务都完成 """ + all_done: bool = True + all_success: bool = True for task in tasks: - if task.status < 0: - self.log_error(_("task:{} {}:{} 迁移失败".format(task.id, task.src_ip, task.src_port))) + if task.status in [0, 1]: + # 存在tasks.status in [0,1]的情况 + all_done = False + all_success = False + return all_done, all_success + if task.status != 2: + # 存在失败的情况 + all_success = False + return all_done, all_success + + def __is_check_time_interval_satisfied(self, execution_frequency: str, last_execute_time: datetime) -> bool: + """ + 判断是否满足校验时间间隔 + """ + current_time = datetime.datetime.now() + if execution_frequency == DtsDataCheckFreq.ONCE_EVERY_THREE_DAYS: + if (current_time - last_execute_time).days < 3: + return False + if execution_frequency == DtsDataCheckFreq.ONCE_WEEKLY: + if (current_time - last_execute_time).days < 7: + return False + return True + + def __is_able_to_new_check_repair(self, global_data: dict, dts_job: TbTendisDTSJob) -> bool: + """ + 判断是否可以进行数据校验修复 + """ + # 指定不做数据校验修复 + if global_data["data_check_repair_setting"]["type"] == DtsDataCheckType.NO_CHECK_NO_REPAIR: + return False + + # 数据复制后自动断开同步,至少进行一次数据校验 + if global_data["sync_disconnect_setting"]["type"] == DtsSyncDisconnType.AUTO_DISCONNECT_AFTER_REPLICATION: + if dts_job.last_data_check_repair_flow_id == "": return True + + # 集群分片数变更、集群类型变更,至少进行一次数据校验 + if global_data["ticket_type"] in [ + DtsBillType.REDIS_CLUSTER_SHARD_NUM_UPDATE.value, + DtsBillType.REDIS_CLUSTER_TYPE_UPDATE.value, + ]: + if dts_job.last_data_check_repair_flow_id == "": + return True + + # 到这里时 + # ["data_check_repair_setting"]["type"] 必然是 [DATA_CHECK_AND_REPAIR, DATA_CHECK_ONLY] + # ["sync_disconnect_setting"]["type"] 必然是 [KEEP_SYNC_WITH_REMINDER] + if global_data["sync_disconnect_setting"]["type"] == DtsSyncDisconnType.KEEP_SYNC_WITH_REMINDER: + if dts_job.last_data_check_repair_flow_id == "": + # 第一次发起校验修复 + return True + stat = FlowTree.objects.get(root_id=dts_job.last_data_check_repair_flow_id).status + if stat not in [StateType.FAILED, StateType.FINISHED]: + # 上次校验修复未完成 + return False + # 上次校验修复已完成 + if ( + global_data["data_check_repair_setting"]["execution_frequency"] + == DtsDataCheckFreq.ONCE_AFTER_REPLICATION + ): + # 复制完成后执行一次校验修复 + return False + if self.__is_check_time_interval_satisfied( + global_data["data_check_repair_setting"]["execution_frequency"], + dts_job.last_data_check_repair_flow_execute_time, + ): + return True + return False + + def __is_able_to_disconnect(self, global_data: dict, dts_job: TbTendisDTSJob) -> bool: + """ + 判断是否可以断开同步关系 + """ + if global_data["sync_disconnect_setting"]["type"] == DtsSyncDisconnType.AUTO_DISCONNECT_AFTER_REPLICATION: + # 复制完成后自动断开同步关系,并且需要进行数据校验修复,则需要等待数据校验修复完成后再断开同步关系 + if dts_job.last_data_check_repair_flow_id != "": + stat = FlowTree.objects.get(root_id=dts_job.last_data_check_repair_flow_id).status + if stat == StateType.FINISHED: + return True + return False + + # 数据复制完成后保持同步关系 + # global_data["sync_disconnect_setting"]["type"] == keep_sync_with_reminder return False - def __is_all_tasks_success(self, tasks: List[TbTendisDtsTask]) -> bool: + @transaction.atomic() + def __disconnect_all_tasks_sync(self, tasks: List[TbTendisDtsTask]): """ - 判断是否所有任务都成功 + 执行断开同步 """ for task in tasks: - if task.status != 2: - return False - return True + if task.status != 2 and task.sync_operate not in [ + DtsOperateType.SYNC_STOP_TODO.value, + DtsOperateType.SYNC_STOP_SUCC.value, + ]: + task.sync_operate = DtsOperateType.SYNC_STOP_TODO + task.save(update_fields=["sync_operate"]) def __is_all_tasks_incr_sync(self, tasks: List[TbTendisDtsTask]) -> bool: """ @@ -829,6 +827,39 @@ def __is_all_tasks_incr_sync(self, tasks: List[TbTendisDtsTask]) -> bool: return False return True + def __new_data_check_repair_job(self, global_data: dict, dts_job: TbTendisDTSJob): + repair_enabled: bool = ( + global_data["data_check_repair_setting"]["type"] == DtsDataCheckType.DATA_CHECK_AND_REPAIR.value + or global_data["sync_disconnect_setting"]["type"] + == DtsSyncDisconnType.AUTO_DISCONNECT_AFTER_REPLICATION.value + ) + ticket_data: dict = { + "uid": global_data["uid"], + "bk_biz_id": global_data["bk_biz_id"], + "created_by": global_data["created_by"], + "ticket_type": TicketType.REDIS_DATACOPY_CHECK_REPAIR.value, + "execute_mode": ExecuteMode.AUTO_EXECUTION.value, + "specified_execution_time": "", + "global_timeout": TimeoutVars.NEVER.value, + "data_repair_enabled": repair_enabled, + "repair_mode": DtsDataRepairMode.AUTO_REPAIR.value, + "infos": [ + { + "bill_id": dts_job.bill_id, + "src_cluster": dts_job.src_cluster, + "src_instances": ["all"], + "dst_cluster": dts_job.dst_cluster, + "key_white_regex": dts_job.key_white_regex, + "key_black_regex": dts_job.key_black_regex, + } + ], + } + self.log_info(f"new_data_check_repair_job ticket_data:{ticket_data}") + root_id = uuid.uuid1().hex + flow = RedisClusterDataCheckRepairFlow(root_id=root_id, data=ticket_data) + flow.redis_cluster_data_check_repair_flow() + self.log_info(f"new_data_check_repair_job flow_id:{root_id}") + class RedisDtsExecuteComponent(Component): name = __name__ @@ -836,11 +867,14 @@ class RedisDtsExecuteComponent(Component): bound_service = RedisDtsExecuteService -class RedisDtsOnlineSwitchPrecheck(BaseService): +class NewDstClusterInstallJobAndWatchStatus(BaseService): """ - redis dts在线切换前置检查 + 新建 目标集群安装任务 并检测任务状态 """ + __need_schedule__ = True + interval = StaticIntervalGenerator(10) + def _execute(self, data, parent_data): kwargs = data.get_one_of_inputs("kwargs") global_data = data.get_one_of_inputs("global_data") @@ -849,140 +883,587 @@ def _execute(self, data, parent_data): if trans_data is None or trans_data == "${trans_data}": # 表示没有加载上下文内容,则在此添加 trans_data = getattr(flow_context, kwargs["set_trans_data_dataclass"])() - try: - # 检查源集群所有proxy节点状态是否正常 - src_proxy_instances = self.get_cluster_proxy_instances(trans_data.bk_biz_id, trans_data.src_cluster_id) - trans_data.src_proxy_instances = src_proxy_instances - not_running_proxys_cnt = 0 - for proxy in src_proxy_instances: - if proxy["status"] != InstanceStatus.RUNNING.value: - not_running_proxys_cnt += 1 - if not_running_proxys_cnt > 0: - self.log_error( - _("{}中有{}个proxy不是running状态".format(trans_data.src_cluster_addr, not_running_proxys_cnt)) + + if trans_data.dst_cluster_install_flow_id and trans_data.dst_cluster_install_flow_id != "": + self.log_info( + "NewDstClusterInstallJobAndWatchStatus dst_cluster_install_flow_id:{}".format( + trans_data.dst_cluster_install_flow_id ) - return False - # 检查源集群所有proxy节点的backend是否一致 - self.__check_twemproxy_backends(trans_data) - self.__check_predixy_servers(trans_data) - except Exception as e: - logger.error(f"redis dts online switch precheck failed {e}") + ) + data.outputs["trans_data"] = trans_data + return True + + ticket_data: dict = { + "uid": global_data["uid"], + "ticket_type": TicketType.REDIS_CLUSTER_APPLY.value, + "bk_biz_id": kwargs["cluster"]["dst_install_param"]["bk_biz_id"], + "bk_cloud_id": kwargs["cluster"]["dst_install_param"]["bk_cloud_id"], + "created_by": kwargs["cluster"]["dst_install_param"]["created_by"], + "proxy_port": kwargs["cluster"]["dst_install_param"]["cluster_port"], + "domain_name": kwargs["cluster"]["dst_install_param"]["cluster_domain"], + "cluster_name": kwargs["cluster"]["dst_install_param"]["cluster_name"], + "cluster_alias": kwargs["cluster"]["dst_install_param"]["cluster_alias"], + "cluster_type": kwargs["cluster"]["dst_install_param"]["cluster_type"], + "city_code": kwargs["cluster"]["dst_install_param"].get("region", ""), + "shard_num": kwargs["cluster"]["dst_install_param"]["shard_num"], + "group_num": len(kwargs["cluster"]["dst_install_param"]["backend_group"]), + "maxmemory": kwargs["cluster"]["dst_install_param"]["maxmemory"], + "db_version": kwargs["cluster"]["dst_install_param"]["db_version"], + "databases": kwargs["cluster"]["dst_install_param"]["redis_databases"], + "proxy_pwd": kwargs["cluster"]["dst_install_param"]["cluster_password"], + "redis_pwd": kwargs["cluster"]["dst_install_param"]["redis_password"], + "nodes": { + "proxy": kwargs["cluster"]["dst_install_param"]["proxy"], + "backend_group": kwargs["cluster"]["dst_install_param"]["backend_group"], + }, + "resource_spec": kwargs["cluster"]["dst_install_param"]["resource_spec"], + } + self.log_info("NewDstClusterInstallJobAndWatchStatus ticket_data==>:{}".format(ticket_data)) + root_id = uuid.uuid1().hex + if ticket_data["cluster_type"] == ClusterType.TendisPredixyTendisplusCluster.value: + flow = TendisPlusApplyFlow(root_id=root_id, data=ticket_data) + flow.deploy_tendisplus_cluster_flow() + elif ticket_data["cluster_type"] == ClusterType.TendisTwemproxyRedisInstance.value: + flow = RedisClusterApplyFlow(root_id=root_id, data=ticket_data) + flow.deploy_redis_cluster_flow() + elif ticket_data["cluster_type"] == ClusterType.TwemproxyTendisSSDInstance.value: + flow = RedisClusterApplyFlow(root_id=root_id, data=ticket_data) + flow.deploy_redis_cluster_flow() + else: + raise Exception("cluster_type:{} is not support".format(ticket_data["cluster_type"])) + + self.log_info("NewDstClusterInstallJobAndWatchStatus flow_id==>:{}".format(root_id)) + trans_data.dst_cluster_install_flow_id = root_id + + data.outputs["trans_data"] = trans_data + return True + + def _schedule(self, data, parent_data, callback_data=None) -> bool: + kwargs = data.get_one_of_inputs("kwargs") + global_data = data.get_one_of_inputs("global_data") + trans_data: RedisDtsContext = data.get_one_of_inputs("trans_data") + + if trans_data is None or trans_data == "${trans_data}": + # 表示没有加载上下文内容,则在此添加 + trans_data = getattr(flow_context, kwargs["set_trans_data_dataclass"])() + + stat = FlowTree.objects.get(root_id=trans_data.dst_cluster_install_flow_id).status + if stat not in [StateType.FINISHED.value, StateType.FAILED.value]: + self.log_info( + "dst_cluster_install_job flow_id:{} status:{}".format(trans_data.dst_cluster_install_flow_id, stat) + ) + return True + if stat == StateType.FAILED.value: + self.log_error("dst_cluster_install_job flow_id:{} failed".format(trans_data.dst_cluster_install_flow_id)) + self.finish_schedule() return False + self.finish_schedule() + self.log_info("dst_cluster_install_job flow_id:{} finished".format(trans_data.dst_cluster_install_flow_id)) + return True + def inputs_format(self) -> List: return [ Service.InputItem(name="kwargs", key="kwargs", type="dict", requiredc=True), Service.InputItem(name="global_data", key="global_data", type="dict", required=True), ] - @staticmethod - def get_cluster_proxy_instances(bk_biz_id: int, cluster_id: int) -> list: - """ - 获取集群下的proxy实例信息 - """ - try: - cluster = Cluster.objects.get(bk_biz_id=bk_biz_id, id=cluster_id) - proxy_instances = [] - for proxy in cluster.proxyinstance_set.all(): - proxy_instances.append( - { - "ip": proxy.machine.ip, - "port": proxy.port, - "admin_port": proxy.admin_port, - "status": proxy.status, - } - ) - return proxy_instances - except Exception as e: - logger.error(f"get cluster proxy instances failed {e}, cluster_id: {cluster_id}") - raise Exception(f"get cluster proxy instances failed {e}, cluster_id: {cluster_id}") - def __check_twemproxy_backends(self, trans_data: RedisDtsContext): - """ - 检查twemproxy的backends是否一致 - """ - if not is_twemproxy_proxy_type(trans_data.src_cluster_type): - return - proxy_addrs = [ele["ip"] + ":" + str(ele["admin_port"]) for ele in trans_data.src_proxy_instances] - resp = DRSApi.twemproxy_rpc( - { - "addresses": proxy_addrs, - "db_num": 0, - "password": "", - "command": "get nosqlproxy servers", - "bk_cloud_id": trans_data.bk_cloud_id, - } - ) - proxys_backend_md5 = [] - for ele in resp: - backends_ret, _ = decode_twemproxy_backends(ele["result"]) - sorted_backends = sorted(backends_ret, key=lambda x: x.segment_start) - sorted_str = "" - for bck in sorted_backends: - sorted_str += bck.string_without_app() + "\n" - # 求sorted_str的md5值 - md5 = hashlib.md5(sorted_str.encode("utf-8")).hexdigest() - proxys_backend_md5.append( - { - "proxy_addr": ele["address"], - "backend_md5": md5, - } - ) - # 检查md5是否一致 - sorted_md5 = sorted(proxys_backend_md5, key=lambda x: x["backend_md5"]) - if sorted_md5[0]["backend_md5"] != sorted_md5[-1]["backend_md5"]: - self.log_error( - "twemproxy[{}->{}] backends is not same".format( - sorted_md5[0]["proxy_addr"], sorted_md5[-1]["proxy_addr"] - ) - ) - raise Exception( - "twemproxy[{}->{}] backends is not same".format( - sorted_md5[0]["proxy_addr"], sorted_md5[-1]["proxy_addr"] - ) - ) +class NewDstClusterInstallJobAndWatchStatusComponent(Component): + name = __name__ + code = "new_dst_cluster_install_job_and_watch_status" + bound_service = NewDstClusterInstallJobAndWatchStatus - def __check_predixy_servers(self, trans_data: RedisDtsContext): - """ - 检查predixy的servers是否一致 - """ - if not is_predixy_proxy_type(trans_data.src_cluster_type): - return - proxy_addrs = [ele["ip"] + ":" + str(ele["admin_port"]) for ele in trans_data.src_proxy_instances] - resp = DRSApi.redis_rpc( - { - "addresses": proxy_addrs, - "db_num": 0, - "password": "", - "command": "info servers", - "bk_cloud_id": trans_data.bk_cloud_id, - } + +class NewDstClusterFlushJobAndWatchStatus(BaseService): + """ + 新建 目标集群清档任务 并检测任务状态 + """ + + __need_schedule__ = True + interval = StaticIntervalGenerator(10) + + def _execute(self, data, parent_data): + kwargs = data.get_one_of_inputs("kwargs") + global_data = data.get_one_of_inputs("global_data") + trans_data: RedisDtsContext = data.get_one_of_inputs("trans_data") + + if trans_data is None or trans_data == "${trans_data}": + # 表示没有加载上下文内容,则在此添加 + trans_data = getattr(flow_context, kwargs["set_trans_data_dataclass"])() + + if trans_data.dst_cluster_flush_flow_id and trans_data.dst_cluster_flush_flow_id != "": + self.log_info( + "NewDstClusterFlushJobAndWatchStatus dst_cluster_flush_flow_id:{}".format( + trans_data.dst_cluster_flush_flow_id + ) + ) + data.outputs["trans_data"] = trans_data + return True + dst_domain = kwargs["cluster"]["dst"]["cluster_addr"].split(":")[0] + ticket_data: dict = { + "uid": global_data["uid"], + "ticket_type": TicketType.REDIS_PURGE.value, + "bk_biz_id": global_data["bk_biz_id"], + "bk_cloud_id": kwargs["cluster"]["dst"]["bk_cloud_id"], + "created_by": global_data["created_by"], + "rules": [ + { + "cluster_id": kwargs["cluster"]["dst"]["cluster_id"], + "cluster_type": kwargs["cluster"]["dst"]["cluster_type"], + "domain": dst_domain, + "target": "master", + "force": True, + "backup": True, + "db_list": [0], + "flushall": True, + } + ], + } + self.log_info("NewDstClusterFlushJobAndWatchStatus ticket_data==>:{}".format(ticket_data)) + root_id = uuid.uuid1().hex + flow = RedisFlushDataFlow(root_id=root_id, data=ticket_data) + flow.redis_flush_data_flow() + + self.log_info("NewDstClusterFlushJobAndWatchStatus flow_id==>:{}".format(root_id)) + trans_data.dst_cluster_flush_flow_id = root_id + + data.outputs["trans_data"] = trans_data + return True + + def _schedule(self, data, parent_data, callback_data=None) -> bool: + kwargs = data.get_one_of_inputs("kwargs") + global_data = data.get_one_of_inputs("global_data") + trans_data: RedisDtsContext = data.get_one_of_inputs("trans_data") + + if trans_data is None or trans_data == "${trans_data}": + # 表示没有加载上下文内容,则在此添加 + trans_data = getattr(flow_context, kwargs["set_trans_data_dataclass"])() + + stat = FlowTree.objects.get(root_id=trans_data.dst_cluster_flush_flow_id).status + if stat not in [StateType.FINISHED.value, StateType.FAILED.value]: + self.log_info( + "dst_cluster_flush_job flow_id:{} status:{}".format(trans_data.dst_cluster_flush_flow_id, stat) + ) + return True + if stat == StateType.FAILED.value: + self.log_error("dst_cluster_flush_job flow_id:{} failed".format(trans_data.dst_cluster_flush_flow_id)) + self.finish_schedule() + return False + + self.finish_schedule() + self.log_info("dst_cluster_flush_job flow_id:{} finished".format(trans_data.dst_cluster_flush_flow_id)) + return True + + def inputs_format(self) -> List: + return [ + Service.InputItem(name="kwargs", key="kwargs", type="dict", requiredc=True), + Service.InputItem(name="global_data", key="global_data", type="dict", required=True), + ] + + +class NewDstClusterFlushJobAndWatchStatusComponent(Component): + name = __name__ + code = "new_dst_cluster_flush_job_and_watch_status" + bound_service = NewDstClusterFlushJobAndWatchStatus + + +class NewDtsOnlineSwitchJobAndWatchStatus(BaseService): + """ + 新建 redis dts在线切换任务并且检测任务状态 + """ + + __need_schedule__ = True + interval = StaticIntervalGenerator(10) + + def _execute(self, data, parent_data): + kwargs = data.get_one_of_inputs("kwargs") + global_data = data.get_one_of_inputs("global_data") + trans_data: RedisDtsContext = data.get_one_of_inputs("trans_data") + + if trans_data is None or trans_data == "${trans_data}": + # 表示没有加载上下文内容,则在此添加 + trans_data = getattr(flow_context, kwargs["set_trans_data_dataclass"])() + + self.log_info( + "NewDtsOnlineSwitchJobAndWatchStatus uid=>{} src_cluster_addr:{} dst_cluster_addr:{}".format( + global_data["uid"], kwargs["cluster"]["src"]["cluster_addr"], kwargs["cluster"]["dst"]["cluster_addr"] + ) + ) + job_row = TbTendisDTSJob.objects.get( + bill_id=global_data["uid"], + src_cluster=kwargs["cluster"]["src"]["cluster_addr"], + dst_cluster=kwargs["cluster"]["dst"]["cluster_addr"], ) - proxys_backend_md5 = [] - for ele in resp: - backends_ret = decode_predixy_info_servers(ele["result"]) - sorted_backends = sorted(backends_ret, key=lambda x: x.server) - sorted_str = "" - for bck in sorted_backends: - sorted_str += bck.__str__() + "\n" - # 求sorted_str的md5值 - md5 = hashlib.md5(sorted_str.encode("utf-8")).hexdigest() - proxys_backend_md5.append( + if job_row.online_switch_flow_id != "": + data.outputs["trans_data"] = trans_data + return True + ticket_data: dict = { + "uid": global_data["uid"], + "bk_biz_id": global_data["bk_biz_id"], + "created_by": global_data["created_by"], + "ticket_type": TicketType.REDIS_DTS_ONLINE_SWITCH.value, + "online_switch_type": job_row.online_switch_type, + "infos": [ { - "proxy_addr": ele["address"], - "backend_md5": md5, + "bill_id": job_row.bill_id, + "src_cluster": job_row.src_cluster, + "dst_cluster": job_row.dst_cluster, } + ], + } + self.log_info(f"new_dts_online_switch_job ticket_data:{ticket_data}") + from backend.flow.engine.bamboo.scene.redis.redis_cluster_data_copy import RedisClusterDataCopyFlow + + root_id = uuid.uuid1().hex + flow = RedisClusterDataCopyFlow(root_id=root_id, data=ticket_data) + flow.online_switch_flow() + + job_row.online_switch_flow_id = root_id + job_row.save(update_fields=["online_switch_flow_id"]) + + self.log_info("new_dts_online_switch_job flow_id:{}".format(root_id)) + data.outputs["trans_data"] = trans_data + return True + + def _schedule(self, data, parent_data, callback_data=None) -> bool: + kwargs = data.get_one_of_inputs("kwargs") + global_data = data.get_one_of_inputs("global_data") + trans_data: RedisDtsContext = data.get_one_of_inputs("trans_data") + + if trans_data is None or trans_data == "${trans_data}": + # 表示没有加载上下文内容,则在此添加 + trans_data = getattr(flow_context, kwargs["set_trans_data_dataclass"])() + + job_row = TbTendisDTSJob.objects.get( + bill_id=global_data["uid"], + src_cluster=kwargs["cluster"]["src"]["cluster_addr"], + dst_cluster=kwargs["cluster"]["dst"]["cluster_addr"], + ) + stat = FlowTree.objects.get(root_id=job_row.online_switch_flow_id).status + if stat not in [StateType.FINISHED.value, StateType.FAILED.value]: + self.log_info("dts_online_switch_job flow_id:{} status:{}".format(job_row.online_switch_flow_id, stat)) + return True + if stat == StateType.FAILED.value: + self.log_error("dts_online_switch_job flow_id:{} failed".format(job_row.online_switch_flow_id)) + self.finish_schedule() + return False + + self.finish_schedule() + self.log_info("dts_online_switch_job flow_id:{} finished".format(job_row.online_switch_flow_id)) + return True + + def inputs_format(self) -> List: + return [ + Service.InputItem(name="kwargs", key="kwargs", type="dict", requiredc=True), + Service.InputItem(name="global_data", key="global_data", type="dict", required=True), + ] + + +class NewDtsOnlineSwitchJobAndWatchStatusComponent(Component): + name = __name__ + code = "new_dts_online_switch_job_and_watch_status" + bound_service = NewDtsOnlineSwitchJobAndWatchStatus + + +class RedisDtsDisconnectSyncService(BaseService): + """ + redis dts断开同步关系 + """ + + __need_schedule__ = True + interval = StaticIntervalGenerator(10) + + def _execute(self, data, parent_data): + kwargs = data.get_one_of_inputs("kwargs") + global_data = data.get_one_of_inputs("global_data") + trans_data: RedisDtsContext = data.get_one_of_inputs("trans_data") + + if trans_data is None or trans_data == "${trans_data}": + # 表示没有加载上下文内容,则在此添加 + trans_data = getattr(flow_context, kwargs["set_trans_data_dataclass"])() + + self.log_info( + "RedisDtsDisconnectSyncService bill_id=>{} src_cluster_addr:{} dst_cluster_addr:{}".format( + kwargs["cluster"]["bill_id"], kwargs["cluster"]["src_cluster"], kwargs["cluster"]["dst_cluster"] + ) + ) + with transaction.atomic(): + where = ( + Q(bill_id=kwargs["cluster"]["bill_id"]) + & Q(src_cluster=kwargs["cluster"]["src_cluster"]) + & Q(dst_cluster=kwargs["cluster"]["dst_cluster"]) ) - # 检查md5是否一致 - sorted_md5 = sorted(proxys_backend_md5, key=lambda x: x["backend_md5"]) - if sorted_md5[0]["backend_md5"] != sorted_md5[-1]["backend_md5"]: + for task in TbTendisDtsTask.objects.filter(where): + if task.sync_operate not in [ + DtsOperateType.SYNC_STOP_TODO.value, + DtsOperateType.SYNC_STOP_SUCC.value, + ]: + task.sync_operate = DtsOperateType.SYNC_STOP_TODO.value + task.message = task.sync_operate + "..." + task.update_time = datetime.datetime.now() + task.save(update_fields=["sync_operate", "message", "update_time"]) + self.log_info( + "bill_id:{} src_ip:{} src_port:{} operate:{}".format( + task.bill_id, task.src_ip, task.src_port, task.sync_operate + ) + ) + + data.outputs["trans_data"] = trans_data + return True + + def _schedule(self, data, parent_data, callback_data=None) -> bool: + kwargs = data.get_one_of_inputs("kwargs") + global_data = data.get_one_of_inputs("global_data") + trans_data: RedisDtsContext = data.get_one_of_inputs("trans_data") + + if trans_data is None or trans_data == "${trans_data}": + # 表示没有加载上下文内容,则在此添加 + trans_data = getattr(flow_context, kwargs["set_trans_data_dataclass"])() + + running_task_cnt = 0 + failed_task_cnt = 0 + where = ( + Q(bill_id=kwargs["cluster"]["bill_id"]) + & Q(src_cluster=kwargs["cluster"]["src_cluster"]) + & Q(dst_cluster=kwargs["cluster"]["dst_cluster"]) + ) + for task in TbTendisDtsTask.objects.filter(where): + if task.status < 0: + failed_task_cnt += 1 + elif task.status in [0, 1]: + running_task_cnt += 1 + if running_task_cnt > 0: + self.log_info( + "uid=>{} src_cluster_addr:{} dst_cluster_addr:{} running_task_cnt:{}".format( + kwargs["cluster"]["bill_id"], + kwargs["cluster"]["src_cluster"], + kwargs["cluster"]["dst_cluster"], + running_task_cnt, + ) + ) + return True + if failed_task_cnt > 0: self.log_error( - "predixy[{}->{}] backends is not same".format( - sorted_md5[0]["proxy_addr"], sorted_md5[-1]["proxy_addr"] + "uid=>{} src_cluster_addr:{} dst_cluster_addr:{} failed_task_cnt:{}".format( + kwargs["cluster"]["bill_id"], + kwargs["cluster"]["src_cluster"], + kwargs["cluster"]["dst_cluster"], + failed_task_cnt, ) ) - raise Exception( - "predixy[{}->{}] backends is not same".format( - sorted_md5[0]["proxy_addr"], sorted_md5[-1]["proxy_addr"] + return False + + self.log_info( + "uid=>{} src_cluster_addr:{} dst_cluster_addr:{} all tasks competed".format( + kwargs["cluster"]["bill_id"], + kwargs["cluster"]["src_cluster"], + kwargs["cluster"]["dst_cluster"], + ) + ) + + self.finish_schedule() + return True + + def inputs_format(self) -> List: + return [ + Service.InputItem(name="kwargs", key="kwargs", type="dict", requiredc=True), + Service.InputItem(name="global_data", key="global_data", type="dict", required=True), + ] + + +class RedisDtsDisconnectSyncComponent(Component): + name = __name__ + code = "redis_dts_disconnect_sync" + bound_service = RedisDtsDisconnectSyncService + + +class NewDstClusterCloseJobAndWatchStatus(BaseService): + """ + 新建 dst cluster禁用任务并且检测任务状态 + """ + + __need_schedule__ = True + interval = StaticIntervalGenerator(10) + + def _execute(self, data, parent_data): + kwargs = data.get_one_of_inputs("kwargs") + global_data = data.get_one_of_inputs("global_data") + trans_data: RedisDtsContext = data.get_one_of_inputs("trans_data") + + if trans_data is None or trans_data == "${trans_data}": + # 表示没有加载上下文内容,则在此添加 + trans_data = getattr(flow_context, kwargs["set_trans_data_dataclass"])() + + self.log_info( + "NewDstClusterCloseJobAndWatchStatus uid=>{} src_cluster_addr:{} dst_cluster_addr:{}".format( + global_data["uid"], kwargs["cluster"]["src"]["cluster_addr"], kwargs["cluster"]["dst"]["cluster_addr"] + ) + ) + job_row = TbTendisDTSJob.objects.get( + bill_id=global_data["uid"], + src_cluster=kwargs["cluster"]["src"]["cluster_addr"], + dst_cluster=kwargs["cluster"]["dst"]["cluster_addr"], + ) + if job_row.dst_cluster_close_flow_id != "": + data.outputs["trans_data"] = trans_data + return True + ticket_data: dict = { + "uid": global_data["uid"], + "bk_biz_id": global_data["bk_biz_id"], + "created_by": global_data["created_by"], + "ticket_type": TicketType.REDIS_CLOSE.value, + "cluster_id": job_row.dst_cluster_id, + "force": False, + } + self.log_info(f"redis_cluster_close dst_cluster:{job_row.dst_cluster} ticket_data:{ticket_data}") + root_id = uuid.uuid1().hex + flow = RedisClusterOpenCloseFlow(root_id=root_id, data=ticket_data) + flow.redis_cluster_open_close_flow() + + job_row.dst_cluster_close_flow_id = root_id + job_row.save(update_fields=["dst_cluster_close_flow_id"]) + + self.log_info("new_dst_redis_cluster_close_job flow_id:{}".format(root_id)) + data.outputs["trans_data"] = trans_data + return True + + def _schedule(self, data, parent_data, callback_data=None) -> bool: + kwargs = data.get_one_of_inputs("kwargs") + global_data = data.get_one_of_inputs("global_data") + trans_data: RedisDtsContext = data.get_one_of_inputs("trans_data") + + if trans_data is None or trans_data == "${trans_data}": + # 表示没有加载上下文内容,则在此添加 + trans_data = getattr(flow_context, kwargs["set_trans_data_dataclass"])() + + job_row = TbTendisDTSJob.objects.get( + bill_id=global_data["uid"], + src_cluster=kwargs["cluster"]["src"]["cluster_addr"], + dst_cluster=kwargs["cluster"]["dst"]["cluster_addr"], + ) + stat = FlowTree.objects.get(root_id=job_row.dst_cluster_close_flow_id).status + if stat not in [StateType.FINISHED.value, StateType.FAILED.value]: + self.log_info( + "dst_redis_cluster_close_job flow_id:{} status:{}".format(job_row.dst_cluster_close_flow_id, stat) + ) + return True + if stat == StateType.FAILED.value: + self.log_error( + "dst_redis_cluster_close_job flow_id:{} failed".format(job_row.dst_cluster_shutdown_flow_id) + ) + self.finish_schedule() + return False + + self.finish_schedule() + self.log_info("dst_redis_cluster_close_job flow_id:{} finished".format(job_row.dst_cluster_shutdown_flow_id)) + return True + + def inputs_format(self) -> List: + return [ + Service.InputItem(name="kwargs", key="kwargs", type="dict", requiredc=True), + Service.InputItem(name="global_data", key="global_data", type="dict", required=True), + ] + + +class NewDstClusterCloseJobAndWatchStatusComponent(Component): + name = __name__ + code = "new_dst_cluster_close_job_and_watch_status" + bound_service = NewDstClusterCloseJobAndWatchStatus + + +class NewDstClusterShutdownJobAndWatchStatus(BaseService): + """ + 新建 dst cluster下架任务并且检测任务状态 + """ + + __need_schedule__ = True + interval = StaticIntervalGenerator(10) + + def _execute(self, data, parent_data): + kwargs = data.get_one_of_inputs("kwargs") + global_data = data.get_one_of_inputs("global_data") + trans_data: RedisDtsContext = data.get_one_of_inputs("trans_data") + + if trans_data is None or trans_data == "${trans_data}": + # 表示没有加载上下文内容,则在此添加 + trans_data = getattr(flow_context, kwargs["set_trans_data_dataclass"])() + + self.log_info( + "NewDstClusterShutdownJobAndWatchStatus uid=>{} src_cluster_addr:{} dst_cluster_addr:{}".format( + global_data["uid"], kwargs["cluster"]["src"]["cluster_addr"], kwargs["cluster"]["dst"]["cluster_addr"] + ) + ) + job_row = TbTendisDTSJob.objects.get( + bill_id=global_data["uid"], + src_cluster=kwargs["cluster"]["src"]["cluster_addr"], + dst_cluster=kwargs["cluster"]["dst"]["cluster_addr"], + ) + if job_row.dst_cluster_shutdown_flow_id != "": + data.outputs["trans_data"] = trans_data + return True + ticket_data: dict = { + "uid": global_data["uid"], + "bk_biz_id": global_data["bk_biz_id"], + "created_by": global_data["created_by"], + "ticket_type": TicketType.REDIS_DESTROY.value, + "cluster_id": job_row.dst_cluster_id, + } + self.log_info(f"redis_cluster_shutdown ticket_data:{ticket_data}") + root_id = uuid.uuid1().hex + flow = RedisClusterShutdownFlow(root_id=root_id, data=ticket_data) + flow.redis_cluster_shutdown_flow() + + job_row.dst_cluster_shutdown_flow_id = root_id + job_row.save(update_fields=["dst_cluster_shutdown_flow_id"]) + + self.log_info("new_dst_redis_cluster_shutdown_job flow_id:{}".format(root_id)) + data.outputs["trans_data"] = trans_data + return True + + def _schedule(self, data, parent_data, callback_data=None) -> bool: + kwargs = data.get_one_of_inputs("kwargs") + global_data = data.get_one_of_inputs("global_data") + trans_data: RedisDtsContext = data.get_one_of_inputs("trans_data") + + if trans_data is None or trans_data == "${trans_data}": + # 表示没有加载上下文内容,则在此添加 + trans_data = getattr(flow_context, kwargs["set_trans_data_dataclass"])() + + job_row = TbTendisDTSJob.objects.get( + bill_id=global_data["uid"], + src_cluster=kwargs["cluster"]["src"]["cluster_addr"], + dst_cluster=kwargs["cluster"]["dst"]["cluster_addr"], + ) + stat = FlowTree.objects.get(root_id=job_row.dst_cluster_shutdown_flow_id).status + if stat not in [StateType.FINISHED.value, StateType.FAILED.value]: + self.log_info( + "dst_redis_cluster_shutdown_job flow_id:{} status:{}".format( + job_row.dst_cluster_shutdown_flow_id, stat ) ) + return True + if stat == StateType.FAILED.value: + self.log_error( + "dst_redis_cluster_shutdown_job flow_id:{} failed".format(job_row.dst_cluster_shutdown_flow_id) + ) + self.finish_schedule() + return False + + self.finish_schedule() + self.log_info( + "dst_redis_cluster_shutdown_job flow_id:{} finished".format(job_row.dst_cluster_shutdown_flow_id) + ) + return True + + def inputs_format(self) -> List: + return [ + Service.InputItem(name="kwargs", key="kwargs", type="dict", requiredc=True), + Service.InputItem(name="global_data", key="global_data", type="dict", required=True), + ] + + +class NewDstClusterShutdownJobAndWatchStatusComponent(Component): + name = __name__ + code = "new_dst_cluster_shutdown_job_and_watch_status" + bound_service = NewDstClusterShutdownJobAndWatchStatus diff --git a/dbm-ui/backend/flow/plugins/components/collections/redis/redis_dts_server_meta.py b/dbm-ui/backend/flow/plugins/components/collections/redis/redis_dts_server_meta.py new file mode 100644 index 0000000000..436bd92ffa --- /dev/null +++ b/dbm-ui/backend/flow/plugins/components/collections/redis/redis_dts_server_meta.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +import logging +from typing import List + +from pipeline.component_framework.component import Component +from pipeline.core.flow.activity import Service + +import backend.flow.utils.redis.redis_context_dataclass as flow_context +from backend.db_services.redis.redis_dts.models import TendisDtsServer +from backend.flow.plugins.components.collections.common.base_service import BaseService +from backend.ticket.constants import TicketType + +logger = logging.getLogger("flow") + + +class RedisDtsServerMetaService(BaseService): + """ + 更新redis dts server meta + """ + + def _execute(self, data, parent_data) -> bool: + kwargs = data.get_one_of_inputs("kwargs") + global_data = data.get_one_of_inputs("global_data") + trans_data = data.get_one_of_inputs("trans_data") + + if trans_data is None or trans_data == "${trans_data}": + # 表示没有加载上下文内容,则在此添加 + trans_data = getattr(flow_context, kwargs["set_trans_data_dataclass"])() + + if global_data["ticket_type"] == TicketType.REDIS_ADD_DTS_SERVER.value: + cluster = kwargs["cluster"] + row = TendisDtsServer() + row.bk_cloud_id = cluster["bk_cloud_id"] + row.ip = cluster["ip"] + row.city_id = cluster["bk_city_id"] + row.city_name = cluster["bk_city_name"] + row.status = 0 + row.heartbeat_time = "1997-01-01 00:00:00" + row.save() + elif global_data["ticket_type"] == TicketType.REDIS_REMOVE_DTS_SERVER.value: + cluster = kwargs["cluster"] + TendisDtsServer.objects.filter(ip=cluster["ip"], bk_cloud_id=cluster["bk_cloud_id"]).delete() + + return True + + def inputs_format(self) -> List: + return [ + Service.InputItem(name="kwargs", key="kwargs", type="dict", required=True), + Service.InputItem(name="global_data", key="global_data", type="dict", required=True), + ] + + +class RedisDtsServerMetaComponent(Component): + name = __name__ + code = "redis_dts_server_meta" + bound_service = RedisDtsServerMetaService diff --git a/dbm-ui/backend/flow/plugins/components/collections/redis/redis_ticket.py b/dbm-ui/backend/flow/plugins/components/collections/redis/redis_ticket.py new file mode 100644 index 0000000000..de8317d9e9 --- /dev/null +++ b/dbm-ui/backend/flow/plugins/components/collections/redis/redis_ticket.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import copy +import logging +from typing import List + +from django.utils.translation import ugettext_lazy as _ +from pipeline.component_framework.component import Component +from pipeline.core.flow.activity import Service + +from backend.configuration.constants import DBType +from backend.configuration.models.dba import DBAdministrator +from backend.flow.plugins.components.collections.common.base_service import BaseService +from backend.ticket.builders import BuilderFactory +from backend.ticket.constants import TicketStatus, TicketType +from backend.ticket.flow_manager.manager import TicketFlowManager +from backend.ticket.models import Ticket + +logger = logging.getLogger("flow") + + +class RedisTicketService(BaseService): + """ + 更新config + """ + + def _execute(self, data, parent_data) -> bool: + kwargs = data.get_one_of_inputs("kwargs") + global_data = data.get_one_of_inputs("global_data") + trans_data = data.get_one_of_inputs("trans_data") + + logger.info( + "create ticket for cluster {} , details : {}".format(kwargs["immute_domain"], kwargs["ticket_details"]) + ) + redisDBA = DBAdministrator.objects.get(bk_biz_id=kwargs["bk_biz_id"], db_type=DBType.Redis.value) + ticket = Ticket.objects.create( + creator=redisDBA.users[0], + bk_biz_id=kwargs["bk_biz_id"], + ticket_type=kwargs["ticket_type"], + group=DBType.Redis.value, + status=TicketStatus.PENDING.value, + remark=_("自动发起-实例下架-{cluster.immute_domain}"), + details=kwargs["ticket_details"], + is_reviewed=False, + ) + + # 初始化builder类 + builder = BuilderFactory.create_builder(ticket) + builder.patch_ticket_detail() + builder.init_ticket_flows() + TicketFlowManager(ticket=ticket).run_next_flow() + + logger.info("succ create ticket for cluster {} : {}".format(kwargs["immute_domain"], ticket)) + + return True + + def inputs_format(self) -> List: + return [ + Service.InputItem(name="kwargs", key="kwargs", type="dict", required=True), + Service.InputItem(name="global_data", key="global_data", type="dict", required=True), + ] + + +class RedisTicketComponent(Component): + name = __name__ + code = "redis_ticket" + bound_service = RedisTicketService diff --git a/dbm-ui/backend/flow/plugins/components/collections/redis/redis_trans_files.py b/dbm-ui/backend/flow/plugins/components/collections/redis/redis_trans_files.py new file mode 100644 index 0000000000..5a45203622 --- /dev/null +++ b/dbm-ui/backend/flow/plugins/components/collections/redis/redis_trans_files.py @@ -0,0 +1,90 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import copy +import logging +import time +from typing import List + +from django.utils.translation import ugettext as _ +from pipeline.component_framework.component import Component +from pipeline.core.flow.activity import Service + +import backend.flow.utils.redis.redis_context_dataclass as flow_context +from backend import env +from backend.components import JobApi +from backend.core import consts +from backend.flow.consts import MediumFileTypeEnum +from backend.flow.models import FlowNode +from backend.flow.plugins.components.collections.common.base_service import BkJobService + +logger = logging.getLogger("flow") + + +class RedisBackupFileTransService(BkJobService): + """ + 从Redis机器下载介质文件包到目标机器 + """ + + def _execute(self, data, parent_data) -> bool: + kwargs = data.get_one_of_inputs("kwargs") + global_data = data.get_one_of_inputs("global_data") + trans_data = data.get_one_of_inputs("trans_data") + + root_id = kwargs["root_id"] + node_name = kwargs["node_name"] + node_id = kwargs["node_id"] + + backup_infos = trans_data.tendis_backup_info + exec_ips = self.splice_exec_ips_list(ticket_ips=kwargs["exec_ip"]) + target_ip_info = [{"bk_cloud_id": kwargs["bk_cloud_id"], "ip": ip} for ip in exec_ips] + + # 构造数据 + trans_data.backupinfo = backup_infos + source_ip_list = {} + file_list = [] + for backup_inst in backup_infos: + file_list.append(backup_inst["backup_file"]) + if not source_ip_list.get(backup_inst["server_ip"]): + source_ip_list[backup_inst["server_ip"]] = True + self.log_info("get backup files {}:{}".format(source_ip_list.keys(), file_list)) + + # 服务器之间文件传输模式 + file_source = { + "file_list": file_list, + "account": {"alias": "root"}, + "file_type": MediumFileTypeEnum.Server.value, + "server": {"ip_list": [{"bk_cloud_id": kwargs["bk_cloud_id"], "ip": ip} for ip in source_ip_list.keys()]}, + } + + # 拼接fast_trans_file 接口请求参数 + payload = copy.deepcopy(consts.BK_TRANSFER_REPO_PAYLOAD) + payload["bk_biz_id"] = env.JOB_BLUEKING_BIZ_ID + payload["file_source_list"].append(file_source) + payload["target_server"]["ip_list"] = target_ip_info + payload["file_target_path"] = "/data/dbbak" + + self.log_info(_("[{}] 下发介质包参数:{}").format(node_name, payload)) + FlowNode.objects.filter(root_id=root_id, node_id=node_id).update(hosts=exec_ips) + + # 请求传输 + resp = JobApi.fast_transfer_file(payload, raw=True) + + # 传入调用结果,并单调监听任务状态 + data.outputs.ext_result = resp + data.outputs["trans_data"] = trans_data + data.outputs["backup_tasks"] = backup_infos + return True + + +class RedisBackupFileTransComponent(Component): + name = __name__ + code = "redis_backup_file_trans" + bound_service = RedisBackupFileTransService diff --git a/dbm-ui/backend/flow/plugins/components/collections/riak/get_riak_cluster_node.py b/dbm-ui/backend/flow/plugins/components/collections/riak/get_riak_cluster_node.py new file mode 100644 index 0000000000..4caa4d795b --- /dev/null +++ b/dbm-ui/backend/flow/plugins/components/collections/riak/get_riak_cluster_node.py @@ -0,0 +1,90 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import copy +import logging +from typing import List + +from django.db.models import Q +from django.utils.translation import ugettext as _ +from pipeline.component_framework.component import Component +from pipeline.core.flow.activity import Service + +from backend.db_meta.enums import InstanceStatus +from backend.db_meta.models import Cluster, StorageInstance +from backend.flow.plugins.components.collections.common.base_service import BaseService +from backend.ticket.constants import TicketType + +logger = logging.getLogger("flow") + + +class GetRiakClusterNodeService(BaseService): + """ + 根据Riak单据获取集群中的节点 + """ + + def _execute(self, data, parent_data) -> bool: + global_data = data.get_one_of_inputs("global_data") + trans_data = data.get_one_of_inputs("trans_data") + + cluster = Cluster.objects.get(id=global_data["cluster_id"]) + + # 集群下架获取集群中所有的节点 + if global_data["ticket_type"] in ( + TicketType.RIAK_CLUSTER_DESTROY, + TicketType.RIAK_CLUSTER_DISABLE, + TicketType.RIAK_CLUSTER_ENABLE, + ): + storages = StorageInstance.objects.filter(cluster=cluster).all() + trans_data.nodes = list(set([storage.machine.ip for storage in storages])) + self.log_info(_("获取集群所有节点成功。{}").format(trans_data)) + data.outputs["trans_data"] = trans_data + return True + + query_filters = Q(cluster=cluster, status=InstanceStatus.RUNNING.value) + storages = StorageInstance.objects.filter(query_filters).all() + + # 集群扩容,base_node为集群中running的节点 + if global_data["ticket_type"] in TicketType.RIAK_CLUSTER_SCALE_OUT: + base_node = storages[0].machine.ip + trans_data.base_node = base_node + # 集群缩容,base_node为集群中running的节点,并且不能为待剔除的节点 + elif global_data["ticket_type"] == TicketType.RIAK_CLUSTER_SCALE_IN: + scale_in_nodes = [node["ip"] for node in global_data["nodes"]] + running_nodes = list( + set([storage.machine.ip for storage in storages if storage.machine.ip not in scale_in_nodes]) + ) + if len(running_nodes) < 2: + self.log_error("exclude scale in nodes, number of running nodes less than 2") + base_node = running_nodes[0] + trans_data.base_node = base_node + + if ( + global_data["ticket_type"] == TicketType.RIAK_CLUSTER_SCALE_OUT + or global_data["ticket_type"] == TicketType.RIAK_CLUSTER_SCALE_IN + ): + trans_data.nodes = copy.deepcopy(trans_data.operate_nodes) + trans_data.nodes.append(base_node) + + self.log_info(_("获取集群中running节点成功。{}").format(trans_data)) + data.outputs["trans_data"] = trans_data + return True + + def inputs_format(self) -> List: + return [ + Service.InputItem(name="global_data", key="global_data", type="dict", required=True), + Service.InputItem(name="trans_data", key="trans_data", type="dict", required=True), + ] + + +class GetRiakClusterNodeComponent(Component): + name = __name__ + code = "get_riak_cluster_node" + bound_service = GetRiakClusterNodeService diff --git a/dbm-ui/backend/flow/plugins/components/collections/riak/get_riak_resource.py b/dbm-ui/backend/flow/plugins/components/collections/riak/get_riak_resource.py index 79b4b9c51e..4718cd4c16 100644 --- a/dbm-ui/backend/flow/plugins/components/collections/riak/get_riak_resource.py +++ b/dbm-ui/backend/flow/plugins/components/collections/riak/get_riak_resource.py @@ -16,6 +16,7 @@ from pipeline.core.flow.activity import Service from backend.flow.plugins.components.collections.common.base_service import BaseService +from backend.ticket.constants import TicketType logger = logging.getLogger("flow") @@ -28,19 +29,20 @@ class GetRiakResourceService(BaseService): def _execute(self, data, parent_data) -> bool: global_data = data.get_one_of_inputs("global_data") trans_data = data.get_one_of_inputs("trans_data") - kwargs = data.get_one_of_inputs("kwargs") if global_data["ip_source"] == "manual_input": ips = [node["ip"] for node in global_data["nodes"]] - if global_data["ticket_type"] == "RIAK_APPLY" and len(ips) >= 3: - trans_data.ips = ips - trans_data.base_node = trans_data.ips[0] - trans_data.operate_nodes = trans_data.ips[1:] - elif global_data["ticket_type"] == "RIAK_ADD_NODE" and len(ips) >= 0: - trans_data.base_node = kwargs["base_node"] + if global_data["ticket_type"] == TicketType.RIAK_CLUSTER_APPLY and len(ips) >= 3: + trans_data.nodes = ips + trans_data.base_node = ips[0] + trans_data.operate_nodes = ips[1:] + elif ( + global_data["ticket_type"] == TicketType.RIAK_CLUSTER_SCALE_OUT + or global_data["ticket_type"] == TicketType.RIAK_CLUSTER_SCALE_IN + ) and len(ips) >= 1: trans_data.operate_nodes = ips else: - self.log_error(_("获取机器资源失败,新建集群至少3台机器,添加节点至少1台机器")) + self.log_error(_("获取机器资源失败,新建集群至少选择3台机器,扩容或缩容至少选择1台机器")) return False elif self.data["ip_source"] == "resource_pool": pass diff --git a/dbm-ui/backend/flow/plugins/components/collections/riak/riak_db_meta.py b/dbm-ui/backend/flow/plugins/components/collections/riak/riak_db_meta.py index 9ac6173e90..6bd6b0f28c 100644 --- a/dbm-ui/backend/flow/plugins/components/collections/riak/riak_db_meta.py +++ b/dbm-ui/backend/flow/plugins/components/collections/riak/riak_db_meta.py @@ -35,7 +35,7 @@ def _execute(self, data, parent_data) -> bool: result = getattr(riak_meta, kwargs.get("db_meta_class_func"))() - self.log_info("DBMata re successfully") + self.log_info("DBMata write successfully") data.outputs.ext_result = result return result diff --git a/dbm-ui/backend/flow/plugins/components/collections/riak/trans_files.py b/dbm-ui/backend/flow/plugins/components/collections/riak/trans_files.py index 26f769e342..add4a093c9 100644 --- a/dbm-ui/backend/flow/plugins/components/collections/riak/trans_files.py +++ b/dbm-ui/backend/flow/plugins/components/collections/riak/trans_files.py @@ -10,11 +10,9 @@ """ import copy import logging -from typing import List from django.utils.translation import ugettext as _ from pipeline.component_framework.component import Component -from pipeline.core.flow.activity import Service from backend import env from backend.components import JobApi @@ -31,56 +29,83 @@ class TransFileService(BkJobService): 下载介质文件包到目标机器 """ + def __get_exec_ips(self, kwargs, trans_data) -> list: + """ + 获取需要执行的ip list + """ + # 拼接节点执行ip所需要的信息,ip信息统一用list处理拼接 + if kwargs.get("get_trans_data_ip_var"): + exec_ips = self.splice_exec_ips_list(pool_ips=getattr(trans_data, kwargs["get_trans_data_ip_var"])) + else: + exec_ips = self.splice_exec_ips_list(ticket_ips=kwargs["exec_ip"]) + + return exec_ips + def _execute(self, data, parent_data) -> bool: """ - 执行传输文件的原子任务 + 执行传输文件的原子任务。目前文件传输支持两个模式:1:第三方cos原文件传输 2:服务器之间文件传输 + kwargs.get('file_type') 参数用来控传输模式,如果等于1,则采用服务之间的文件传输。否则都作为第三方cos原文件传输 """ kwargs = data.get_one_of_inputs("kwargs") + trans_data = data.get_one_of_inputs("trans_data") root_id = kwargs["root_id"] node_name = kwargs["node_name"] node_id = kwargs["node_id"] - exec_ips = self.splice_exec_ips_list(ticket_ips=kwargs["exec_ip"]) + # 拼接节点执行ip所需要的信息,ip信息统一用list处理拼接 + exec_ips = self.__get_exec_ips(kwargs=kwargs, trans_data=trans_data) if not exec_ips: self.log_error(_("该节点获取到执行ip信息为空,请联系系统管理员{}").format(exec_ips)) + return False - target_ip_info = [{"bk_cloud_id": kwargs["bk_cloud_id"], "ip": ip} for ip in exec_ips] + if kwargs.get("file_type") == MediumFileTypeEnum.Server.value: + # 服务器之间文件传输模式 + if not kwargs["source_ip_list"]: + self.log_error(_("选择服务器之间文件传输模式,应当源文件的机器ip列表不能为空,请联系系统管理员{}").format(kwargs["source_ip_list"])) + return False - # 拼接fast_trans_file 接口请求参数 - payload = copy.deepcopy(consts.BK_TRANSFER_REPO_PAYLOAD) - payload["bk_biz_id"] = env.JOB_BLUEKING_BIZ_ID - payload["file_source_list"].append( - { + file_source = { + "file_list": kwargs["file_list"], + "account": {"alias": "root"}, + "file_type": MediumFileTypeEnum.Server.value, + "server": { + "ip_list": [{"bk_cloud_id": kwargs["bk_cloud_id"], "ip": ip} for ip in kwargs["source_ip_list"]] + }, + } + + else: + # 第三方cos原文件传输模式 + file_source = { "file_list": kwargs["file_list"], "file_type": MediumFileTypeEnum.Repo.value, "file_source_code": env.APP_CODE, } - ) + + target_ip_info = [{"bk_cloud_id": kwargs["bk_cloud_id"], "ip": ip} for ip in exec_ips] + + # 拼接fast_trans_file 接口请求参数 + payload = copy.deepcopy(consts.BK_TRANSFER_REPO_PAYLOAD) + payload["bk_biz_id"] = env.JOB_BLUEKING_BIZ_ID + payload["file_source_list"].append(file_source) payload["target_server"]["ip_list"] = target_ip_info + if kwargs.get("file_target_path"): + kwargs["file_target_path"] = str(kwargs["file_target_path"]).strip() + if kwargs["file_target_path"] is not None and kwargs["file_target_path"] != "": + payload["file_target_path"] = kwargs["file_target_path"] + self.log_info(_("[{}] 下发介质包参数:{}").format(node_name, payload)) FlowNode.objects.filter(root_id=root_id, node_id=node_id).update(hosts=exec_ips) # 请求传输 resp = JobApi.fast_transfer_file(payload, raw=True) - if (resp.get("result") is not True) or (int(resp["code"] != 0)): - raise Exception(f"{str(resp)}") # 传入调用结果,并单调监听任务状态 data.outputs.ext_result = resp return True - def inputs_format(self) -> List: - return [ - Service.InputItem(name="kwargs", key="kwargs", type="dict", required=True), - Service.InputItem(name="global_data", key="global_data", type="dict", required=True), - ] - - def outputs_format(self) -> List: - return [Service.OutputItem(name="exec_ips", key="exec_ips", type="list")] - class TransFileComponent(Component): name = __name__ diff --git a/dbm-ui/backend/flow/plugins/components/collections/spider/add_spider_routing.py b/dbm-ui/backend/flow/plugins/components/collections/spider/add_spider_routing.py index 9b5b4f9dea..9911a17af8 100644 --- a/dbm-ui/backend/flow/plugins/components/collections/spider/add_spider_routing.py +++ b/dbm-ui/backend/flow/plugins/components/collections/spider/add_spider_routing.py @@ -16,7 +16,7 @@ from backend.core.encrypt.handlers import RSAHandler from backend.db_meta.enums import TenDBClusterSpiderRole from backend.db_meta.models import Cluster -from backend.flow.consts import PrivRole +from backend.flow.consts import TDBCTL_USER, PrivRole from backend.flow.engine.bamboo.scene.spider.common.exceptions import ( AddSpiderNodeFailedException, NormalSpiderFlowException, @@ -39,6 +39,59 @@ class AddSpiderRoutingService(BaseService): } """ + def _drop_user(self, spider_ip: str, spider_port: int, cluster: Cluster): + """ + todo 临时方法,后续需要调整这块逻辑再删除 + """ + primary_host = cluster.tendbcluster_ctl_primary_address().split(":")[0] + res = DRSApi.rpc( + { + "addresses": [f"{spider_ip}{IP_PORT_DIVIDER}{spider_port}"], + "cmds": [f"DROP USER if exists '{TDBCTL_USER}'@'{primary_host}'"], + "force": False, + "bk_cloud_id": cluster.bk_cloud_id, + } + ) + if res[0]["error_msg"]: + self.log_error(f"drop user failed:[{res[0]['error_msg']}]") + return False + return True + + def _read_ctl_pass(self, ctl_master, bk_cloud_id): + """ + 获取第一次集群部署中控的密码,保证统一集群的所有中控访问的账号秘密是一致的,避免中控同步出现复制冲突 + """ + res = DRSApi.rpc( + { + "addresses": [f"{ctl_master}"], + "cmds": [f"select Password as result from mysql.servers where Server_name like 'TDBCTL%' limit 1"], + "force": False, + "bk_cloud_id": bk_cloud_id, + } + ) + if res[0]["error_msg"]: + self.log_error(f"drop user failed:[{res[0]['error_msg']}]") + return "" + + return res[0]["cmd_results"][0]["table_data"][0]["result"] + + def _reset_master(self, spider_ip: str, spider_port: int, bk_cloud_id): + """ + 定义reset master 方法,tdbctl添加时专属 + """ + res = DRSApi.rpc( + { + "addresses": [f"{spider_ip}{IP_PORT_DIVIDER}{spider_port}"], + "cmds": [f"reset master"], + "force": False, + "bk_cloud_id": bk_cloud_id, + } + ) + if res[0]["error_msg"]: + self.log_error(f"reset master failed:[{res[0]['error_msg']}]") + return False + return True + def _check_node_is_add(self, spider_ip: str, spider_port: int, cluster: Cluster): """ 定义一个内置的检测方式,添加之前检测节点是否已存在添加 @@ -81,20 +134,26 @@ def _exec_create_node(self, cluster: Cluster, user: str, passwd: str, spider_ip: if not self._check_node_is_add(cluster=cluster, spider_ip=spider_ip, spider_port=spider_port): # 代表这个节点在集群的路由表已经存在,则这里选择跳过 # todo 这里出现重复只能选择跳过,如果选择重做还没有想好逻辑,而且重做会有风险。 - return None + return True + + if tag == "TDBCTL": + # 如果create node 是一个tdbctl,则由于gtid的原因需要先reset master,保证新实例GTID_EXECUTED为空,再create node + if not self._reset_master(spider_ip=spider_ip, spider_port=spider_port, bk_cloud_id=cluster.bk_cloud_id): + return False sql = ( - "tdbctl create node wrapper '{}' options(user '{}'," " password '{}', host '{}', port {}) with database" + "tdbctl create node wrapper '{}' options(user '{}', password '{}', host '{}', port {}) with database" ).format(tag, user, passwd, spider_ip, spider_port) - rpc_params["cmds"] = cmds + [sql] + rpc_params["cmds"] = cmds + [sql] + ["TDBCTL FLUSH ROUTING"] res = DRSApi.rpc(rpc_params) if res[0]["error_msg"]: - raise AddSpiderNodeFailedException(message=_("TdbCtl-create-node failed: {}".format(res[0]["error_msg"]))) + self.log_error("TdbCtl-create-node failed: {}".format(res[0]["error_msg"])) + return False self.log_info("TdbCtl-create-node added successfully [{}:{}]".format(spider_ip, spider_port)) - return None + return True def _add_system_user( self, @@ -103,6 +162,7 @@ def _add_system_user( created_by: str, user: str, passwd: str, + ctl_pass: str, clt_master_ip: str, add_spider_role: str, ): @@ -113,7 +173,9 @@ def _add_system_user( @param created_by: 发起单据的用户名称 @param user: 内置账号名称, @param passwd: 内置账号密码, + @param ctl_pass: 中控的内置账号密码 @param clt_master_ip: 当前集群的中控实例的ip + @param add_spider_role: 添加的spider角色 """ # 获取云区域id的方式,已集群信息为准 spider_port = cluster.proxyinstance_set.first().port @@ -123,6 +185,9 @@ def _add_system_user( encrypted = RSAHandler.encrypt_password(MySQLPrivManagerApi.fetch_public_key(), passwd, salt=None) for spider_ip in add_spiders: + if not self._drop_user(spider_ip=spider_ip["ip"], spider_port=spider_port, cluster=cluster): + return False + content = { "bk_cloud_id": cluster.bk_cloud_id, "bk_biz_id": cluster.bk_biz_id, @@ -144,6 +209,10 @@ def _add_system_user( if add_spider_role == TenDBClusterSpiderRole.SPIDER_MASTER.value: # 部署spider-master实例,必定启动中控实例,这里增加对中控实例的内置授权 content["address"] = f'{spider_ip["ip"]}{IP_PORT_DIVIDER}{admin_port}' + content["role"] = PrivRole.TDBCTL.value + content["psw"] = RSAHandler.encrypt_password( + MySQLPrivManagerApi.fetch_public_key(), ctl_pass, salt=None + ) MySQLPrivManagerApi.add_priv_without_account_rule(content) self.log_info(_("在[{}]创建添加内置账号成功").format(content["address"])) @@ -151,6 +220,8 @@ def _add_system_user( self.log_error(_("[{}]添加用户接口异常,相关信息: {}").format(content["address"], e)) return False + return True + def _execute(self, data, parent_data): kwargs = data.get_one_of_inputs("kwargs") global_data = data.get_one_of_inputs("global_data") @@ -160,17 +231,27 @@ def _execute(self, data, parent_data): ctl_master = cluster.tendbcluster_ctl_primary_address() spider_port = cluster.proxyinstance_set.first().port admin_port = cluster.proxyinstance_set.first().admin_port + ctl_pass = "" + + if kwargs["add_spider_role"] == TenDBClusterSpiderRole.SPIDER_MASTER.value: + # 如果添加的是spider_master,则必须添加中控实例,添加中控实例之前,获取中控实例的密码 + ctl_pass = self._read_ctl_pass(ctl_master=ctl_master, bk_cloud_id=cluster.bk_cloud_id) + if not ctl_pass: + return False # 先对待加入的spider节点添加内置账号,接口幂等 - self._add_system_user( + + if not self._add_system_user( cluster=cluster, add_spiders=kwargs["add_spiders"], created_by=global_data["created_by"], user=kwargs["user"], passwd=kwargs["passwd"], + ctl_pass=ctl_pass, clt_master_ip=ctl_master.split(":")[0], add_spider_role=kwargs["add_spider_role"], - ) + ): + return False # 循环添加路由信息,添加之前判断是否已经存在 for add_spider in kwargs["add_spiders"]: @@ -186,26 +267,28 @@ def _execute(self, data, parent_data): raise NormalSpiderFlowException(message=_("This spider-role is not supported,check")) # 执行添加node的方法,方法幂等 - self._exec_create_node( + if not self._exec_create_node( cluster=cluster, user=kwargs["user"], passwd=kwargs["passwd"], spider_ip=add_spider["ip"], spider_port=spider_port, tag=tag, - ) + ): + return False if kwargs["add_spider_role"] == TenDBClusterSpiderRole.SPIDER_MASTER.value: # 对中控实例也执行添加node行为 tag = "TDBCTL" - self._exec_create_node( + if not self._exec_create_node( cluster=cluster, user=kwargs["user"], - passwd=kwargs["passwd"], + passwd=ctl_pass, spider_ip=add_spider["ip"], spider_port=admin_port, tag=tag, - ) + ): + return False return True diff --git a/dbm-ui/backend/flow/plugins/components/collections/spider/check_cluster_table_using_sub.py b/dbm-ui/backend/flow/plugins/components/collections/spider/check_cluster_table_using_sub.py index 472a862056..566079e880 100644 --- a/dbm-ui/backend/flow/plugins/components/collections/spider/check_cluster_table_using_sub.py +++ b/dbm-ui/backend/flow/plugins/components/collections/spider/check_cluster_table_using_sub.py @@ -36,7 +36,7 @@ def build_check_cluster_table_using_sub_flow(root_id: str, cluster_obj: Cluster, }, ) instance_pipe.add_act( - act_name="", + act_name=_("检查库表是否在用"), act_component_code=GeneralCheckDBInUsingComponent.code, kwargs=asdict(BKCloudIdKwargs(bk_cloud_id=cluster_obj.bk_cloud_id)), ) diff --git a/dbm-ui/backend/flow/plugins/components/collections/spider/ctl_drop_routing.py b/dbm-ui/backend/flow/plugins/components/collections/spider/ctl_drop_routing.py new file mode 100644 index 0000000000..4dd76340b0 --- /dev/null +++ b/dbm-ui/backend/flow/plugins/components/collections/spider/ctl_drop_routing.py @@ -0,0 +1,76 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 backend.components import DRSApi +from backend.db_meta.models import Cluster +from backend.flow.engine.bamboo.scene.spider.common.exceptions import CtlSwitchToSlaveFailedException +from backend.flow.plugins.components.collections.common.base_service import BaseService + + +class CtlDropRoutingService(BaseService): + def _execute(self, data, parent_data): + kwargs = data.get_one_of_inputs("kwargs") + + reduce_ctl = kwargs["reduce_ctl"] + + # 获取cluster对象 + cluster = Cluster.objects.get(id=kwargs["cluster_id"]) + ctl_primary = cluster.tendbcluster_ctl_primary_address() + + rpc_params = { + "addresses": [ctl_primary], + "cmds": [], + "force": False, + "bk_cloud_id": cluster.bk_cloud_id, + } + + # 查询reduce_ctl对应的server_name + reduce_ip = reduce_ctl.split(":")[0] + reduce_port = reduce_ctl.split(":")[1] + + select_sql = [ + "set tc_admin = 1", + f"select Server_name from mysql.servers where host = '{reduce_ip}' and port = {reduce_port}", + ] + rpc_params["cmds"] = select_sql + res = DRSApi.rpc(rpc_params) + if res[0]["error_msg"]: + raise CtlSwitchToSlaveFailedException( + message=_("select mysql.servers failed: {}".format(res[0]["error_msg"])) + ) + + if not res[0]["cmd_results"][1]["table_data"]: + self.log_warning(f"Node [{reduce_ctl}] no longer has routing information") + return True + + else: + server_name = res[0]["cmd_results"][1]["table_data"][0]["Server_name"] + + # 删除节点路由信息 + exec_sql = [ + "set tc_admin=1", + f"TDBCTL DROP NODE IF EXISTS {server_name}", + "TDBCTL FLUSH ROUTING", + ] + rpc_params["cmds"] = exec_sql + res = DRSApi.rpc(rpc_params) + if res[0]["error_msg"]: + raise CtlSwitchToSlaveFailedException( + message=_("exec TDBCTL-DROP-NODE failed: {}".format(res[0]["error_msg"])) + ) + return True + + +class CtlDropRoutingComponent(Component): + name = __name__ + code = "ctl_drop_routing" + bound_service = CtlDropRoutingService diff --git a/dbm-ui/backend/flow/plugins/components/collections/spider/ctl_switch_to_slave.py b/dbm-ui/backend/flow/plugins/components/collections/spider/ctl_switch_to_slave.py new file mode 100644 index 0000000000..61ce54bdb7 --- /dev/null +++ b/dbm-ui/backend/flow/plugins/components/collections/spider/ctl_switch_to_slave.py @@ -0,0 +1,267 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 time import sleep + +from django.utils.translation import ugettext_lazy as _ +from pipeline.component_framework.component import Component + +from backend.components import DBConfigApi, DRSApi, MySQLPrivManagerApi +from backend.components.dbconfig.constants import FormatType, LevelName +from backend.constants import IP_PORT_DIVIDER +from backend.core.encrypt.handlers import RSAHandler +from backend.db_meta.enums import TenDBClusterSpiderRole +from backend.db_meta.models import Cluster +from backend.flow.consts import TDBCTL_USER, ConfigTypeEnum, NameSpaceEnum, PrivRole +from backend.flow.engine.bamboo.scene.spider.common.exceptions import CtlSwitchToSlaveFailedException +from backend.flow.plugins.components.collections.common.base_service import BaseService + + +class CtlSwitchToSlaveService(BaseService): + """ + 定义spider(tenDB cluster)集群的中控集群做主节点提示,适用于spider裁撤场合调用 + 这里暂时不考虑主从互切场景,仅仅做提升主节点场景,因为互切展示没有需求 + 幂等的内容包括: 预检测、断开同步、选择新的主节点、重新同步新主节点 + 私有变量的主要结构体kwargs: + { + “cluster_id”: id, 待关联的集群id + "reduce_ctl_primary": 传入的待删除的中控primary实例,格式“ip:port” + "new_ctl_primary": 传入的待提升primary的中控实例,格式“ip:port” + } + """ + + def _is_running_process(self, rds_params): + """ + 检测待回收的primary是否存在running状态的线程 + """ + check_sql = ( + f"select * from information_schema.TDBCTL_CLUSTER_PROCESSLIST where user = '{TDBCTL_USER}'" + + " and command != 'Sleep' and info not like '%INFORMATION_SCHEMA.PROCESSLIST';" + ) + rds_params["cmds"] = ["set tc_admin=1"] + [check_sql] + res = DRSApi.rpc(rds_params) + if res[0]["error_msg"]: + raise CtlSwitchToSlaveFailedException( + message=_("select processlist failed: {}".format(res[0]["error_msg"])) + ) + + if res[0]["cmd_results"][1]["table_data"]: + self.log_warning(f"There are also {res[0]['cmd_results'][1]['rows_affected']} non-sleep state threads") + return False + + return True + + def _prepare_check(self, cluster: Cluster, reduce_ctl_primary: str): + """ + 检测当前是否可以执行切换 + todo 是否需要检验checksum结果? + """ + cmds = ["set tc_admin=1"] + rpc_params = { + "addresses": [reduce_ctl_primary], + "cmds": cmds, + "force": False, + "bk_cloud_id": cluster.bk_cloud_id, + } + # 检测待下架的中控primary是否能连接上 + check_sql = "select 1;" + rpc_params["cmds"] = cmds + [check_sql] + res = DRSApi.rpc(rpc_params) + if "connection refused" in res[0]["error_msg"]: + # 任务待下架的节点已经故障,应该不做下面的处理,作为故障机处理 + self.log_warning(res[0]["error_msg"]) + return False + + # 检测原primary节点是否正在执行中控命令 + if self._is_running_process(rds_params=rpc_params): + # 如果第一次检验到有running的process,则尝试等待10秒,重新检验一次,如果还存在则退出 + sleep(10) + if not self._is_running_process(rds_params=rpc_params): + raise CtlSwitchToSlaveFailedException( + message=_("After two detections, there are still non-sleep state threads in the instance") + ) + return True + + def _exec_disable_primary(self, cluster: Cluster, reduce_ctl_primary): + """ + 连接待下架的primary,执行执行TDBCTL DISABLE PRIMARY + """ + res = DRSApi.rpc( + { + "addresses": [reduce_ctl_primary], + "cmds": ["set tc_admin = 1", "TDBCTL DISABLE PRIMARY"], + "force": False, + "bk_cloud_id": cluster.bk_cloud_id, + } + ) + if res[0]["error_msg"]: + raise CtlSwitchToSlaveFailedException( + message=_("exec TDBCTL-DISABLE-PRIMARY failed: {}".format(res[0]["error_msg"])) + ) + self.log_info(f"[{reduce_ctl_primary}]exec TDBCTL-DISABLE-PRIMARY success") + return True + + def _stop_slave(self, cluster: Cluster, ctl_set): + # 再分发stop slave命令 + rpc_params = { + "addresses": [], + "cmds": ["set tc_admin=0", "stop slave"], + "force": False, + "bk_cloud_id": cluster.bk_cloud_id, + } + for ctl in ctl_set: + self.log_info(f"exec stop slave in instance[{ctl.machine.ip}{IP_PORT_DIVIDER}{ctl.admin_port}") + rpc_params["addresses"] = [f"{ctl.machine.ip}{IP_PORT_DIVIDER}{ctl.admin_port}"] + res = DRSApi.rpc(rpc_params) + + if res[0]["error_msg"]: + raise CtlSwitchToSlaveFailedException( + message=_(f"exec [{ctl.ip_port}] stop slave failed: {res[0]['error_msg']}") + ) + + return True + + def _new_master_enable_primary(self, cluster: Cluster, new_master, reduce_ctl_primary): + """ + 提升新节点作为主节点的逻辑 + """ + rpc_params = { + "addresses": [f"{new_master.machine.ip}{IP_PORT_DIVIDER}{new_master.admin_port}"], + "cmds": [], + "force": False, + "bk_cloud_id": cluster.bk_cloud_id, + } + + # 查询reduce_ctl_primary对应的server_name + reduce_ip = reduce_ctl_primary.split(":")[0] + reduce_port = reduce_ctl_primary.split(":")[1] + server_name = "test_name" + select_sql = [ + "set tc_admin = 1", + f"select Server_name from mysql.servers where host = '{reduce_ip}' and port = {reduce_port}", + ] + rpc_params["cmds"] = select_sql + res = DRSApi.rpc(rpc_params) + if res[0]["error_msg"]: + raise CtlSwitchToSlaveFailedException( + message=_("select mysql.servers failed: {}".format(res[0]["error_msg"])) + ) + if not res[0]["cmd_results"][1]["table_data"]: + self.log_warning(f"Node [{reduce_ctl_primary}] no longer has routing information") + else: + server_name = res[0]["cmd_results"][1]["table_data"][0]["Server_name"] + + # 提升新主节点 + exec_sql = ["set tc_admin=1", f"TDBCTL DROP NODE IF EXISTS {server_name}", "TDBCTL ENABLE PRIMARY"] + rpc_params["cmds"] = exec_sql + res = DRSApi.rpc(rpc_params) + if res[0]["error_msg"]: + raise CtlSwitchToSlaveFailedException( + message=_("exec TDBCTL-DISABLE-PRIMARY failed: {}".format(res[0]["error_msg"])) + ) + return True + + def _sync_to_new_master(self, cluster: Cluster, new_primary, other_secondary): + """ + 其余的slave节点同步新的master + """ + # 获取同步账号 + data = DBConfigApi.query_conf_item( + { + "bk_biz_id": "0", + "level_name": LevelName.PLAT, + "level_value": "0", + "conf_file": "mysql#user", + "conf_type": ConfigTypeEnum.InitUser, + "namespace": NameSpaceEnum.TenDB.value, + "format": FormatType.MAP, + } + )["content"] + + # 远程授权 + MySQLPrivManagerApi.add_priv_without_account_rule( + { + "bk_cloud_id": cluster.bk_cloud_id, + "bk_biz_id": cluster.bk_biz_id, + "operator": "", + "user": data["repl_user"], + "psw": RSAHandler.encrypt_password( + MySQLPrivManagerApi.fetch_public_key(), data["repl_pwd"], salt=None + ), + "hosts": [slave.machine.ip for slave in other_secondary], + "dbname": "%", + "dml_ddl_priv": "", + "global_priv": "REPLICATION SLAVE, REPLICATION CLIENT", + "address": new_primary, + "role": PrivRole.TDBCTL.value, + } + ) + self.log_info(_("在[{}]创建添加同步账号成功").format(new_primary)) + + # 基于GTID建立同步 + for secondary in other_secondary: + repl_sql = ( + f"CHANGE MASTER TO" + f"MASTER_HOST ='{new_primary.split(':')[0]}'," + f"MASTER_PORT={new_primary.split(':')[1]}," + f"MASTER_USER ='{data['repl_user']}'," + f"MASTER_PASSWORD='{data['repl_pwd']}'," + "MASTER_AUTO_POSITION = 1;" + ) + + res = DRSApi.rpc( + { + "addresses": [f"{secondary.machine.ip}{IP_PORT_DIVIDER}{secondary.admin_port}"], + "cmds": ["set tc_admin = 0", repl_sql], + "force": False, + "bk_cloud_id": cluster.bk_cloud_id, + } + ) + if res[0]["error_msg"]: + raise CtlSwitchToSlaveFailedException(message=_(f"exec change master failed: {res[0]['error_msg']}")) + return True + + def _execute(self, data, parent_data): + kwargs = data.get_one_of_inputs("kwargs") + + reduce_ctl_primary = kwargs["reduce_ctl_primary"] + new_ctl_primary = kwargs["new_ctl_primary"] + + # 获取cluster对象,包括中控实例、 spider端口等 + cluster = Cluster.objects.get(id=kwargs["cluster_id"]) + + # 查询所有的spider-ctl的其余从节点对象 + ctl_set = cluster.proxyinstance_set.filter( + tendbclusterspiderext__spider_role=TenDBClusterSpiderRole.SPIDER_MASTER + ).exclude(machine__ip=reduce_ctl_primary.split(":")[0]) + + # 阶段1 先检测是否当前可以提升主切换 + result = self._prepare_check(cluster=cluster, reduce_ctl_primary=reduce_ctl_primary) + + # 阶段2 尝试连接原来ctl_primary,执行TDBCTL DISABLE PRIMARY + if result: + self._exec_disable_primary(cluster, reduce_ctl_primary) + + # 阶段3 关闭所有从节点的主从同步 + self._stop_slave(cluster, ctl_set) + + # 阶段3 根据传入新的primary节点,计算出其余的从节点 + other_secondary = ctl_set.exclude(machine__ip=new_ctl_primary.split(":")[0]) + + # 阶段4 其余节点同步新的primary节点 + self._sync_to_new_master(cluster, new_ctl_primary, other_secondary) + + # 阶段5 连接新的primary节点,执行剔除原primary节点的命令, 并提升自己为primary TDBCTL ENABLE PRIMARY + self._new_master_enable_primary(cluster, new_ctl_primary, reduce_ctl_primary) + + +class CtlSwitchToSlaveComponent(Component): + name = __name__ + code = "ctl_switch_to_slave" + bound_service = CtlSwitchToSlaveService diff --git a/dbm-ui/backend/flow/plugins/components/collections/spider/drop_spider_ronting.py b/dbm-ui/backend/flow/plugins/components/collections/spider/drop_spider_ronting.py new file mode 100644 index 0000000000..485a3a0f92 --- /dev/null +++ b/dbm-ui/backend/flow/plugins/components/collections/spider/drop_spider_ronting.py @@ -0,0 +1,115 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 backend.components import DRSApi +from backend.db_meta.models import Cluster, ProxyInstance +from backend.flow.engine.bamboo.scene.spider.common.exceptions import DropSpiderNodeFailedException +from backend.flow.plugins.components.collections.common.base_service import BaseService +from backend.flow.utils.mysql.check_client_connections import check_client_connection + + +class DropSpiderRoutingService(BaseService): + def _pre_check(self, cluster: Cluster, reduce_spider: ProxyInstance): + """ + 检测待下架的spider节点是否有存在访问 + """ + + res = check_client_connection(bk_cloud_id=cluster.bk_cloud_id, instances=[reduce_spider.ip_port]) + + if res[0]["error_msg"]: + raise DropSpiderNodeFailedException(message=_("select processlist failed: {}".format(res[0]["error_msg"]))) + + if res[0]["cmd_results"][0]["table_data"]: + self.log_error(f"There are also {len(res[0]['cmd_results'][0]['table_data'])} non-sleep state threads") + process_list = res[0]["cmd_results"][0]["table_data"] + for p in process_list: + # 打印连接 + self.log_error( + f"proc_id: {p['ID']}, command:{p['COMMAND']}, host:{p['HOST']}, info:{p['INFO']}, time:{p['TIME']}" + ) + return False + + return True + + def _exec_drop_routing(self, cluster: Cluster, spider: ProxyInstance): + """ + 执行删除节点路由逻辑 + """ + ctl_primary = cluster.tendbcluster_ctl_primary_address() + + rpc_params = { + "addresses": [ctl_primary], + "cmds": [], + "force": False, + "bk_cloud_id": cluster.bk_cloud_id, + } + + select_sqls = [ + "set tc_admin=1", + f"select Server_name from mysql.servers where host = '{spider.machine.ip}' and port = {spider.port}", + ] + + rpc_params["cmds"] = select_sqls + res = DRSApi.rpc(rpc_params) + + if res[0]["error_msg"]: + raise DropSpiderNodeFailedException( + message=_("select mysql.servers failed: {}".format(res[0]["error_msg"])) + ) + + if not res[0]["cmd_results"][1]["table_data"]: + self.log_warning(f"Node [{spider.ip_port}] no longer has routing information") + return True + + else: + server_name = res[0]["cmd_results"][1]["table_data"][0]["Server_name"] + + # 删除节点路由信息 + exec_sql = [ + "set tc_admin=1", + f"TDBCTL DROP NODE IF EXISTS {server_name}", + "TDBCTL FLUSH ROUTING", + ] + rpc_params["cmds"] = exec_sql + res = DRSApi.rpc(rpc_params) + if res[0]["error_msg"]: + raise DropSpiderNodeFailedException( + message=_("exec TDBCTL-DROP-NODE failed: {}".format(res[0]["error_msg"])) + ) + return True + + def _execute(self, data, parent_data): + kwargs = data.get_one_of_inputs("kwargs") + + reduce_spiders = kwargs["reduce_spiders"] + is_safe = kwargs["is_safe"] + cluster = Cluster.objects.get(id=kwargs["cluster_id"]) + + for spider in reduce_spiders: + # spider机器是专属于一套集群,单机单实例 + s = cluster.proxyinstance_set.get(machine__ip=spider["ip"]) + + if is_safe: + if not self._pre_check(cluster, s): + # 检测不通过退出 + return False + + # 执行删除路由 + self.log_info(f"exec drop node [{s.ip_port}]") + self._exec_drop_routing(cluster, s) + return True + + +class DropSpiderRoutingComponent(Component): + name = __name__ + code = "drop_spider_routing" + bound_service = DropSpiderRoutingService diff --git a/dbm-ui/backend/flow/plugins/components/collections/spider/remote_migrate_cut_over.py b/dbm-ui/backend/flow/plugins/components/collections/spider/remote_migrate_cut_over.py new file mode 100644 index 0000000000..c034e0f768 --- /dev/null +++ b/dbm-ui/backend/flow/plugins/components/collections/spider/remote_migrate_cut_over.py @@ -0,0 +1,79 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging + +from pipeline.component_framework.component import Component + +from backend.components import MySQLPrivManagerApi +from backend.core.encrypt.handlers import RSAHandler +from backend.db_meta.models import Cluster +from backend.flow.consts import TDBCTL_USER +from backend.flow.plugins.components.collections.mysql.exec_actuator_script import ExecuteDBActuatorScriptService +from backend.flow.utils.mysql.mysql_act_playload import MysqlActPayload + +logger = logging.getLogger("flow") + + +class RemoteMigrateCutOverService(ExecuteDBActuatorScriptService): + """ + 定义执行tendbcluster的remote成对切换的活动 + 活动节点流程: + 1: 给新机器都需要加内置账号 + 2: 执行成对切换的db_actor命令 + """ + + def _execute(self, data, parent_data, callback=None) -> bool: + + kwargs = data.get_one_of_inputs("kwargs") + global_data = data.get_one_of_inputs("global_data") + + cluster = Cluster.objects.get(id=global_data["cluster_id"]) + ctl_primary = cluster.tendbcluster_ctl_primary_address() + + # 添加临时账号 + encrypt_switch_pwd = RSAHandler.encrypt_password( + MySQLPrivManagerApi.fetch_public_key(), global_data["tdbctl_pass"], salt=None + ) + + params = { + "bk_cloud_id": cluster.bk_cloud_id, + "bk_biz_id": global_data["bk_biz_id"], + "operator": global_data.get("created_by", ""), + "user": TDBCTL_USER, + "psw": encrypt_switch_pwd, + "dbname": "%", + "dml_ddl_priv": "", + "global_priv": "all privileges", + "address": "", + "hosts": [ctl_primary.split(":")[0]], + } + + for info in global_data["migrate_tuples"]: + params["address"] = info["new_master"] + MySQLPrivManagerApi.add_priv_without_account_rule(params=params) + self.log_info(f"add tdbctl user in master instance [{info['new_master']}]") + + params["address"] = info["new_slave"] + MySQLPrivManagerApi.add_priv_without_account_rule(params=params) + self.log_info(f"add tdbctl user in slave instance [{info['new_slave']}]") + + # 执行pt-table-sync 的 db-act命令 + data.get_one_of_inputs("kwargs")["bk_cloud_id"] = cluster.bk_cloud_id + data.get_one_of_inputs("kwargs")[ + "get_mysql_payload_func" + ] = MysqlActPayload.tendb_cluster_remote_migrate.__name__ + data.get_one_of_inputs("kwargs")["exec_ip"] = ctl_primary.split(":")[0] + return super()._execute(data, parent_data) + + +class RemoteMigrateCutOverComponent(Component): + name = __name__ + code = "remote_migrate_cut_over" + bound_service = RemoteMigrateCutOverService diff --git a/dbm-ui/backend/flow/plugins/components/collections/tbinlogdumper/__init__.py b/dbm-ui/backend/flow/plugins/components/collections/tbinlogdumper/__init__.py new file mode 100644 index 0000000000..28b2474aa1 --- /dev/null +++ b/dbm-ui/backend/flow/plugins/components/collections/tbinlogdumper/__init__.py @@ -0,0 +1,9 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" diff --git a/dbm-ui/backend/flow/plugins/components/collections/tbinlogdumper/dumper_data.py b/dbm-ui/backend/flow/plugins/components/collections/tbinlogdumper/dumper_data.py new file mode 100644 index 0000000000..fd1eaabdc2 --- /dev/null +++ b/dbm-ui/backend/flow/plugins/components/collections/tbinlogdumper/dumper_data.py @@ -0,0 +1,66 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import uuid + +from pipeline.component_framework.component import Component + +from backend.flow.consts import DBA_SYSTEM_USER +from backend.flow.plugins.components.collections.mysql.exec_actuator_script import ExecuteDBActuatorScriptService +from backend.flow.utils.mysql.mysql_act_playload import MysqlActPayload +from backend.flow.utils.tbinlogdumper.tbinlogdumper_act_payload import TBinlogDumperActPayload + + +class TBinlogDumperFullSyncDataService(ExecuteDBActuatorScriptService): + """ + 针对TBinlogdumper的场景,针对库表做逻辑备份 + """ + + def _execute(self, data, parent_data): + + kwargs = data.get_one_of_inputs("kwargs") + global_data = data.get_one_of_inputs("global_data") + + backup_ip = kwargs["backup_ip"] + backup_port = kwargs["backup_port"] + backup_role = kwargs["backup_role"] + + # 获取需要备份的库表信息,根据TBinlogdumper设置同步信息 + dumper_conf = TBinlogDumperActPayload.get_tbinlogdumper_config( + module_id=kwargs["module_id"], bk_biz_id=global_data["bk_biz_id"] + )["mysqld"] + + # 拼装备份你库表正则表达式, 目前只考虑replicate_do_table 和 replicate_wild_do_table的 过滤同步场景 + backup_regex = "" + if dumper_conf.get("replicate_do_table"): + backup_regex = dumper_conf["replicate_do_table"].replace(",", "|") + if dumper_conf.get("replicate_wild_do_table"): + tmp = dumper_conf["replicate_wild_do_table"].replace("%", "*") + backup_regex += "|" + backup_regex += tmp.replace(",", "|") + + # 下发库表备份指令 + data.get_one_of_inputs("global_data")["port"] = backup_port + data.get_one_of_inputs("global_data")["role"] = backup_role + + data.get_one_of_inputs("global_data")["db_table_filter_regex"] = backup_regex + + data.get_one_of_inputs("kwargs")["bk_cloud_id"] = kwargs["bk_cloud_id"] + data.get_one_of_inputs("kwargs")[ + "get_mysql_payload_func" + ] = TBinlogDumperActPayload.tbinlogdumper_backup_demand_payload.__name__ + data.get_one_of_inputs("kwargs")["exec_ip"] = backup_ip + data.get_one_of_inputs("kwargs")["run_as_system_user"] = DBA_SYSTEM_USER + return super()._execute(data, parent_data) + + +class TBinlogDumperFullSyncDataComponent(Component): + name = __name__ + code = "tbinlogdumper_full_sync_data" + bound_service = TBinlogDumperFullSyncDataService diff --git a/dbm-ui/backend/flow/plugins/components/collections/tbinlogdumper/stop_slave.py b/dbm-ui/backend/flow/plugins/components/collections/tbinlogdumper/stop_slave.py new file mode 100644 index 0000000000..8b69d0d0b3 --- /dev/null +++ b/dbm-ui/backend/flow/plugins/components/collections/tbinlogdumper/stop_slave.py @@ -0,0 +1,51 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 pipeline.component_framework.component import Component + +from backend.components import DRSApi +from backend.constants import IP_PORT_DIVIDER +from backend.flow.plugins.components.collections.common.base_service import BaseService + + +class TBinlogDumperStopSlaveService(BaseService): + """ + 对TBinlogDumper关闭数据同步 + """ + + def _execute(self, data, parent_data): + + kwargs = data.get_one_of_inputs("kwargs") + address = f"{kwargs['tbinlogdumper_ip']}{IP_PORT_DIVIDER}{kwargs['tbinlogdumper_port']}" + + res = DRSApi.rpc( + { + "addresses": [address], + "cmds": ["stop slave"], + "force": False, + "bk_cloud_id": kwargs["bk_cloud_id"], + } + ) + + if res[0]["error_msg"]: + if kwargs["is_safe"]: + self.log_error(f"stop slave [{address}] failed:[{res[0]['error_msg']}]") + return False + else: + self.log_warning(f"stop slave [{address}] failed:[{res[0]['error_msg']}], but is_safe is false,skip") + return True + + self.log_info(f"stop slave [{address}] success") + return True + + +class TBinlogDumperStopSlaveComponent(Component): + name = __name__ + code = "tbinlogdumper_stop_slave" + bound_service = TBinlogDumperStopSlaveService diff --git a/dbm-ui/backend/flow/plugins/components/collections/tbinlogdumper/trans_backup_file.py b/dbm-ui/backend/flow/plugins/components/collections/tbinlogdumper/trans_backup_file.py new file mode 100644 index 0000000000..668f9392e8 --- /dev/null +++ b/dbm-ui/backend/flow/plugins/components/collections/tbinlogdumper/trans_backup_file.py @@ -0,0 +1,39 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +import json +import logging + +from django.utils.translation import ugettext as _ +from pipeline.component_framework.component import Component + +from backend.flow.plugins.components.collections.mysql.trans_flies import TransFileService +from backend.flow.utils.mysql.common.compare_time import compare_time +from backend.ticket.constants import TicketType + +logger = logging.getLogger("flow") + + +class TBinlogDumperTransFileService(TransFileService): + def _execute(self, data, parent_data) -> bool: + """ + 执行传输文件的原子任务, 针对tbinlogdumper的全量同步 + """ + trans_data = data.get_one_of_inputs("trans_data") + data.get_one_of_inputs("kwargs")["file_list"] = [f"{trans_data.backup_info['backup_dir']}/*"] + data.get_one_of_inputs("kwargs")["file_target_path"] = trans_data.backup_info["backup_dir"] + + return super()._execute(data, parent_data) + + +class TBinlogDumperTransFileComponent(Component): + name = __name__ + code = "tbinlogdumper_trans_file" + bound_service = TBinlogDumperTransFileService diff --git a/dbm-ui/backend/flow/signal/handlers.py b/dbm-ui/backend/flow/signal/handlers.py index 9c7a162f66..47b77b3bab 100644 --- a/dbm-ui/backend/flow/signal/handlers.py +++ b/dbm-ui/backend/flow/signal/handlers.py @@ -15,13 +15,16 @@ from django.utils.translation import ugettext as _ from pipeline.eri.signals import post_set_state -from backend.flow.consts import StateType +from backend.db_dirty.handlers import DBDirtyMachineHandler +from backend.flow.consts import FAILED_STATES, StateType from backend.flow.engine.bamboo.engine import BambooEngine from backend.flow.models import FlowNode, FlowTree -from backend.ticket.constants import FlowCallbackType, FlowType, TicketFlowStatus +from backend.ticket.builders import BuilderFactory +from backend.ticket.constants import BAMBOO_STATE__TICKET_STATE_MAP, FlowCallbackType, FlowType, TicketFlowStatus from backend.ticket.flow_manager.inner import InnerFlow from backend.ticket.flow_manager.manager import TicketFlowManager -from backend.ticket.models import Ticket +from backend.ticket.models import Flow, Ticket +from backend.utils.basic import get_target_items_from_details logger = logging.getLogger("flow") @@ -42,31 +45,38 @@ def post_set_state_signal_handler(sender, node_id, to_state, version, root_id, * ) try: tree = FlowTree.objects.get(root_id=root_id) - origin_tree_status = tree.status - - # 如果当前节点或者流程已失败,则状态为失败 - if to_state == StateType.FAILED or pipeline_states[root_id]["state"] == StateType.FAILED: - target_tree_status = StateType.FAILED - # 如果流程已撤销,则状态为撤销 - elif pipeline_states[root_id]["state"] == StateType.REVOKED: - target_tree_status = StateType.REVOKED - # 如果当前节点和流程都已完成,则状态为完成 - elif to_state == StateType.FINISHED and pipeline_states[root_id]["state"] == StateType.FINISHED: - target_tree_status = StateType.FINISHED - # 如果当前节点已完成,流程不处于完成态,则状态为进行 - elif to_state == StateType.FINISHED and pipeline_states[root_id]["state"] != StateType.FINISHED: - target_tree_status = StateType.RUNNING - else: - target_tree_status = to_state - - if origin_tree_status != target_tree_status: - tree.updated_at = now - tree.status = target_tree_status - tree.save() - callback_ticket(tree.uid, root_id) except FlowTree.DoesNotExist: logger.debug(_("【状态信号捕获】未查找到FlowTree root_id={}").format(root_id)) - pass + return + + # 流转当前的flow状态 + origin_tree_status = tree.status + # 如果当前节点或者流程已失败,则状态为失败 + if to_state == StateType.FAILED or pipeline_states[root_id]["state"] == StateType.FAILED: + target_tree_status = StateType.FAILED + # 如果流程已撤销,则状态为撤销 + elif pipeline_states[root_id]["state"] == StateType.REVOKED: + target_tree_status = StateType.REVOKED + # 如果当前节点和流程都已完成,则状态为完成 + elif to_state == StateType.FINISHED and pipeline_states[root_id]["state"] == StateType.FINISHED: + target_tree_status = StateType.FINISHED + # 如果当前节点已完成,流程不处于完成态,则状态为进行 + elif to_state == StateType.FINISHED and pipeline_states[root_id]["state"] != StateType.FINISHED: + target_tree_status = StateType.RUNNING + else: + target_tree_status = to_state + + # 如果状态发生改变,则触发单据回调和污点池转移 + if origin_tree_status != target_tree_status: + try: + # 更新flow tree和inner flow的状态 + tree.updated_at, tree.status = now, target_tree_status + tree.save() + handle_dirty_machine(tree.uid, root_id, origin_tree_status, target_tree_status) + callback_ticket(tree.uid, root_id) + except Exception as e: # pylint: disable=broad-except + logger.debug(_("【状态信号捕获】污点池处理/回调单据 发生错误,错误信息{}").format(e)) + return def callback_ticket(ticket_id, root_id): @@ -91,3 +101,33 @@ def callback_ticket(ticket_id, root_id): if current_flow and current_flow.flow_obj_id == root_id: manager = TicketFlowManager(ticket=ticket) manager.run_next_flow() + + +def handle_dirty_machine(ticket_id, root_id, origin_tree_status, target_tree_status): + """处理执行失败/重试成功涉及的污点池机器""" + if (origin_tree_status not in FAILED_STATES) and (target_tree_status not in FAILED_STATES): + return + + try: + ticket = Ticket.objects.get(id=ticket_id) + flow = Flow.objects.get(flow_obj_id=root_id) + # 如果不是部署类单据,则无需处理 + if ticket.ticket_type not in BuilderFactory.apply_ticket_type: + return + except (Ticket.DoesNotExist, Flow.DoesNotExist, ValueError): + return + + # 如果初始状态是失败,则证明是重试,将机器从污点池中移除 + bk_host_ids = get_target_items_from_details( + obj=ticket.details, match_keys=["host_id", "bk_host_id", "bk_host_ids"] + ) + if origin_tree_status in FAILED_STATES: + logger.info(_("【状态信号捕获】主机列表:{} 将从污点池挪出").format(bk_host_ids)) + DBDirtyMachineHandler.remove_dirty_machines(bk_host_ids) + + # 如果是目标状态失败,则证明是执行失败,将机器加入污点池 + if target_tree_status in FAILED_STATES: + logger.info(_("【状态信号捕获】单据-{}:任务-{}执行失败,主机列表:{}将挪到污点池").format(ticket_id, root_id, bk_host_ids)) + DBDirtyMachineHandler.insert_dirty_machines( + bk_biz_id=ticket.bk_biz_id, bk_host_ids=bk_host_ids, ticket=ticket, flow=flow + ) diff --git a/dbm-ui/backend/flow/urls.py b/dbm-ui/backend/flow/urls.py index c32d0f3d4c..decdd2af08 100644 --- a/dbm-ui/backend/flow/urls.py +++ b/dbm-ui/backend/flow/urls.py @@ -63,6 +63,7 @@ from backend.flow.views.mysql_ha_switch import MySQLHASwitchSceneApiView from backend.flow.views.mysql_ha_truncate_data import MySQLHATruncateDataView from backend.flow.views.mysql_migrate_cluster import MigrateMysqlClusterSceneApiView +from backend.flow.views.mysql_open_area import MysqlOpenAreaSceneApiView from backend.flow.views.mysql_partition import MysqlPartitionSceneApiView from backend.flow.views.mysql_proxy_add import AddMySQLProxySceneApiView from backend.flow.views.mysql_proxy_switch import SwitchMySQLProxySceneApiView @@ -79,10 +80,12 @@ from backend.flow.views.mysql_single_rename_database import MySQLSingleRenameDatabaseView from backend.flow.views.mysql_single_truncate_data import MySQLSingleTruncateDataView from backend.flow.views.name_service import ( - NameServiceClbCreateSceneApiView, - NameServiceClbDeleteSceneApiView, - NameServicePolarisCreateSceneApiView, - NameServicePolarisDeleteSceneApiView, + ClbCreateSceneApiView, + ClbDeleteSceneApiView, + DomainBindClbIpSceneApiView, + DomainUnBindClbIpSceneApiView, + PolarisCreateSceneApiView, + PolarisDeleteSceneApiView, ) from backend.flow.views.pulsar_apply import InstallPulsarSceneApiView from backend.flow.views.pulsar_destroy import DestroyPulsarSceneApiView @@ -93,25 +96,38 @@ from backend.flow.views.redis_cluster import ( InstallRedisCacheClusterSceneApiView, InstallTendisplusClusterSceneApiView, + RedisAddDtsServerSceneApiView, + RedisBackendScaleSceneApiView, + RedisClusterAddSlaveApiView, RedisClusterBackupSceneApiView, - RedisClusterDtsSceneApiView, + RedisClusterDataCheckRepairApiView, + RedisClusterDataCopySceneApiView, RedisClusterOpenCloseSceneApiView, + RedisClusterShardNumUpdateSceneApiView, RedisClusterShutdownSceneApiView, + RedisClusterTypeUpdateSceneApiView, + RedisDataStructureSceneApiView, + RedisDataStructureTaskDeleteSceneApiView, RedisFlushDataSceneApiView, RedisProxyScaleSceneApiView, + RedisRemoveDtsServerSceneApiView, SingleProxyShutdownSceneApiView, SingleRedisShutdownSceneApiView, ) from backend.flow.views.redis_keys import RedisKeysDeleteSceneApiView, RedisKeysExtractSceneApiView from backend.flow.views.redis_scene import ( - RedisClusterMasterCutOffSceneApiView, - RedisClusterSlaveCutOffSceneApiView, + RedisClusterCompleteReplaceSceneApiView, + RedisClusterMSSwitchSceneApiView, RedisInstallDbmonSceneApiView, ) from backend.flow.views.riak_apply import RiakApplySceneApiView -from backend.flow.views.rollback_pipeline import PipelineTreeApiView, RollbackPipelineApiView +from backend.flow.views.riak_destroy import RiakClusterDestroyApiView +from backend.flow.views.riak_disable import RiakClusterDisableApiView +from backend.flow.views.riak_enable import RiakClusterEnableApiView +from backend.flow.views.riak_scale_in import RiakClusterScaleInApiView +from backend.flow.views.riak_scale_out import RiakClusterScaleOutApiView +from backend.flow.views.spider_add_mnt import AddSpiderMNTSceneApiView from backend.flow.views.spider_add_nodes import AddSpiderNodesSceneApiView -from backend.flow.views.spider_add_tmp_node import AddTmpSpiderSceneApiView from backend.flow.views.spider_checksum import SpiderChecksumSceneApiView from backend.flow.views.spider_cluster_apply import InstallSpiderClusterSceneApiView from backend.flow.views.spider_cluster_database_table_backup import TenDBClusterDatabaseTableBackupView @@ -120,18 +136,28 @@ DisableSpiderSceneApiView, EnableSpiderSceneApiView, ) +from backend.flow.views.spider_cluster_flashback import TenDBClusterFlashbackView from backend.flow.views.spider_cluster_full_backup import TenDBClusterFullBackupView from backend.flow.views.spider_cluster_rename_database import TenDBClusterRenameDatabaseView from backend.flow.views.spider_cluster_truncate_database import TenDBClusterTruncateDatabaseView from backend.flow.views.spider_partition import SpiderPartitionSceneApiView +from backend.flow.views.spider_reduce_mnt import ReduceSpiderMNTSceneApiView +from backend.flow.views.spider_reduce_nodes import ReduceSpiderNodesSceneApiView from backend.flow.views.spider_semantic_check import SpiderSemanticCheckSceneApiView from backend.flow.views.spider_slave_apply import InstallSpiderSlaveClusterSceneApiView +from backend.flow.views.spider_slave_destroy import DestroySpiderSlaveClusterSceneApiView from backend.flow.views.spider_sql_import import SpiderSqlImportSceneApiView from backend.flow.views.sql_semantic_check import SqlSemanticCheckSceneApiView +from backend.flow.views.tbinlogdumper_add import InstallTBinlogDumperSceneApiView +from backend.flow.views.tbinlogdumper_reduce import ReduceTBinlogDumperSceneApiView +from backend.flow.views.tbinlogdumper_switch import SwitchTBinlogDumperSceneApiView +from backend.flow.views.tendb_cluster_remote_fail_over import RemoteFailOverSceneApiView +from backend.flow.views.tendb_cluster_remote_rebalance import RemoteRebalanceSceneApiView +from backend.flow.views.tendb_cluster_remote_switch import RemoteSwitchSceneApiView +from backend.flow.views.tendb_cluster_rollback_data import TendbClusterRollbackDataSceneApiView +from backend.flow.views.tendb_ha_standardize import TenDBHAStandardizeView urlpatterns = [ - url(r"^scene/rollback$", RollbackPipelineApiView.as_view()), - url(r"^scene/tree/(\w+)$", PipelineTreeApiView.as_view()), # redis api url begin url(r"^scene/install_redis_cache_cluster_apply$", InstallRedisCacheClusterSceneApiView.as_view()), url(r"^scene/install_redis_ssd_cluster_apply$", InstallRedisCacheClusterSceneApiView.as_view()), @@ -143,21 +169,31 @@ url(r"^scene/redis_shutdown$", RedisClusterShutdownSceneApiView.as_view()), url(r"^scene/redis_flush_data$", RedisFlushDataSceneApiView.as_view()), url(r"^scene/redis_proxy_scale$", RedisProxyScaleSceneApiView.as_view()), + url(r"^scene/redis_backend_scale$", RedisBackendScaleSceneApiView.as_view()), url(r"^scene/single_redis_shutdown$", SingleRedisShutdownSceneApiView.as_view()), url(r"^scene/single_proxy_shutdown$", SingleProxyShutdownSceneApiView.as_view()), - url(r"^scene/cutoff/redis_cluster_slave$", RedisClusterSlaveCutOffSceneApiView.as_view()), - url(r"^scene/cutoff/redis_cluster_master$", RedisClusterMasterCutOffSceneApiView.as_view()), - url(r"^scene/cutoff/redis_cluster_proxy$", RedisProxyScaleSceneApiView.as_view()), + url(r"^scene/cutoff/redis_cluster$", RedisClusterCompleteReplaceSceneApiView.as_view()), + url(r"^scene/switch/redis_cluster$", RedisClusterMSSwitchSceneApiView.as_view()), url(r"^scene/install/dbmon$", RedisInstallDbmonSceneApiView.as_view()), - url(r"^scene/redis_cluster_dts$", RedisClusterDtsSceneApiView.as_view()), + url(r"^scene/redis_cluster_data_copy$", RedisClusterDataCopySceneApiView.as_view()), + url(r"^scene/redis_cluster_shard_num_update$", RedisClusterShardNumUpdateSceneApiView.as_view()), + url(r"^scene/redis_cluster_type_update$", RedisClusterTypeUpdateSceneApiView.as_view()), + url(r"^scene/redis_cluster_data_check_repair$", RedisClusterDataCheckRepairApiView.as_view()), + url(r"^scene/redis_add_dts_server$", RedisAddDtsServerSceneApiView.as_view()), + url(r"^scene/redis_remove_dts_server$", RedisRemoveDtsServerSceneApiView.as_view()), + url(r"^scene/redis_data_structure$", RedisDataStructureSceneApiView.as_view()), + url(r"^scene/redis_data_structure_task_delete$", RedisDataStructureTaskDeleteSceneApiView.as_view()), + url(r"^scene/redis_cluster_add_slave$", RedisClusterAddSlaveApiView.as_view()), # redis api url end # name_service start # name_service clb - url(r"^scene/nameservice_clb_create$", NameServiceClbCreateSceneApiView.as_view()), - url(r"^scene/nameservice_clb_delete$", NameServiceClbDeleteSceneApiView.as_view()), + url(r"^scene/nameservice_clb_create$", ClbCreateSceneApiView.as_view()), + url(r"^scene/nameservice_clb_delete$", ClbDeleteSceneApiView.as_view()), + url(r"^scene/nameservice_domain_bind_clb_ip$", DomainBindClbIpSceneApiView.as_view()), + url(r"^scene/nameservice_domain_unbind_clb_ip$", DomainUnBindClbIpSceneApiView.as_view()), # name_service polaris - url(r"^scene/nameservice_polaris_create$", NameServicePolarisCreateSceneApiView.as_view()), - url(r"^scene/nameservice_polaris_delete$", NameServicePolarisDeleteSceneApiView.as_view()), + url(r"^scene/nameservice_polaris_create$", PolarisCreateSceneApiView.as_view()), + url(r"^scene/nameservice_polaris_delete$", PolarisDeleteSceneApiView.as_view()), # name_service end url(r"^scene/install_mysql_apply$", InstallMySQLSingleSceneApiView.as_view()), url(r"^scene/install_mysql_ha_apply$", InstallMySQLHASceneApiView.as_view()), @@ -240,7 +276,7 @@ url(r"^scene/reboot_pulsar$", RebootPulsarSceneApiView.as_view()), url(r"^scene/import_resource_init$", ImportResourceInitStepApiView.as_view()), # spider - url(r"^scene/add_tmp_spider_node$", AddTmpSpiderSceneApiView.as_view()), + url(r"^scene/add_spider_mnt$", AddSpiderMNTSceneApiView.as_view()), url(r"^scene/install_tendb_cluster$", InstallSpiderClusterSceneApiView.as_view()), url(r"^scene/destroy_tendb_cluster$", DestroySpiderClusterSceneApiView.as_view()), url(r"^scene/spider_checksum$", SpiderChecksumSceneApiView.as_view()), @@ -256,12 +292,34 @@ # tendbcluster db重命名 url(r"^scene/tendbcluster_rename_database$", TenDBClusterRenameDatabaseView.as_view()), # tendbcluster 清档 - url(r"^scene/tendbcluster_truncate_database$", TenDBClusterTruncateDatabaseView.as_view()), + url(r"^scene/tendbcluster_truncate_data$", TenDBClusterTruncateDatabaseView.as_view()), # tendbcluster 库表备份 url(r"^scene/tendbcluster_database_table_backup$", TenDBClusterDatabaseTableBackupView.as_view()), # spider 添加 url(r"^scene/add_spider_nodes$", AddSpiderNodesSceneApiView.as_view()), url(r"^scene/tendbcluster_full_backup$", TenDBClusterFullBackupView.as_view()), + # spider 减少 + url(r"^scene/reduce_spider_nodes$", ReduceSpiderNodesSceneApiView.as_view()), # riak url(r"^scene/riak_cluster_apply$", RiakApplySceneApiView.as_view()), + url(r"^scene/tendbcluster_flashback$", TenDBClusterFlashbackView.as_view()), + url(r"^scene/riak_cluster_scale_out$", RiakClusterScaleOutApiView.as_view()), + url(r"^scene/riak_cluster_scale_in$", RiakClusterScaleInApiView.as_view()), + url(r"^scene/riak_cluster_destroy$", RiakClusterDestroyApiView.as_view()), + url(r"^scene/riak_cluster_enable$", RiakClusterEnableApiView.as_view()), + url(r"^scene/riak_cluster_disable$", RiakClusterDisableApiView.as_view()), + # tendbcluster 切换类 + url(r"^scene/tendb_cluster_remote_switch$", RemoteSwitchSceneApiView.as_view()), + url(r"^scene/tendb_cluster_remote_fail_over$", RemoteFailOverSceneApiView.as_view()), + # remote 节点扩缩容 + url(r"^scene/tendb_cluster_remote_rebalance$", RemoteRebalanceSceneApiView.as_view()), + url(r"^scene/tendb_cluster_rollback_data$", TendbClusterRollbackDataSceneApiView.as_view()), + url("^scene/destroy_tendb_slave_cluster$", DestroySpiderSlaveClusterSceneApiView.as_view()), + url("^scene/reduce_spider_mnt$", ReduceSpiderMNTSceneApiView.as_view()), + # tbinlogdumper + url("^scene/install_tbinlogumper$", InstallTBinlogDumperSceneApiView.as_view()), + url("^scene/reduce_tbinlogumper$", ReduceTBinlogDumperSceneApiView.as_view()), + url("^scene/switch_tbinlogumper$", SwitchTBinlogDumperSceneApiView.as_view()), + url("^scene/tendbha_standardize$", TenDBHAStandardizeView.as_view()), + url("^scene/mysql_open_area$", MysqlOpenAreaSceneApiView.as_view()), ] diff --git a/dbm-ui/backend/flow/utils/base/__init__.py b/dbm-ui/backend/flow/utils/base/__init__.py new file mode 100644 index 0000000000..aa5085c628 --- /dev/null +++ b/dbm-ui/backend/flow/utils/base/__init__.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" diff --git a/dbm-ui/backend/flow/utils/base/cc_topo_operate.py b/dbm-ui/backend/flow/utils/base/cc_topo_operate.py new file mode 100644 index 0000000000..f1ff3e283a --- /dev/null +++ b/dbm-ui/backend/flow/utils/base/cc_topo_operate.py @@ -0,0 +1,217 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging +from collections import defaultdict +from dataclasses import asdict +from typing import Dict, List, Union + +from django.db.models import QuerySet + +from backend.configuration.models import SystemSettings +from backend.constants import CommonInstanceLabels +from backend.db_meta.enums import AccessLayer +from backend.db_meta.models import AppCache, Cluster, ClusterMonitorTopo, Machine, ProxyInstance, StorageInstance +from backend.db_meta.models.cluster_monitor import INSTANCE_MONITOR_PLUGINS +from backend.exceptions import ValidationError +from backend.flow.utils.cc_manage import CcManage + +logger = logging.getLogger("flow") + + +class CCTopoOperator: + db_type = None + + def __init__(self, cluster: Union[Cluster, List[Cluster]], ticket_data: dict = None): + """ + 支持单集群/多集群两种模式 + 大部分情况下一台主机只属于一个集群 + 单机多实例的场景,允许一台机器有多个集群,如 MySQL 的场景 + 这里统一当做多集群处理 + """ + if self.db_type is None: + raise NotImplementedError("db_type can not be None") + + if isinstance(cluster, Cluster): + self.clusters: List[Cluster] = [cluster] + else: + self.clusters: List[Cluster] = cluster + self.ticket_data = ticket_data or {} + + # 仅允许同一业务的集群操作 + bk_biz_ids = list(set([cluster.bk_biz_id for cluster in self.clusters])) + if len(bk_biz_ids) != 1: + raise ValidationError("different cluster biz is not supporting") + self.bk_biz_id = bk_biz_ids[0] + self.is_bk_module_created = False + + def create_bk_module(self): + """ + # 根据cluster_id,创建对应的域名名称模块 + """ + if self.db_type is None: + raise NotImplementedError(f"{self.__module__}db_type is not define") + for cluster in self.clusters: + CcManage(bk_biz_id=cluster.bk_biz_id).get_or_create_set_module( + db_type=self.db_type, + cluster_type=cluster.cluster_type, + bk_module_name=cluster.immute_domain, + cluster_id=cluster.id, + creator=cluster.creator, + ) + + # 创建完成后进行缓存,不重复检查和创建 + self.is_bk_module_created = True + + def transfer_instances_to_cluster_module( + self, instances: Union[List[StorageInstance], List[ProxyInstance], QuerySet] + ): + """ + 转移实例到对应的集群模块下,并添加服务实例 + """ + if not self.is_bk_module_created: + self.create_bk_module() + + cluster_ids = [cluster.id for cluster in self.clusters] + # 根据机器类型对实例进行分组 + machine_type_instances_map: Dict[str, List[Union[StorageInstance, ProxyInstance]]] = defaultdict(list) + for ins in instances: + machine_type_instances_map[ins.machine_type].append(ins) + + for machine_type, ins_list in machine_type_instances_map.items(): + bk_host_ids = list(set([ins.machine.bk_host_id for ins in ins_list])) + bk_module_ids = list( + ClusterMonitorTopo.objects.filter(cluster_id__in=cluster_ids, machine_type=machine_type).values_list( + "bk_module_id", flat=True + ) + ) + # 批量转移主机 + CcManage(self.bk_biz_id).transfer_host_module(bk_host_ids, bk_module_ids) + # 创建 CMDB 服务实例 + self.init_instances_service(machine_type, ins_list) + + def init_instances_service(self, machine_type, instances=None): + """ + 创建服务实例 + """ + cluster_module_id_map = {} + func_name = INSTANCE_MONITOR_PLUGINS[self.db_type][machine_type]["func_name"] + hosting_biz_id = SystemSettings.get_exact_hosting_biz(self.bk_biz_id) + for ins in instances: + cluster = ins.cluster.first() + # 查询实例对应的模块 ID + bk_module_id = cluster_module_id_map.get(cluster.id) + if not bk_module_id: + print(ClusterMonitorTopo.objects.all().values()) + bk_module_id = ClusterMonitorTopo.objects.get( + bk_biz_id=hosting_biz_id, cluster_id=cluster.id, machine_type=machine_type + ).bk_module_id + cluster_module_id_map[cluster.id] = bk_module_id + + # 写入服务实例 + self.init_instance_service( + cluster=cluster, + ins=ins, + bk_module_id=bk_module_id, + instance_role=self.generate_ins_instance_role(ins), + func_name=func_name, + ) + + def init_unique_service(self, machine_type): + """ + 适配部分场景下,某种 machine_type 只需要一个服务实例的情况 + """ + for cluster in self.clusters: + # 若服务实例存在,忽略即可 + if ( + StorageInstance.objects.filter(cluster=cluster, machine_type=machine_type) + .exclude(bk_instance_id=0) + .exists() + ): + continue + + # 若服务实例不存在,则添加 + func_name = INSTANCE_MONITOR_PLUGINS[self.db_type][machine_type]["func_name"] + bk_module_id = ClusterMonitorTopo.objects.get( + bk_biz_id=cluster.bk_biz_id, cluster_id=cluster.id, machine_type=machine_type + ).bk_module_id + instance = StorageInstance.objects.filter(cluster=cluster, machine_type=machine_type).first() + self.init_instance_service( + cluster=cluster, + ins=instance, + bk_module_id=bk_module_id, + instance_role=instance.instance_role, + func_name=func_name, + ) + + @staticmethod + def generate_ins_instance_role(ins: Union[StorageInstance, ProxyInstance]): + """ + 生成服务实例的 instance role + """ + return ins.instance_role if ins.access_layer == AccessLayer.STORAGE else AccessLayer.PROXY.value + + def generate_ins_labels( + self, cluster: Cluster, ins: Union[StorageInstance, ProxyInstance], instance_role: str + ) -> dict: + """ + 生成服务实例标签 + """ + labels = asdict( + CommonInstanceLabels( + app=AppCache.get_app_attr(cluster.bk_biz_id, default=cluster.bk_biz_id), + app_id=str(cluster.bk_biz_id), + app_name=AppCache.get_app_attr(cluster.bk_biz_id, "db_app_abbr", cluster.bk_biz_id), + bk_biz_id=str(cluster.bk_biz_id), + bk_cloud_id=str(cluster.bk_cloud_id), + cluster_domain=cluster.immute_domain, + cluster_name=cluster.name, + cluster_type=cluster.cluster_type, + instance_role=instance_role, + instance_host=ins.machine.ip, + instance_port=str(ins.port), + instance=f"{ins.machine.ip}-{ins.port}", + ) + ) + labels.update(self.generate_custom_labels(ins)) + return labels + + def generate_custom_labels(self, ins: Union[StorageInstance, ProxyInstance]) -> dict: + """ + 生成自定义标签,即 CommonInstanceLabels 不满足的标签 + 如 DB 组件无额外标签,则不需要定义 + """ + return {} + + def init_instance_service( + self, + cluster: Cluster, + ins: Union[StorageInstance, ProxyInstance], + bk_module_id: int, + instance_role: str, + func_name: str, + ): + """ + 添加服务实例 + """ + inst_labels = self.generate_ins_labels(cluster, ins, instance_role) + + bk_instance_id = CcManage(self.bk_biz_id).add_service_instance( + bk_module_id=bk_module_id, + bk_host_id=ins.machine.bk_host_id, + listen_ip=ins.machine.ip, + listen_port=ins.port, + func_name=func_name, + bk_process_name=f"{self.db_type}-{ins.machine_type}", + labels_dict=inst_labels, + ) + # 保存到数据库 + ins.bk_instance_id = bk_instance_id + ins.save(update_fields=["bk_instance_id"]) diff --git a/dbm-ui/backend/flow/utils/base/payload_handler.py b/dbm-ui/backend/flow/utils/base/payload_handler.py new file mode 100644 index 0000000000..9b38f4f271 --- /dev/null +++ b/dbm-ui/backend/flow/utils/base/payload_handler.py @@ -0,0 +1,126 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +import logging +import re +from typing import Any + +from backend import env +from backend.components import DBConfigApi +from backend.components.dbconfig.constants import FormatType, LevelName +from backend.constants import IP_RE_PATTERN +from backend.core.encrypt.constants import RSAConfigType +from backend.core.encrypt.handlers import RSAHandler +from backend.db_proxy.constants import ExtensionType +from backend.db_proxy.models import DBExtension +from backend.flow.consts import ConfigTypeEnum, NameSpaceEnum +from backend.ticket.constants import TicketType + +apply_list = [TicketType.MYSQL_SINGLE_APPLY.value, TicketType.MYSQL_HA_APPLY.value] + +logger = logging.getLogger("flow") + + +class PayloadHandler(object): + def __init__(self, bk_cloud_id: int, ticket_data: dict, cluster: dict, cluster_type: str = None): + """ + @param bk_cloud_id 操作的云区域 + @param ticket_data 单据信息 + @param cluster 需要操作的集群信息 + @param cluster_type 表示操作的集群类型,会决定到db_config获取配置的空间 + """ + self.init_mysql_config = {} + self.bk_cloud_id = bk_cloud_id + self.ticket_data = ticket_data + self.cluster = cluster + self.cluster_type = cluster_type + self.mysql_pkg = None + self.proxy_pkg = None + self.checksum_pkg = None + self.mysql_crond_pkg = None + self.mysql_monitor_pkg = None + self.account = self.get_mysql_account() + + # todo 后面可能优化这个问题 + if self.ticket_data.get("module"): + self.db_module_id = self.ticket_data["module"] + elif self.cluster and self.cluster.get("db_module_id"): + self.db_module_id = self.cluster["db_module_id"] + else: + self.db_module_id = 0 + + @staticmethod + def get_mysql_account() -> Any: + """ + 获取mysql实例内置帐户密码 + """ + data = DBConfigApi.query_conf_item( + { + "bk_biz_id": "0", + "level_name": LevelName.PLAT, + "level_value": "0", + "conf_file": "mysql#user", + "conf_type": ConfigTypeEnum.InitUser, + "namespace": NameSpaceEnum.TenDB.value, + "format": FormatType.MAP, + } + ) + return data["content"] + + @staticmethod + def __get_super_account_bypass(): + """ + 旁路逻辑:获取环境变量中的access_hosts, 用户名和密码 + """ + access_hosts = env.TEST_ACCESS_HOSTS or re.compile(IP_RE_PATTERN).findall(env.DRS_APIGW_DOMAIN) + drs_account_data = { + "access_hosts": access_hosts, + "user": env.DRS_USERNAME, + "pwd": env.DRS_PASSWORD, + } + + access_hosts = env.TEST_ACCESS_HOSTS or re.compile(IP_RE_PATTERN).findall(env.DBHA_APIGW_DOMAIN_LIST) + dbha_account_data = { + "access_hosts": access_hosts, + "user": env.DBHA_USERNAME, + "pwd": env.DBHA_PASSWORD, + } + + return drs_account_data, dbha_account_data + + def get_super_account(self): + """ + 获取mysql机器系统管理账号信息 + """ + + if env.DRS_USERNAME and env.DBHA_USERNAME: + return self.__get_super_account_bypass() + + rsa = RSAHandler.get_or_generate_rsa_in_db(RSAConfigType.get_rsa_cloud_name(self.bk_cloud_id)) + + drs = DBExtension.get_latest_extension(bk_cloud_id=self.bk_cloud_id, extension_type=ExtensionType.DRS) + drs_account_data = { + "access_hosts": DBExtension.get_extension_access_hosts( + bk_cloud_id=self.bk_cloud_id, extension_type=ExtensionType.DRS + ), + "pwd": RSAHandler.decrypt_password(rsa.rsa_private_key.content, drs.details["pwd"]), + "user": RSAHandler.decrypt_password(rsa.rsa_private_key.content, drs.details["user"]), + } + + dbha = DBExtension.get_latest_extension(bk_cloud_id=self.bk_cloud_id, extension_type=ExtensionType.DBHA) + dbha_account_data = { + "access_hosts": DBExtension.get_extension_access_hosts( + bk_cloud_id=self.bk_cloud_id, extension_type=ExtensionType.DBHA + ), + "pwd": RSAHandler.decrypt_password(rsa.rsa_private_key.content, dbha.details["pwd"]), + "user": RSAHandler.decrypt_password(rsa.rsa_private_key.content, dbha.details["user"]), + } + + return drs_account_data, dbha_account_data diff --git a/dbm-ui/backend/flow/utils/cc_manage.py b/dbm-ui/backend/flow/utils/cc_manage.py index d69e306288..b26f5bdaad 100644 --- a/dbm-ui/backend/flow/utils/cc_manage.py +++ b/dbm-ui/backend/flow/utils/cc_manage.py @@ -8,141 +8,326 @@ 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. """ + import logging +from typing import Dict, List -from django.utils.translation import ugettext_lazy as _ +from django.db import transaction -from backend import env from backend.components import CCApi -from backend.configuration.constants import FREE_BK_MODULE_ID from backend.configuration.models import SystemSettings -from backend.db_meta.models import Machine +from backend.db_meta.enums import ClusterTypeMachineTypeDefine +from backend.db_meta.models import AppMonitorTopo, Cluster, ClusterMonitorTopo, StorageInstance +from backend.db_meta.models.cluster_monitor import INSTANCE_MONITOR_PLUGINS, SET_NAME_TEMPLATE from backend.db_services.ipchooser.constants import IDLE_HOST_MODULE logger = logging.getLogger("flow") class CcManage(object): - @classmethod - def transfer_machines(cls, bk_biz_id: int, bk_cloud_id: int, ip_modules: list): - - free_bk_module_id = 0 - transfer_groups = [] - bk_host_ids = [] - for transfer_group in ip_modules: - ips = transfer_group["ips"] - kwargs = { - "fields": [ - "bk_host_id", - "bk_host_innerip", - ], - "host_property_filter": { - "condition": "AND", - "rules": [ - {"field": "bk_host_innerip", "operator": "in", "value": ips}, - {"field": "bk_cloud_id", "operator": "equal", "value": bk_cloud_id}, - ], - }, - } - res = CCApi.list_hosts_without_biz(kwargs) + """ + 涉及到 bk-cmdb 的操作,都收敛到这个类中 + 在这里通过 hosting_biz_id 来决定真实操作 cmdb 的业务 + """ - group_bk_host_ids = [] - for host_info in res["info"]: - group_bk_host_ids.append(host_info["bk_host_id"]) - bk_host_ids.append(host_info["bk_host_id"]) + def __init__(self, bk_biz_id: int): + # 主机在 cmdb 上实际托管的业务 + self.hosting_biz_id = SystemSettings.get_exact_hosting_biz(bk_biz_id) - if len(group_bk_host_ids) != len(ips): - raise ValueError(_("查询主机bk_host_id失败[数量不匹配]"), kwargs, res) + def get_or_create_set_module( + self, + db_type: str, + cluster_type: str, + bk_module_name: str, + cluster_id: int = 0, + instance_id: int = 0, + creator: str = "", + ): + """创建监控拓扑相关模块""" - transfer_groups.append({"bk_module_id": transfer_group["bk_module_id"], "bk_host_id": group_bk_host_ids}) - - if env.DBA_APP_BK_BIZ_ID != bk_biz_id: - # 获取dba空闲机模块的bk_module_id - internal_set_info = CCApi.get_biz_internal_module({"bk_biz_id": env.DBA_APP_BK_BIZ_ID}, use_admin=True) - for module in internal_set_info["module"]: - if module["default"] == IDLE_HOST_MODULE: - free_bk_module_id = module["bk_module_id"] + machine_topo = {} + for machine_type in ClusterTypeMachineTypeDefine[cluster_type]: + monitor_plugin = INSTANCE_MONITOR_PLUGINS[db_type][machine_type] + try: + app_monitor_topo = AppMonitorTopo.objects.get( + bk_biz_id=self.hosting_biz_id, db_type=db_type, machine_type=machine_type + ) + except AppMonitorTopo.DoesNotExist: + # 集群拓扑不存在则创建 + bk_set_name = SET_NAME_TEMPLATE.format(db_type=db_type, monitor_plugin_name=monitor_plugin["name"]) + res = CCApi.search_set({"bk_biz_id": self.hosting_biz_id, "condition": {"bk_set_name": bk_set_name}}) + if res["count"]: + bk_set = res["info"][0] + else: + bk_set = CCApi.create_set( + { + "bk_biz_id": self.hosting_biz_id, + "data": {"bk_parent_id": self.hosting_biz_id, "bk_set_name": bk_set_name}, + }, + use_admin=True, + ) + app_monitor_topo, created = AppMonitorTopo.objects.update_or_create( + defaults={"monitor_plugin_id": monitor_plugin["plugin_id"]}, + bk_biz_id=self.hosting_biz_id, + machine_type=machine_type, + db_type=db_type, + monitor_plugin=monitor_plugin["name"], + bk_set_id=bk_set["bk_set_id"], + bk_set_name=bk_set["bk_set_name"], + ) - if free_bk_module_id == 0: - raise ValueError(_("查询空闲机模块ID bk_module_id 失败"), env.DBA_APP_BK_BIZ_ID) + bk_set_id = app_monitor_topo.bk_set_id - # 从业务挪到dba空闲机模块下 - CCApi.transfer_host_across_biz( + # cmdb get_or_create_module + res = CCApi.search_module( { - "src_bk_biz_id": bk_biz_id, - "dst_bk_biz_id": env.DBA_APP_BK_BIZ_ID, - "bk_host_id": bk_host_ids, - "bk_module_id": free_bk_module_id, - "is_increment": False, + "bk_biz_id": self.hosting_biz_id, + "bk_set_id": bk_set_id, + "condition": {"bk_module_name": bk_module_name}, }, use_admin=True, ) - # 按模块转移:db业务空闲机 -> db业务下的集群模块 - for transfer_group in transfer_groups: - CCApi.transfer_host_module( + if res["count"]: + bk_module = res["info"][0] + else: + bk_module = CCApi.create_module( + { + "bk_biz_id": self.hosting_biz_id, + "bk_set_id": bk_set_id, + "data": {"bk_parent_id": bk_set_id, "bk_module_name": bk_module_name}, + }, + use_admin=True, + ) + + # 保留一份集群监控拓扑数据 + topo, created = ClusterMonitorTopo.objects.get_or_create( + defaults={"creator": creator}, + machine_type=machine_type, + bk_biz_id=self.hosting_biz_id, + cluster_id=cluster_id, + instance_id=instance_id, + bk_set_id=bk_set_id, + bk_module_id=bk_module["bk_module_id"], + ) + machine_topo[machine_type] = topo.bk_module_id + + return machine_topo + + def transfer_host_to_idlemodule( + self, bk_biz_id: int, bk_host_ids: List[int], biz_idle_module: int = None, host_topo: List[Dict] = None + ): + """将主机转移到当前业务的空闲模块""" + + # 获取业务的空闲模块和主机拓扑信息 + if not biz_idle_module: + biz_internal_module = CCApi.get_biz_internal_module({"bk_biz_id": bk_biz_id}, use_admin=True) + module_type__module = {module["default"]: module for module in biz_internal_module["module"]} + biz_idle_module = module_type__module[IDLE_HOST_MODULE] + + if not host_topo: + host_topo = CCApi.find_host_biz_relations({"bk_host_id": bk_host_ids}) + + host__idle_module = {host["bk_host_id"]: host["bk_module_id"] for host in host_topo} + transfer_host_ids: List[int] = [ + host_id for host_id in bk_host_ids if host__idle_module[host_id] != biz_idle_module + ] + + if transfer_host_ids: + CCApi.transfer_host_to_idlemodule({"bk_biz_id": bk_biz_id, "bk_host_id": transfer_host_ids}) + + def transfer_host_module(self, bk_host_ids: list, target_module_ids: list): + """ + 跨业务转移主机,需要先做中转处理 + 循环判断处理,逻辑保证幂等操作 + 考虑这几种情况: + 1. 业务空闲机 -> 业务模块 + 2. 业务空闲机 -> DBA 业务模块 + 3. DBA 业务空闲机 -> 业务模块 + 4. DBA 业务空闲机 -> DBA 业务模块 + 5. DBA 业务资源池 -> DBA 业务空闲机 -> 业务模块/DBA 业务模块 + """ + if not bk_host_ids: + # 有些角色允许为空,所以要忽略 + return + + # 查询当前bk_hosts_ids的业务对应关系 + logger.info(f"transfer_host_module, bk_host_ids:{bk_host_ids}") + hosts = CCApi.find_host_biz_relations({"bk_host_id": bk_host_ids}) + + # 主机当前业务跟目标业务不一致,需转移业务的主机 + need_across_biz_host_ids = [] + src_bk_biz_id = None + for host in hosts: + host_biz_id = host["bk_biz_id"] + if host_biz_id != self.hosting_biz_id: + src_bk_biz_id = host_biz_id + need_across_biz_host_ids.append(host["bk_host_id"]) + + # 业务id -> 业务空闲机模块映射 + dst_biz_internal_module = CCApi.get_biz_internal_module({"bk_biz_id": self.hosting_biz_id}, use_admin=True) + free_bk_module_id = None + # 获取dba空闲机模块的 bk_module_id + for module in dst_biz_internal_module["module"]: + if module["default"] == IDLE_HOST_MODULE: + free_bk_module_id = module["bk_module_id"] + + # 将待跨业务转移主机先转移到当前业务的空闲机 + if need_across_biz_host_ids: + self.transfer_host_to_idlemodule( + bk_biz_id=src_bk_biz_id, bk_host_ids=need_across_biz_host_ids, host_topo=hosts + ) + CCApi.transfer_host_across_biz( { - "bk_biz_id": env.DBA_APP_BK_BIZ_ID, - "bk_host_id": transfer_group["bk_host_id"], - "bk_module_id": [transfer_group["bk_module_id"]], + "src_bk_biz_id": src_bk_biz_id, + "dst_bk_biz_id": self.hosting_biz_id, + "bk_host_id": need_across_biz_host_ids, + "bk_module_id": free_bk_module_id, "is_increment": False, }, use_admin=True, ) - @classmethod - def transfer_machines_idle(cls, ips: list): + # 主机转移到对应的模块下,机器可能对应多个集群,所有主机转移到多个模块下是合理的 + CCApi.transfer_host_module( + { + "bk_biz_id": self.hosting_biz_id, + "bk_host_id": bk_host_ids, + "bk_module_id": target_module_ids, + "is_increment": False, + }, + use_admin=True, + ) + + def recycle_host(self, bk_host_ids: list): """ - DBA业务,将机器转移到空闲机模块 + 转移到待回收模块 + 转移主机后会自动删除服务实例,无需额外操作 """ - machines = Machine.objects.filter(ip__in=ips) - bk_host_ids = [m.bk_host_id for m in machines] - CCApi.transfer_host_to_idlemodule({"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "bk_host_id": bk_host_ids}) + CCApi.transfer_host_to_recyclemodule({"bk_biz_id": self.hosting_biz_id, "bk_host_id": bk_host_ids}) - @classmethod - def transfer_host_module(cls, bk_host_ids: list, target_module_ids: list): + def add_service_instance( + self, + bk_module_id: int, + bk_host_id: int, + listen_ip: str, + listen_port: int, + func_name: str, + bk_process_name: str, + labels_dict: dict = None, + ) -> int: """ - 跨业务转移主机,需要先做中转处理 - 循环判断处理,逻辑保证幂等操作 + 定义添加bk-cc的服务实例的公共方法 + @param: bk_module_id: 模块idx + @param: bk_host_id: 机器id + @param: listen_ip: 进程监听ip + @param: listen_port: 进程监听端口(监控依赖) + @param: func_name: 程序的二进制名称 比如zookeeper的二进制名称是java,则填java(监控依赖) + @param: bk_process_name: 对外显示的服务名 比如程序的二进制名称为java的服务zookeeper,则填zookeeper + @param: labels_dict: 待加入的标签字典 """ - # 查询当前bk_hosts_ids的业务对应关系 - hosts = CCApi.find_host_biz_relations({"bk_host_id": bk_host_ids}) + # 添加服务实例信息,目前只操作一个,所以返回也是只有一个元素 + bk_instance_ids = list( + CCApi.create_service_instance( + { + "bk_biz_id": self.hosting_biz_id, + "bk_module_id": bk_module_id, + "instances": [ + { + "bk_host_id": bk_host_id, + "processes": [ + { + "process_template_id": 0, + "process_info": { + "bk_func_name": func_name, + "bk_process_name": bk_process_name, + "bind_info": [ + { + "enable": True, + "ip": listen_ip, + "port": str(listen_port), + "protocol": "1", + # "type": func_type, + } + ], + }, + } + ], + } + ], + } + ) + ) + self.add_label_for_service_instance(bk_instance_ids, labels_dict) + return bk_instance_ids[0] - biz_internal_module = CCApi.get_biz_internal_module({"bk_biz_id": env.DBA_APP_BK_BIZ_ID}, use_admin=True) + def add_label_for_service_instance(self, bk_instance_ids: list, labels_dict: dict): - for host in hosts: - # 根据查询出来当前的host关系信息,做处理 + # 添加集群信息标签 + if labels_dict: + CCApi.add_label_for_service_instance( + { + "bk_biz_id": self.hosting_biz_id, + "instance_ids": bk_instance_ids, + "labels": labels_dict, + } + ) - if env.DBA_APP_BK_BIZ_ID != host["bk_biz_id"]: - free_bk_module_id = int(SystemSettings.get_setting_value(FREE_BK_MODULE_ID, default="0")) - if free_bk_module_id == 0: - # 获取dba空闲机模块的bk_module_id - for module in biz_internal_module["module"]: - if module["default"] == IDLE_HOST_MODULE: - free_bk_module_id = module["bk_module_id"] + def delete_service_instance(self, bk_instance_ids: List[int]): + # 这里因为id不存在会导致接口异常退出,这里暂时接收所有错误,不让它直接退出 + try: + CCApi.delete_service_instance( + { + "bk_biz_id": self.hosting_biz_id, + "service_instance_ids": bk_instance_ids, + } + ) + except Exception as error: + logger.warning(error) - SystemSettings.insert_setting_value(FREE_BK_MODULE_ID, free_bk_module_id) + def delete_cc_module(self, db_type: str, cluster_type: str, cluster_id: int = 0, instance_id: int = 0): + """ + 封装方法:现在bkcc的模块是跟cluster_id、db_type 的结合对应 + 根据这些信息删除对应模块, 使用场景是回收集群 + """ - # 从业务挪到dba空闲机模块下 - CCApi.transfer_host_across_biz( - { - "src_bk_biz_id": int(host["bk_biz_id"]), - "dst_bk_biz_id": env.DBA_APP_BK_BIZ_ID, - "bk_host_id": [int(host["bk_host_id"])], - "bk_module_id": free_bk_module_id, - "is_increment": False, - }, - use_admin=True, - ) + for machine_type in ClusterTypeMachineTypeDefine[cluster_type]: + bk_set_id = AppMonitorTopo.objects.get( + bk_biz_id=self.hosting_biz_id, db_type=db_type, machine_type=machine_type + ).bk_set_id + + bk_module_obj = ClusterMonitorTopo.objects.get( + machine_type=machine_type, + bk_biz_id=self.hosting_biz_id, + cluster_id=cluster_id, + instance_id=instance_id, + bk_set_id=bk_set_id, + ) + # 检查模块下是否还有机器 - # 主机转移到对应的模块下,机器可能对应多个集群,所有主机转移到多个模块下是合理的 - CCApi.transfer_host_module( + CCApi.delete_module( { - "bk_biz_id": env.DBA_APP_BK_BIZ_ID, - "bk_host_id": [int(host["bk_host_id"])], - "bk_module_id": target_module_ids, - "is_increment": False, - }, - use_admin=True, + "bk_biz_id": bk_module_obj.bk_biz_id, + "bk_set_id": bk_module_obj.bk_set_id, + "bk_module_id": bk_module_obj.bk_module_id, + } ) + bk_module_obj.delete(keep_parents=True) + + @transaction.atomic + def delete_cluster_modules(self, db_type, cluster: Cluster): + """ + @param db_type: db组件类型 + @param cluster: 集群对象 + """ + self.delete_cc_module(db_type, cluster.cluster_type, cluster_id=cluster.id) + + @transaction.atomic + def delete_instance_modules(self, db_type: str, ins: StorageInstance, cluster_type: str): + """ + @param db_type: db组件类型 + @param ins: 待删除模块所关联的ins_id + @param cluster_type: 集群类型 + """ + self.delete_cc_module(db_type, cluster_type, instance_id=ins.id) diff --git a/dbm-ui/backend/flow/utils/clb_manage.py b/dbm-ui/backend/flow/utils/clb_manage.py new file mode 100644 index 0000000000..893cae55b2 --- /dev/null +++ b/dbm-ui/backend/flow/utils/clb_manage.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging + +from backend.components import NameServiceApi +from backend.db_meta.models import CLBEntryDetail + +logger = logging.getLogger("flow") + + +def GetClbByIp(clb_ip: str): + clb_info = CLBEntryDetail.objects.filter(clb_ip=clb_ip).values()[0] + return CLBManage(clb_info["clb_id"], clb_info["clb_ip"], clb_info["listener_id"], clb_info["clb_region"]) + + +class CLBManage(object): + """ + 定义clb域名管理类 + 目前服务部署模式:clb属于插件类接入工具,使用后,扩缩容等涉及到变更的操作,需要同步修改clb + """ + + def __init__(self, clb_id: str, clb_ip: str, listener_id: str, clb_region: str): + self.clb_ip = clb_ip + self.clb_id = clb_id + self.listener_id = listener_id + self.clb_region = clb_region + + def del_clb_rs(self, instance_list: list) -> bool: + """ + 删除clb后端的rs记录;适用场景:proxy下架的时候需要删除 + """ + NameServiceApi.clb_deregister_part_target( + { + "region": self.clb_region, + "loadbalancerid": self.clb_id, + "listenerid": self.listener_id, + "ips": instance_list, + }, + raw=True, + ) + return True + + def add_clb_rs(self, instance_list: list) -> bool: + """ + 增加clb后端的rs记录;适用场景:proxy上架的时候需要新增 + """ + NameServiceApi.clb_register_part_target( + { + "region": self.clb_region, + "loadbalancerid": self.clb_id, + "listenerid": self.listener_id, + "ips": instance_list, + }, + raw=True, + ) + return True + + def deregiste_clb(self) -> bool: + """ + 删除clb后端的rs记录,并删除clb; + 适用场景:集群下架 + """ + NameServiceApi.clb_deregister_target_and_del_lb( + { + "region": self.clb_region, + "loadbalancerid": self.clb_id, + "listenerid": self.listener_id, + }, + raw=True, + ) + return True diff --git a/dbm-ui/backend/flow/utils/cloud/cloud_module_operate.py b/dbm-ui/backend/flow/utils/cloud/cloud_module_operate.py index 35594658e4..a846b64ee9 100644 --- a/dbm-ui/backend/flow/utils/cloud/cloud_module_operate.py +++ b/dbm-ui/backend/flow/utils/cloud/cloud_module_operate.py @@ -15,7 +15,8 @@ from backend import env from backend.components import CCApi -from backend.flow.consts import CLOUD_SERVICE_SET_NAME, CloudServiceModuleName +from backend.db_services.ipchooser.constants import DB_MANAGE_SET +from backend.flow.consts import CloudServiceModuleName logger = logging.getLogger("flow") @@ -146,7 +147,7 @@ def transfer_hosts_in_cloud_module( @param bk_host_ids: 转移主机的ID列表 """ - bk_set_id = cls.get_or_create_set(bk_biz_id=bk_biz_id, bk_set_name=CLOUD_SERVICE_SET_NAME) + bk_set_id = cls.get_or_create_set(bk_biz_id=bk_biz_id, bk_set_name=DB_MANAGE_SET) transfer_module_id = cls.get_or_create_module( bk_biz_id=bk_biz_id, bk_set_id=bk_set_id, bk_module_name=bk_module_name ) @@ -168,7 +169,7 @@ def remove_host_from_origin_module(cls, bk_biz_id: int, bk_module_name: str, bk_ @param bk_module_name: 待删除的模块名 @param bk_host_ids: 转移主机的ID列表 """ - bk_set_id = cls.get_or_create_set(bk_biz_id=bk_biz_id, bk_set_name=CLOUD_SERVICE_SET_NAME) + bk_set_id = cls.get_or_create_set(bk_biz_id=bk_biz_id, bk_set_name=DB_MANAGE_SET) host_id__module_ids_map = cls.find_cloud_module_host_relation(bk_biz_id, bk_set_id) origin_module_id = cls.get_or_create_module( bk_biz_id=bk_biz_id, bk_set_id=bk_set_id, bk_module_name=bk_module_name diff --git a/dbm-ui/backend/flow/utils/cloud/script_template/nginx_template.py b/dbm-ui/backend/flow/utils/cloud/script_template/nginx_template.py index 9817776564..b549bcd16f 100644 --- a/dbm-ui/backend/flow/utils/cloud/script_template/nginx_template.py +++ b/dbm-ui/backend/flow/utils/cloud/script_template/nginx_template.py @@ -122,7 +122,7 @@ }" > $path/nginx-portable/conf/nginx.conf; # 开启nginx服务 -touch $path/nginx-portable/logs/nginx.pid +touch $path/nginx-portable/logs/nginx.pid; $path/nginx-portable/nginx-portable start; sleep 5; @@ -141,6 +141,29 @@ echo "nginx-pid: $(cat $path/nginx-portable/logs/nginx.pid)"; is_port_listen_by_pid 80; exit $? + +# 增加对nginx日志文件的定时清理 +clear_script_path="$path/nginx-portable/cron-clear-logs.sh"; +echo -e " +nginx_log_path="$path/nginx-portable/logs" +# 设置最大日志为500MB +max_log_size=$((500 * 1024 * 1024)) + +# 目前主要清理access_log和err_log +access_log_size=\$(stat -c%s "$path/nginx-portable/logs/access.log") +if [ "\$access_log_size" -gt "\$max_log_size" ]; then + echo > $nginx_log_path/access.log; +fi + +err_log_size=\$(stat -c%s "$path/nginx-portable/logs/error.log") +if [ "\$err_log_size" -gt "\$max_log_size" ]; then + echo > $nginx_log_path/err_log_size.log; +fi +" > $clear_script_path; + +chmod +x $clear_script_path; +# 每小时定时探测执行 +(crontab -l ; echo "0 * * * * $clear_script_path") 2>&1 | grep -v "no crontab" | sort | uniq | crontab - """ nginx_stop_template = """ diff --git a/dbm-ui/backend/flow/utils/common_act_dataclass.py b/dbm-ui/backend/flow/utils/common_act_dataclass.py new file mode 100644 index 0000000000..43061ec4fa --- /dev/null +++ b/dbm-ui/backend/flow/utils/common_act_dataclass.py @@ -0,0 +1,25 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 dataclasses import dataclass + +from backend.flow.consts import BACKUP_DEFAULT_OS_USER + + +@dataclass() +class DownloadBackupClientKwargs: + """ + 定义下载并安装backup_client + BACKUP_DEFAULT_OS_USER = mysql + """ + + bk_cloud_id: int + download_host_list: list + backup_os_user: str = BACKUP_DEFAULT_OS_USER diff --git a/dbm-ui/backend/flow/utils/dns_manage.py b/dbm-ui/backend/flow/utils/dns_manage.py index 0b3120c7ed..1be70b7a03 100644 --- a/dbm-ui/backend/flow/utils/dns_manage.py +++ b/dbm-ui/backend/flow/utils/dns_manage.py @@ -11,7 +11,7 @@ import logging from backend.components import CCApi, GcsDnsApi -from backend.db_meta.enums import ClusterEntryType +from backend.db_meta.enums import ClusterEntryRole, ClusterEntryType from backend.db_meta.models import Cluster, ClusterEntry from backend.dbm_init.constants import CC_APP_ABBR_ATTR @@ -68,7 +68,7 @@ def create_domain(self, instance_list: list, add_domain_name: str) -> bool: ) return True - def delete_domain(self, cluster_id: int) -> bool: + def delete_domain(self, cluster_id: int, is_only_delete_slave_domain=False) -> bool: """ 删除域名, 删除域名的方式是传入的集群id(cluster_id) ,清理db-meta注册的域名信息, 适用场景:集群回收 @param cluster_id : 集群id @@ -76,7 +76,12 @@ def delete_domain(self, cluster_id: int) -> bool: # ClusterEntry表查询出所有dns类型的访问方式 cluster = Cluster.objects.get(id=cluster_id) - dns_info = ClusterEntry.objects.filter(cluster=cluster, cluster_entry_type=ClusterEntryType.DNS).all() + if is_only_delete_slave_domain: + dns_info = ClusterEntry.objects.filter( + cluster=cluster, cluster_entry_type=ClusterEntryType.DNS, role=ClusterEntryRole.SLAVE_ENTRY.value + ).all() + else: + dns_info = ClusterEntry.objects.filter(cluster=cluster, cluster_entry_type=ClusterEntryType.DNS).all() for d in dns_info: delete_domain_payload = [{"domain_name": f"{d.entry}."}] logger.info(d.entry) diff --git a/dbm-ui/backend/flow/utils/es/es_db_meta.py b/dbm-ui/backend/flow/utils/es/es_db_meta.py index 0e4fa9e2de..f8f6d12794 100644 --- a/dbm-ui/backend/flow/utils/es/es_db_meta.py +++ b/dbm-ui/backend/flow/utils/es/es_db_meta.py @@ -15,6 +15,7 @@ from backend.db_meta import api from backend.db_meta.enums import InstanceRole, MachineType +from backend.db_services.dbbase.constants import IpSource from backend.flow.consts import ES_DEFAULT_INSTANCE_NUM, ESRoleEnum, LevelInfoEnum logger = logging.getLogger("flow") @@ -61,13 +62,19 @@ def __generate_machine(self) -> list: machines = [] for role in self.role_machine_dict.keys(): for ip in self.__get_node_ips_by_role(role): - machines.append( - { - "ip": ip, - "bk_biz_id": int(self.ticket_data["bk_biz_id"]), - "machine_type": self.role_machine_dict[role], - } - ) + machine = { + "ip": ip, + "bk_biz_id": int(self.ticket_data["bk_biz_id"]), + "machine_type": self.role_machine_dict[role], + } + if self.ticket_data["ip_source"] == IpSource.RESOURCE_POOL: + machine.update( + { + "spec_id": self.ticket_data["resource_spec"][role]["id"], + "spec_config": self.ticket_data["resource_spec"][role], + } + ) + machines.append(machine) return machines def __generate_storage_instance(self) -> list: diff --git a/dbm-ui/backend/flow/utils/es/es_module_operate.py b/dbm-ui/backend/flow/utils/es/es_module_operate.py index 956dda6dc8..fadcf991c6 100644 --- a/dbm-ui/backend/flow/utils/es/es_module_operate.py +++ b/dbm-ui/backend/flow/utils/es/es_module_operate.py @@ -8,107 +8,22 @@ 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 dataclasses import asdict, dataclass +from backend.configuration.constants import DBType +from backend.db_meta.enums import MachineType +from backend.flow.utils.base.cc_topo_operate import CCTopoOperator -from backend import env -from backend.constants import CommonInstanceLabels -from backend.db_meta.api.common import add_service_instance -from backend.db_meta.api.db_module import get_or_create -from backend.db_meta.models import AppCache, Cluster, ClusterMonitorTopo, Machine -from backend.dbm_init.constants import CC_APP_ABBR_ATTR -from backend.flow.utils.cc_manage import CcManage +class EsCCTopoOperator(CCTopoOperator): + db_type = DBType.Es.value -@dataclass() -# 定义注册es服务实例需要的labels标签结构 -class EsInstanceLabels(CommonInstanceLabels): - pass + def init_instances_service(self, machine_type, instances=None): + """ + ES 只需要给集群的 一个 datanode 添加一个服务实例,下发一个采集器即可 + 否则会导致聚合数据重复 + """ + if machine_type != MachineType.ES_DATANODE.value: + # 非 datanode,不创建服务实例 + return - -def create_bk_module_for_cluster_id(cluster_id: int): - """ - # 根据cluster_id,创建对应的域名名称模块 - """ - cluster = Cluster.objects.get(id=cluster_id) - - get_or_create( - bk_biz_id=cluster.bk_biz_id, - cluster_id=cluster.id, - cluster_type=cluster.cluster_type, - cluster_domain=cluster.immute_domain, - creator=cluster.creator, - ) - - -def transfer_host_in_cluster_module( - cluster_id: int, - ip_list: list, - machine_type: str, - bk_cloud_id: int, -): - """ - 根据机器的ip和machine_type的信息,选择对应的bk_set_id, 并将主机转移到对应cluster模块下 - @param cluster_id: 对应的cluster id - @param ip_list: 待转移的ip 列表 - @param machine_type: 机器的类型 - @param bk_cloud_id: 机器列表所在的云区域,兼容后续跨云管理 - """ - transfer_bk_module_ids = [] - transfer_bk_host_ids = [] - for ip in ip_list: - machine = Machine.objects.get(ip=ip, bk_cloud_id=bk_cloud_id) - transfer_bk_host_ids.append(machine.bk_host_id) - transfer_bk_host_ids = list(set(transfer_bk_host_ids)) - - cluster = Cluster.objects.get(id=cluster_id) - bk_module_obj = ClusterMonitorTopo.objects.get( - bk_biz_id=cluster.bk_biz_id, cluster_id=cluster_id, machine_type=machine_type - ) - transfer_bk_module_ids.append(bk_module_obj.bk_module_id) - - # 主机转移到对应的模块下,机器可能对应多个集群,所有主机转移到多个模块下是合理的 - # CCApi.transfer_host_module( - # { - # "bk_biz_id": env.DBA_APP_BK_BIZ_ID, - # "bk_host_id": transfer_bk_host_ids, - # "bk_module_id": transfer_bk_module_ids, - # "is_increment": False, - # }, - # use_admin=True, - # ) - - CcManage.transfer_host_module(transfer_bk_host_ids, transfer_bk_module_ids) - - -def init_instance_service(cluster, ins, bk_module_id, instance_role, func_name): - """ - 添加服务实例 - todo 目前分割符: 不支持,暂时用中划线- - """ - ins_labels = asdict( - EsInstanceLabels( - app=AppCache.get_app_attr(cluster.bk_biz_id, default=cluster.bk_biz_id), - app_id=str(cluster.bk_biz_id), - app_name=AppCache.get_app_attr(cluster.bk_biz_id, CC_APP_ABBR_ATTR, cluster.bk_biz_id), - bk_biz_id=str(cluster.bk_biz_id), - bk_cloud_id=str(cluster.bk_cloud_id), - cluster_domain=cluster.immute_domain, - cluster_name=cluster.name, - cluster_type=cluster.cluster_type, - instance_role=instance_role, - instance_host=ins.machine.ip, - instance=f"{ins.machine.ip}-{ins.port}", - ) - ) - - bk_instance_id = add_service_instance( - bk_module_id=bk_module_id, - bk_host_id=ins.machine.bk_host_id, - listen_ip=ins.machine.ip, - listen_port=ins.port, - func_name=func_name, - labels_dict=ins_labels, - ) - # 保存到数据库 - ins.bk_instance_id = bk_instance_id - ins.save(update_fields=["bk_instance_id"]) + # machine_type==datanode, 只需一个服务实例 + self.init_unique_service(machine_type) diff --git a/dbm-ui/backend/flow/utils/hdfs/__init__.py b/dbm-ui/backend/flow/utils/hdfs/__init__.py new file mode 100644 index 0000000000..aa5085c628 --- /dev/null +++ b/dbm-ui/backend/flow/utils/hdfs/__init__.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" diff --git a/dbm-ui/backend/flow/utils/hdfs/bk_module_operate.py b/dbm-ui/backend/flow/utils/hdfs/bk_module_operate.py deleted file mode 100644 index 3855ecd549..0000000000 --- a/dbm-ui/backend/flow/utils/hdfs/bk_module_operate.py +++ /dev/null @@ -1,160 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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 dataclasses import asdict, dataclass - -from backend import env -from backend.constants import CommonInstanceLabels -from backend.db_meta.api.common import add_service_instance -from backend.db_meta.api.db_module import get_or_create -from backend.db_meta.enums import InstanceRole, MachineType -from backend.db_meta.models import AppCache, Cluster, ClusterMonitorTopo, Machine, StorageInstance -from backend.dbm_init.constants import CC_APP_ABBR_ATTR -from backend.flow.consts import InstanceFuncAliasEnum -from backend.flow.utils.cc_manage import CcManage -from backend.flow.utils.hdfs.consts import ( - DATANODE_DEFAULT_HTTP_PORT, - DATANODE_DEFAULT_PORT, - DATANODE_DEFAULT_RPC_PORT, - NAME_NODE_DEFAULT_PORT, -) - - -@dataclass() -# 定义注册HDFS服务监控实例需要的labels标签结构 -class HdfsInstanceLabels(CommonInstanceLabels): - rpc_port: str - service_rpc_port: str - jmx_http_port: str - instance_name: str - - -def create_bk_module_for_cluster_id(cluster_id: int): - """ - # 根据cluster_id,创建对应的域名名称模块 - """ - cluster = Cluster.objects.get(id=cluster_id) - - get_or_create( - bk_biz_id=cluster.bk_biz_id, - cluster_id=cluster.id, - cluster_type=cluster.cluster_type, - cluster_domain=cluster.immute_domain, - creator=cluster.creator, - ) - - -def transfer_host_in_cluster_module(cluster_id: int, ip_list: list, machine_type: str, kwargs: dict, bk_cloud_id: int): - """ - 根据机器的ip和machine_type的信息,选择对应的bk_set_id, 并将主机转移到对应cluster模块下 - @param cluster_id: cluster id - @param ip_list: 待转移的ip 列表 - @param machine_type: 机器的类型 - @param kwargs: 集群创建信息,包含集群自定义端口号 - @param bk_cloud_id: 机器列表所在的云区域,兼容后续跨云管理 - """ - transfer_bk_module_ids = [] - transfer_bk_host_ids = [] - cluster = Cluster.objects.get(id=cluster_id) - for ip in ip_list: - machine = Machine.objects.get(ip=ip, bk_cloud_id=bk_cloud_id) - transfer_bk_host_ids.append(machine.bk_host_id) - - bk_module_obj = ClusterMonitorTopo.objects.get( - bk_biz_id=cluster.bk_biz_id, - cluster_id=cluster.id, - machine_type=machine_type, - ) - transfer_bk_module_ids.append(bk_module_obj.bk_module_id) - - # 主机转移到对应的模块下,机器可能对应多个集群,所有主机转移到多个模块下是合理的 - # CCApi.transfer_host_module( - # { - # "bk_biz_id": env.DBA_APP_BK_BIZ_ID, - # "bk_host_id": transfer_bk_host_ids, - # "bk_module_id": transfer_bk_module_ids, - # "is_increment": False, - # }, - # use_admin=True, - # ) - - CcManage.transfer_host_module(transfer_bk_host_ids, transfer_bk_module_ids) - - if machine_type == MachineType.HDFS_MASTER.value: - # HDFS master机器筛选NameNode实例主机 添加服务实例 - for ins in StorageInstance.objects.filter( - cluster=cluster, machine__ip__in=ip_list, instance_role=InstanceRole.HDFS_NAME_NODE.value - ): - init_hdfs_instance_service( - cluster=cluster, - ins=ins, - bk_module_id=bk_module_obj.bk_module_id, - instance_role=ins.instance_role, - func_name=InstanceFuncAliasEnum.HDFS_NAME_NODE_FUNC_ALIAS.value, - kwargs=kwargs, - ) - elif machine_type == MachineType.HDFS_DATANODE.value: - for ins in StorageInstance.objects.filter( - cluster=cluster, machine__ip__in=ip_list, instance_role=InstanceRole.HDFS_DATA_NODE.value - ): - init_hdfs_instance_service( - cluster=cluster, - ins=ins, - bk_module_id=bk_module_obj.bk_module_id, - instance_role=ins.instance_role, - func_name=InstanceFuncAliasEnum.HDFS_DATA_NODE_FUNC_ALIAS.value, - kwargs=kwargs, - ) - - -def init_hdfs_instance_service(cluster, ins, bk_module_id, instance_role, func_name, kwargs: dict): - """ - 添加服务实例 - """ - rpc_port = DATANODE_DEFAULT_RPC_PORT - jmx_http_port = DATANODE_DEFAULT_HTTP_PORT - service_rpc_port = DATANODE_DEFAULT_PORT - - if instance_role == InstanceRole.HDFS_NAME_NODE.value: - rpc_port = kwargs["rpc_port"] - jmx_http_port = kwargs["http_port"] - service_rpc_port = NAME_NODE_DEFAULT_PORT - - instance_labels = asdict( - HdfsInstanceLabels( - app=AppCache.get_app_attr(cluster.bk_biz_id, default=cluster.bk_biz_id), - app_id=str(cluster.bk_biz_id), - app_name=AppCache.get_app_attr(cluster.bk_biz_id, CC_APP_ABBR_ATTR, cluster.bk_biz_id), - bk_biz_id=str(cluster.bk_biz_id), - bk_cloud_id=str(cluster.bk_cloud_id), - cluster_domain=cluster.immute_domain, - cluster_name=cluster.name, - cluster_type=cluster.cluster_type, - instance_role=instance_role, - instance_host=ins.machine.ip, - instance=f"{ins.machine.ip}-{ins.port}", - instance_name=ins.name, - rpc_port=str(rpc_port), - jmx_http_port=str(jmx_http_port), - service_rpc_port=str(service_rpc_port), - ) - ) - - bk_instance_id = add_service_instance( - bk_module_id=bk_module_id, - bk_host_id=ins.machine.bk_host_id, - listen_ip=ins.machine.ip, - listen_port=ins.port, - func_name=func_name, - labels_dict=instance_labels, - ) - # 保存到数据库 - ins.bk_instance_id = bk_instance_id - ins.save(update_fields=["bk_instance_id"]) diff --git a/dbm-ui/backend/flow/utils/hdfs/hdfs_db_meta.py b/dbm-ui/backend/flow/utils/hdfs/hdfs_db_meta.py index a76c396f9a..10d6b4224c 100644 --- a/dbm-ui/backend/flow/utils/hdfs/hdfs_db_meta.py +++ b/dbm-ui/backend/flow/utils/hdfs/hdfs_db_meta.py @@ -16,9 +16,10 @@ from backend.db_meta import api from backend.db_meta.enums import InstanceRole, MachineType from backend.db_meta.models import Cluster +from backend.db_services.dbbase.constants import IpSource from backend.flow.consts import HdfsRoleEnum, LevelInfoEnum -from backend.flow.utils.hdfs.bk_module_operate import create_bk_module_for_cluster_id, transfer_host_in_cluster_module from backend.flow.utils.hdfs.consts import DATANODE_DEFAULT_PORT, JOURNAL_NODE_DEFAULT_PORT, ZOOKEEPER_DEFAULT_PORT +from backend.flow.utils.hdfs.hdfs_module_operate import HdfsCCTopoOperator from backend.ticket.constants import TicketType logger = logging.getLogger("flow") @@ -43,6 +44,11 @@ def __init__(self, ticket_data: dict): @param ticket_data : 单据信息 """ self.ticket_data = ticket_data + self.role_machine_dict = { + HdfsRoleEnum.ZooKeeper.value: MachineType.HDFS_MASTER.value, + HdfsRoleEnum.NameNode.value: MachineType.HDFS_MASTER.value, + HdfsRoleEnum.DataNode.value: MachineType.HDFS_DATANODE.value, + } def write(self) -> bool: function_name = self.ticket_data["ticket_type"].lower() @@ -74,10 +80,12 @@ def hdfs_apply(self) -> bool: api.machine.create( bk_cloud_id=self.ticket_data["bk_cloud_id"], machines=machines, creator=self.ticket_data["created_by"] ) - api.storage_instance.create(instances=storage_instances, creator=self.ticket_data["created_by"]) - cluster_id = api.cluster.hdfs.create(**cluster) + storage_objs = api.storage_instance.create( + instances=storage_instances, creator=self.ticket_data["created_by"] + ) + cluster = api.cluster.hdfs.create(**cluster) # 根据已插入dbmeta的Cluster实体创建CC模块,主机转模块 - self.__create_and_transfer_module(cluster_id) + HdfsCCTopoOperator(cluster, self.ticket_data).transfer_instances_to_cluster_module(storage_objs) return True @@ -85,25 +93,23 @@ def hdfs_scale_up(self) -> bool: # 扩容HDFS集群 更新cmdb machines = self.__generate_machine() storage_instances = self.__generate_storage_instance() + cluster_id = self.ticket_data["cluster_id"] with atomic(): # 绑定事务更新cmdb api.machine.create( bk_cloud_id=self.ticket_data["bk_cloud_id"], machines=machines, creator=self.ticket_data["created_by"] ) - api.storage_instance.create(instances=storage_instances, creator=self.ticket_data["created_by"]) + storage_objs = api.storage_instance.create( + instances=storage_instances, creator=self.ticket_data["created_by"] + ) api.cluster.hdfs.scale_up( - cluster_id=self.ticket_data["cluster_id"], + cluster_id=cluster_id, storages=storage_instances, ) # 扩容时仅转移新主机模块 - transfer_host_in_cluster_module( - cluster_id=self.ticket_data["cluster_id"], - ip_list=self.ticket_data["new_dn_ips"], - machine_type=MachineType.HDFS_DATANODE.value, - bk_cloud_id=self.ticket_data["bk_cloud_id"], - kwargs=self.ticket_data, - ) + cluster = Cluster.objects.get(id=cluster_id) + HdfsCCTopoOperator(cluster, self.ticket_data).transfer_instances_to_cluster_module(storage_objs) return True def hdfs_shrink(self) -> bool: @@ -221,18 +227,41 @@ def __generate_machine(self) -> list: bk_biz_id = self.ticket_data["bk_biz_id"] if self.ticket_data["ticket_type"] == TicketType.HDFS_APPLY: - unique_master_ips = {self.ticket_data["nn1_ip"], self.ticket_data["nn2_ip"]} - unique_master_ips.update(self.ticket_data["zk_ips"]) - unique_master_ips.update(self.ticket_data["jn_ips"]) - for master_ip in unique_master_ips: - machines.append( - {"ip": master_ip, "bk_biz_id": bk_biz_id, "machine_type": MachineType.HDFS_MASTER.value} - ) - for dn_ip in self.ticket_data["dn_ips"]: - machines.append({"ip": dn_ip, "bk_biz_id": bk_biz_id, "machine_type": MachineType.HDFS_DATANODE.value}) + nn_ips = {self.ticket_data["nn1_ip"], self.ticket_data["nn2_ip"]} + # 通过遍历 HdfsRoleEnum 区分 + for role, machine_type in self.role_machine_dict.items(): + for node in self.ticket_data["nodes"][role]: + # ZK与NN混用时,跳过machine插入 + if role == HdfsRoleEnum.ZooKeeper.value and node["ip"] in nn_ips: + continue + machine = { + "ip": node["ip"], + "bk_biz_id": bk_biz_id, + "machine_type": machine_type, + } + if self.ticket_data["ip_source"] == IpSource.RESOURCE_POOL: + machine.update( + { + "spec_id": self.ticket_data["resource_spec"][role]["id"], + "spec_config": self.ticket_data["resource_spec"][role], + } + ) + machines.append(machine) elif self.ticket_data["ticket_type"] == TicketType.HDFS_SCALE_UP: for dn_ip in self.ticket_data["new_dn_ips"]: - machines.append({"ip": dn_ip, "bk_biz_id": bk_biz_id, "machine_type": MachineType.HDFS_DATANODE.value}) + machine = { + "ip": dn_ip, + "bk_biz_id": bk_biz_id, + "machine_type": MachineType.HDFS_DATANODE.value, + } + if self.ticket_data["ip_source"] == IpSource.RESOURCE_POOL: + machine.update( + { + "spec_id": self.ticket_data["resource_spec"][HdfsRoleEnum.DataNode.value]["id"], + "spec_config": self.ticket_data["resource_spec"][HdfsRoleEnum.DataNode.value], + } + ) + machines.append(machine) elif self.ticket_data["ticket_type"] == TicketType.HDFS_SHRINK: for dn_ip in self.ticket_data["del_dn_ips"]: machines.append({"ip": dn_ip, "bk_biz_id": bk_biz_id, "machine_type": MachineType.HDFS_DATANODE.value}) @@ -336,42 +365,3 @@ def __generate_replace_machine(self, replace_role: str) -> list: {"ip": zk_ip, "bk_biz_id": bk_biz_id, "machine_type": MachineType.HDFS_MASTER.value} ) return machines - - def __create_and_transfer_module(self, cluster_id: int): - # 创建集群DBMeta后,再调用CC创建集群拓扑 - create_bk_module_for_cluster_id(cluster_id) - - # HDFS主机转移模块、添加对应的服务实例 - cluster = Cluster.objects.get(id=cluster_id) - - master_ips = list( - set( - cluster.storageinstance_set.filter( - instance_role__in=[ - InstanceRole.HDFS_NAME_NODE, - InstanceRole.HDFS_ZOOKEEPER, - InstanceRole.HDFS_JOURNAL_NODE, - ] - ).values_list("machine__ip", flat=True) - ) - ) - transfer_host_in_cluster_module( - cluster_id=cluster_id, - ip_list=master_ips, - machine_type=MachineType.HDFS_MASTER.value, - bk_cloud_id=cluster.bk_cloud_id, - kwargs=self.ticket_data, - ) - - dn_ips = list( - cluster.storageinstance_set.filter(instance_role=InstanceRole.HDFS_DATA_NODE).values_list( - "machine__ip", flat=True - ) - ) - transfer_host_in_cluster_module( - cluster_id=cluster_id, - ip_list=dn_ips, - machine_type=MachineType.HDFS_DATANODE.value, - bk_cloud_id=cluster.bk_cloud_id, - kwargs=self.ticket_data, - ) diff --git a/dbm-ui/backend/flow/utils/hdfs/hdfs_module_operate.py b/dbm-ui/backend/flow/utils/hdfs/hdfs_module_operate.py new file mode 100644 index 0000000000..3ef8d64ebb --- /dev/null +++ b/dbm-ui/backend/flow/utils/hdfs/hdfs_module_operate.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 typing import Union + +from backend.configuration.constants import DBType +from backend.db_meta.enums import InstanceRole, MachineType +from backend.db_meta.models import ProxyInstance, StorageInstance +from backend.flow.utils.base.cc_topo_operate import CCTopoOperator +from backend.flow.utils.hdfs.consts import ( + DATANODE_DEFAULT_HTTP_PORT, + DATANODE_DEFAULT_PORT, + DATANODE_DEFAULT_RPC_PORT, + NAME_NODE_DEFAULT_PORT, +) + + +class HdfsCCTopoOperator(CCTopoOperator): + db_type = DBType.Hdfs.value + + def generate_custom_labels(self, ins: Union[StorageInstance, ProxyInstance]) -> dict: + # 定义注册HDFS服务监控实例需要的labels标签结构 + + rpc_port = DATANODE_DEFAULT_RPC_PORT + jmx_http_port = DATANODE_DEFAULT_HTTP_PORT + service_rpc_port = DATANODE_DEFAULT_PORT + + if ins.instance_role == InstanceRole.HDFS_NAME_NODE.value: + rpc_port = self.ticket_data.get("rpc_port", rpc_port) + jmx_http_port = self.ticket_data.get("http_port", jmx_http_port) + service_rpc_port = NAME_NODE_DEFAULT_PORT + + return { + "instance_name": ins.name, + "rpc_port": str(rpc_port), + "jmx_http_port": str(jmx_http_port), + "service_rpc_port": str(service_rpc_port), + } + + def init_instances_service(self, machine_type, instances=None): + """ + 1. 所有 HDFS_DATANODE 都需要添加服务实例并监控 + 2. HDFS_MASTER 包括 NAME_NODE,ZOOKEEPER,JOURNAL_NODE + 3. 只有 NAME_NODE 需要添加服务实例并监控 + """ + if machine_type == MachineType.HDFS_MASTER: + instances = filter(lambda ins: ins.instance_role == InstanceRole.HDFS_NAME_NODE, instances) + super(HdfsCCTopoOperator, self).init_instances_service(machine_type, instances) diff --git a/dbm-ui/backend/flow/utils/influxdb/__init__.py b/dbm-ui/backend/flow/utils/influxdb/__init__.py new file mode 100644 index 0000000000..aa5085c628 --- /dev/null +++ b/dbm-ui/backend/flow/utils/influxdb/__init__.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" diff --git a/dbm-ui/backend/flow/utils/influxdb/bk_module_operate.py b/dbm-ui/backend/flow/utils/influxdb/bk_module_operate.py deleted file mode 100644 index 7467e11b2d..0000000000 --- a/dbm-ui/backend/flow/utils/influxdb/bk_module_operate.py +++ /dev/null @@ -1,98 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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 dataclasses import asdict, dataclass - -from backend.constants import CommonInstanceLabels -from backend.db_meta.api.common import add_service_instance -from backend.db_meta.api.db_module import get_or_create_influxdb -from backend.db_meta.enums import ClusterType -from backend.db_meta.models import AppCache, ClusterMonitorTopo -from backend.flow.utils.cc_manage import CcManage - - -@dataclass() -class InfluxDBInstanceLabels(CommonInstanceLabels): - instance_port: str - exporter_port: str - db_group: str - - -def create_bk_module(bk_biz_id: int, storages: list, creator: str = ""): - for storage in storages: - get_or_create_influxdb( - bk_biz_id=bk_biz_id, - instance_id=storage.id, - cluster_type=ClusterType.Influxdb.value, - instance_ip=storage.machine.ip, - creator=creator, - ) - - -def transfer_host_in_cluster_module( - bk_biz_id: int, - storages: list, - machine_type: str, - group_name: str, -): - """ - 根据机器的ip和machine_type的信息,选择对应的bk_set_id, 并将主机转移到对应cluster模块下 - @param bk_biz_id: 主机所属业务ID - @param storages: 对应的storages 列表 - @param machine_type: 机器的类型 - @param group_name: 组名称 - """ - for storage in storages: - bk_module_id = ClusterMonitorTopo.objects.get( - bk_biz_id=storage.bk_biz_id, instance_id=storage.id, machine_type=machine_type - ).bk_module_id - - CcManage.transfer_host_module([storage.machine.bk_host_id], [bk_module_id]) - - init_instance_service( - ins=storage, - bk_module_id=bk_module_id, - instance_role=storage.instance_role, - func_name="telegraf", - group_name=group_name, - ) - - -def init_instance_service(ins, bk_module_id, instance_role, func_name, group_name): - ins_labels = asdict( - InfluxDBInstanceLabels( - app=AppCache.get_app_attr(ins.bk_biz_id, default=ins.bk_biz_id), - app_id=str(ins.bk_biz_id), - app_name=AppCache.get_app_attr(ins.bk_biz_id, "db_app_abbr", ins.bk_biz_id), - bk_biz_id=str(ins.bk_biz_id), - bk_cloud_id=str(ins.machine.bk_cloud_id), - cluster_domain=ins.machine.ip, - cluster_name=ins.machine.ip, - cluster_type=ClusterType.Influxdb.value, - instance_role=instance_role, - instance_host=ins.machine.ip, - instance=f"{ins.machine.ip}-9274", - instance_port=str(ins.port), - exporter_port=str(9274), - db_group=group_name, - ) - ) - - bk_instance_id = add_service_instance( - bk_module_id=bk_module_id, - bk_host_id=ins.machine.bk_host_id, - listen_ip=ins.machine.ip, - listen_port=9274, - func_name=func_name, - labels_dict=ins_labels, - ) - # 保存到数据库 - ins.bk_instance_id = bk_instance_id - ins.save(update_fields=["bk_instance_id"]) diff --git a/dbm-ui/backend/flow/utils/influxdb/consts.py b/dbm-ui/backend/flow/utils/influxdb/consts.py new file mode 100644 index 0000000000..a8a450eb89 --- /dev/null +++ b/dbm-ui/backend/flow/utils/influxdb/consts.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +INFLUXDB_EXPORTER_PORT = 9274 diff --git a/dbm-ui/backend/flow/utils/influxdb/influxdb_db_meta.py b/dbm-ui/backend/flow/utils/influxdb/influxdb_db_meta.py index c21b1363a4..8c901f4777 100644 --- a/dbm-ui/backend/flow/utils/influxdb/influxdb_db_meta.py +++ b/dbm-ui/backend/flow/utils/influxdb/influxdb_db_meta.py @@ -16,7 +16,8 @@ from backend.db_meta import api from backend.db_meta.enums import InstanceRole, MachineType from backend.db_meta.models import StorageInstance -from backend.flow.utils.influxdb.bk_module_operate import create_bk_module, transfer_host_in_cluster_module +from backend.db_services.dbbase.constants import IpSource +from backend.flow.utils.influxdb.influxdb_module_operate import InfluxdbCCTopoOperator logger = logging.getLogger("flow") @@ -48,14 +49,19 @@ def __generate_machine(self) -> list: machines = [] for role in self.role_machine_dict.keys(): for node in self.__get_node_ips_by_role(role): - machines.append( - { - "ip": node["ip"], - "bk_biz_id": int(self.ticket_data["bk_biz_id"]), - "machine_type": self.role_machine_dict[role], - "bk_cloud_id": node["bk_cloud_id"], - } - ) + machine = { + "ip": node["ip"], + "bk_biz_id": int(self.ticket_data["bk_biz_id"]), + "machine_type": self.role_machine_dict[role], + } + if self.ticket_data["ip_source"] == IpSource.RESOURCE_POOL: + machine.update( + { + "spec_id": self.ticket_data["resource_spec"][role]["id"], + "spec_config": self.ticket_data["resource_spec"][role], + } + ) + machines.append(machine) return machines def __generate_storage_instance(self, global_port=None) -> list: @@ -79,14 +85,20 @@ def __get_instance_ips(self) -> list: def __generate_new_machine(self) -> list: machines = [] for node in self.ticket_data["new_nodes"]["influxdb"]: - machines.append( - { - "ip": node["ip"], - "bk_biz_id": int(self.ticket_data["bk_biz_id"]), - "machine_type": "influxdb", - "bk_cloud_id": node["bk_cloud_id"], - } - ) + machine = { + "ip": node["ip"], + "bk_biz_id": int(self.ticket_data["bk_biz_id"]), + "machine_type": "influxdb", + "bk_cloud_id": node["bk_cloud_id"], + } + if self.ticket_data["ip_source"] == IpSource.RESOURCE_POOL: + machine.update( + { + "spec_id": self.ticket_data["resource_spec"]["influxdb"]["id"], + "spec_config": self.ticket_data["resource_spec"]["influxdb"], + } + ) + machines.append(machine) return machines def __generate_new_storage_instance(self) -> list: @@ -131,16 +143,8 @@ def influxdb_apply(self) -> bool: # 生成模块 ips = [influxdb["ip"] for influxdb in self.ticket_data["nodes"]["influxdb"]] storages = StorageInstance.find_storage_instance_by_addresses(ips) - create_bk_module( - bk_biz_id=self.ticket_data["bk_biz_id"], - storages=storages, - creator=self.ticket_data["created_by"], - ) - transfer_host_in_cluster_module( - bk_biz_id=self.ticket_data["bk_biz_id"], - storages=storages, - machine_type=MachineType.INFLUXDB.value, - group_name=self.ticket_data["group_name"], + InfluxdbCCTopoOperator(storages).transfer_host_in_cluster_module( + machine_type=MachineType.INFLUXDB.value, group_name=self.ticket_data["group_name"] ) return True @@ -180,14 +184,7 @@ def influxdb_replace(self) -> bool: # 生成模块 ips = [influxdb["ip"] for influxdb in self.ticket_data["new_nodes"]["influxdb"]] storages = StorageInstance.find_storage_instance_by_addresses(ips) - create_bk_module( - bk_biz_id=self.ticket_data["bk_biz_id"], - storages=storages, - creator=self.ticket_data["created_by"], - ) - transfer_host_in_cluster_module( - bk_biz_id=self.ticket_data["bk_biz_id"], - storages=storages, + InfluxdbCCTopoOperator(storages).transfer_host_in_cluster_module( machine_type=MachineType.INFLUXDB.value, group_name=self.ticket_data["group_name"], ) diff --git a/dbm-ui/backend/flow/utils/influxdb/influxdb_module_operate.py b/dbm-ui/backend/flow/utils/influxdb/influxdb_module_operate.py new file mode 100644 index 0000000000..03ad4ca00c --- /dev/null +++ b/dbm-ui/backend/flow/utils/influxdb/influxdb_module_operate.py @@ -0,0 +1,103 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 dataclasses import asdict, dataclass +from typing import List + +from backend.configuration.constants import DBType +from backend.constants import CommonInstanceLabels +from backend.db_meta.enums import ClusterType +from backend.db_meta.models import AppCache, ClusterMonitorTopo, StorageInstance +from backend.exceptions import ValidationError +from backend.flow.consts import InstanceFuncAliasEnum +from backend.flow.utils.cc_manage import CcManage +from backend.flow.utils.influxdb.consts import INFLUXDB_EXPORTER_PORT + + +@dataclass() +class InfluxDBInstanceLabels(CommonInstanceLabels): + instance_port: str + exporter_port: str + db_group: str + + +class InfluxdbCCTopoOperator: + """ + Influxdb 拓扑处理,由于 Influxdb 不以 Cluster 组织结构 + 这里单独处理,后续如也有通过 group 来组织的数据库,可以进一步封装 + """ + + def __init__(self, storages: List[StorageInstance]): + self.storages = storages + + # 仅允许同一业务的实例操作 + bk_biz_ids = [ins.bk_biz_id for ins in self.storages] + if len(bk_biz_ids) != 1: + raise ValidationError("different cluster biz is not supporting") + self.bk_biz_id = bk_biz_ids[0] + self.is_bk_module_created = False + + def create_bk_module(self): + for storage in self.storages: + CcManage(bk_biz_id=storage.bk_biz_id).get_or_create_set_module( + db_type=DBType.InfluxDB.value, + cluster_type=ClusterType.Influxdb.value, + bk_module_name=storage.machine.ip, + instance_id=storage.id, + creator=storage.creator, + ) + self.is_bk_module_created = True + + def transfer_host_in_cluster_module(self, machine_type: str, group_name: str): + """ + 根据机器的ip和machine_type的信息,选择对应的bk_set_id, 并将主机转移到对应cluster模块下 + @param machine_type: 机器的类型 + @param group_name: 组名称 + """ + if not self.is_bk_module_created: + self.create_bk_module() + for ins in self.storages: + bk_module_id = ClusterMonitorTopo.objects.get( + bk_biz_id=ins.bk_biz_id, instance_id=ins.id, machine_type=machine_type + ).bk_module_id + + CcManage(ins.bk_biz_id).transfer_host_module([ins.machine.bk_host_id], [bk_module_id]) + + ins_labels = asdict( + InfluxDBInstanceLabels( + app=AppCache.get_app_attr(ins.bk_biz_id, default=ins.bk_biz_id), + app_id=str(ins.bk_biz_id), + app_name=AppCache.get_app_attr(ins.bk_biz_id, "db_app_abbr", ins.bk_biz_id), + bk_biz_id=str(ins.bk_biz_id), + bk_cloud_id=str(ins.machine.bk_cloud_id), + cluster_domain=ins.machine.ip, + cluster_name=ins.machine.ip, + cluster_type=ClusterType.Influxdb.value, + instance_role=ins.instance_role, + instance_host=ins.machine.ip, + instance=f"{ins.machine.ip}-{INFLUXDB_EXPORTER_PORT}", + instance_port=str(ins.port), + exporter_port=str(INFLUXDB_EXPORTER_PORT), + db_group=group_name, + ) + ) + + bk_instance_id = CcManage(ins.bk_biz_id).add_service_instance( + bk_module_id=bk_module_id, + bk_host_id=ins.machine.bk_host_id, + listen_ip=ins.machine.ip, + listen_port=ins.port, + func_name=InstanceFuncAliasEnum.INFLUXDB_FUNC_ALIAS.value, + bk_process_name=DBType.InfluxDB.value, + labels_dict=ins_labels, + ) + # 保存到数据库 + ins.bk_instance_id = bk_instance_id + ins.save(update_fields=["bk_instance_id"]) diff --git a/dbm-ui/backend/flow/utils/kafka/__init__.py b/dbm-ui/backend/flow/utils/kafka/__init__.py new file mode 100644 index 0000000000..aa5085c628 --- /dev/null +++ b/dbm-ui/backend/flow/utils/kafka/__init__.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" diff --git a/dbm-ui/backend/flow/utils/kafka/bk_module_operate.py b/dbm-ui/backend/flow/utils/kafka/bk_module_operate.py deleted file mode 100644 index d84a388d3e..0000000000 --- a/dbm-ui/backend/flow/utils/kafka/bk_module_operate.py +++ /dev/null @@ -1,131 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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 dataclasses import asdict - -from backend import env -from backend.constants import CommonInstanceLabels -from backend.db_meta.api.common import add_service_instance -from backend.db_meta.api.db_module import get_or_create -from backend.db_meta.enums import MachineType -from backend.db_meta.models import AppCache, Cluster, ClusterMonitorTopo, Machine, StorageInstance -from backend.dbm_init.constants import CC_APP_ABBR_ATTR -from backend.flow.utils.cc_manage import CcManage - - -def create_bk_module_for_cluster_id(cluster_ids: list): - """ - # 根据cluster_id,创建对应的域名名称模块 - """ - for cluster_id in cluster_ids: - cluster = Cluster.objects.get(id=cluster_id) - - get_or_create( - bk_biz_id=cluster.bk_biz_id, - cluster_id=cluster.id, - cluster_type=cluster.cluster_type, - cluster_domain=cluster.immute_domain, - creator=cluster.creator, - ) - - -def transfer_host_in_cluster_module( - cluster_ids: list, - ip_list: list, - machine_type: str, - bk_cloud_id: int, -): - """ - 根据机器的ip和machine_type的信息,选择对应的bk_set_id, 并将主机转移到对应cluster模块下 - @param cluster_ids: 对应的cluster id 列表 - @param ip_list: 待转移的ip 列表 - @param machine_type: 机器的类型 - @param bk_cloud_id: 机器列表所在的云区域,兼容后续跨云管理 - """ - transfer_bk_module_ids = [] - transfer_bk_host_ids = [] - - for ip in ip_list: - machine = Machine.objects.get(ip=ip, bk_cloud_id=bk_cloud_id) - transfer_bk_host_ids.append(machine.bk_host_id) - for cluster_id in cluster_ids: - cluster = Cluster.objects.get(id=cluster_id) - bk_module_obj = ClusterMonitorTopo.objects.get( - bk_biz_id=cluster.bk_biz_id, cluster_id=cluster_id, machine_type=machine_type - ) - transfer_bk_module_ids.append(bk_module_obj.bk_module_id) - - # 主机转移到对应的模块下,机器可能对应多个集群,所有主机转移到多个模块下是合理的 - # CCApi.transfer_host_module( - # { - # "bk_biz_id": env.DBA_APP_BK_BIZ_ID, - # "bk_host_id": transfer_bk_host_ids, - # "bk_module_id": transfer_bk_module_ids, - # "is_increment": False, - # }, - # use_admin=True, - # ) - - CcManage.transfer_host_module(transfer_bk_host_ids, transfer_bk_module_ids) - - for cluster in Cluster.objects.filter(id__in=cluster_ids): - bk_module_id = ClusterMonitorTopo.objects.get( - bk_biz_id=cluster.bk_biz_id, cluster_id=cluster.id, machine_type=machine_type - ).bk_module_id - - if machine_type == MachineType.BROKER.value: - for ins in StorageInstance.objects.filter(cluster=cluster, machine__ip__in=ip_list): - init_instance_service( - cluster=cluster, - ins=ins, - bk_module_id=bk_module_id, - instance_role=ins.instance_role, - func_name="java", - ) - elif machine_type == MachineType.ZOOKEEPER.value: - # 消费延迟 - ins = StorageInstance.objects.filter(cluster=cluster, machine__ip__in=ip_list)[0] - init_instance_service( - cluster=cluster, - ins=ins, - bk_module_id=bk_module_id, - instance_role=ins.instance_role, - func_name="java", - ) - - -def init_instance_service(cluster, ins, bk_module_id, instance_role, func_name): - ins_labels = asdict( - CommonInstanceLabels( - app=AppCache.get_app_attr(cluster.bk_biz_id, default=cluster.bk_biz_id), - app_id=str(cluster.bk_biz_id), - app_name=AppCache.get_app_attr(cluster.bk_biz_id, CC_APP_ABBR_ATTR, cluster.bk_biz_id), - bk_biz_id=str(cluster.bk_biz_id), - bk_cloud_id=str(cluster.bk_cloud_id), - cluster_domain=cluster.immute_domain, - cluster_name=cluster.name, - cluster_type=cluster.cluster_type, - instance_role=instance_role, - instance_host=ins.machine.ip, - instance=f"{ins.machine.ip}-{ins.port}", - ) - ) - - bk_instance_id = add_service_instance( - bk_module_id=bk_module_id, - bk_host_id=ins.machine.bk_host_id, - listen_ip=ins.machine.ip, - listen_port=ins.port, - func_name=func_name, - labels_dict=ins_labels, - ) - # 保存到数据库 - ins.bk_instance_id = bk_instance_id - ins.save(update_fields=["bk_instance_id"]) diff --git a/dbm-ui/backend/flow/utils/kafka/kafka_act_playload.py b/dbm-ui/backend/flow/utils/kafka/kafka_act_playload.py index a12f3db5ce..c734bcfaa4 100644 --- a/dbm-ui/backend/flow/utils/kafka/kafka_act_playload.py +++ b/dbm-ui/backend/flow/utils/kafka/kafka_act_playload.py @@ -97,6 +97,8 @@ def get_payload(self, action, host) -> dict: if not self.ticket_data.get("adminUser"): self.ticket_data["adminUser"] = self.kafka_config["adminUser"] self.ticket_data["adminPassword"] = self.kafka_config["adminPassword"] + if not self.ticket_data.get("no_security"): + self.ticket_data["no_security"] = 0 return { "db_type": DBActuatorTypeEnum.Kafka.value, "action": action, @@ -116,6 +118,7 @@ def get_payload(self, action, host) -> dict: "cluster_name": self.ticket_data["cluster_name"], "username": self.ticket_data["adminUser"], "password": self.ticket_data["adminPassword"], + "no_security": self.ticket_data["no_security"], }, }, } diff --git a/dbm-ui/backend/flow/utils/kafka/kafka_db_meta.py b/dbm-ui/backend/flow/utils/kafka/kafka_db_meta.py index 03f1e60ef1..dace0aa6eb 100644 --- a/dbm-ui/backend/flow/utils/kafka/kafka_db_meta.py +++ b/dbm-ui/backend/flow/utils/kafka/kafka_db_meta.py @@ -15,8 +15,10 @@ from backend.db_meta import api from backend.db_meta.enums import InstanceRole, MachineType -from backend.flow.consts import DEFAULT_DB_MODULE_ID -from backend.flow.utils.kafka.bk_module_operate import create_bk_module_for_cluster_id, transfer_host_in_cluster_module +from backend.db_meta.models import Cluster +from backend.db_services.dbbase.constants import IpSource +from backend.flow.consts import DEFAULT_DB_MODULE_ID, KafkaRoleEnum +from backend.flow.utils.kafka.kafka_module_operate import KafkaCCTopoOperator logger = logging.getLogger("flow") @@ -59,14 +61,20 @@ def __generate_machine(self) -> list: machines = [] for role in self.role_machine_dict.keys(): for node in self.__get_node_by_role(role): - machines.append( - { - "ip": node["ip"], - "bk_biz_id": int(self.ticket_data["bk_biz_id"]), - "machine_type": self.role_machine_dict[role], - "bk_cloud_id": node["bk_cloud_id"], - } - ) + machine = { + "ip": node["ip"], + "bk_biz_id": int(self.ticket_data["bk_biz_id"]), + "machine_type": self.role_machine_dict[role], + "bk_cloud_id": node["bk_cloud_id"], + } + if self.ticket_data["ip_source"] == IpSource.RESOURCE_POOL: + machine.update( + { + "spec_id": self.ticket_data["resource_spec"][role]["id"], + "spec_config": self.ticket_data["resource_spec"][role], + } + ) + machines.append(machine) return machines def __generate_storage_instance(self) -> list: @@ -88,14 +96,20 @@ def __generate_new(self) -> (list, list): instances = [] if self.ticket_data["new_nodes"].get("zookeeper"): for node in self.ticket_data["new_nodes"]["zookeeper"]: - machines.append( - { - "ip": node["ip"], - "bk_biz_id": int(self.ticket_data["bk_biz_id"]), - "machine_type": "zookeeper", - "bk_cloud_id": node["bk_cloud_id"], - } - ) + machine = { + "ip": node["ip"], + "bk_biz_id": int(self.ticket_data["bk_biz_id"]), + "machine_type": "zookeeper", + "bk_cloud_id": node["bk_cloud_id"], + } + if self.ticket_data["ip_source"] == IpSource.RESOURCE_POOL: + machine.update( + { + "spec_id": self.ticket_data["resource_spec"][KafkaRoleEnum.ZOOKEEPER.value]["id"], + "spec_config": self.ticket_data["resource_spec"][KafkaRoleEnum.ZOOKEEPER.value], + } + ) + machines.append(machine) instances.append( { "ip": node["ip"], @@ -106,14 +120,20 @@ def __generate_new(self) -> (list, list): ) if self.ticket_data["new_nodes"].get("broker"): for node in self.ticket_data["new_nodes"]["broker"]: - machines.append( - { - "ip": node["ip"], - "bk_biz_id": int(self.ticket_data["bk_biz_id"]), - "machine_type": "broker", - "bk_cloud_id": node["bk_cloud_id"], - } - ) + machine = { + "ip": node["ip"], + "bk_biz_id": int(self.ticket_data["bk_biz_id"]), + "machine_type": "broker", + "bk_cloud_id": node["bk_cloud_id"], + } + if self.ticket_data["ip_source"] == IpSource.RESOURCE_POOL: + machine.update( + { + "spec_id": self.ticket_data["resource_spec"][KafkaRoleEnum.BROKER.value]["id"], + "spec_config": self.ticket_data["resource_spec"][KafkaRoleEnum.BROKER.value], + } + ) + machines.append(machine) instances.append( { "ip": node["ip"], @@ -183,26 +203,12 @@ def kafka_apply(self) -> bool: machines=machines, creator=self.ticket_data["created_by"], ) - api.storage_instance.create(instances=storage_instances, creator=self.ticket_data["created_by"]) - new_cluster_id = api.cluster.kafka.create(**cluster) - # 生成模块 - create_bk_module_for_cluster_id(cluster_ids=[new_cluster_id]) - - broker_ip_list = self.__get_node_ips_by_role("broker") - transfer_host_in_cluster_module( - cluster_ids=[new_cluster_id], - ip_list=broker_ip_list, - machine_type=MachineType.BROKER.value, - bk_cloud_id=bk_cloud_id, - ) - - zookeeper_ip_list = self.__get_node_ips_by_role("zookeeper") - transfer_host_in_cluster_module( - cluster_ids=[new_cluster_id], - ip_list=zookeeper_ip_list, - machine_type=MachineType.ZOOKEEPER.value, - bk_cloud_id=bk_cloud_id, + storage_objs = api.storage_instance.create( + instances=storage_instances, creator=self.ticket_data["created_by"] ) + new_cluster = api.cluster.kafka.create(**cluster) + # 生成模块、转移主机、添加服务实例 + KafkaCCTopoOperator(new_cluster).transfer_instances_to_cluster_module(storage_objs) return True @@ -211,6 +217,7 @@ def kafka_scale_up(self) -> bool: machines = self.__generate_machine() storage_instances = self.__generate_storage_instance() bk_cloud_id = machines[0]["bk_cloud_id"] + cluster = Cluster.objects.get(id=self.ticket_data["cluster_id"]) with atomic(): # 绑定事务更新cmdb api.machine.create( @@ -218,15 +225,11 @@ def kafka_scale_up(self) -> bool: machines=machines, creator=self.ticket_data["created_by"], ) - api.storage_instance.create(instances=storage_instances, creator=self.ticket_data["created_by"]) - api.cluster.kafka.scale_up(cluster_id=self.ticket_data["cluster_id"], storages=storage_instances) - broker_ip_list = self.__get_node_ips_by_role("broker") - transfer_host_in_cluster_module( - cluster_ids=[self.ticket_data["cluster_id"]], - ip_list=broker_ip_list, - machine_type=MachineType.BROKER.value, - bk_cloud_id=bk_cloud_id, + storage_objs = api.storage_instance.create( + instances=storage_instances, creator=self.ticket_data["created_by"] ) + api.cluster.kafka.scale_up(cluster_id=self.ticket_data["cluster_id"], storages=storage_instances) + KafkaCCTopoOperator(cluster).transfer_instances_to_cluster_module(storage_objs) return True @@ -259,6 +262,7 @@ def kafka_replace(self) -> bool: old_storage_instances = self.__generate_old_storage_instance() new_broker_ip_list = self.__get_new_broker_ips() bk_cloud_id = new_machines[0]["bk_cloud_id"] + cluster = Cluster.objects.get(id=self.ticket_data["cluster_id"]) with atomic(): # 绑定事务更新cmdb api.machine.create( @@ -266,24 +270,13 @@ def kafka_replace(self) -> bool: machines=new_machines, creator=self.ticket_data["created_by"], ) - api.storage_instance.create(instances=new_storage_instances, creator=self.ticket_data["created_by"]) + storage_objs = api.storage_instance.create( + instances=new_storage_instances, creator=self.ticket_data["created_by"] + ) api.cluster.kafka.replace( cluster_id=self.ticket_data["cluster_id"], old_storages=old_storage_instances, new_storages=new_storage_instances, ) - if self.ticket_data.get("zk_ip"): - transfer_host_in_cluster_module( - cluster_ids=[self.ticket_data["cluster_id"]], - ip_list=[self.ticket_data["zk_ip"]], - machine_type=MachineType.ZOOKEEPER.value, - bk_cloud_id=bk_cloud_id, - ) - if new_broker_ip_list: - transfer_host_in_cluster_module( - cluster_ids=[self.ticket_data["cluster_id"]], - ip_list=new_broker_ip_list, - machine_type=MachineType.BROKER.value, - bk_cloud_id=bk_cloud_id, - ) + KafkaCCTopoOperator(cluster).transfer_instances_to_cluster_module(storage_objs) return True diff --git a/dbm-ui/backend/flow/utils/kafka/kafka_module_operate.py b/dbm-ui/backend/flow/utils/kafka/kafka_module_operate.py new file mode 100644 index 0000000000..af27b237fa --- /dev/null +++ b/dbm-ui/backend/flow/utils/kafka/kafka_module_operate.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 backend.configuration.constants import DBType +from backend.db_meta.enums import MachineType +from backend.flow.utils.base.cc_topo_operate import CCTopoOperator + + +class KafkaCCTopoOperator(CCTopoOperator): + db_type = DBType.Kafka.value + + def init_instances_service(self, machine_type, instances=None): + """ + KAFKA 的 zk 只需要一个采集器,采集消费延迟的数据 + """ + if machine_type != MachineType.ZOOKEEPER.value: + # 非 zk 节点,使用默认方法创建服务实例 + super(KafkaCCTopoOperator, self).init_instances_service(machine_type, instances) + return + + # machine_type==zk, 消费延迟,仅需一个服务实例 + self.init_unique_service(machine_type) diff --git a/dbm-ui/backend/flow/utils/mongodb/__init__.py b/dbm-ui/backend/flow/utils/mongodb/__init__.py new file mode 100644 index 0000000000..aa5085c628 --- /dev/null +++ b/dbm-ui/backend/flow/utils/mongodb/__init__.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" diff --git a/dbm-ui/backend/flow/utils/mongodb/mongodb_module_operate.py b/dbm-ui/backend/flow/utils/mongodb/mongodb_module_operate.py new file mode 100644 index 0000000000..83ebb8af44 --- /dev/null +++ b/dbm-ui/backend/flow/utils/mongodb/mongodb_module_operate.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 backend.configuration.constants import DBType +from backend.flow.utils.base.cc_topo_operate import CCTopoOperator + + +class MongoDBCCTopoOperator(CCTopoOperator): + db_type = DBType.MongoDB.value diff --git a/dbm-ui/backend/flow/utils/mysql/__init__.py b/dbm-ui/backend/flow/utils/mysql/__init__.py new file mode 100644 index 0000000000..aa5085c628 --- /dev/null +++ b/dbm-ui/backend/flow/utils/mysql/__init__.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" diff --git a/dbm-ui/backend/flow/utils/mysql/bk_module_operate.py b/dbm-ui/backend/flow/utils/mysql/bk_module_operate.py deleted file mode 100644 index d88671ee4c..0000000000 --- a/dbm-ui/backend/flow/utils/mysql/bk_module_operate.py +++ /dev/null @@ -1,151 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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 dataclasses import asdict, dataclass - -from backend import env -from backend.constants import CommonInstanceLabels -from backend.db_meta.api.common import add_service_instance -from backend.db_meta.api.db_module import get_or_create -from backend.db_meta.enums import MachineType -from backend.db_meta.models import AppCache, Cluster, ClusterMonitorTopo, Machine, ProxyInstance, StorageInstance -from backend.dbm_init.constants import CC_APP_ABBR_ATTR -from backend.flow.consts import InstanceFuncAliasEnum -from backend.flow.utils.cc_manage import CcManage -from backend.ticket.constants import InstanceType - - -@dataclass() -# 定义注册mysql/proxy/spider服务实例需要的labels标签结构 -class MySQLInstanceLabels(CommonInstanceLabels): - exporter_conf_path: str - - -def create_bk_module_for_cluster_id(cluster_ids: list): - """ - # 根据cluster_id,创建对应的域名名称模块 - """ - for cluster_id in cluster_ids: - cluster = Cluster.objects.get(id=cluster_id) - - get_or_create( - bk_biz_id=cluster.bk_biz_id, - cluster_id=cluster.id, - cluster_type=cluster.cluster_type, - cluster_domain=cluster.immute_domain, - creator=cluster.creator, - ) - - -def transfer_host_in_cluster_module(cluster_ids: list, ip_list: list, machine_type: str, bk_cloud_id: int): - """ - 根据机器的ip和machine_type的信息,选择对应的bk_set_id, 并将主机转移到对应cluster模块下 - @param cluster_ids: 对应的cluster id 列表 - @param ip_list: 待转移的ip 列表 - @param machine_type: 机器的类型 - @param bk_cloud_id: 机器列表所在的云区域,兼容后续跨云管理 - """ - transfer_bk_module_ids = [] - transfer_bk_host_ids = [] - for ip in ip_list: - machine = Machine.objects.get(ip=ip, bk_cloud_id=bk_cloud_id) - transfer_bk_host_ids.append(machine.bk_host_id) - - for cluster_id in cluster_ids: - cluster = Cluster.objects.get(id=cluster_id) - bk_module_obj = ClusterMonitorTopo.objects.get( - bk_biz_id=cluster.bk_biz_id, cluster_id=cluster_id, machine_type=machine_type - ) - transfer_bk_module_ids.append(bk_module_obj.bk_module_id) - - # 主机转移到对应的模块下,机器可能对应多个集群,所有主机转移到多个模块下是合理的 - # CCApi.transfer_host_module( - # { - # "bk_biz_id": env.DBA_APP_BK_BIZ_ID, - # "bk_host_id": transfer_bk_host_ids, - # "bk_module_id": transfer_bk_module_ids, - # "is_increment": False, - # }, - # use_admin=True, - # ) - - CcManage.transfer_host_module(transfer_bk_host_ids, transfer_bk_module_ids) - - for cluster in Cluster.objects.filter(id__in=cluster_ids): - bk_module_id = ClusterMonitorTopo.objects.get( - bk_biz_id=cluster.bk_biz_id, cluster_id=cluster.id, machine_type=machine_type - ).bk_module_id - - if machine_type == MachineType.PROXY.value: - # proxy机器添加服务实例 - # proxy_instance表没有所谓的实例角色,则用instance_type枚举来代替 - for ins in ProxyInstance.objects.filter(cluster=cluster, machine__ip__in=ip_list): - init_instance_service( - cluster=cluster, - ins=ins, - bk_module_id=bk_module_id, - instance_role=InstanceType.PROXY.value, - func_name=InstanceFuncAliasEnum.MYSQL_PROXY_FUNC_ALIAS.value, - ) - # 增加对spider机器注册服务实例的处理逻辑 - elif machine_type == MachineType.SPIDER.value: - for ins in ProxyInstance.objects.filter(cluster=cluster, machine__ip__in=ip_list): - init_instance_service( - cluster=cluster, - ins=ins, - bk_module_id=bk_module_id, - instance_role=ins.tendbclusterspiderext.spider_role, - func_name=InstanceFuncAliasEnum.MYSQL_FUNC_ALIAS.value, - ) - else: - # mysql机器添加对应实例 - for ins in StorageInstance.objects.filter(cluster=cluster, machine__ip__in=ip_list): - init_instance_service( - cluster=cluster, - ins=ins, - bk_module_id=bk_module_id, - instance_role=ins.instance_role, - func_name=InstanceFuncAliasEnum.MYSQL_FUNC_ALIAS.value, - ) - - -def init_instance_service(cluster, ins, bk_module_id, instance_role, func_name): - """ - 添加服务实例 - todo 目前分割符: 不支持,暂时用中划线- - """ - ins_labels = asdict( - MySQLInstanceLabels( - app=AppCache.get_app_attr(cluster.bk_biz_id, default=cluster.bk_biz_id), - app_id=str(cluster.bk_biz_id), - app_name=AppCache.get_app_attr(cluster.bk_biz_id, CC_APP_ABBR_ATTR, cluster.bk_biz_id), - bk_biz_id=str(cluster.bk_biz_id), - bk_cloud_id=str(cluster.bk_cloud_id), - cluster_domain=cluster.immute_domain, - cluster_name=cluster.name, - cluster_type=cluster.cluster_type, - instance_role=instance_role, - instance_host=ins.machine.ip, - instance=f"{ins.machine.ip}-{ins.port}", - exporter_conf_path=f"exporter_{ins.port}.cnf", - ) - ) - - bk_instance_id = add_service_instance( - bk_module_id=bk_module_id, - bk_host_id=ins.machine.bk_host_id, - listen_ip=ins.machine.ip, - listen_port=ins.port, - func_name=func_name, - labels_dict=ins_labels, - ) - # 保存到数据库 - ins.bk_instance_id = bk_instance_id - ins.save(update_fields=["bk_instance_id"]) diff --git a/dbm-ui/backend/flow/utils/mysql/check_client_connections.py b/dbm-ui/backend/flow/utils/mysql/check_client_connections.py new file mode 100644 index 0000000000..7acc3cb0ee --- /dev/null +++ b/dbm-ui/backend/flow/utils/mysql/check_client_connections.py @@ -0,0 +1,59 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 backend.components import DBConfigApi, DRSApi +from backend.components.dbconfig.constants import FormatType, LevelName +from backend.flow.consts import MYSQL_SYS_USER, ConfigTypeEnum, NameSpaceEnum +from backend.flow.utils.mysql.get_mysql_sys_user import get_mysql_sys_users + + +def check_client_connection(bk_cloud_id: int, instances: list, is_filter_sleep: bool = False): + """ + 通过drs接口检测实例是否存在用户线程 + @param bk_cloud_id: 操作的云区域 + @param instances: 需要判断的实例列表,每个元素的str:{ip:port} + @param is_filter_sleep: 本次检测是否过滤sleep状态的连接,默认不过滤 + """ + + # 获取内置账号名称 + data = DBConfigApi.query_conf_item( + { + "bk_biz_id": "0", + "level_name": LevelName.PLAT, + "level_value": "0", + "conf_file": "mysql#user", + "conf_type": ConfigTypeEnum.InitUser, + "namespace": NameSpaceEnum.TenDB.value, + "format": FormatType.MAP, + } + )["content"] + admin_user_name_list = [data["admin_user"], data["backup_user"], data["monitor_user"], data["repl_user"]] + + # 对于tendb-cluster集群的实例,这里不考虑过滤内置账号的session,因为执行ddl时候,实例会存在内置账号session + # 过滤会有风险 + users = ",".join( + ["'" + str(x) + "'" for x in MYSQL_SYS_USER + admin_user_name_list + get_mysql_sys_users(bk_cloud_id)] + ) + if is_filter_sleep: + check_sql = f"select * from information_schema.processlist where command != 'Sleep' and User not in ({users})" + else: + check_sql = f"select * from information_schema.processlist where User not in ({users})" + + res = DRSApi.rpc( + { + "addresses": instances, + "cmds": [check_sql], + "force": False, + "bk_cloud_id": bk_cloud_id, + } + ) + + return res diff --git a/dbm-ui/backend/flow/utils/mysql/get_mysql_sys_user.py b/dbm-ui/backend/flow/utils/mysql/get_mysql_sys_user.py new file mode 100644 index 0000000000..813ad7e66c --- /dev/null +++ b/dbm-ui/backend/flow/utils/mysql/get_mysql_sys_user.py @@ -0,0 +1,34 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 backend import env +from backend.core.encrypt.constants import RSAConfigType +from backend.core.encrypt.handlers import RSAHandler +from backend.db_proxy.constants import ExtensionType +from backend.db_proxy.models import DBExtension + + +def get_mysql_sys_users(bk_cloud_id) -> list: + """ + 增加方法:收集SaaS内mysql/spider的系统账号列表,作为固定参数传入待执行Actuator指令 + """ + sys_users_map = { + ExtensionType.DRS: env.DRS_USERNAME, + ExtensionType.DBHA: env.DBHA_USERNAME, + } + sys_users = [] + for key, value in sys_users_map.items(): + if value: + sys_users.append(value) + else: + rsa = RSAHandler.get_or_generate_rsa_in_db(RSAConfigType.get_rsa_cloud_name(bk_cloud_id)) + info = DBExtension.get_latest_extension(bk_cloud_id=bk_cloud_id, extension_type=key) + sys_users.append(RSAHandler.decrypt_password(rsa.rsa_private_key.content, info.details["user"])) + + return sys_users diff --git a/dbm-ui/backend/flow/utils/mysql/mysql_act_dataclass.py b/dbm-ui/backend/flow/utils/mysql/mysql_act_dataclass.py index 51866b428c..1c59e4a296 100644 --- a/dbm-ui/backend/flow/utils/mysql/mysql_act_dataclass.py +++ b/dbm-ui/backend/flow/utils/mysql/mysql_act_dataclass.py @@ -12,7 +12,9 @@ from dataclasses import dataclass, field from typing import Any, Optional +from backend.configuration.constants import DBType from backend.db_meta.enums import ClusterType +from backend.env import BACKUP_DOWNLOAD_USER from backend.flow.consts import DBA_ROOT_USER, DnsOpType, MediumFileTypeEnum from backend.flow.utils.mysql.mysql_act_playload import MysqlActPayload @@ -64,6 +66,7 @@ class P2PFileBaseKwargs: bk_cloud_id: int # 对应的云区域ID file_list: list # 需要传送的源文件列表 source_ip_list: list # 源文件的机器IP列表 + run_as_system_user: str = None # 表示执行job的api的操作用户, None 默认是用root用户 file_type: Optional[MediumFileTypeEnum] = MediumFileTypeEnum.Server.value file_target_path: str = None # 表示下载到目标机器的路径,如果传None,默认则传/data/install @@ -104,6 +107,7 @@ class DownloadMediaBaseKwargs: bk_cloud_id: int # 对应的云区域ID file_list: list # 需要传送的源文件列表 + run_as_system_user: str = None # 表示执行job的api的操作用户, None 默认是用root用户 file_type: Optional[MediumFileTypeEnum] = MediumFileTypeEnum.Repo.value file_target_path: str = None # 表示下载到目标机器的路径,如果传None,默认则传/data/install @@ -177,6 +181,8 @@ class DeleteClusterDnsKwargs: bk_cloud_id: int # 操作的云区域id delete_cluster_id: int # 操作的集群,回收集群时需要 dns_op_type: Optional[DnsOpType] = DnsOpType.CLUSTER_DELETE.value # 操作的域名方式 + # 是否仅删除从域名 + is_only_delete_slave_domain: bool = False @dataclass() @@ -212,7 +218,7 @@ class InstanceUserCloneKwargs: """ # 表示克隆的信息,格式样例:[{"source": "1.1.1.1", "target": "2.2.2.2"}..] - clone_data: list = field(default_factory=list) + clone_data: list = field(default_factory=list) # 克隆数据 @dataclass() @@ -322,11 +328,11 @@ class DelServiceInstKwargs: 删除集群内服务实例的专属私有变量 """ - cluster_id: str # 对应的cluster_id + cluster_id: int # 对应的cluster_id del_instance_list: list # 删除对应的实例信息 -@dataclass +@dataclass() class DownloadBackupFileKwargs: """ 定义下载mysql备份文件的变量结构体 @@ -335,6 +341,37 @@ class DownloadBackupFileKwargs: bk_cloud_id: int task_ids: list dest_ip: str - login_user: str desc_dir: str reason: str + login_user: str = BACKUP_DOWNLOAD_USER + cluster: dict = None + + +# +# @dataclass() +# class RollbackDownloadBinlogKwargs(DownloadBackupFileKwargs): +# """ +# 定义下载mysql定点回档 本地+时间 模式的下载binlog +# """ +# +# cluster: dict + + +@dataclass +class CheckClientConnKwargs: + """ + 定义检测客户端连接的私有变量结构体 + """ + + bk_cloud_id: int + check_instances: list + + +@dataclass +class VerifyChecksumKwargs: + """ + 定义检测checksum结果的私有变量结构体 + """ + + bk_cloud_id: int + checksum_instance_tuples: list diff --git a/dbm-ui/backend/flow/utils/mysql/mysql_act_playload.py b/dbm-ui/backend/flow/utils/mysql/mysql_act_playload.py index 0ebcdb66c8..3677890e3c 100644 --- a/dbm-ui/backend/flow/utils/mysql/mysql_act_playload.py +++ b/dbm-ui/backend/flow/utils/mysql/mysql_act_playload.py @@ -11,8 +11,7 @@ import copy import logging import os -import re -from typing import Any +from typing import Any, List from django.conf import settings from django.utils.translation import ugettext as _ @@ -21,30 +20,33 @@ from backend.components import DBConfigApi from backend.components.dbconfig.constants import FormatType, LevelName, ReqType from backend.configuration.models import SystemSettings -from backend.constants import IP_RE_PATTERN from backend.core import consts from backend.core.consts import BK_PKG_INSTALL_PATH from backend.core.encrypt.constants import RSAConfigType from backend.core.encrypt.handlers import RSAHandler -from backend.db_meta.enums import InstanceInnerRole, MachineType +from backend.db_meta.enums import ClusterType, InstanceInnerRole, MachineType from backend.db_meta.exceptions import DBMetaException -from backend.db_meta.models import Cluster, Machine, ProxyInstance, StorageInstance +from backend.db_meta.models import Cluster, Machine, ProxyInstance, StorageInstance, StorageInstanceTuple from backend.db_package.models import Package -from backend.db_proxy.constants import ExtensionType -from backend.db_proxy.models import DBCloudProxy, DBExtension +from backend.db_proxy.models import DBCloudProxy from backend.db_services.mysql.sql_import.constants import BKREPO_SQLFILE_PATH from backend.flow.consts import ( CHECKSUM_DB, SYSTEM_DBS, + TDBCTL_USER, CHECKSUM_TABlE_PREFIX, ConfigTypeEnum, DataSyncSource, DBActuatorActionEnum, DBActuatorTypeEnum, MediumEnum, + MysqlChangeMasterType, NameSpaceEnum, + RollbackType, ) from backend.flow.engine.bamboo.scene.common.get_real_version import get_mysql_real_version, get_spider_real_version +from backend.flow.utils.base.payload_handler import PayloadHandler +from backend.flow.utils.tbinlogdumper.tbinlogdumper_act_payload import TBinlogDumperActPayload from backend.ticket.constants import TicketType apply_list = [TicketType.MYSQL_SINGLE_APPLY.value, TicketType.MYSQL_HA_APPLY.value] @@ -52,42 +54,13 @@ logger = logging.getLogger("flow") -class MysqlActPayload(object): +class MysqlActPayload(PayloadHandler, TBinlogDumperActPayload): """ 定义mysql不同执行类型,拼接不同的payload参数,对应不同的dict结构体。 + todo 后续要优化这块代码,因为类太大,建议按照场景拆分,然后继承,例如TBinlogDumperActPayload继承TBinlogDumper相关的方法 + todo 比如spider场景拆出来、公共部分的拆出来等 """ - def __init__(self, bk_cloud_id: int, ticket_data: dict, cluster: dict, cluster_type: str = None): - """ - @param bk_cloud_id 操作的云区域 - @param ticket_data 单据信息 - @param cluster 需要操作的集群信息 - @param cluster_type 表示操作的集群类型,会决定到db_config获取配置的空间 - """ - self.init_mysql_config = {} - self.bk_cloud_id = bk_cloud_id - self.ticket_data = ticket_data - self.cluster = cluster - self.cluster_type = cluster_type - self.mysql_pkg = None - self.proxy_pkg = None - self.checksum_pkg = None - self.mysql_crond_pkg = None - self.mysql_monitor_pkg = None - self.account = self.__get_mysql_account() - - # self.db_module_id = ( - # self.ticket_data["module"] if self.ticket_data.get("module") else self.cluster.get("db_module_id") - # ) - # 尝试获取db_module_id , 有些单据不需要db_module_id,则最终给0 - # todo 后面可能优化这个问题 - if self.ticket_data.get("module"): - self.db_module_id = self.ticket_data["module"] - elif self.cluster and self.cluster.get("db_module_id"): - self.db_module_id = self.cluster["db_module_id"] - else: - self.db_module_id = 0 - @staticmethod def __get_mysql_account() -> Any: """ @@ -106,56 +79,6 @@ def __get_mysql_account() -> Any: ) return data["content"] - def __get_super_account_bypass(self): - """ - 旁路逻辑:获取环境变量中的access_hosts, 用户名和密码 - """ - access_hosts = env.TEST_ACCESS_HOSTS or re.compile(IP_RE_PATTERN).findall(env.DRS_APIGW_DOMAIN) - drs_account_data = { - "access_hosts": access_hosts, - "user": env.DRS_USERNAME, - "pwd": env.DRS_PASSWORD, - } - - access_hosts = env.TEST_ACCESS_HOSTS or re.compile(IP_RE_PATTERN).findall(env.DBHA_APIGW_DOMAIN_LIST) - dbha_account_data = { - "access_hosts": access_hosts, - "user": env.DBHA_USERNAME, - "pwd": env.DBHA_PASSWORD, - } - - return drs_account_data, dbha_account_data - - def __get_super_account(self): - """ - 获取mysql机器系统管理账号信息 - """ - - if env.DRS_USERNAME and env.DBHA_USERNAME: - return self.__get_super_account_bypass() - - rsa = RSAHandler.get_or_generate_rsa_in_db(RSAConfigType.get_rsa_cloud_name(self.bk_cloud_id)) - - drs = DBExtension.get_latest_extension(bk_cloud_id=self.bk_cloud_id, extension_type=ExtensionType.DRS) - drs_account_data = { - "access_hosts": DBExtension.get_extension_access_hosts( - bk_cloud_id=self.bk_cloud_id, extension_type=ExtensionType.DRS - ), - "pwd": RSAHandler.decrypt_password(rsa.rsa_private_key.content, drs.details["pwd"]), - "user": RSAHandler.decrypt_password(rsa.rsa_private_key.content, drs.details["user"]), - } - - dbha = DBExtension.get_latest_extension(bk_cloud_id=self.bk_cloud_id, extension_type=ExtensionType.DBHA) - dbha_account_data = { - "access_hosts": DBExtension.get_extension_access_hosts( - bk_cloud_id=self.bk_cloud_id, extension_type=ExtensionType.DBHA - ), - "pwd": RSAHandler.decrypt_password(rsa.rsa_private_key.content, dbha.details["pwd"]), - "user": RSAHandler.decrypt_password(rsa.rsa_private_key.content, dbha.details["user"]), - } - - return drs_account_data, dbha_account_data - @staticmethod def __get_proxy_account() -> Any: """ @@ -308,7 +231,7 @@ def get_install_mysql_payload(self, **kwargs) -> dict: for port in install_mysql_ports: mysql_config[port] = copy.deepcopy(self.init_mysql_config[port]) - drs_account, dbha_account = self.__get_super_account() + drs_account, dbha_account = self.get_super_account() return { "db_type": DBActuatorTypeEnum.MySQL.value, @@ -354,7 +277,7 @@ def get_install_spider_payload(self, **kwargs): ) spider_auto_incr_mode_map[port] = self.cluster["auto_incr_value"] - drs_account, dbha_account = self.__get_super_account() + drs_account, dbha_account = self.get_super_account() return { "db_type": DBActuatorTypeEnum.Spider.value, @@ -397,7 +320,7 @@ def get_install_spider_ctl_payload(self, **kwargs): ctl_pkg = Package.get_latest_package(version=MediumEnum.Latest, pkg_type=MediumEnum.tdbCtl) version_no = get_mysql_real_version(ctl_pkg.name) - drs_account, dbha_account = self.__get_super_account() + drs_account, dbha_account = self.get_super_account() return { "db_type": DBActuatorTypeEnum.SpiderCtl.value, "action": DBActuatorActionEnum.Deploy.value, @@ -470,6 +393,12 @@ def get_grant_mysql_repl_user_payload(self, **kwargs) -> dict: """ 拼接创建repl账号的payload参数(在master节点执行) """ + repl_host = ( + kwargs["trans_data"].get("new_slave_ip", self.cluster["new_slave_ip"]) + if kwargs.get("trans_data") + else self.cluster["new_slave_ip"] + ) + return { "db_type": DBActuatorTypeEnum.MySQL.value, "action": DBActuatorActionEnum.GrantRepl.value, @@ -478,7 +407,7 @@ def get_grant_mysql_repl_user_payload(self, **kwargs) -> dict: "extend": { "host": kwargs["ip"], "port": self.cluster["mysql_port"], - "repl_hosts": [kwargs["trans_data"].get("new_slave_ip", self.cluster["new_slave_ip"])], + "repl_hosts": [repl_host], }, }, } @@ -542,8 +471,9 @@ def get_find_local_backup_payload(self, **kwargs) -> dict: def get_change_master_payload(self, **kwargs) -> dict: """ 拼接同步主从的payload参数(在slave节点执行), 获取master的位点信息的场景通过上下文获取 - todo 后续可能支持多角度传入master的位点信息的拼接 + todo mysql_port获取方案对于同已存储对内,因为同集群内的实例端口都一致,兼容旧的方式,后续考虑去取 """ + default_port = self.cluster.get("mysql_port", 0) return { "db_type": DBActuatorTypeEnum.MySQL.value, "action": DBActuatorActionEnum.ChangeMaster.value, @@ -551,9 +481,9 @@ def get_change_master_payload(self, **kwargs) -> dict: "general": {"runtime_account": self.account}, "extend": { "host": kwargs["ip"], - "port": self.cluster["mysql_port"], + "port": self.cluster.get("slave_port", default_port), "master_host": kwargs["trans_data"].get("new_master_ip", self.cluster["new_master_ip"]), - "master_port": self.cluster["mysql_port"], + "master_port": self.cluster.get("master_port", default_port), "is_gtid": False, "max_tolerate_delay": 0, "force": self.ticket_data.get("change_master_force", False), @@ -658,12 +588,14 @@ def get_uninstall_spider_ctl_payload(self, **kwargs) -> dict: def get_install_db_backup_payload(self, **kwargs) -> dict: """ 安装备份程序,目前是必须是先录入元信息后,才执行备份程序的安装 + 非spider-master角色实例不安装备份程序,已在外层屏蔽 """ db_backup_pkg = Package.get_latest_package(version=MediumEnum.Latest, pkg_type=MediumEnum.DbBackup) cfg = self.__get_dbbackup_config() mysql_ports = [] port_domain_map = {} cluster_id_map = {} + shard_port_map = {} # port as key machine = Machine.objects.get(ip=kwargs["ip"]) if machine.machine_type == MachineType.SPIDER.value: @@ -672,6 +604,14 @@ def get_install_db_backup_payload(self, **kwargs) -> dict: elif machine.machine_type in [MachineType.REMOTE.value, MachineType.BACKEND.value, MachineType.SINGLE.value]: ins_list = StorageInstance.objects.filter(machine__ip=kwargs["ip"]) role = ins_list[0].instance_inner_role + # tendbcluster remote 补充shard信息 + if machine.machine_type == MachineType.REMOTE.value: + for ins in ins_list: + if ins.instance_inner_role == InstanceInnerRole.MASTER.value: + tp = StorageInstanceTuple.objects.filter(ejector=ins).first() + else: + tp = StorageInstanceTuple.objects.get(receiver=ins) + shard_port_map[ins.port] = tp.tendbclusterstorageset.shard_id else: raise DBMetaException(message=_("不支持的机器类型: {}".format(machine.machine_type))) @@ -681,6 +621,14 @@ def get_install_db_backup_payload(self, **kwargs) -> dict: port_domain_map[instance.port] = cluster.immute_domain cluster_id_map[instance.port] = cluster.id + shard_port_map[instance.port] = shard_port_map.get(instance.port, 0) + + # # 如果是spider-master类型机器,中控实例也需要安装备份程序 + # if role == TenDBClusterSpiderRole.SPIDER_MASTER.value: + # mysql_ports.append(instance.admin_port) + # port_domain_map[instance.admin_port] = cluster.immute_domain + # cluster_id_map[instance.admin_port] = cluster.id + cluster_type = ins_list[0].cluster.get().cluster_type return { @@ -702,6 +650,7 @@ def get_install_db_backup_payload(self, **kwargs) -> dict: "cluster_id": cluster_id_map, "cluster_type": cluster_type, "exec_user": self.ticket_data["created_by"], + "shard_value": shard_port_map, }, }, } @@ -985,7 +934,7 @@ def get_set_backend_toward_slave_payload(self, **kwargs): def get_rollback_data_download_backupfile_payload(self, **kwargs) -> dict: """ - 下载定点恢复的全库备份介质 + 下载定点恢复的全库备份介质 作废 """ payload = { "db_type": DBActuatorTypeEnum.Download.value, @@ -1059,9 +1008,12 @@ def get_rollback_data_download_binlog_payload(self, **kwargs) -> dict: def get_rollback_data_restore_payload(self, **kwargs): """ - MYSQL SLAVE 恢复 + MYSQL 定点回档恢复备份介质 """ - index_file = os.path.basename(self.cluster["total_backupinfo"]["index_file"]) + if self.cluster.get("rollback_type", "") == RollbackType.LOCAL_AND_TIME: + index_file = os.path.basename(kwargs["trans_data"]["backupinfo"]["index_file"]) + else: + index_file = os.path.basename(self.cluster["backupinfo"]["index"]["file_name"]) payload = { "db_type": DBActuatorTypeEnum.MySQL.value, "action": DBActuatorActionEnum.RestoreSlave.value, @@ -1074,9 +1026,8 @@ def get_rollback_data_restore_payload(self, **kwargs): "index": [index_file], }, "tgt_instance": { - # "host": self.cluster["new_slave_ip"], "host": kwargs["ip"], - "port": self.cluster["master_port"], + "port": self.cluster["rollback_port"], "user": self.account["admin_user"], "pwd": self.account["admin_pwd"], "socket": None, @@ -1098,64 +1049,16 @@ def get_rollback_data_restore_payload(self, **kwargs): } return payload - def get_rollback_data_recover_binlog_payload(self, **kwargs): - """ - MYSQL定点恢复之binglog前滚 - """ - # todo 如果没有binlog? - if kwargs["trans_data"]["binlog_files"] is None: - binlog_files = "" - else: - binlog_files = [i["file_name"] for i in kwargs["trans_data"]["binlog_files"]] - - payload = { - "db_type": DBActuatorTypeEnum.MySQL.value, - "action": DBActuatorActionEnum.RecoverBinlog.value, - "payload": { - "general": {"runtime_account": self.account}, - "extend": { - "work_dir": self.cluster["file_target_path"], - "binlog_dir": self.cluster["file_target_path"], - "binlog_files": binlog_files, - "tgt_instance": { - # "host": self.cluster["new_slave_ip"], - "host": kwargs["ip"], - "port": self.cluster["master_port"], - "user": self.account["admin_user"], - "pwd": self.account["admin_pwd"], - "socket": None, - "charset": self.cluster["charset"], - "options": "", - }, - "recover_opt": { - "start_time_bak": self.cluster["backup_time"], - "stop_time": self.cluster["rollback_time"], - "idempotent_mode": True, - "not_write_binlog": True, - "mysql_client_opt": {"max_allowed_packet": 1073741824}, - "databases": self.cluster["databases"], - "tables": self.cluster["tables"], - "databases_ignore": self.cluster["databases_ignore"], - "tables_ignore": self.cluster["tables_ignore"], - "start_pos": int(kwargs["trans_data"]["change_master_info"]["master_log_pos"]), - }, - "parse_only": False, - "binlog_start_file": kwargs["trans_data"]["change_master_info"]["master_log_file"], - }, - }, - } - return payload - def get_checksum_payload(self, **kwargs) -> dict: """ 数据校验 """ db_patterns = [ - ele if ele.endswith("%") else "{}_{}".format(ele, self.ticket_data["shard_id"]) + ele if ele.endswith("%") or ele == "*" else "{}_{}".format(ele, self.ticket_data["shard_id"]) for ele in self.ticket_data["db_patterns"] ] ignore_dbs = [ - ele if ele.endswith("%") else "{}_{}".format(ele, self.ticket_data["shard_id"]) + ele if ele.endswith("%") or ele == "*" else "{}_{}".format(ele, self.ticket_data["shard_id"]) for ele in self.ticket_data["ignore_dbs"] ] return { @@ -1241,8 +1144,8 @@ def get_pt_table_sync_payload(self, **kwargs) -> dict: }, } if is_routine_trigger: - data["start_time"] = self.cluster["start_time"] - data["end_time"] = self.cluster["end_time"] + data["payload"]["extend"]["start_time"] = self.ticket_data["start_time"] + data["payload"]["extend"]["end_time"] = self.ticket_data["end_time"] return data @@ -1250,7 +1153,30 @@ def get_mysql_flashback_payload(self, **kwargs) -> dict: """ mysql flashback """ - payload = { + targets = kwargs["trans_data"]["targets"] + databases = list(targets.keys()) + tables = list(targets[databases[0]].keys()) + return self.__flashback_payload(databases, tables, **kwargs) + + def get_spider_flashback_payload(self, **kwargs) -> dict: + """ + tendbcluster flashback + """ + targets = kwargs["trans_data"]["targets"] + shard_id = self.cluster["shard_id"] + + # 由于 flashback 库表输入的语义特性 + # 每个 database 对应的 tables 肯定是一样的 + # 所以可以这么取巧的拿出来 + databases = list(targets.keys()) + tables = list(targets[databases[0]].keys()) + + databases = ["{}_{}".format(ele, shard_id) for ele in databases] + + return self.__flashback_payload(databases, tables, **kwargs) + + def __flashback_payload(self, databases: List, tables: List, **kwargs) -> dict: + return { "db_type": DBActuatorTypeEnum.MySQL.value, "action": DBActuatorActionEnum.FlashBackBinlog.value, "payload": { @@ -1266,10 +1192,10 @@ def get_mysql_flashback_payload(self, **kwargs) -> dict: "options": "", }, "recover_opt": { - "databases": self.cluster["databases"], - "databases_ignore": self.cluster["databases_ignore"], - "tables": self.cluster["tables"], - "tables_ignore": self.cluster["tables_ignore"], + "databases": databases, + "databases_ignore": [], + "tables": tables, + "tables_ignore": [], "filter_rows": "", }, # 原始 binlog 目录,如果不提供,则自动为实例 binlog 目录 @@ -1277,14 +1203,13 @@ def get_mysql_flashback_payload(self, **kwargs) -> dict: # binlog列表,如果不提供,则自动从本地查找符合时间范围的 binlog "binlog_files": None, "work_dir": self.cluster["work_dir"], - "tools": {"mysqlbinlog": self.cluster["mysqlbinlog_rollback"]}, + # "tools": {"mysqlbinlog": self.cluster["mysqlbinlog_rollback"]}, # 闪回的目标时间点,对应 recover-binlog 的 start_time, 精确到秒。 "target_time": self.cluster["start_time"], "stop_time": self.cluster["end_time"], }, }, } - return payload def get_install_mysql_checksum_payload(self, **kwargs) -> dict: self.checksum_pkg = Package.get_latest_package(version=MediumEnum.Latest, pkg_type=MediumEnum.MySQLChecksum) @@ -1409,47 +1334,6 @@ def get_install_dba_toolkit_payload(self, **kwargs): }, } - def get_rollback_local_data_restore_payload(self, **kwargs): - """ - MYSQL SLAVE 恢复 - """ - index_file = os.path.basename(kwargs["trans_data"]["backupinfo"]["index_file"]) - payload = { - "db_type": DBActuatorTypeEnum.MySQL.value, - "action": DBActuatorActionEnum.RestoreSlave.value, - "payload": { - "general": {"runtime_account": self.account}, - "extend": { - "work_dir": self.cluster["file_target_path"], - "backup_dir": self.cluster["file_target_path"], - "backup_files": { - "index": [index_file], - }, - "tgt_instance": { - # "host": self.cluster["new_slave_ip"], - "host": kwargs["ip"], - "port": self.cluster["master_port"], - "user": self.account["admin_user"], - "pwd": self.account["admin_pwd"], - "socket": None, - "charset": self.cluster["charset"], - "options": "", - }, - "restore_opts": { - "databases": self.cluster["databases"], - "tables": self.cluster["tables"], - "ignore_databases": self.cluster["databases_ignore"], - "ignore_tables": self.cluster["tables_ignore"], - "recover_binlog": True, - }, - "src_instance": {"host": self.cluster["master_ip"], "port": self.cluster["master_port"]}, - "change_master": self.cluster["change_master"], - "work_id": "", - }, - }, - } - return payload - def get_install_restore_backup_payload(self, **kwargs) -> dict: """ 安装备份程序,针对从库重建、主从迁移的,实例还不属于集群 @@ -1651,7 +1535,7 @@ def get_grant_repl_for_ctl_payload(self, **kwargs) -> dict: "extend": { "host": kwargs["ip"], "port": self.cluster["mysql_port"], - "repl_hosts": kwargs["trans_data"].get("slaves", self.cluster["slaves"]), + "repl_hosts": self.cluster["slaves"], }, }, } @@ -1668,7 +1552,7 @@ def get_change_master_for_gitd_payload(self, **kwargs) -> dict: "extend": { "host": kwargs["ip"], "port": self.cluster["mysql_port"], - "master_host": kwargs["trans_data"].get("master_ip", self.cluster["master_ip"]), + "master_host": self.cluster["master_ip"], "master_port": self.cluster["mysql_port"], "is_gtid": True, "max_tolerate_delay": 0, @@ -1792,17 +1676,403 @@ def mysql_backup_demand_payload(self, **kwargs): "extend": { "host": self.ticket_data["ip"], "port": self.ticket_data["port"], + "role": self.ticket_data["role"], "backup_type": self.ticket_data["backup_type"], "backup_gsd": self.ticket_data["backup_gsd"], "regex": kwargs["trans_data"]["db_table_filter_regex"], "backup_id": self.ticket_data["backup_id"].__str__(), "bill_id": str(self.ticket_data["uid"]), "custom_backup_dir": self.ticket_data.get("custom_backup_dir", ""), + "shard_id": self.ticket_data.get("shard_id", 0), + }, + }, + } + + def tendb_cluster_remote_switch(self, **kwargs): + """ + 定义拼接TenDB-Cluster集群的remote互切/主故障切换的payload参数 + """ + cluster = Cluster.objects.get(id=self.ticket_data["cluster_id"]) + + switch_paris = [] + for tuples in self.ticket_data["switch_tuples"]: + objs = cluster.storageinstance_set.filter(machine__ip=tuples["master"]["ip"]) + for master in objs: + slave = StorageInstanceTuple.objects.get(ejector=master).receiver + switch_paris.append( + { + "master": {"host": master.machine.ip, "port": master.port}, + "slave": {"host": slave.machine.ip, "port": slave.port}, + } + ) + + return { + "db_type": DBActuatorTypeEnum.SpiderCtl.value, + "action": DBActuatorActionEnum.TenDBClusterBackendSwitch.value, + "payload": { + "general": {"runtime_account": self.account}, + "extend": { + "host": kwargs["ip"], + "port": cluster.proxyinstance_set.first().admin_port, + "slave_delay_check": self.ticket_data["is_check_delay"], + "force": self.ticket_data["force"], + "switch_paris": switch_paris, + }, + }, + } + + def tendb_cluster_remote_migrate(self, **kwargs): + """ + 定义拼接TenDB-Cluster成对迁移的payload参数 + """ + cluster = Cluster.objects.get(id=self.ticket_data["cluster_id"]) + migrate_cutover_pairs = [] + for info in self.ticket_data["migrate_tuples"]: + migrate_cutover_pairs.append( + { + "origin_master": { + "host": info["old_master"].split(":")[0], + "port": int(info["old_master"].split(":")[1]), + }, + "dest_master": { + "host": info["new_master"].split(":")[0], + "port": int(info["new_master"].split(":")[1]), + "user": TDBCTL_USER, + "password": self.ticket_data["tdbctl_pass"], + }, + "dest_slave": { + "host": info["new_slave"].split(":")[0], + "port": int(info["new_slave"].split(":")[1]), + "user": TDBCTL_USER, + "password": self.ticket_data["tdbctl_pass"], + }, + } + ) + + return { + "db_type": DBActuatorTypeEnum.SpiderCtl.value, + "action": DBActuatorActionEnum.TenDBClusterMigrateCutOver.value, + "payload": { + "general": {"runtime_account": self.account}, + "extend": { + "host": kwargs["ip"], + "port": cluster.proxyinstance_set.first().admin_port, + "slave_delay_check": self.ticket_data["slave_delay_check"], + "migrate_cutover_pairs": migrate_cutover_pairs, + }, + }, + } + + def tendb_restore_remotedb_payload(self, **kwargs): + """ + tendb 恢复remote实例 + """ + index_file = os.path.basename(self.cluster["backupinfo"]["index"]["file_name"]) + payload = { + "db_type": DBActuatorTypeEnum.MySQL.value, + "action": DBActuatorActionEnum.RestoreSlave.value, + "payload": { + "general": {"runtime_account": self.account}, + "extend": { + "work_dir": self.cluster["file_target_path"], + "backup_dir": self.cluster["file_target_path"], + "backup_files": { + # "full": None, + "index": [index_file], + # "priv": None, + }, + "tgt_instance": { + "host": self.cluster["restore_ip"], + "port": self.cluster["restore_port"], + "user": self.account["admin_user"], + "pwd": self.account["admin_pwd"], + "socket": None, + "charset": self.cluster["charset"], + "options": "", + }, + "src_instance": {"host": self.cluster["source_ip"], "port": self.cluster["source_port"]}, + "change_master": self.cluster["change_master"], + "work_id": "", }, }, } + return payload - def mysql_backup_demand_payload_on_ctl(self, **kwargs): - payload = self.mysql_backup_demand_payload(**kwargs) - payload["payload"]["extend"]["port"] += 1000 + def tendb_grant_remotedb_repl_user(self, **kwargs) -> dict: + """ + 拼接创建repl账号的payload参数(在master节点执行) + """ + return { + "db_type": DBActuatorTypeEnum.MySQL.value, + "action": DBActuatorActionEnum.GrantRepl.value, + "payload": { + "general": {"runtime_account": self.account}, + "extend": { + "host": self.cluster["target_ip"], + "port": self.cluster["target_port"], + "repl_hosts": [self.cluster["repl_ip"]], + }, + }, + } + + def tendb_remotedb_change_master(self, **kwargs) -> dict: + """ + 拼接同步主从的payload参数(在slave节点执行), 获取master的位点信息的场景通过上下文获取 + todo 后续可能支持多角度传入master的位点信息的拼接 + """ + if self.cluster["change_master_type"] == MysqlChangeMasterType.MASTERSTATUS.value: + bin_file = kwargs["trans_data"]["show_master_status_info"]["bin_file"] + bin_position = int(kwargs["trans_data"]["show_master_status_info"]["bin_position"]) + else: + bin_file = kwargs["trans_data"]["change_master_info"]["master_log_file"] + bin_position = int(kwargs["trans_data"]["change_master_info"]["master_log_pos"]) + bin_file = bin_file.strip().strip("'") + return { + "db_type": DBActuatorTypeEnum.MySQL.value, + "action": DBActuatorActionEnum.ChangeMaster.value, + "payload": { + "general": {"runtime_account": self.account}, + "extend": { + "host": self.cluster["repl_ip"], + "port": self.cluster["repl_port"], + "master_host": self.cluster["target_ip"], + "master_port": self.cluster["target_port"], + "is_gtid": False, + "max_tolerate_delay": 0, + "force": self.cluster["change_master_force"], + "bin_file": bin_file, + "bin_position": bin_position, + }, + }, + } + + def tendb_recover_binlog_payload(self, **kwargs): + """ + MYSQL 实例 前滚binglog + """ + if self.cluster.get("rollback_type", "") == RollbackType.LOCAL_AND_TIME: + binlog_files = kwargs["trans_data"]["binlog_files"] + backup_time = kwargs["trans_data"]["backup_time"] + else: + binlog_files = self.cluster["binlog_files"] + backup_time = self.cluster["backup_time"] + payload = { + "db_type": DBActuatorTypeEnum.MySQL.value, + "action": DBActuatorActionEnum.RecoverBinlog.value, + "payload": { + "general": {"runtime_account": self.account}, + "extend": { + "work_dir": self.cluster["file_target_path"], + "binlog_dir": self.cluster["file_target_path"], + "binlog_files": binlog_files, + "tgt_instance": { + "host": kwargs["ip"], + "port": self.cluster["rollback_port"], + "user": self.account["admin_user"], + "pwd": self.account["admin_pwd"], + "socket": None, + "charset": self.cluster["charset"], + "options": "", + }, + "recover_opt": { + "start_time_bak": backup_time, + "stop_time": self.cluster["rollback_time"], + "idempotent_mode": True, + "not_write_binlog": True, + "mysql_client_opt": {"max_allowed_packet": 1073741824}, + "databases": self.cluster["databases"], + "tables": self.cluster["tables"], + "databases_ignore": self.cluster["databases_ignore"], + "tables_ignore": self.cluster["tables_ignore"], + "start_pos": int(kwargs["trans_data"]["change_master_info"]["master_log_pos"]), + }, + "parse_only": False, + "binlog_start_file": kwargs["trans_data"]["change_master_info"]["master_log_file"], + }, + }, + } return payload + + def get_install_tmp_db_backup_payload(self, **kwargs): + """ + 数据恢复时安装临时备份程序。大部分信息可忽略不计 + """ + db_backup_pkg = Package.get_latest_package(version=MediumEnum.Latest, pkg_type=MediumEnum.DbBackup) + cfg = self.__get_dbbackup_config() + return { + "db_type": DBActuatorTypeEnum.MySQL.value, + "action": DBActuatorActionEnum.DeployDbbackup.value, + "payload": { + "general": {"runtime_account": self.account}, + "extend": { + "pkg": db_backup_pkg.name, + "pkg_md5": db_backup_pkg.md5, + "host": kwargs["ip"], + "ports": [0], + "bk_cloud_id": int(self.bk_cloud_id), + "bk_biz_id": int(self.ticket_data["bk_biz_id"]), + "role": InstanceInnerRole.MASTER.value, + "configs": cfg["ini"], + "options": cfg["options"], + "cluster_address": {}, + "cluster_id": {}, + "cluster_type": ClusterType.TenDBHA, + "exec_user": self.ticket_data["created_by"], + "shard_value": {}, + "untar_only": True, + }, + }, + } + + def get_open_area_dump_schema_payload(self, **kwargs): + """ + 开区导出表结构 + @param kwargs: + @return: + """ + fileserver = {} + rsa = RSAHandler.get_or_generate_rsa_in_db(RSAConfigType.PROXYPASS.value) + db_cloud_token = RSAHandler.encrypt_password( + rsa.rsa_public_key.content, f"{self.bk_cloud_id}_dbactuator_token" + ) + + nginx_ip = DBCloudProxy.objects.filter(bk_cloud_id=self.bk_cloud_id).last().internal_address + bkrepo_url = f"http://{nginx_ip}/apis/proxypass" if self.bk_cloud_id else settings.BKREPO_ENDPOINT_URL + + if self.cluster["is_upload_bkrepo"]: + fileserver.update( + { + "url": bkrepo_url, + "bucket": settings.BKREPO_BUCKET, + "username": settings.BKREPO_USERNAME, + "password": settings.BKREPO_PASSWORD, + "project": settings.BKREPO_PROJECT, + "upload_path": BKREPO_SQLFILE_PATH, + } + ) + + return { + "db_type": DBActuatorTypeEnum.MySQL.value, # spider集群也用mysql类型 + "action": DBActuatorActionEnum.MysqlOpenAreaDumpSchema.value, + "payload": { + "general": {"runtime_account": self.account}, + "extend": { + "host": kwargs["ip"], + "port": self.cluster["port"], + "charset": "default", + "root_id": self.cluster["root_id"], + "bk_cloud_id": self.bk_cloud_id, + "db_cloud_token": db_cloud_token, + "dump_dir_name": f"{self.cluster['root_id']}_schema", + "fileserver": fileserver, + "open_area_param": self.cluster["open_area_param"], + }, + }, + } + + def get_open_area_import_schema_payload(self, **kwargs): + """ + 开区导入表结构 + @param kwargs: + @return: + """ + return { + "db_type": DBActuatorTypeEnum.MySQL.value, # spider集群也用mysql类型 + "action": DBActuatorActionEnum.MysqlOpenAreaImportSchema.value, + "payload": { + "general": {"runtime_account": self.account}, + "extend": { + "host": kwargs["ip"], + "port": self.cluster["port"], + "charset": "default", + "root_id": self.cluster["root_id"], + "bk_cloud_id": self.bk_cloud_id, + "dump_dir_name": f"{self.cluster['root_id']}_schema", + "open_area_param": self.cluster["open_area_param"], + }, + }, + } + + def get_open_area_dump_data_payload(self, **kwargs): + """ + 开区导出表数据 + @param kwargs: + @return: + """ + fileserver = {} + rsa = RSAHandler.get_or_generate_rsa_in_db(RSAConfigType.PROXYPASS.value) + db_cloud_token = RSAHandler.encrypt_password( + rsa.rsa_public_key.content, f"{self.bk_cloud_id}_dbactuator_token" + ) + + nginx_ip = DBCloudProxy.objects.filter(bk_cloud_id=self.bk_cloud_id).last().internal_address + bkrepo_url = f"http://{nginx_ip}/apis/proxypass" if self.bk_cloud_id else settings.BKREPO_ENDPOINT_URL + + if self.cluster["is_upload_bkrepo"]: + fileserver.update( + { + "url": bkrepo_url, + "bucket": settings.BKREPO_BUCKET, + "username": settings.BKREPO_USERNAME, + "password": settings.BKREPO_PASSWORD, + "project": settings.BKREPO_PROJECT, + "upload_path": BKREPO_SQLFILE_PATH, + } + ) + return { + "db_type": DBActuatorTypeEnum.MySQL.value, # spider集群也用mysql类型 + "action": DBActuatorActionEnum.MysqlOpenAreaDumpData.value, + "payload": { + "general": {"runtime_account": self.account}, + "extend": { + "host": kwargs["ip"], + "port": self.cluster["port"], + "charset": "default", + "root_id": self.cluster["root_id"], + "bk_cloud_id": self.bk_cloud_id, + "db_cloud_token": db_cloud_token, + "dump_dir_name": f"{self.cluster['root_id']}_data", + "fileserver": fileserver, + "open_area_param": self.cluster["open_area_param"], + }, + }, + } + + def get_open_area_import_data_payload(self, **kwargs): + """ + 开区导入表数据 + @param kwargs: + @return: + """ + return { + "db_type": DBActuatorTypeEnum.MySQL.value, # spider集群也用mysql类型 + "action": DBActuatorActionEnum.MysqlOpenAreaImportData.value, + "payload": { + "general": {"runtime_account": self.account}, + "extend": { + "host": kwargs["ip"], + "port": self.cluster["port"], + "charset": "default", + "root_id": self.cluster["root_id"], + "bk_cloud_id": self.bk_cloud_id, + "dump_dir_name": f"{self.cluster['root_id']}_data", + "open_area_param": self.cluster["open_area_param"], + }, + }, + } + + def enable_tokudb_payload(self, **kwargs): + """ + enable Tokudb engine for mysql instance + """ + return { + "db_type": DBActuatorTypeEnum.MySQL.value, + "action": DBActuatorActionEnum.EnableTokudb.value, + "payload": { + "general": {"runtime_account": self.account}, + "extend": { + "host": kwargs["ip"], + "ports": self.ticket_data.get("mysql_ports", []), + }, + }, + } diff --git a/dbm-ui/backend/flow/utils/mysql/mysql_bk_config.py b/dbm-ui/backend/flow/utils/mysql/mysql_bk_config.py new file mode 100644 index 0000000000..739833891b --- /dev/null +++ b/dbm-ui/backend/flow/utils/mysql/mysql_bk_config.py @@ -0,0 +1,36 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 typing import Any + +from backend.components import DBConfigApi +from backend.components.dbconfig.constants import FormatType, LevelName + +""" +定义一些mysql场景通过bk-config服务获取一些信息的公共方法,方便管理,减少代码重复率 +""" + + +def get_mysql_version_and_charset(bk_biz_id, db_module_id, cluster_type) -> Any: + """ + 根据业务id和模块id,通过bk—config获取版本号和字符集信息 + """ + data = DBConfigApi.query_conf_item( + { + "bk_biz_id": str(bk_biz_id), + "level_name": LevelName.MODULE, + "level_value": str(db_module_id), + "conf_file": "deploy_info", + "conf_type": "deploy", + "namespace": cluster_type, + "format": FormatType.MAP, + } + )["content"] + + return data["charset"], data["db_version"] diff --git a/dbm-ui/backend/flow/utils/mysql/mysql_context_dataclass.py b/dbm-ui/backend/flow/utils/mysql/mysql_context_dataclass.py index dd18f3438e..6038c7875b 100644 --- a/dbm-ui/backend/flow/utils/mysql/mysql_context_dataclass.py +++ b/dbm-ui/backend/flow/utils/mysql/mysql_context_dataclass.py @@ -53,10 +53,13 @@ class ClusterInfoContext: change_master_info: dict = field(default_factory=dict) latest_backup_file: str = None backupinfo: dict = None + backup_time: str = None backup_role: str = None - binlog_files: list = None + binlog_files: str = None + binlog_files_list: list = None master_backup_file: dict = None slave_backup_file: dict = None + show_master_status_info: dict = field(default_factory=dict) @staticmethod def get_sync_info_var_name() -> str: @@ -146,7 +149,7 @@ class MySQLTruncateDataContext: ip: str = None port: int = None targets: Dict = None - show_open_fence: bool = None + # show_open_fence: bool = None old_new_map: dict = None db_table_filter_regex: str = None db_filter_regex: str = None @@ -303,3 +306,8 @@ class MySQLBackupDemandContext: @staticmethod def get_backup_ip_var_name() -> str: return "ip" + + +@dataclass() +class MySQLFlashBackContext: + targets: Dict = None diff --git a/dbm-ui/backend/flow/utils/mysql/mysql_db_meta.py b/dbm-ui/backend/flow/utils/mysql/mysql_db_meta.py index 25ef9bcf36..966385c983 100644 --- a/dbm-ui/backend/flow/utils/mysql/mysql_db_meta.py +++ b/dbm-ui/backend/flow/utils/mysql/mysql_db_meta.py @@ -13,17 +13,17 @@ from django.db.transaction import atomic -from backend import env from backend.configuration.constants import DBType from backend.db_meta import api from backend.db_meta.api.cluster.tendbha.handler import TenDBHAClusterHandler from backend.db_meta.api.cluster.tendbsingle.handler import TenDBSingleClusterHandler -from backend.db_meta.api.common import CCApi, del_service_instance from backend.db_meta.enums import ClusterPhase, InstanceInnerRole, InstanceRole, InstanceStatus, MachineType -from backend.db_meta.models import Cluster, ProxyInstance, StorageInstance, StorageInstanceTuple +from backend.db_meta.models import Cluster, StorageInstance, StorageInstanceTuple from backend.db_package.models import Package from backend.flow.consts import MediumEnum from backend.flow.engine.bamboo.scene.common.get_real_version import get_mysql_real_version +from backend.flow.utils.cc_manage import CcManage +from backend.flow.utils.mysql.mysql_module_operate import MysqlCCTopoOperator logger = logging.getLogger("flow") @@ -41,6 +41,7 @@ def __init__(self, ticket_data: dict, cluster: dict): """ self.ticket_data = ticket_data self.cluster = cluster + self.bk_biz_id = self.ticket_data["bk_biz_id"] def mysql_single_apply(self) -> bool: """ @@ -49,7 +50,7 @@ def mysql_single_apply(self) -> bool: """ def_resource_spec = {"single": {"id": 0}} TenDBSingleClusterHandler.create( - bk_biz_id=self.ticket_data["bk_biz_id"], + bk_biz_id=self.bk_biz_id, major_version=self.ticket_data["db_version"], ip=self.cluster["new_ip"], clusters=self.ticket_data["clusters"], @@ -73,7 +74,7 @@ def mysql_ha_apply(self) -> bool: } kwargs = { - "bk_biz_id": int(self.ticket_data["bk_biz_id"]), + "bk_biz_id": int(self.bk_biz_id), "db_module_id": int(self.ticket_data["module"]), "major_version": self.ticket_data["db_version"], "cluster_ip_dict": copy.deepcopy(cluster_ip_dict), @@ -91,16 +92,14 @@ def mysql_single_destroy(self) -> bool: """ 下架mysql单节点版集群,删除元信息 """ - TenDBSingleClusterHandler( - bk_biz_id=self.ticket_data["bk_biz_id"], cluster_id=self.cluster["id"] - ).decommission() + TenDBSingleClusterHandler(bk_biz_id=self.bk_biz_id, cluster_id=self.cluster["id"]).decommission() return True def mysql_ha_destroy(self) -> bool: """ 下架mysql主从版集群,删除元信息 """ - TenDBHAClusterHandler(bk_biz_id=self.ticket_data["bk_biz_id"], cluster_id=self.cluster["id"]).decommission() + TenDBHAClusterHandler(bk_biz_id=self.bk_biz_id, cluster_id=self.cluster["id"]).decommission() return True def mysql_proxy_add(self) -> bool: @@ -111,7 +110,7 @@ def mysql_proxy_add(self) -> bool: machines = [ { "ip": self.cluster["proxy_ip"]["ip"], - "bk_biz_id": int(self.ticket_data["bk_biz_id"]), + "bk_biz_id": int(self.bk_biz_id), "machine_type": MachineType.PROXY.value, }, ] @@ -145,7 +144,7 @@ def mysql_proxy_switch(self): machines = [ { "ip": self.cluster["target_proxy_ip"]["ip"], - "bk_biz_id": int(self.ticket_data["bk_biz_id"]), + "bk_biz_id": int(self.bk_biz_id), "machine_type": MachineType.PROXY.value, }, ] @@ -182,7 +181,7 @@ def mysql_restore_slave_add_instance(self): machines = [ { "ip": self.cluster["new_slave_ip"], - "bk_biz_id": int(self.ticket_data["bk_biz_id"]), + "bk_biz_id": int(self.bk_biz_id), "machine_type": MachineType.BACKEND.value, }, ] @@ -213,7 +212,7 @@ def mysql_restore_slave_add_instance(self): api.machine.create( bk_cloud_id=self.cluster["bk_cloud_id"], machines=machines, creator=self.ticket_data["created_by"] ) - api.storage_instance.create( + storage_objs = api.storage_instance.create( instances=storage_instances, creator=self.ticket_data["created_by"], time_zone=self.cluster["time_zone"], @@ -222,10 +221,9 @@ def mysql_restore_slave_add_instance(self): # 新建的实例处于游离态,关联到每个相对应的集群ID。 api.cluster.tendbha.cluster_add_storage(cluster_list) # ip转移模块,ip底下关联的每个实例注册到服务(即可监控) - api.machine.trans_module( - bk_cloud_id=self.cluster["bk_cloud_id"], - cluster_ids=clusterid_list, - machines=[self.cluster["new_slave_ip"]], + + MysqlCCTopoOperator(Cluster.objects.filter(id__in=clusterid_list)).transfer_instances_to_cluster_module( + storage_objs ) return True @@ -283,10 +281,10 @@ def mysql_restore_remove_old_slave(self): cluster_id=self.cluster["cluster_id"], target_slave_ip=self.cluster["old_slave_ip"] ) # 删除实例ID注册服。 - bk_instance_id = StorageInstance.objects.get( + storage = StorageInstance.objects.get( machine__ip=self.cluster["old_slave_ip"], port=self.cluster["master_port"] - ).bk_instance_id - del_service_instance(bk_instance_id=bk_instance_id) + ) + CcManage(self.bk_biz_id).delete_service_instance(bk_instance_ids=[storage.bk_instance_id]) # 删除实例元数据信息 api.storage_instance.delete( @@ -300,30 +298,8 @@ def mysql_restore_remove_old_slave(self): ) if not StorageInstance.objects.filter(machine__ip=self.cluster["old_slave_ip"]).exists(): - # 机器转移模块到空闲模块,转移到空闲模块 实例ID的服务会自动注销。 - api.machine.trans_module( - bk_cloud_id=self.cluster["bk_cloud_id"], machines=[self.cluster["old_slave_ip"]], idle=True - ) api.machine.delete([self.cluster["old_slave_ip"]], bk_cloud_id=self.cluster["bk_cloud_id"]) - def machine_single_create(self): - """ - 定义machine录入的方法 - 传入的ip可以暂时通过cluster变量的结构体来提供, - todo 方法废弃,后续去除 - """ - api.machine.create( - machines=[ - { - "ip": self.cluster["ip"], - "bk_biz_id": self.ticket_data["bk_biz_id"], - "machine_type": MachineType.SINGLE.value, - } - ], - creator=self.ticket_data["created_by"], - ) - return True - def mysql_migrate_cluster_add_instance(self): """ 集群成对迁移之一:安装新节点,添加实例元数据,关联到集群,转移机器模块 @@ -335,12 +311,12 @@ def mysql_migrate_cluster_add_instance(self): machines = [ { "ip": self.cluster["new_master_ip"], - "bk_biz_id": int(self.ticket_data["bk_biz_id"]), + "bk_biz_id": int(self.bk_biz_id), "machine_type": MachineType.BACKEND.value, }, { "ip": self.cluster["new_slave_ip"], - "bk_biz_id": int(self.ticket_data["bk_biz_id"]), + "bk_biz_id": int(self.bk_biz_id), "machine_type": MachineType.BACKEND.value, }, ] @@ -388,7 +364,7 @@ def mysql_migrate_cluster_add_instance(self): api.machine.create( bk_cloud_id=self.cluster["bk_cloud_id"], machines=machines, creator=self.ticket_data["created_by"] ) - api.storage_instance.create( + storage_objs = api.storage_instance.create( instances=storage_instances, creator=self.ticket_data["created_by"], time_zone=self.cluster["time_zone"], @@ -396,11 +372,10 @@ def mysql_migrate_cluster_add_instance(self): ) api.cluster.tendbha.cluster_add_storage(cluster_list) # 转移模块,实例ID注册服务 - api.machine.trans_module( - bk_cloud_id=self.cluster["bk_cloud_id"], - cluster_ids=clusterid_list, - machines=[self.cluster["new_master_ip"], self.cluster["new_slave_ip"]], - ) + + clusters = Cluster.objects.filter(id__in=clusterid_list) + MysqlCCTopoOperator(clusters).transfer_instances_to_cluster_module(storage_objs) + api.cluster.tendbha.add_storage_tuple( master_ip=self.cluster["new_master_ip"], slave_ip=self.cluster["new_slave_ip"], @@ -460,22 +435,17 @@ def mysql_ha_switch(self): proxy.storageinstance.remove(master_storage_objs) proxy.storageinstance.add(slave_storage_objs) + cc_manage = CcManage(self.bk_biz_id) # 切换新master服务实例角色标签 - CCApi.add_label_for_service_instance( - { - "bk_biz_id": env.DBA_APP_BK_BIZ_ID, - "instance_ids": [slave_storage_objs.bk_instance_id], - "labels": {"instance_role": InstanceRole.BACKEND_MASTER.value}, - } + cc_manage.add_label_for_service_instance( + bk_instance_ids=[slave_storage_objs.bk_instance_id], + labels_dict={"instance_role": InstanceRole.BACKEND_MASTER.value}, ) # 切换新slave服务实例角色标签 - CCApi.add_label_for_service_instance( - { - "bk_biz_id": env.DBA_APP_BK_BIZ_ID, - "instance_ids": [master_storage_objs.bk_instance_id], - "labels": {"instance_role": InstanceRole.BACKEND_SLAVE.value}, - } + cc_manage.add_label_for_service_instance( + bk_instance_ids=[master_storage_objs.bk_instance_id], + labels_dict={"instance_role": InstanceRole.BACKEND_SLAVE.value}, ) def mysql_cluster_offline(self): @@ -562,9 +532,6 @@ def mysql_rollback_remove_instance(self): if not StorageInstance.objects.filter( machine__bk_cloud_id=self.cluster["bk_cloud_id"], machine__ip=self.cluster["rollback_ip"] ).exists(): - api.machine.trans_module( - bk_cloud_id=self.cluster["bk_cloud_id"], machines=[self.cluster["rollback_ip"]], idle=True - ) api.machine.delete([self.cluster["rollback_ip"]], bk_cloud_id=self.cluster["bk_cloud_id"]) def mysql_cluster_migrate_remote_instance(self): @@ -580,14 +547,15 @@ def mysql_cluster_migrate_remote_instance(self): port_list=[self.cluster["backend_port"]], ) # 删除实例ID注册服。 - bk_instance_id = StorageInstance.objects.get( + storage = StorageInstance.objects.get( machine__ip=self.cluster["old_slave_ip"], port=self.cluster["backend_port"] - ).bk_instance_id - del_service_instance(bk_instance_id=bk_instance_id) - bk_instance_id = StorageInstance.objects.get( + ) + cc_manage = CcManage(self.bk_biz_id) + cc_manage.delete_service_instance(bk_instance_ids=[storage.bk_instance_id]) + storage = StorageInstance.objects.get( machine__ip=self.cluster["master_ip"], port=self.cluster["backend_port"] - ).bk_instance_id - del_service_instance(bk_instance_id=bk_instance_id) + ) + cc_manage.delete_service_instance(bk_instance_ids=[storage.bk_instance_id]) # 删除实例元数据信息 api.storage_instance.delete( [ @@ -604,21 +572,35 @@ def mysql_cluster_migrate_remote_instance(self): ] ) - if not StorageInstance.objects.filter(machine__ip=self.cluster["old_slave_ip"]).exists(): - # 机器转移模块到空闲模块,转移到空闲模块 实例ID的服务会自动注销。 - api.machine.trans_module( - bk_cloud_id=self.cluster["bk_cloud_id"], - cluster_ids=[self.cluster["cluster_id"]], - machines=[self.cluster["old_slave_ip"]], - idle=True, - ) + slave_qs = StorageInstance.objects.filter( + machine__ip=self.cluster["old_slave_ip"], machine__bk_cloud_id=self.cluster["bk_cloud_id"] + ) + if not slave_qs.exists(): api.machine.delete([self.cluster["old_slave_ip"]], bk_cloud_id=self.cluster["bk_cloud_id"]) if not StorageInstance.objects.filter(machine__ip=self.cluster["master_ip"]).exists(): - api.machine.trans_module( - bk_cloud_id=self.cluster["bk_cloud_id"], - cluster_ids=[self.cluster["cluster_id"]], - machines=[self.cluster["master_ip"]], - idle=True, - ) api.machine.delete([self.cluster["master_ip"]], bk_cloud_id=self.cluster["bk_cloud_id"]) + + def add_tbinlogdumper(self): + """ + 添加TBinlogDumper实例 + """ + TenDBHAClusterHandler(bk_biz_id=self.bk_biz_id, cluster_id=self.ticket_data["cluster_id"]).add_tbinlogdumper( + add_confs=self.ticket_data["add_confs"] + ) + + def reduce_tbinlogdumper(self): + """ + 减少TBinlogDumper实例 + """ + TenDBHAClusterHandler( + bk_biz_id=self.bk_biz_id, cluster_id=self.ticket_data["cluster_id"] + ).reduce_tbinlogdumper(id_list=self.ticket_data["reduce_ids"]) + + def switch_tbinlogdumper(self): + """ + 迁移TBinlogDumper 实例 + """ + TenDBHAClusterHandler( + bk_biz_id=self.bk_biz_id, cluster_id=self.ticket_data["cluster_id"] + ).switch_tbinlogdumper_for_cluster(switch_ids=self.cluster["switch_ids"]) diff --git a/dbm-ui/backend/flow/utils/mysql/mysql_module_operate.py b/dbm-ui/backend/flow/utils/mysql/mysql_module_operate.py new file mode 100644 index 0000000000..b9c26f7b9b --- /dev/null +++ b/dbm-ui/backend/flow/utils/mysql/mysql_module_operate.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 typing import Union + +from backend.configuration.constants import DBType +from backend.db_meta.enums import AccessLayer, MachineType +from backend.db_meta.models import ClusterMonitorTopo, ProxyInstance, StorageInstance +from backend.flow.consts import InstanceFuncAliasEnum +from backend.flow.utils.base.cc_topo_operate import CCTopoOperator +from backend.ticket.constants import InstanceType + + +class MysqlCCTopoOperator(CCTopoOperator): + db_type = DBType.MySQL.value + + def generate_custom_labels(self, inst: Union[StorageInstance, ProxyInstance]) -> dict: + # 定义注册mysql/proxy/spider服务实例需要的labels标签结构 + return {"exporter_conf_path": f"exporter_{inst.port}.cnf"} + + @staticmethod + def generate_ins_instance_role(ins: Union[StorageInstance, ProxyInstance]): + """ + 生成服务实例的 instance role + """ + if ins.machine_type == MachineType.PROXY.value: + # proxy_instance表没有所谓的实例角色,则用 AccessLayer 枚举来代替 + return AccessLayer.PROXY.value + if ins.machine_type == MachineType.SPIDER.value: + # spider 有单独的角色管理 + return ins.tendbclusterspiderext.spider_role + return ins.instance_role diff --git a/dbm-ui/backend/flow/utils/name_service/__init__.py b/dbm-ui/backend/flow/utils/name_service/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/dbm-ui/backend/flow/utils/name_service/name_service_dataclass.py b/dbm-ui/backend/flow/utils/name_service/name_service_dataclass.py new file mode 100644 index 0000000000..394528e0da --- /dev/null +++ b/dbm-ui/backend/flow/utils/name_service/name_service_dataclass.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +# name_service相关单据上下文 +from dataclasses import dataclass +from typing import Any, Optional + + +@dataclass() +class ActKwargs: + """节点私有变量数据类""" + + # 集群id + cluster_id: int = None + # 操作人员 + creator: str = None + # 名字服务类型 + name_service_operation_type: str = None + # 加载到上下文的dataclass类的名称 + set_trans_data_dataclass: str = None + + +@dataclass() +class TransDataKwargs: + """可读写上下文""" + + # 调用第三方接口返回的数据 + output: Optional[Any] = None diff --git a/dbm-ui/backend/flow/utils/polaris_manage.py b/dbm-ui/backend/flow/utils/polaris_manage.py new file mode 100644 index 0000000000..a27a89019d --- /dev/null +++ b/dbm-ui/backend/flow/utils/polaris_manage.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging + +from backend.components import NameServiceApi +from backend.db_meta.models import PolarisEntryDetail + +logger = logging.getLogger("flow") + + +def GetPolarisManageByName(servicename: str): + polaris_info = PolarisEntryDetail.objects.filter(polaris_name=servicename).values()[0] + return PolarisManage( + polaris_info["polaris_name"], + polaris_info["polaris_token"], + polaris_info["polaris_l5"], + polaris_info["alias_token"], + ) + + +class PolarisManage(object): + """ + 定义polaris域名管理类 + """ + + def __init__(self, servicename: str, servicetoken: str, alias: str, aliastoken: str): + self.servicename = servicename + self.servicetoken = servicetoken + self.alias = alias + self.aliastoken = aliastoken + + def del_polaris_rs(self, instance_list: list) -> bool: + """ + 删除polaris后端的rs记录;适用场景:proxy下架的时候需要删除 + """ + NameServiceApi.polaris_unbind_part_targets( + {"servicename": self.servicename, "servicetoken": self.servicetoken, "ips": instance_list}, + raw=True, + ) + return True + + def add_polaris_rs(self, instance_list: list) -> bool: + """ + 增加polaris后端的rs记录;适用场景:proxy上架的时候需要新增 + """ + NameServiceApi.polaris_bind_part_targets( + {"servicename": self.servicename, "servicetoken": self.servicetoken, "ips": instance_list}, + raw=True, + ) + return True + + def deregiste_polaris(self) -> bool: + """ + 删除polaris后端rs记录,并删除polaris;适用场景:集群下架 + """ + NameServiceApi.polaris_unbind_targets_and_delete_alias_service( + { + "servicename": self.servicename, + "servicetoken": self.servicetoken, + "alias": self.alias, + "aliastoken": self.aliastoken, + }, + raw=True, + ) + return True diff --git a/dbm-ui/backend/flow/utils/pulsar/__init__.py b/dbm-ui/backend/flow/utils/pulsar/__init__.py new file mode 100644 index 0000000000..aa5085c628 --- /dev/null +++ b/dbm-ui/backend/flow/utils/pulsar/__init__.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" diff --git a/dbm-ui/backend/flow/utils/pulsar/pulsar_db_meta.py b/dbm-ui/backend/flow/utils/pulsar/pulsar_db_meta.py index 7b5c3d7374..3a5c16a973 100644 --- a/dbm-ui/backend/flow/utils/pulsar/pulsar_db_meta.py +++ b/dbm-ui/backend/flow/utils/pulsar/pulsar_db_meta.py @@ -14,6 +14,7 @@ from backend.db_meta import api from backend.db_meta.enums import InstanceRole, MachineType +from backend.db_services.dbbase.constants import IpSource from backend.flow.consts import LevelInfoEnum, PulsarRoleEnum from backend.flow.utils.pulsar.consts import PULSAR_BOOKKEEPER_SERVICE_PORT, PULSAR_ZOOKEEPER_SERVICE_PORT @@ -64,13 +65,19 @@ def __generate_machine(self) -> list: machines = [] for role in PulsarRoleEnum: for ip in self.__get_node_ips_by_role(role): - machines.append( - { - "ip": ip, - "bk_biz_id": int(self.ticket_data["bk_biz_id"]), - "machine_type": self.role_machine_dict[role], - } - ) + machine = { + "ip": ip, + "bk_biz_id": int(self.ticket_data["bk_biz_id"]), + "machine_type": self.role_machine_dict[role], + } + if self.ticket_data["ip_source"] == IpSource.RESOURCE_POOL: + machine.update( + { + "spec_id": self.ticket_data["resource_spec"][role]["id"], + "spec_config": self.ticket_data["resource_spec"][role], + } + ) + machines.append(machine) return machines def __generate_storage_instance(self) -> list: @@ -177,13 +184,19 @@ def pulsar_replace(self) -> bool: ) new_machines = [] for ip in self.ticket_data["new_zk_ips"]: - new_machines.append( - { - "ip": ip, - "bk_biz_id": int(self.ticket_data["bk_biz_id"]), - "machine_type": self.role_machine_dict[PulsarRoleEnum.ZooKeeper], - } - ) + machine = { + "ip": ip, + "bk_biz_id": int(self.ticket_data["bk_biz_id"]), + "machine_type": self.role_machine_dict[PulsarRoleEnum.ZooKeeper.value], + } + if self.ticket_data["ip_source"] == IpSource.RESOURCE_POOL: + machine.update( + { + "spec_id": self.ticket_data["resource_spec"][PulsarRoleEnum.ZooKeeper.value]["id"], + "spec_config": self.ticket_data["resource_spec"][PulsarRoleEnum.ZooKeeper.value], + } + ) + new_machines.append(machine) with atomic(): # 绑定事务更新cmdb api.machine.create( diff --git a/dbm-ui/backend/flow/utils/pulsar/pulsar_module_operate.py b/dbm-ui/backend/flow/utils/pulsar/pulsar_module_operate.py index ac0a10c0b6..f427484d16 100644 --- a/dbm-ui/backend/flow/utils/pulsar/pulsar_module_operate.py +++ b/dbm-ui/backend/flow/utils/pulsar/pulsar_module_operate.py @@ -8,108 +8,9 @@ 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 dataclasses import asdict, dataclass +from backend.configuration.constants import DBType +from backend.flow.utils.base.cc_topo_operate import CCTopoOperator -from backend.constants import CommonInstanceLabels -from backend.db_meta.api.common import add_service_instance -from backend.db_meta.api.db_module import get_or_create -from backend.db_meta.models import AppCache, Cluster, ClusterMonitorTopo, Machine -from backend.dbm_init.constants import CC_APP_ABBR_ATTR -from backend.flow.utils.cc_manage import CcManage - -@dataclass() -# 定义注册Pulsar服务实例需要的labels标签结构 -class PulsarInstanceLabels(CommonInstanceLabels): - # metrics_port: str - instance_port: str - - -def create_bk_module_for_cluster_id(cluster_id: int): - """ - # 根据cluster_id,创建对应的域名名称模块 - """ - cluster = Cluster.objects.get(id=cluster_id) - - get_or_create( - bk_biz_id=cluster.bk_biz_id, - cluster_id=cluster.id, - cluster_type=cluster.cluster_type, - cluster_domain=cluster.immute_domain, - creator=cluster.creator, - ) - - -def transfer_host_in_cluster_module( - cluster_id: int, - ip_list: list, - machine_type: str, - bk_cloud_id: int, -): - """ - 根据机器的ip和machine_type的信息,选择对应的bk_set_id, 并将主机转移到对应cluster模块下 - @param cluster_id: 对应的cluster id - @param ip_list: 待转移的ip 列表 - @param machine_type: 机器的类型 - @param bk_cloud_id: 机器列表所在的云区域,兼容后续跨云管理 - """ - transfer_bk_module_ids = [] - transfer_bk_host_ids = [] - for ip in ip_list: - machine = Machine.objects.get(ip=ip, bk_cloud_id=bk_cloud_id) - transfer_bk_host_ids.append(machine.bk_host_id) - transfer_bk_host_ids = list(set(transfer_bk_host_ids)) - - cluster = Cluster.objects.get(id=cluster_id) - bk_module_obj = ClusterMonitorTopo.objects.get( - bk_biz_id=cluster.bk_biz_id, cluster_id=cluster_id, machine_type=machine_type - ) - transfer_bk_module_ids.append(bk_module_obj.bk_module_id) - - # 主机转移到对应的模块下,机器可能对应多个集群,所有主机转移到多个模块下是合理的 - # CCApi.transfer_host_module( - # { - # "bk_biz_id": env.DBA_APP_BK_BIZ_ID, - # "bk_host_id": transfer_bk_host_ids, - # "bk_module_id": transfer_bk_module_ids, - # "is_increment": False, - # }, - # use_admin=True, - # ) - - CcManage.transfer_host_module(transfer_bk_host_ids, transfer_bk_module_ids) - - -def init_instance_service(cluster, ins, bk_module_id, instance_role, func_name): - """ - 添加服务实例 - 目前分割符: 不支持,暂时用中划线- - """ - ins_labels = asdict( - PulsarInstanceLabels( - app=AppCache.get_app_attr(cluster.bk_biz_id, default=cluster.bk_biz_id), - app_id=str(cluster.bk_biz_id), - app_name=AppCache.get_app_attr(cluster.bk_biz_id, CC_APP_ABBR_ATTR, cluster.bk_biz_id), - bk_biz_id=str(cluster.bk_biz_id), - bk_cloud_id=str(cluster.bk_cloud_id), - cluster_domain=cluster.immute_domain, - cluster_name=cluster.name, - cluster_type=cluster.cluster_type, - instance_role=instance_role, - instance_host=ins.machine.ip, - instance=f"{ins.machine.ip}-{ins.port}", - instance_port=str(ins.port), - ) - ) - - bk_instance_id = add_service_instance( - bk_module_id=bk_module_id, - bk_host_id=ins.machine.bk_host_id, - listen_ip=ins.machine.ip, - listen_port=ins.port, - func_name=func_name, - labels_dict=ins_labels, - ) - # 保存到数据库 - ins.bk_instance_id = bk_instance_id - ins.save(update_fields=["bk_instance_id"]) +class PulsarCCTopoOperator(CCTopoOperator): + db_type = DBType.Pulsar.value diff --git a/dbm-ui/backend/flow/utils/redis/__init__.py b/dbm-ui/backend/flow/utils/redis/__init__.py new file mode 100644 index 0000000000..aa5085c628 --- /dev/null +++ b/dbm-ui/backend/flow/utils/redis/__init__.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" diff --git a/dbm-ui/backend/flow/utils/redis/redis_act_playload.py b/dbm-ui/backend/flow/utils/redis/redis_act_playload.py index bb21337c31..fafb7332c4 100644 --- a/dbm-ui/backend/flow/utils/redis/redis_act_playload.py +++ b/dbm-ui/backend/flow/utils/redis/redis_act_playload.py @@ -10,16 +10,29 @@ """ import copy import logging.config +import time from typing import Any +from django.utils.translation import ugettext as _ + +from backend import env from backend.components import DBConfigApi from backend.components.dbconfig.constants import FormatType, LevelName, OpType, ReqType from backend.configuration.constants import DBType from backend.configuration.models.system import SystemSettings -from backend.constants import BACKUP_SYS_STATUS, DEFAULT_BK_CLOUD_ID -from backend.db_meta.enums import InstanceRole, MachineType +from backend.constants import BACKUP_SYS_STATUS, IP_PORT_DIVIDER +from backend.db_meta import api as metaApi +from backend.db_meta.api.cluster import nosqlcomm from backend.db_meta.enums.cluster_type import ClusterType +from backend.db_meta.models import Cluster from backend.db_package.models import Package +from backend.db_services.redis.util import ( + is_predixy_proxy_type, + is_redis_instance_type, + is_tendisplus_instance_type, + is_tendisssd_instance_type, + is_twemproxy_proxy_type, +) from backend.db_services.version.constants import PredixyVersion, TwemproxyVersion from backend.flow.consts import ( DEFAULT_CONFIG_CONFIRM, @@ -37,14 +50,25 @@ from backend.ticket.constants import TicketType logger = logging.getLogger("flow") -apply_list = [TicketType.REDIS_SINGLE_APPLY.value, TicketType.REDIS_CLUSTER_APPLY.value] +apply_list = [ + TicketType.REDIS_SINGLE_APPLY.value, + TicketType.REDIS_CLUSTER_APPLY.value, + TicketType.REDIS_CLUSTER_SHARD_NUM_UPDATE.value, + TicketType.REDIS_CLUSTER_TYPE_UPDATE.value, +] global_list = [TicketType.REDIS_KEYS_DELETE.value] -scale_list = [TicketType.REDIS_SCALE.value, TicketType.PROXY_SCALE.value] +proxy_scale_list = [ + TicketType.PROXY_SCALE_UP.value, + TicketType.PROXY_SCALE_DOWN.value, +] +redis_scale_list = [ + TicketType.REDIS_SCALE_UPDOWN.value, +] cutoff_list = [ - TicketType.REDIS_CLUSTER_MASTER_CUTOFF.value, - TicketType.REDIS_CLUSTER_SLAVE_CUTOFF.value, - TicketType.REDIS_CLUSTER_PROXY_CUTOFF.value, + TicketType.REDIS_CLUSTER_CUTOFF.value, + TicketType.REDIS_CLUSTER_ADD_SLAVE.value, ] +tool_list = [TicketType.REDIS_DATA_STRUCTURE.value, TicketType.REDIS_DATA_STRUCTURE_TASK_DELETE.value] twemproxy_cluster_type_list = [ ClusterType.TendisTwemproxyRedisInstance.value, ClusterType.TwemproxyTendisSSDInstance.value, @@ -70,6 +94,7 @@ def __init__(self, ticket_data: dict, cluster: dict): self.proxy_pkg = None self.namespace = None self.proxy_version = None + self.init_proxy_config = None self.ticket_data = ticket_data self.cluster = cluster self.bk_biz_id = str(self.ticket_data["bk_biz_id"]) @@ -78,19 +103,20 @@ def __init__(self, ticket_data: dict, cluster: dict): ) self.__init_dbconfig_params() - if self.ticket_data["ticket_type"] in apply_list + cutoff_list: + if self.ticket_data["ticket_type"] in apply_list + cutoff_list + tool_list: self.account = self.__get_define_config(NameSpaceEnum.Common, ConfigFileEnum.OS, ConfigTypeEnum.OSConf) - self.init_redis_config = self.__get_define_config( - self.namespace, self.ticket_data["db_version"], ConfigTypeEnum.DBConf - ) - self.init_proxy_config = self.__get_define_config( - self.namespace, self.proxy_version, ConfigTypeEnum.ProxyConf - ) + if "db_version" in self.ticket_data: + self.init_redis_config = self.__get_define_config( + self.namespace, self.ticket_data["db_version"], ConfigTypeEnum.DBConf + ) + self.init_proxy_config = self.__get_define_config( + self.namespace, self.proxy_version, ConfigTypeEnum.ProxyConf + ) if self.ticket_data["ticket_type"] in global_list: self.global_config = self.__get_define_config( NameSpaceEnum.Common, ConfigFileEnum.Redis, ConfigTypeEnum.ActConf ) - if self.ticket_data["ticket_type"] in scale_list: + if self.ticket_data["ticket_type"] in redis_scale_list + proxy_scale_list: self.account = self.__get_define_config(NameSpaceEnum.Common, ConfigFileEnum.OS, ConfigTypeEnum.OSConf) def __init_dbconfig_params(self) -> Any: @@ -128,7 +154,7 @@ def set_redis_config(self, clusterMap: dict) -> Any: data = DBConfigApi.upsert_conf_item( { "conf_file_info": { - "conf_file": self.ticket_data["db_version"], + "conf_file": clusterMap["db_version"], "conf_type": ConfigTypeEnum.DBConf, "namespace": self.namespace, }, @@ -138,7 +164,7 @@ def set_redis_config(self, clusterMap: dict) -> Any: "req_type": ReqType.SAVE_AND_PUBLISH, "bk_biz_id": self.bk_biz_id, "level_name": LevelName.CLUSTER, - "level_value": self.ticket_data["domain_name"], + "level_value": clusterMap["domain_name"], } ) return data @@ -165,6 +191,108 @@ def delete_redis_config(self, clusterMap: dict): ) return data + def redis_conf_names_by_cluster_type(self, cluster_type: str) -> list: + conf_names: list = ["requirepass"] + if is_redis_instance_type(cluster_type) or is_tendisplus_instance_type(cluster_type): + conf_names.append("cluster-enabled") + if is_redis_instance_type(cluster_type) or is_tendisssd_instance_type(cluster_type): + conf_names.append("maxmemory") + conf_names.append("databases") + return conf_names + + def dts_swap_redis_config(self, clusterMap: dict): + """交换源集群和目标集群的redis配置""" + logger.info(_("开始交换源集群和目标集群的redis配置")) + logger.info(_("获取源集群:{} redis配置").format(clusterMap["src_cluster_domain"])) + src_resp = DBConfigApi.query_conf_item( + params={ + "bk_biz_id": str(clusterMap["bk_biz_id"]), + "level_name": LevelName.CLUSTER, + "level_value": clusterMap["src_cluster_domain"], + "level_info": {"module": str(DEFAULT_DB_MODULE_ID)}, + "conf_file": clusterMap["src_cluster_version"], + "conf_type": ConfigTypeEnum.DBConf, + "namespace": clusterMap["src_cluster_type"], + "format": FormatType.MAP, + } + ) + src_conf_names = self.redis_conf_names_by_cluster_type(clusterMap["src_cluster_type"]) + src_conf_items = [] + for conf_name in src_conf_names: + if conf_name in src_resp["content"]: + src_conf_items.append( + {"conf_name": conf_name, "conf_value": src_resp["content"][conf_name], "op_type": OpType.UPDATE} + ) + + logger.info(_("获取目标集群:{} redis配置").format(clusterMap["dst_cluster_domain"])) + dst_resp = DBConfigApi.query_conf_item( + params={ + "bk_biz_id": str(clusterMap["bk_biz_id"]), + "level_name": LevelName.CLUSTER, + "level_value": clusterMap["dst_cluster_domain"], + "level_info": {"module": str(DEFAULT_DB_MODULE_ID)}, + "conf_file": clusterMap["dst_cluster_version"], + "conf_type": ConfigTypeEnum.DBConf, + "namespace": clusterMap["dst_cluster_type"], + "format": FormatType.MAP, + } + ) + dst_conf_names = self.redis_conf_names_by_cluster_type(clusterMap["dst_cluster_type"]) + dst_conf_items = [] + for conf_name in dst_conf_names: + if conf_name in dst_resp["content"]: + dst_conf_items.append( + {"conf_name": conf_name, "conf_value": dst_resp["content"][conf_name], "op_type": OpType.UPDATE} + ) + + upsert_param = { + "conf_file_info": { + "conf_file": "", # 需要替换成真实值 + "conf_type": ConfigTypeEnum.DBConf, + "namespace": "", # 需要替换成真实值 + }, + "conf_items": [], # 需要替换成真实值 + "level_info": {"module": str(DEFAULT_DB_MODULE_ID)}, + "confirm": DEFAULT_CONFIG_CONFIRM, + "req_type": ReqType.SAVE_AND_PUBLISH, + "bk_biz_id": str(clusterMap["bk_biz_id"]), + "level_name": LevelName.CLUSTER, + "level_value": "", # 需要替换成真实值 + } + + if src_conf_names != dst_conf_names: + # 源集群、目标集群类型有变化, 先删除原有的配置,再更新配置,避免残留 + src_remove_items = [{"conf_name": conf_name, "op_type": OpType.REMOVE} for conf_name in src_conf_names] + upsert_param["conf_file_info"]["namespace"] = clusterMap["src_cluster_type"] + upsert_param["conf_file_info"]["conf_file"] = clusterMap["src_cluster_version"] + upsert_param["conf_items"] = src_remove_items + upsert_param["level_value"] = clusterMap["src_cluster_domain"] + logger.info(_("删除源集群:{} redis配置,upsert_param:{}".format(clusterMap["src_cluster_domain"], upsert_param))) + DBConfigApi.upsert_conf_item(upsert_param) + + dst_remove_items = [{"conf_name": conf_name, "op_type": OpType.REMOVE} for conf_name in dst_conf_names] + upsert_param["conf_file_info"]["namespace"] = clusterMap["dst_cluster_type"] + upsert_param["conf_file_info"]["conf_file"] = clusterMap["dst_cluster_version"] + upsert_param["conf_items"] = dst_remove_items + upsert_param["level_value"] = clusterMap["dst_cluster_domain"] + logger.info(_("删除目标集群:{} redis配置,upsert_param:{}").format(clusterMap["dst_cluster_domain"], upsert_param)) + DBConfigApi.upsert_conf_item(upsert_param) + time.sleep(2) + # 源集群 写入目标集群的配置 + upsert_param["conf_file_info"]["namespace"] = clusterMap["dst_cluster_type"] + upsert_param["conf_file_info"]["conf_file"] = clusterMap["dst_cluster_version"] + upsert_param["conf_items"] = dst_conf_items + upsert_param["level_value"] = clusterMap["src_cluster_domain"] + logger.info(_("更新源集群redis配置 为 目标集群的配置,upsert_param:{}".format(upsert_param))) + DBConfigApi.upsert_conf_item(upsert_param) + # 目标集群 写入源集群的配置 + upsert_param["conf_file_info"]["namespace"] = clusterMap["src_cluster_type"] + upsert_param["conf_file_info"]["conf_file"] = clusterMap["src_cluster_version"] + upsert_param["conf_items"] = src_conf_items + upsert_param["level_value"] = clusterMap["dst_cluster_domain"] + logger.info(_("更新目标集群redis配置 为 源集群的配置,upsert_param:{}".format(upsert_param))) + DBConfigApi.upsert_conf_item(upsert_param) + def delete_proxy_config(self, clusterMap: dict): conf_items = [ {"conf_name": conf_name, "op_type": OpType.REMOVE} for conf_name, _ in clusterMap["conf"].items() @@ -225,11 +353,125 @@ def set_proxy_config(self, clusterMap: dict) -> Any: "req_type": ReqType.SAVE_AND_PUBLISH, "bk_biz_id": self.bk_biz_id, "level_name": LevelName.CLUSTER, - "level_value": self.ticket_data["domain_name"], + "level_value": clusterMap["domain_name"], } ) return data + def dts_swap_proxy_config_version(self, clusterMap: dict) -> Any: + """ + 交换源集群和目标集群 dbconfig 中的proxy版本信息,有可能 twemproxy集群 切换到 predixy集群 s + """ + proxy_conf_names = ["password", "redis_password", "port"] + logger.info(_("交换源集群和目标集群 dbconfig 中的proxy版本信息")) + logger.info(_("获取源集群:{} proxy配置").format(clusterMap["src_cluster_domain"])) + src_resp = DBConfigApi.query_conf_item( + params={ + "bk_biz_id": str(clusterMap["bk_biz_id"]), + "level_name": LevelName.CLUSTER, + "level_value": clusterMap["src_cluster_domain"], + "level_info": {"module": str(DEFAULT_DB_MODULE_ID)}, + "conf_file": clusterMap["src_proxy_version"], + "conf_type": ConfigTypeEnum.ProxyConf, + "namespace": clusterMap["src_cluster_type"], + "format": FormatType.MAP, + } + ) + src_conf_upsert_items = [] + for conf_name in proxy_conf_names: + src_conf_upsert_items.append( + {"conf_name": conf_name, "conf_value": src_resp["content"][conf_name], "op_type": OpType.UPDATE} + ) + logger.info(_("src_conf_upsert_items==>{}".format(src_conf_upsert_items))) + + logger.info(_("获取目标集群:{} proxy配置").format(clusterMap["dst_cluster_domain"])) + dst_resp = DBConfigApi.query_conf_item( + params={ + "bk_biz_id": str(clusterMap["bk_biz_id"]), + "level_name": LevelName.CLUSTER, + "level_value": clusterMap["dst_cluster_domain"], + "level_info": {"module": str(DEFAULT_DB_MODULE_ID)}, + "conf_file": clusterMap["dst_proxy_version"], + "conf_type": ConfigTypeEnum.ProxyConf, + "namespace": clusterMap["dst_cluster_type"], + "format": FormatType.MAP, + } + ) + dst_conf_upsert_items = [] + for conf_name in proxy_conf_names: + dst_conf_upsert_items.append( + {"conf_name": conf_name, "conf_value": dst_resp["content"][conf_name], "op_type": OpType.UPDATE} + ) + logger.info(_("dst_conf_upsert_items==>{}".format(dst_conf_upsert_items))) + + upsert_param = { + "conf_file_info": { + "conf_file": "", # 需要替换成真实值 + "conf_type": ConfigTypeEnum.ProxyConf, + "namespace": "", # 需要替换成真实值 + }, + "conf_items": [], # 需要替换成真实值 + "level_info": {"module": str(DEFAULT_DB_MODULE_ID)}, + "confirm": DEFAULT_CONFIG_CONFIRM, + "req_type": ReqType.SAVE_AND_PUBLISH, + "bk_biz_id": str(clusterMap["bk_biz_id"]), + "level_name": LevelName.CLUSTER, + "level_value": "", # 需要替换成真实值 + } + + remove_items = [] + for conf_name in proxy_conf_names: + remove_items.append({"conf_name": conf_name, "op_type": OpType.REMOVE}) + # 删除源集群的proxy配置 + src_remove_param = copy.deepcopy(upsert_param) + src_remove_param["conf_file_info"]["conf_file"] = clusterMap["src_proxy_version"] + src_remove_param["conf_file_info"]["namespace"] = clusterMap["src_cluster_type"] + src_remove_param["conf_items"] = remove_items + src_remove_param["level_value"] = clusterMap["src_cluster_domain"] + logger.info( + _("删除源集群:{} proxy配置,src_remove_param:{}").format(clusterMap["src_cluster_domain"], src_remove_param) + ) + DBConfigApi.upsert_conf_item(src_remove_param) + + # 删除目标集群的proxy配置 + dst_remove_param = copy.deepcopy(upsert_param) + dst_remove_param["conf_file_info"]["conf_file"] = clusterMap["dst_proxy_version"] + dst_remove_param["conf_file_info"]["namespace"] = clusterMap["dst_cluster_type"] + dst_remove_param["conf_items"] = remove_items + dst_remove_param["level_value"] = clusterMap["dst_cluster_domain"] + logger.info( + _("删除目标集群:{} proxy配置,dst_remove_param:{}").format(clusterMap["dst_cluster_domain"], dst_remove_param) + ) + DBConfigApi.upsert_conf_item(dst_remove_param) + + time.sleep(2) + + # 更新源集群的proxy版本信息 + src_upsert_param = copy.deepcopy(upsert_param) + src_upsert_param["conf_file_info"]["conf_file"] = clusterMap["dst_proxy_version"] # 替换成目标集群的proxy版本 + src_upsert_param["conf_file_info"]["namespace"] = clusterMap["dst_cluster_type"] + src_upsert_param["conf_items"] = src_conf_upsert_items + src_upsert_param["level_value"] = clusterMap["src_cluster_domain"] + logger.info( + _("更新源集群:{} dbconfig 中proxy版本等信息,src_upsert_param:{}").format( + clusterMap["src_cluster_domain"], src_upsert_param + ) + ) + DBConfigApi.upsert_conf_item(src_upsert_param) + + # 更新目标集群的proxy版本信息 + dst_upsert_param = copy.deepcopy(upsert_param) + dst_upsert_param["conf_file_info"]["conf_file"] = clusterMap["src_proxy_version"] # 替换成源集群的proxy版本 + dst_upsert_param["conf_file_info"]["namespace"] = clusterMap["src_cluster_type"] + dst_upsert_param["conf_items"] = dst_conf_upsert_items + dst_upsert_param["level_value"] = clusterMap["dst_cluster_domain"] + logger.info( + _("更新目标集群:{} dbconfig 中proxy版本等信息,dst_upsert_param:{}").format( + clusterMap["dst_cluster_domain"], dst_upsert_param + ) + ) + DBConfigApi.upsert_conf_item(dst_upsert_param) + def get_sys_init_payload(self, **kwargs) -> dict: """ 初始化机器 @@ -373,11 +615,12 @@ def get_clustermeet_slotsassign_payload(self, **kwargs) -> dict: """ rediscluster 集群建立 """ + params = kwargs["params"] return { "db_type": DBActuatorTypeEnum.Redis.value, "action": RedisActuatorActionEnum.CLUSTER_MEET.value, "payload": { - "password": self.ticket_data["redis_pwd"], + "password": params["password"], "slots_auto_assign": True, }, } @@ -480,7 +723,7 @@ def redis_cluster_backup_payload(self, **kwargs) -> dict: "ports": self.cluster[ip], "backup_type": self.cluster["backup_type"], "domain": self.cluster["domain_name"], - "without_to_backup_sys": BACKUP_SYS_STATUS, + "without_to_backup_sys": not BACKUP_SYS_STATUS, }, } @@ -488,9 +731,6 @@ def proxy_operate_payload(self, **kwargs) -> dict: """ proxy启停、下架 """ - ip = kwargs["ip"] - port = self.cluster[ip] - op = str.lower(self.cluster["operate"]) action = "" if self.cluster["cluster_type"] in twemproxy_cluster_type_list: action = DBActuatorTypeEnum.Twemproxy.value + "_" + RedisActuatorActionEnum.Operate.value @@ -499,7 +739,7 @@ def proxy_operate_payload(self, **kwargs) -> dict: return { "db_type": DBActuatorTypeEnum.Proxy.value, "action": action, - "payload": {"ip": ip, "port": port, "operate": op}, + "payload": {}, } def redis_shutdown_payload(self, **kwargs) -> dict: @@ -641,6 +881,7 @@ def get_redis_batch_replicate(self, **kwargs) -> dict: }] """ params = kwargs["params"] + self.namespace = params["cluster_type"] redis_config = self.__get_cluster_config(params["immute_domain"], params["db_version"], ConfigTypeEnum.DBConf) replica_pairs = [] @@ -667,29 +908,35 @@ def get_redis_install_4_scene(self, **kwargs) -> dict: {"exec_ip":"xxx", "start_port":30000,"inst_num":12,"cluster_type":"","db_version":"","immute_domain":""} """ params = kwargs["params"] - redis_config = self.__get_cluster_config(params["immute_domain"], params["db_version"], ConfigTypeEnum.DBConf) + self.namespace = params["cluster_type"] self.__get_redis_pkg(params["cluster_type"], params["db_version"]) + redis_config = self.__get_cluster_config(params["immute_domain"], params["db_version"], ConfigTypeEnum.DBConf) redis_conf = copy.deepcopy(redis_config) + install_payload = { + "dbtoolspkg": {"pkg": self.tools_pkg.name, "pkg_md5": self.tools_pkg.md5}, + "pkg": self.redis_pkg.name, + "pkg_md5": self.redis_pkg.md5, + "db_type": params["cluster_type"], + "password": redis_config["requirepass"], + "data_dirs": ConfigDefaultEnum.DATA_DIRS, + "ports": [], + "databases": 1, # 给个默认值吧 + "maxmemory": 666, # 给个默认值吧 + "ip": params["exec_ip"], + "inst_num": int(params["inst_num"]), + "start_port": int(params["start_port"]), + "redis_conf_configs": redis_conf, + } + # tendisplus cluster 模式暂时不需要特别指定这两个参数 + if self.namespace in [ClusterType.TendisTwemproxyRedisInstance, ClusterType.TwemproxyTendisSSDInstance]: + install_payload["databases"] = int(redis_config["databases"]) + install_payload["maxmemory"] = int(float(redis_config["maxmemory"])) + return { "db_type": DBActuatorTypeEnum.Redis.value, "action": DBActuatorTypeEnum.Redis.value + "_" + RedisActuatorActionEnum.Install.value, - "payload": { - "dbtoolspkg": {"pkg": self.tools_pkg.name, "pkg_md5": self.tools_pkg.md5}, - "pkg": self.redis_pkg.name, - "pkg_md5": self.redis_pkg.md5, - "password": redis_config["requirepass"], - "databases": int(redis_config["databases"]), # , - "db_type": params["cluster_type"], - "maxmemory": int(float(redis_config["maxmemory"])), # - "data_dirs": ConfigDefaultEnum.DATA_DIRS, - "ports": [], - "redis_conf_configs": redis_conf, - "inst_num": int(params["inst_num"]), - # 以下为流程中需要补充的参数 - "ip": params["exec_ip"], - "start_port": int(params["start_port"]), - }, + "payload": install_payload, } def get_install_redis_apply_payload(self, **kwargs) -> dict: @@ -697,6 +944,7 @@ def get_install_redis_apply_payload(self, **kwargs) -> dict: 安装redisredis """ params = kwargs["params"] + self.namespace = params["cluster_type"] self.__get_redis_pkg(params["cluster_type"], params["db_version"]) redis_conf = copy.deepcopy(self.init_redis_config) @@ -735,8 +983,8 @@ def redis_cluster_backup_4_scene(self, **kwargs) -> dict: "payload": { "bk_biz_id": str(params["bk_biz_id"]), "domain": params["immute_domain"], - "ip": params["exec_ip"], - "ports": [params["backup_instance"]], + "ip": params["backup_host"], + "ports": params["backup_instances"], # "start_port":30000, # "inst_num":10, "backup_type": "normal_backup", @@ -818,31 +1066,39 @@ def redis_twemproxy_backends_4_scene(self, **kwargs) -> dict: params = kwargs["params"] return { - "db_type": DBActuatorTypeEnum.Redis.value, - "action": DBActuatorTypeEnum.Redis.value + "_" + RedisActuatorActionEnum.CheckSync.value, + "db_type": DBActuatorTypeEnum.Proxy.value, + "action": DBActuatorTypeEnum.Twemproxy.value + "_" + RedisActuatorActionEnum.CheckProxysMd5.value, "payload": {"instances": params["instances"], "cluster_type": params["cluster_type"]}, } # twemproxy 架构-实例切换 def redis_twemproxy_arch_switch_4_scene(self, **kwargs) -> dict: """{ - "cluster_meta":{}, + "cluster_id":0, + "immute_domain":"", + "cluster_type":"", "switch_info":{}, "switch_condition":{}, } """ - params = kwargs["params"] - cluster_meta = params["cluster_meta"] - - proxy_config = self.__get_cluster_config( - cluster_meta["immute_domain"], self.proxy_version, ConfigTypeEnum.ProxyConf - ) + params, proxy_version = kwargs["params"], "" + self.namespace = params["cluster_type"] + if self.namespace in [ + ClusterType.TendisTwemproxyRedisInstance.value, + ClusterType.TwemproxyTendisSSDInstance.value, + ]: + proxy_version = ConfigFileEnum.Twemproxy + elif self.namespace == ClusterType.TendisPredixyTendisplusCluster.value: + proxy_version = ConfigFileEnum.Predixy + proxy_config = self.__get_cluster_config(params["immute_domain"], proxy_version, ConfigTypeEnum.ProxyConf) + cluster_meta = nosqlcomm.other.get_cluster_detail(cluster_id=params["cluster_id"])[0] cluster_meta["proxy_pass"] = proxy_config["password"] cluster_meta["storage_pass"] = proxy_config["redis_password"] + logger.info("switch cluster {}, switch infos : {}".format(params["immute_domain"], params["switch_info"])) return { "db_type": DBActuatorTypeEnum.Redis.value, - "action": DBActuatorTypeEnum.Redis.value + "_" + RedisActuatorActionEnum.CheckSync.value, + "action": DBActuatorTypeEnum.Redis.value + "_" + RedisActuatorActionEnum.SwitchBackends.value, "payload": { "cluster_meta": cluster_meta, # dict "switch_info": params["switch_info"], # list @@ -859,6 +1115,7 @@ def redis_dts_datacheck_payload(self, **kwargs) -> dict: version=MediumEnum.Latest, pkg_type=MediumEnum.RedisTools, db_type=DBType.Redis ) ip = kwargs["ip"] + current_src_ip = kwargs["params"]["current_src_ip"] if kwargs["params"].get("current_src_ip") else ip return { "db_type": DBActuatorTypeEnum.Redis.value, "action": DBActuatorTypeEnum.Redis.value + "_" + RedisActuatorActionEnum.DTS_DATACHECK.value, @@ -867,14 +1124,373 @@ def redis_dts_datacheck_payload(self, **kwargs) -> dict: "pkg_md5": tools_pkg.md5, "bk_biz_id": self.bk_biz_id, "dts_copy_type": self.cluster["dts_copy_type"], - "src_redis_ip": ip, - "src_redis_port_segmentlist": self.cluster["meta_src_cluster_data"]["ports_group_by_ip"][ip], + "src_redis_ip": current_src_ip, + "src_redis_port_segmentlist": self.cluster[current_src_ip], + "src_hash_tag": False, + "src_redis_password": self.cluster["src_redis_password"], + "src_cluster_addr": self.cluster["src_cluster_addr"], + "dst_cluster_addr": self.cluster["dst_cluster_addr"], + "dst_cluster_password": self.cluster["dst_cluster_password"], + "key_white_regex": self.cluster["key_white_regex"], + "key_black_regex": self.cluster["key_black_regex"], + }, + } + + # redis dts数据修复 + def redis_dts_datarepair_payload(self, **kwargs) -> dict: + """ + redis dts数据修复 + """ + tools_pkg = Package.get_latest_package( + version=MediumEnum.Latest, pkg_type=MediumEnum.RedisTools, db_type=DBType.Redis + ) + ip = kwargs["ip"] + current_src_ip = kwargs["params"]["current_src_ip"] if kwargs["params"].get("current_src_ip") else ip + return { + "db_type": DBActuatorTypeEnum.Redis.value, + "action": DBActuatorTypeEnum.Redis.value + "_" + RedisActuatorActionEnum.DTS_DATAREPAIR.value, + "payload": { + "pkg": tools_pkg.name, + "pkg_md5": tools_pkg.md5, + "bk_biz_id": self.bk_biz_id, + "dts_copy_type": self.cluster["dts_copy_type"], + "src_redis_ip": current_src_ip, + "src_redis_port_segmentlist": self.cluster[current_src_ip], "src_hash_tag": False, - "src_redis_password": self.cluster["meta_src_cluster_data"]["src_redis_password"], - "src_cluster_addr": self.cluster["meta_src_cluster_data"]["src_cluster_addr"], - "dst_cluster_addr": self.cluster["meta_dst_cluster_data"]["dst_cluster_addr"], - "dst_cluster_password": self.cluster["meta_dst_cluster_data"]["dst_cluster_password"], + "src_redis_password": self.cluster["src_redis_password"], + "src_cluster_addr": self.cluster["src_cluster_addr"], + "dst_cluster_addr": self.cluster["dst_cluster_addr"], + "dst_cluster_password": self.cluster["dst_cluster_password"], "key_white_regex": self.cluster["key_white_regex"], "key_black_regex": self.cluster["key_black_regex"], }, } + + # redis dts 源集群 proxy在线切换 + def redis_dts_src_proxys_online_switch_payload(self, **kwargs) -> dict: + """ + redis dts 在线切换 + """ + params = kwargs["params"] + proxy_pkg: Package = None + if is_twemproxy_proxy_type(params["dst_cluster_type"]): + proxy_pkg = Package.get_latest_package( + version=TwemproxyVersion.TwemproxyLatest, pkg_type=MediumEnum.Twemproxy, db_type=DBType.Redis + ) + elif is_predixy_proxy_type(params["dst_cluster_type"]): + proxy_pkg = Package.get_latest_package( + version=PredixyVersion.PredixyLatest, pkg_type=MediumEnum.Predixy, db_type=DBType.Redis + ) + logger.info( + "redis_dts_src_proxys_online_switch_payload dst_proxy_config ===>{}".format( + self.cluster["dst_proxy_config"] + ) + ) + dst_proxy_config = self.cluster["dst_proxy_config"] + dst_proxy_config_data = "" + dst_proxy_ip = params["my_dst_proxy_ip"] + for k, v in dst_proxy_config.items(): + dst_proxy_ip = k + if isinstance(v, dict) and "data" in v: + dst_proxy_config_data = v["data"] + break + return { + "db_type": DBActuatorTypeEnum.Redis.value, + "action": DBActuatorTypeEnum.Redis.value + "_" + RedisActuatorActionEnum.DTS_ONLINE_SWITCH.value, + "payload": { + "dst_proxy_pkg": { + "pkg": proxy_pkg.name, + "pkg_md5": proxy_pkg.md5, + }, + "dts_bill_id": params["dts_bill_id"], + "src_proxy_ip": params["src_proxy_ip"], + "src_proxy_port": int(params["src_proxy_port"]), + "src_proxy_password": params["src_proxy_password"], + "src_cluster_type": params["src_cluster_type"], + "dst_proxy_ip": dst_proxy_ip, + "dst_proxy_port": int(params["dst_proxy_port"]), + "dst_proxy_password": params["dst_proxy_password"], + "dst_cluster_type": params["dst_cluster_type"], + "dst_redis_ip": params["dst_redis_ip"], + "dst_redis_port": int(params["dst_redis_port"]), + "dst_proxy_config_content": dst_proxy_config_data, + }, + } + + # redis dts 目标集群 proxy 在线切换 + def redis_dts_dst_proxys_online_switch_payload(self, **kwargs) -> dict: + """ + redis dts 在线切换 + """ + params = kwargs["params"] + proxy_pkg: Package = None + if is_twemproxy_proxy_type(params["dst_cluster_type"]): + proxy_pkg = Package.get_latest_package( + version=TwemproxyVersion.TwemproxyLatest, pkg_type=MediumEnum.Twemproxy, db_type=DBType.Redis + ) + elif is_predixy_proxy_type(params["dst_cluster_type"]): + proxy_pkg = Package.get_latest_package( + version=PredixyVersion.PredixyLatest, pkg_type=MediumEnum.Predixy, db_type=DBType.Redis + ) + logger.info( + "redis_dts_dst_proxys_online_switch_payload src_proxy_config ===>{}".format( + self.cluster["src_proxy_config"] + ) + ) + src_proxy_config = self.cluster["src_proxy_config"] + src_proxy_config_data = "" + dst_proxy_ip = params["my_dst_proxy_ip"] + for k, v in src_proxy_config.items(): + dst_proxy_ip = k + if isinstance(v, dict) and "data" in v: + src_proxy_config_data = v["data"] + break + return { + "db_type": DBActuatorTypeEnum.Redis.value, + "action": DBActuatorTypeEnum.Redis.value + "_" + RedisActuatorActionEnum.DTS_ONLINE_SWITCH.value, + "payload": { + "dst_proxy_pkg": { + "pkg": proxy_pkg.name, + "pkg_md5": proxy_pkg.md5, + }, + "dts_bill_id": params["dts_bill_id"], + "src_proxy_ip": params["src_proxy_ip"], + "src_proxy_port": int(params["src_proxy_port"]), + "src_proxy_password": params["src_proxy_password"], + "src_cluster_type": params["src_cluster_type"], + "dst_proxy_ip": dst_proxy_ip, + "dst_proxy_port": int(params["dst_proxy_port"]), + "dst_proxy_password": params["dst_proxy_password"], + "dst_cluster_type": params["dst_cluster_type"], + "dst_redis_ip": params["dst_redis_ip"], + "dst_redis_port": int(params["dst_redis_port"]), + "dst_proxy_config_content": src_proxy_config_data, + }, + } + + # Tendis ssd 重建热备 + def redis_tendisssd_dr_restore_4_scene(self, **kwargs) -> dict: + """#### Tendis ssd 重建热备 + { "backup_tasks":[] # from backup output. + "master_ip":params["master_ip"], + "master_ports":params["master_ports"], + "slave_ip":params["slave_ip"], + "slave_ports":params["slave_ports"], + } + """ + params = kwargs["params"] + self.namespace = params["cluster_type"] + proxy_config = self.__get_cluster_config(params["immute_domain"], self.proxy_version, ConfigTypeEnum.ProxyConf) + + return { + "db_type": DBActuatorTypeEnum.Redis.value, + "action": DBActuatorTypeEnum.TendisSSD.value + "_" + RedisActuatorActionEnum.DR_RESTORE.value, + "payload": { + "master_ip": params["master_ip"], + "master_ports": params["master_ports"], + "master_auth": proxy_config["redis_password"], + "slave_ip": params["slave_ip"], + "slave_ports": params["slave_ports"], + "slave_password": proxy_config["redis_password"], + "task_dir": "/data/dbbak", + "backup_dir": "/data/dbbak", + }, + } + + def get_add_dts_server_payload(self, **kwargs) -> dict: + """ + 获取dts server部署的payload + """ + dts_server_pkg = Package.get_latest_package( + version=MediumEnum.Latest, pkg_type=MediumEnum.RedisDts, db_type=DBType.Redis + ) + return { + "db_type": DBActuatorTypeEnum.Redis.value, + "action": DBActuatorTypeEnum.Redis.value + "_" + RedisActuatorActionEnum.ADD_DTS_SERVER.value, + "payload": { + "pkg": dts_server_pkg.name, + "pkg_md5": dts_server_pkg.md5, + "bk_biz_id": self.bk_biz_id, + "bk_dbm_nginx_url": self.cluster["nginx_url"], + "bk_dbm_cloud_id": self.cluster["bk_cloud_id"], + "bk_dbm_cloud_token": self.cluster["cloud_token"], + "system_user": self.cluster["system_user"], + "system_password": self.cluster["system_password"], + "city_name": self.cluster["bk_city_name"], + "warning_msg_notifiers": "xxxxx", + }, + } + + def get_remove_dts_server_payload(self, **kwargs) -> dict: + """ + 获取dts server删除的payload + """ + dts_server_pkg = Package.get_latest_package( + version=MediumEnum.Latest, pkg_type=MediumEnum.RedisDts, db_type=DBType.Redis + ) + return { + "db_type": DBActuatorTypeEnum.Redis.value, + "action": DBActuatorTypeEnum.Redis.value + "_" + RedisActuatorActionEnum.REMOVE_DTS_SERVER.value, + "payload": { + "pkg": dts_server_pkg.name, + "pkg_md5": dts_server_pkg.md5, + }, + } + + # redis 数据构造 + + def redis_data_structure(self, **kwargs) -> dict: + """ + redis 数据构造 + """ + params = kwargs["params"] + print("params", params) + return { + "db_type": DBActuatorTypeEnum.Redis.value, + "action": DBActuatorTypeEnum.Redis.value + "_" + RedisActuatorActionEnum.DATA_STRUCTURE.value, + "payload": { + "source_ip": params["data_params"]["source_ip"], + "source_ports": params["data_params"]["source_ports"], + "new_temp_ip": params["data_params"]["new_temp_ip"], + "new_temp_ports": params["data_params"]["new_temp_ports"], + "recovery_time_point": params["data_params"]["recovery_time_point"], + "is_precheck": params["data_params"]["is_precheck"], + "tendis_type": params["data_params"]["tendis_type"], + "user": self.account["user"], + "password": self.account["user_pwd"], + "base_info": { + "url": env.IBS_INFO_URL, + "sys_id": env.IBS_INFO_SYSID, + "key": env.IBS_INFO_KEY, + }, + }, + } + + # redis 数据构造集群建立 + def rollback_clustermeet_payload(self, **kwargs) -> dict: + """ + 数据构造 rediscluster 集群建立 + """ + params = kwargs["params"] + redis_config = self.__get_cluster_config(params["immute_domain"], params["db_version"], ConfigTypeEnum.DBConf) + bacth_pairs = [] + for instance in params["all_instance"]: + ip, port = instance.split(":") + pair = { + "master_ip": ip, + "master_port": int(port), + } + bacth_pairs.append(pair) + return { + "db_type": DBActuatorTypeEnum.Redis.value, + "action": RedisActuatorActionEnum.CLUSTER_MEET.value, + "payload": { + "password": redis_config["requirepass"], + "slots_auto_assign": True, + "replica_pairs": bacth_pairs, + }, + } + + # 数据构造 proxy 进程下架 + def proxy_shutdown_payload(self, **kwargs) -> dict: + """ + 数据构造 proxy 进程下架 + """ + params = kwargs["params"] + ip = params["proxy_ip"] + port = params["proxy_port"] + op = params["operate"] + action = "" + if self.cluster["cluster_type"] in twemproxy_cluster_type_list: + action = DBActuatorTypeEnum.Twemproxy.value + "_" + RedisActuatorActionEnum.Operate.value + elif self.cluster["cluster_type"] in predixy_cluster_type_list: + action = DBActuatorTypeEnum.Predixy.value + "_" + RedisActuatorActionEnum.Operate.value + return { + "db_type": DBActuatorTypeEnum.Proxy.value, + "action": action, + "payload": {"ip": ip, "port": port, "operate": op}, + } + + # redis 数据构造集群重建和校验 + def clustermeet_check_payload(self, **kwargs) -> dict: + """ + 数据构造 rediscluster meet建立集群关系并检查集群状态 + """ + params = kwargs["params"] + redis_config = self.__get_cluster_config(params["immute_domain"], params["db_version"], ConfigTypeEnum.DBConf) + bacth_pairs = [] + for instance in params["all_instance"]: + ip, port = instance.split(IP_PORT_DIVIDER) + bacth_pairs.append( + { + "master_ip": ip, + "master_port": int(port), + } + ) + return { + "db_type": DBActuatorTypeEnum.Redis.value, + "action": RedisActuatorActionEnum.CLUSTER_MEET_CHECK.value, + "payload": { + "password": redis_config["requirepass"], + "replica_pairs": bacth_pairs, + }, + } + + # 把心节点添加到已有的集群 + def redis_cluster_meet_4_scene(self, **kwargs) -> dict: + """{ + "immute_domain":"", + "db_version":"", + "meet_instances":[], + } + """ + params = kwargs["params"] + redis_config = self.__get_cluster_config(params["immute_domain"], params["db_version"], ConfigTypeEnum.DBConf) + cluster = Cluster.objects.get(immute_domain=params["immute_domain"]) + cluster_info = metaApi.cluster.nosqlcomm.get_cluster_detail(cluster.id)[0] + bacth_pairs = [] + oneNodeInCluster = cluster_info["redis_master_set"][0] + bacth_pairs.append( + { + "master_ip": oneNodeInCluster.split(IP_PORT_DIVIDER)[0], + "master_port": int(oneNodeInCluster.split(IP_PORT_DIVIDER)[1]), + } + ) + bacth_pairs.extend(params["meet_instances"]) + return { + "db_type": DBActuatorTypeEnum.Redis.value, + "action": RedisActuatorActionEnum.CLUSTER_MEET.value, + "payload": { + "password": redis_config["requirepass"], + "replica_pairs": bacth_pairs, + "slots_auto_assign": False, + "use_for_expansion": False, + }, + } + + # 从集群中去掉节点 + def redis_cluster_forget_4_scene(self, **kwargs) -> dict: + """{ + "immute_domain":"", + "db_version":"", + "forget_instances":[], + } + """ + params = kwargs["params"] + redis_config = self.__get_cluster_config(params["immute_domain"], params["db_version"], ConfigTypeEnum.DBConf) + cluster = Cluster.objects.get(immute_domain=params["immute_domain"]) + cluster_info = metaApi.cluster.nosqlcomm.get_cluster_detail(cluster.id)[0] + + return { + "db_type": DBActuatorTypeEnum.Redis.value, + "action": DBActuatorTypeEnum.Redis.value + "_" + RedisActuatorActionEnum.ClusterForget.value, + "payload": { + "cluster_meta": { + "storage_pass": redis_config["requirepass"], + "immute_domain": cluster_info["immute_domain"], + "cluster_type": cluster_info["cluster_type"], + "redis_master_set": cluster_info["redis_master_set"], + }, + "forget_list": params["forget_instances"], + }, + } diff --git a/dbm-ui/backend/flow/utils/redis/redis_context_dataclass.py b/dbm-ui/backend/flow/utils/redis/redis_context_dataclass.py index 0084437611..f7e3c0facd 100644 --- a/dbm-ui/backend/flow/utils/redis/redis_context_dataclass.py +++ b/dbm-ui/backend/flow/utils/redis/redis_context_dataclass.py @@ -30,6 +30,28 @@ class DnsKwargs: dns_op_exec_port: int = None # 如果做添加或者更新域名管理,执行实例的port +@dataclass() +class ClbKwargs: + """ + 定义clb管理的活动节点专属参数 + """ + + clb_op_type: Optional[Any] # 操作方式 + clb_ip: str = None # clb ip + clb_op_exec_port: int = None # 如果做添加或者更新域名管理,执行实例的port + + +@dataclass() +class PolarisKwargs: + """ + 定义polaris管理的活动节点专属参数 + """ + + polaris_op_type: Optional[Any] # 操作方式 + servicename: str = None + polaris_op_exec_port: int = None + + @dataclass() class ActKwargs: """ @@ -46,7 +68,6 @@ class ActKwargs: extend_attr: list = field(default_factory=list) # 表示单据执行的额外参数 meta_func_name: str = None # 元数据dbmeta的方法名称,空则传入None meta_read_flag: bool = False # 元数据dbmetea操作类型是是否是读 - ip_index: int = None # 遍历ip列表时的下标索引 write_op: str = None bk_cloud_id: int = DEFAULT_BK_CLOUD_ID # 云区域id,默认为直连区域 @@ -57,6 +78,7 @@ class CommonContext: 通用上下文 """ + tendis_backup_info: list = None # 执行备份后的信息 redis_act_payload: Optional[Any] = None # 代表获取payload参数的类 @@ -88,35 +110,74 @@ class RedisDtsContext: redis dts上下文 """ - bk_biz_id: str = None # 代表业务id - bk_cloud_id: int = None # 代表云区域id - dts_bill_type: str = None # 代表dts单据类型 - dts_copy_type: str = None # 代表dts数据复制类型 - src_cluster_id: str = None # 代表源集群id - src_cluster_addr: str = None # 代表源集群地址 - src_cluster_password: str = None # 代表源集群密码 - src_cluster_type: str = None # 代表源集群类型 - src_cluster_region: str = None # 代表源集群所在区域 - src_cluster_running_master: dict = None # 代表源集群一个正在运行的master - src_redis_password: str = None # 代表源redis密码 - src_slave_instances: list = None # 代表源redis slave实例列表 - src_slave_hosts: list = None # 代表源redis slave host列表 - src_proxy_instances: list = None # 代表源redis proxy实例列表 - src_proxy_config: str = None # 代表源redis proxy配置 - - dst_cluster_id: str = None # 代表目标集群id - dst_cluster_addr: str = None # 代表目标集群地址 - dst_cluster_password: str = None # 代表目标集群密码 - dst_redis_password: str = None # 代表目的redis密码 - dst_cluster_type: str = None # 代表目标集群类型 - dst_proxy_config: str = None # 代表目标redis proxy配置 - - key_white_regex: str = None # 代表key白名单正则 - key_black_regex: str = None # 代表key黑名单正则 + redis_act_payload: Optional[Any] = None # 代表获取payload参数的类 disk_used: dict = field(default_factory=dict) - + dst_cluster_install_flow_id: str = None # 代表目标集群安装flow id + dst_cluster_flush_flow_id: str = None # 代表目标集群清档 flow id job_id: int = None # 代表dts job id,对应表tb_tendis_dts_job task_ids: list = None # 代表dts task id列表,对应表tb_tendis_dts_task + tendis_backup_info: list = None # 执行备份后的信息 + + +@dataclass() +class RedisDtsOnlineSwitchContext: + """ + redis dts在线切换上下文 + """ + + redis_act_payload: Optional[Any] = None # 代表获取payload参数的类 + src_proxy_config: dict = field(default_factory=dict) + dst_proxy_config: dict = field(default_factory=dict) + tendis_backup_info: list = None # 执行备份后的信息 + + +@dataclass +class RedisDataStructureContext: + """ + redis 数据构造,上下文dataclass类 + """ + + tendis_backup_info: list = None # 执行备份后的信息 + new_master_ips: list = None # 代表在资源池分配到的master列表 + shard_num: int = None # 集群分片数 + inst_num: int = None # 集群单机实例个数 + servers: list = None # proxy分片规则数组 + new_install_proxy_exec_ip: str = None # 选取其中一台master作为部署 Proxy 的 IP + start_port: int = None + cluster_id: str = None + redis_act_payload: Optional[Any] = None # 代表获取payload参数的类 - def toJSON(self): - return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True, indent=4) + def cal_twemproxy_serveres(self, name) -> list: + """ + 计算twemproxy的servers 列表 + - redisip:redisport:1 admin beginSeg-endSeg 1 + "servers": ["1.1.1.1:30000 xxx 0-219999 1","1.1.1.1:30001 xxx 220000-419999 1"] + """ + shard_num = self.shard_num + redis_list = self.new_master_ips + name = name + + inst_num = self.inst_num + seg_num = DEFAULT_TWEMPROXY_SEG_TOTOL_NUM // shard_num + seg_no = 0 + + # 计算分片 + servers = [] + for ip in redis_list: + for inst_no in range(0, inst_num): + port = DEFAULT_REDIS_START_PORT + inst_no + begin_seg = seg_no * seg_num + end_seg = seg_num * (seg_no + 1) - 1 + if inst_no == inst_num - 1 and end_seg != DEFAULT_TWEMPROXY_SEG_TOTOL_NUM: + end_seg = DEFAULT_TWEMPROXY_SEG_TOTOL_NUM - 1 + seg_no = seg_no + 1 + servers.append("{}:{} {} {}-{} {}".format(ip, port, name, begin_seg, end_seg, 1)) + return servers + + @staticmethod + def get_redis_master_var_name() -> str: + return "new_master_ips" + + @staticmethod + def get_proxy_exec_ip_var_name() -> str: + return "new_install_proxy_exec_ip" diff --git a/dbm-ui/backend/flow/utils/redis/redis_db_meta.py b/dbm-ui/backend/flow/utils/redis/redis_db_meta.py index 3e0da7e536..454cacfc07 100644 --- a/dbm-ui/backend/flow/utils/redis/redis_db_meta.py +++ b/dbm-ui/backend/flow/utils/redis/redis_db_meta.py @@ -8,19 +8,36 @@ 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. """ +import base64 +import copy import logging +from typing import Any +from django.db import transaction from django.db.transaction import atomic from django.utils.translation import ugettext as _ +from backend.components import DBConfigApi +from backend.components.dbconfig.constants import FormatType, LevelName from backend.db_meta import api +from backend.db_meta.api.cluster.nosqlcomm.create_cluster import update_cluster_type from backend.db_meta.api.cluster.tendiscache.handler import TendisCacheClusterHandler from backend.db_meta.api.cluster.tendispluscluster.handler import TendisPlusClusterHandler from backend.db_meta.api.cluster.tendisssd.handler import TendisSSDClusterHandler -from backend.db_meta.enums import ClusterPhase, ClusterType, InstanceInnerRole, InstanceRole, MachineType -from backend.db_meta.models import Cluster, StorageInstance +from backend.db_meta.enums import ( + AccessLayer, + ClusterEntryType, + ClusterPhase, + ClusterType, + InstanceInnerRole, + InstanceRole, + MachineType, +) +from backend.db_meta.models import Cluster, ClusterEntry, Machine, ProxyInstance, StorageInstance, StorageInstanceTuple from backend.db_services.dbbase.constants import IP_PORT_DIVIDER, SPACE_DIVIDER -from backend.flow.consts import DEFAULT_DB_MODULE_ID, InstanceStatus +from backend.db_services.redis.rollback.models import TbTendisRollbackTasks +from backend.flow.consts import DEFAULT_DB_MODULE_ID, ConfigFileEnum, ConfigTypeEnum, InstanceStatus +from backend.flow.utils.redis.redis_module_operate import RedisCCTopoOperator from backend.ticket.constants import TicketType logger = logging.getLogger("flow") @@ -63,7 +80,15 @@ def proxy_install(self) -> bool: proxies = [] machine_type = self.cluster["machine_type"] for ip in self.cluster["new_proxy_ips"]: - machines.append({"bk_biz_id": self.cluster["bk_biz_id"], "ip": ip, "machine_type": machine_type}) + machines.append( + { + "bk_biz_id": self.cluster["bk_biz_id"], + "ip": ip, + "machine_type": machine_type, + "spec_id": self.cluster["spec_id"], + "spec_config": self.cluster["spec_config"], + } + ) proxies.append({"ip": ip, "port": self.cluster["port"]}) with atomic(): @@ -77,20 +102,13 @@ def proxy_install(self) -> bool: def proxy_add_cluster(self) -> bool: """ - proxy上架,并加入集群 + proxy只做加入集群操作 """ proxies = [] - machines = [] - machine_type = self.cluster["machine_type"] for ip in self.cluster["proxy_ips"]: proxies.append({"ip": ip, "port": self.cluster["proxy_port"]}) - machines.append({"bk_biz_id": self.ticket_data["bk_biz_id"], "ip": ip, "machine_type": machine_type}) cluster = Cluster.objects.get(immute_domain=self.cluster["domain_name"]) with atomic(): - api.machine.create( - machines=machines, creator=self.ticket_data["created_by"], bk_cloud_id=self.ticket_data["bk_cloud_id"] - ) - api.proxy_instance.create(proxies=proxies) api.cluster.nosqlcomm.add_proxies(cluster, proxies=proxies) return True @@ -146,35 +164,58 @@ def redis_install(self) -> bool: """ Redis实例上架、单机器级别。 """ - machines = [] - ins = [] - if self.ticket_data["cluster_type"] == ClusterType.TendisTwemproxyRedisInstance.value: + machines, ins, cluster_type = [], [], "" + if "cluster_type" in self.ticket_data: + cluster_type = self.ticket_data["cluster_type"] + else: + cluster_type = self.cluster["cluster_type"] + + if cluster_type == ClusterType.TendisTwemproxyRedisInstance.value: machine_type = MachineType.TENDISCACHE.value - elif self.ticket_data["cluster_type"] == ClusterType.TendisPredixyTendisplusCluster.value: + elif cluster_type == ClusterType.TendisPredixyTendisplusCluster.value: machine_type = MachineType.TENDISPLUS.value - elif self.ticket_data["cluster_type"] == ClusterType.TwemproxyTendisSSDInstance.value: + elif cluster_type == ClusterType.TwemproxyTendisSSDInstance.value: machine_type = MachineType.TENDISSSD.value else: machine_type = "" if self.cluster.get("new_master_ips"): for ip in self.cluster.get("new_master_ips"): - machines.append({"bk_biz_id": self.ticket_data["bk_biz_id"], "ip": ip, "machine_type": machine_type}) + machines.append( + { + "bk_biz_id": self.ticket_data["bk_biz_id"], + "ip": ip, + "machine_type": machine_type, + "spec_id": self.cluster["spec_id"], + "spec_config": self.cluster["spec_config"], + } + ) for n in range(0, self.cluster["inst_num"]): port = n + self.cluster["start_port"] ins.append({"ip": ip, "port": port, "instance_role": InstanceRole.REDIS_MASTER.value}) if self.cluster.get("new_slave_ips"): for ip in self.cluster.get("new_slave_ips"): - machines.append({"bk_biz_id": self.ticket_data["bk_biz_id"], "ip": ip, "machine_type": machine_type}) + machines.append( + { + "bk_biz_id": self.ticket_data["bk_biz_id"], + "ip": ip, + "machine_type": machine_type, + "spec_id": self.cluster["spec_id"], + "spec_config": self.cluster["spec_config"], + } + ) for n in range(0, self.cluster["inst_num"]): port = n + self.cluster["start_port"] ins.append({"ip": ip, "port": port, "instance_role": InstanceRole.REDIS_SLAVE.value}) with atomic(): - api.machine.create( - machines=machines, creator=self.ticket_data["created_by"], bk_cloud_id=self.ticket_data["bk_cloud_id"] - ) + bk_cloud_id = 0 + if "bk_cloud_id" in self.ticket_data: + bk_cloud_id = self.ticket_data["bk_cloud_id"] + else: + bk_cloud_id = self.cluster["bk_cloud_id"] + api.machine.create(machines=machines, creator=self.ticket_data["created_by"], bk_cloud_id=bk_cloud_id) api.storage_instance.create(instances=ins, creator=self.ticket_data["created_by"]) return True @@ -210,7 +251,7 @@ def redis_make_cluster(self) -> bool: """ 建立集群关系 """ - proxy_port = self.ticket_data["proxy_port"] + proxy_port = self.cluster["proxy_port"] proxies = [{"ip": proxy_ip, "port": proxy_port} for proxy_ip in self.cluster["new_proxy_ips"]] storages = [] @@ -218,36 +259,36 @@ def redis_make_cluster(self) -> bool: ip_port, _, seg_range, _ = server.split(SPACE_DIVIDER) ip, port = ip_port.split(IP_PORT_DIVIDER) storages.append({"ip": ip, "port": port, "seg_range": seg_range}) - if self.ticket_data["cluster_type"] == ClusterType.TendisTwemproxyRedisInstance.value: + if self.cluster["cluster_type"] == ClusterType.TendisTwemproxyRedisInstance.value: TendisCacheClusterHandler.create( **{ - "bk_biz_id": self.ticket_data["bk_biz_id"], - "bk_cloud_id": self.ticket_data["bk_cloud_id"], - "name": self.ticket_data["cluster_name"], - "alias": self.ticket_data["cluster_alias"], - "major_version": self.ticket_data["db_version"], - "immute_domain": self.ticket_data["domain_name"], + "bk_biz_id": self.cluster["bk_biz_id"], + "bk_cloud_id": self.cluster["bk_cloud_id"], + "name": self.cluster["cluster_name"], + "alias": self.cluster["cluster_alias"], + "major_version": self.cluster["db_version"], + "immute_domain": self.cluster["immute_domain"], "db_module_id": DEFAULT_DB_MODULE_ID, "proxies": proxies, "storages": storages, - "creator": self.ticket_data["created_by"], - "region": self.ticket_data.get("city_code"), + "creator": self.cluster["created_by"], + "region": self.cluster.get("region", ""), } ) - elif self.ticket_data["cluster_type"] == ClusterType.TwemproxyTendisSSDInstance.value: + elif self.cluster["cluster_type"] == ClusterType.TwemproxyTendisSSDInstance.value: TendisSSDClusterHandler.create( **{ - "bk_biz_id": self.ticket_data["bk_biz_id"], - "bk_cloud_id": self.ticket_data["bk_cloud_id"], - "name": self.ticket_data["cluster_name"], - "alias": self.ticket_data["cluster_alias"], - "major_version": self.ticket_data["db_version"], - "immute_domain": self.ticket_data["domain_name"], + "bk_biz_id": self.cluster["bk_biz_id"], + "bk_cloud_id": self.cluster["bk_cloud_id"], + "name": self.cluster["cluster_name"], + "alias": self.cluster["cluster_alias"], + "major_version": self.cluster["db_version"], + "immute_domain": self.cluster["immute_domain"], "db_module_id": DEFAULT_DB_MODULE_ID, "proxies": proxies, "storages": storages, - "creator": self.ticket_data["created_by"], - "region": self.ticket_data.get("city_code"), + "creator": self.cluster["created_by"], + "region": self.cluster.get("region", ""), } ) @@ -257,7 +298,7 @@ def tendisplus_make_cluster(self) -> bool: """ tendisplus建立集群关系 """ - proxy_port = self.ticket_data["proxy_port"] + proxy_port = self.cluster["proxy_port"] proxies = [{"ip": proxy_ip, "port": proxy_port} for proxy_ip in self.cluster["new_proxy_ips"]] inst_num = self.cluster["inst_num"] master_start_port = self.cluster["start_port"] @@ -271,17 +312,17 @@ def tendisplus_make_cluster(self) -> bool: TendisPlusClusterHandler.create( **{ - "bk_biz_id": self.ticket_data["bk_biz_id"], - "bk_cloud_id": self.ticket_data["bk_cloud_id"], - "name": self.ticket_data["cluster_name"], - "alias": self.ticket_data["cluster_alias"], - "major_version": self.ticket_data["db_version"], - "immute_domain": self.ticket_data["domain_name"], + "bk_biz_id": self.cluster["bk_biz_id"], + "bk_cloud_id": self.cluster["bk_cloud_id"], + "name": self.cluster["cluster_name"], + "alias": self.cluster["cluster_alias"], + "major_version": self.cluster["db_version"], + "immute_domain": self.cluster["immute_domain"], "db_module_id": DEFAULT_DB_MODULE_ID, "proxies": proxies, "storages": storages, - "creator": self.ticket_data["created_by"], - "region": self.ticket_data.get("city_code"), + "creator": self.cluster["created_by"], + "region": self.cluster.get("region", ""), } ) return True @@ -355,11 +396,11 @@ def redis_replace_pair(self) -> bool: tendiss, replic_tuple = [], [] for relation in self.cluster["sync_relation"]: receiver = relation["receiver"] - slave_obj = StorageInstance.objects.filter(machine__ip=receiver["ip"], port=receiver["port"]) + slave_obj = StorageInstance.objects.get(machine__ip=receiver["ip"], port=receiver["port"]) slave_obj.cluster_type = self.cluster["cluster_type"] slave_obj.instance_role = InstanceRole.REDIS_SLAVE.value slave_obj.instance_inner_role = InstanceInnerRole.SLAVE.value - slave_obj.update(update_fields=["instance_role", "instance_inner_role", "cluster_type"]) + slave_obj.save(update_fields=["instance_role", "instance_inner_role", "cluster_type"]) tendiss.append( { @@ -376,9 +417,29 @@ def instances_status_update(self) -> bool: Redis/Proxy 实例修改实例状态 {"ip":"","ports":[],"status":11} """ with atomic(): - api.cluster.nosqlcomm.decommission_instances( - ip=self.cluster["ip"], bk_cloud_id=self.cluster["bk_cloud_id"], ports=self.cluster["ports"] - ) + machine_obj = Machine.objects.get(ip=self.cluster["meta_update_ip"]) + if machine_obj.access_layer == AccessLayer.PROXY.value: + ProxyInstance.objects.filter( + machine__ip=self.cluster["meta_update_ip"], port__in=self.cluster["meta_udpate_ports"] + ).update(status=self.cluster["meta_update_status"]) + else: + StorageInstance.objects.filter( + machine__ip=self.cluster["meta_update_ip"], port__in=self.cluster["meta_udpate_ports"] + ).update(status=self.cluster["meta_update_status"]) + return True + + def instances_failover_4_scene(self) -> bool: + """1.修改状态、2.切换角色""" + self.instances_status_update() + with atomic(): + for port in self.cluster["meta_udpate_ports"]: + old_master = StorageInstance.objects.get(machine__ip=self.cluster["meta_update_ip"], port=port) + old_slave = old_master.as_ejector.get().receiver + StorageInstanceTuple.objects.get(ejector=old_master, receiver=old_slave).delete(keep_parents=True) + StorageInstanceTuple.objects.create(ejector=old_slave, receiver=old_master) + old_master.instance_role = InstanceRole.REDIS_SLAVE.value + old_master.instance_inner_role = InstanceInnerRole.SLAVE.value + old_master.save(update_fields=["instance_role", "instance_inner_role"]) return True def tendis_switch_4_scene(self): @@ -386,4 +447,315 @@ def tendis_switch_4_scene(self): cluster = Cluster.objects.get( bk_cloud_id=self.cluster["bk_cloud_id"], immute_domain=self.cluster["immute_domain"] ) - api.cluster.nosqlcomm.switch_tendis(cluster=cluster, tendisss=self.cluster["sync_relation"]) + api.cluster.nosqlcomm.switch_tendis( + cluster=cluster, + tendisss=self.cluster["sync_relation"], + switch_type=self.cluster["switch_condition"]["sync_type"], + ) + + def update_rollback_task_status(self) -> bool: + """ + 更新构造记录为已销毁 + """ + task = TbTendisRollbackTasks.objects.filter( + related_rollback_bill_id=self.cluster["related_rollback_bill_id"], + bk_biz_id=self.cluster["bk_biz_id"], + prod_cluster=self.cluster["prod_cluster"], + ).update(destroyed_status=self.cluster["destroyed_status"]) + return task + + def __get_cluster_config(self, domain_name: str, db_version: str, conf_type: str, namespace: str) -> Any: + """ + 获取已部署的实例配置 + """ + + data = DBConfigApi.query_conf_item( + params={ + "bk_biz_id": str(self.ticket_data["bk_biz_id"]), + "level_name": LevelName.CLUSTER, + "level_value": domain_name, + "level_info": {"module": str(DEFAULT_DB_MODULE_ID)}, + "conf_file": db_version, + "conf_type": conf_type, + "namespace": namespace, + "format": FormatType.MAP, + } + ) + return data["content"] + + @transaction.atomic + def data_construction_tasks_operate(self): + """ + 写入构造记录元数据 + """ + + if self.cluster["cluster_type"] == ClusterType.TendisTwemproxyRedisInstance.value: + self.cluster["proxy_version"] = ConfigFileEnum.Twemproxy + elif self.cluster["cluster_type"] == ClusterType.TwemproxyTendisSSDInstance.value: + self.cluster["proxy_version"] = ConfigFileEnum.Twemproxy + elif self.cluster["cluster_type"] == ClusterType.TendisPredixyTendisplusCluster.value: + self.cluster["proxy_version"] = ConfigFileEnum.Predixy + proxy_config = self.__get_cluster_config( + self.cluster["domain_name"], + self.cluster["proxy_version"], + ConfigTypeEnum.ProxyConf, + self.cluster["cluster_type"], + ) + + task = TbTendisRollbackTasks( + creator=self.ticket_data["created_by"], + related_rollback_bill_id=self.ticket_data["uid"], + bk_biz_id=self.ticket_data["bk_biz_id"], + bk_cloud_id=self.cluster["bk_cloud_id"], + prod_cluster_type=self.cluster["prod_cluster_type"], + prod_cluster_id=self.cluster["prod_cluster_id"], + specification=self.cluster["specification"], + prod_cluster=self.cluster["prod_cluster"], + prod_instance_range=self.cluster["prod_instance_range"], + temp_cluster_type=self.cluster["temp_cluster_type"], + temp_instance_range=self.cluster["temp_instance_range"], + temp_proxy_password=base64.b64encode(proxy_config["password"].encode("utf-8")), + temp_cluster_proxy=self.cluster["temp_cluster_proxy"], + prod_temp_instance_pairs=self.cluster["prod_temp_instance_pairs"], + host_count=self.cluster["host_count"], + recovery_time_point=self.cluster["recovery_time_point"], + status=self.cluster["status"], + temp_redis_password=base64.b64encode(proxy_config["redis_password"].encode("utf-8")), + ) + task.save() + + def redis_rollback_host_transfer(self) -> bool: + """ + 数据构造临时机器挪动cc模块到对应源集群下 + """ + with atomic(): + receiver_objs = [] + for ins in self.cluster["tendiss"]: + new_ejector_obj = StorageInstance.objects.get( + machine__ip=ins["receiver"]["ip"], port=ins["receiver"]["port"] + ) + logger.info(" need move cc module") + receiver_objs.append(new_ejector_obj) + cluster = Cluster.objects.get( + bk_cloud_id=self.cluster["bk_cloud_id"], immute_domain=self.cluster["immute_domain"] + ) + RedisCCTopoOperator(cluster).transfer_instances_to_cluster_module(receiver_objs) + return True + + def redis_role_swap_4_scene(self) -> bool: + """ + 主从互切 + act_kwargs.cluster["role_swap_host"].append({"new_ejector":new_host_master,"new_receiver":old_master}) + """ + with atomic(): + for ins in self.cluster["role_swap_ins"]: + ins1 = StorageInstance.objects.get(machine__ip=ins["new_receiver_ip"], port=ins["new_receiver_port"]) + ins2 = StorageInstance.objects.get(machine__ip=ins["new_ejector_ip"], port=ins["new_ejector_port"]) + + # 修改 proxy backend + temp_proxy_set = list(ins1.proxyinstance_set.all()) + ins1.proxyinstance_set.clear() + ins1.proxyinstance_set.add(*ins2.proxyinstance_set.all()) + ins2.proxyinstance_set.clear() + ins2.proxyinstance_set.add(*temp_proxy_set) + # 变更同步关系 + StorageInstanceTuple.objects.get(ejector=ins1, receiver=ins2).delete(keep_parents=True) + StorageInstanceTuple.objects.create(ejector=ins2, receiver=ins1) + + # 变更角色 + temp_instance_role = ins1.instance_role + tmep_instance_inner_role = ins1.instance_inner_role + + ins1.instance_role = ins2.instance_role + ins1.instance_inner_role = ins2.instance_inner_role + + ins2.instance_role = temp_instance_role + ins2.instance_inner_role = tmep_instance_inner_role + + ins1.save(update_fields=["instance_role", "instance_inner_role"]) + ins2.save(update_fields=["instance_role", "instance_inner_role"]) + + return True + + def tendis_add_clb_domain_4_scene(self): + """ 增加CLB 域名 """ + cluster = Cluster.objects.get( + bk_cloud_id=self.cluster["bk_cloud_id"], immute_domain=self.cluster["immute_domain"] + ) + clb = cluster.clusterentry_set.filter(cluster_entry_type=ClusterEntryType.CLB.value).first() + logger.info("add clb domain 4 clb.{} : {}".format(cluster.immute_domain, clb.entry)) + cluster_entry = ClusterEntry.objects.create( + cluster=cluster, + cluster_entry_type=ClusterEntryType.CLBDNS, + entry="clb.{}".format(cluster.immute_domain), + creator=self.ticket_data["created_by"], + forward_to_id=clb.id, + ) + cluster_entry.save() + + def tendis_bind_clb_domain_4_scene(self): + """ 主域名直接指向CLB """ + cluster = Cluster.objects.get( + bk_cloud_id=self.cluster["bk_cloud_id"], immute_domain=self.cluster["immute_domain"] + ) + immuteEntry = cluster.clusterentry_set.filter( + cluster_entry_type=ClusterEntryType.DNS.value, entry=cluster.immute_domain + ).first() + clbEntry = cluster.clusterentry_set.filter(cluster_entry_type=ClusterEntryType.CLB.value).first() + + logger.info("bind immute domain {} 2 clb {}".format(cluster.immute_domain, clbEntry.entry)) + immuteEntry.forward_to_id = clbEntry.id + immuteEntry.creator = self.ticket_data["created_by"] + immuteEntry.save(update_fields=["forward_to_id", "creator"]) + + # 主域名解绑CLB + def tendis_unBind_clb_domain_4_scene(self): + """ 主域名解绑CLB """ + cluster = Cluster.objects.get( + bk_cloud_id=self.cluster["bk_cloud_id"], immute_domain=self.cluster["immute_domain"] + ) + immuteEntry = cluster.clusterentry_set.filter( + cluster_entry_type=ClusterEntryType.DNS.value, entry=cluster.immute_domain + ).first() + logger.info("Unbind immute domain {} 2 clbid: {}".format(cluster.immute_domain, immuteEntry.forward_to_id)) + + immuteEntry.forward_to_id = None + immuteEntry.creator = self.ticket_data["created_by"] + immuteEntry.save(update_fields=["forward_to_id", "creator"]) + + def get_inst_list(self, storageinstances) -> list: + src_host = [] + for inst in storageinstances: + src_host.append({"host_id": inst.machine.bk_host_id, "ip": inst.machine.ip, "port": inst.port}) + return src_host + + def dts_online_switch_swap_two_cluster_storage(self): + """ + dts在线切换,交换两个集群的storageinstances + """ + src_cluster_id: int = self.cluster["src_cluster_id"] + dst_cluster_id: int = self.cluster["dst_cluster_id"] + src_cluster = Cluster.objects.get(id=src_cluster_id) + dst_cluster = Cluster.objects.get(id=dst_cluster_id) + src_proxyinstances = copy.deepcopy(src_cluster.proxyinstance_set.all()) + dst_proxyinstances = copy.deepcopy(dst_cluster.proxyinstance_set.all()) + + src_proxy_storageinstances = copy.deepcopy(src_proxyinstances[0].storageinstance.all()) + dst_proxy_storageinstances = copy.deepcopy(dst_proxyinstances[0].storageinstance.all()) + + src_storageinstances = copy.deepcopy(src_cluster.storageinstance_set.all()) + dst_storageinstances = copy.deepcopy(dst_cluster.storageinstance_set.all()) + + src_nosqldtl = copy.deepcopy(src_cluster.nosqlstoragesetdtl_set.all()) + dst_nosqldtl = copy.deepcopy(dst_cluster.nosqlstoragesetdtl_set.all()) + + logger.info( + _("dts_online_switch_swap_two_cluster_storage 1111 first get src_inst_list:{}").format( + self.get_inst_list(src_storageinstances) + ) + ) + logger.info( + _("dts_online_switch_swap_two_cluster_storage 1111 first get src dst_inst_list:{}").format( + self.get_inst_list(dst_storageinstances) + ) + ) + + src_cluster_info: dict = { + "cluster_type": src_cluster.cluster_type, + "major_version": src_cluster.major_version, + "region": src_cluster.region, + "db_module_id": src_cluster.db_module_id, + "proxy_machine_type": src_proxyinstances[0].machine_type, + } + dst_cluster_info: dict = { + "cluster_type": dst_cluster.cluster_type, + "major_version": dst_cluster.major_version, + "region": dst_cluster.region, + "db_module_id": dst_cluster.db_module_id, + "proxy_machine_type": dst_proxyinstances[0].machine_type, + } + + with atomic(): + # 交换cluster_type 等信息 + logger.info(_("dts 交换两个集群的 cluster_type 等信息")) + src_cluster.cluster_type = dst_cluster_info["cluster_type"] + src_cluster.major_version = dst_cluster_info["major_version"] + src_cluster.region = dst_cluster_info["region"] + src_cluster.db_module_id = dst_cluster_info["db_module_id"] + + dst_cluster.cluster_type = src_cluster_info["cluster_type"] + dst_cluster.major_version = src_cluster_info["major_version"] + dst_cluster.region = src_cluster_info["region"] + dst_cluster.db_module_id = src_cluster_info["db_module_id"] + + # 交换cluster storageinstances + logger.info(_("dts 交换两个集群的 storageinstances")) + src_cluster.storageinstance_set.clear() + src_cluster.storageinstance_set.add(*dst_storageinstances) + src_cluster.save() + + dst_cluster.storageinstance_set.clear() + dst_cluster.storageinstance_set.add(*src_storageinstances) + dst_cluster.save() + + logger.info( + _("dts_online_switch_swap_two_cluster_storage 2222 交换两个集群strorageinstance完成 src_inst_list:{}").format( + self.get_inst_list(src_storageinstances) + ) + ) + logger.info( + _("dts_online_switch_swap_two_cluster_storage 2222 交换两个集群strorageinstance完成 dst_inst_list:{}").format( + self.get_inst_list(dst_storageinstances) + ) + ) + + # 交换cluster nosqlstoragesetdtl + logger.info(_("dts 交换两个集群的 nosqlstoragesetdtl")) + for nosqldtl_obj in src_nosqldtl: + nosqldtl_obj.cluster = dst_cluster + nosqldtl_obj.save() + + for nosqldtl_obj in dst_nosqldtl: + nosqldtl_obj.cluster = src_cluster + nosqldtl_obj.save() + + # 交换proxy storageinstances 和 machine_type + logger.info(_("dts 交换两个集群 proxy 的 storageinstances")) + for src_proxy in src_proxyinstances: + src_proxy.storageinstance.clear() + src_proxy.storageinstance.add(*dst_proxy_storageinstances) + + src_proxy.machine_type = dst_cluster_info["proxy_machine_type"] + src_proxy.save() + + src_proxy.machine.machine_type = dst_cluster_info["proxy_machine_type"] + src_proxy.machine.save(update_fields=["machine_type"]) + update_cluster_type(src_proxyinstances, dst_cluster_info["cluster_type"]) + + for dst_proxy in dst_proxyinstances: + dst_proxy.storageinstance.clear() + dst_proxy.storageinstance.add(*src_proxy_storageinstances) + + dst_proxy.machine_type = src_cluster_info["proxy_machine_type"] + dst_proxy.save() + + dst_proxy.machine.machine_type = src_cluster_info["proxy_machine_type"] + dst_proxy.machine.save(update_fields=["machine_type"]) + update_cluster_type(dst_proxyinstances, src_cluster_info["cluster_type"]) + + # 交换 cc module + logger.info(_("dts 交换两个集群的 cc module")) + logger.info( + _( + "dts_online_switch_swap_two_cluster_storage 3333 转移目标机器模块到源集群下,src_cluster:{} dst_inst_list:{}" + ).format(src_cluster.immute_domain, self.get_inst_list(dst_storageinstances)) + ) + RedisCCTopoOperator(src_cluster).transfer_instances_to_cluster_module(dst_storageinstances) + logger.info( + _( + "dts_online_switch_swap_two_cluster_storage 3333 转移源机器模块到目标集群下,dst_cluster:{} src_inst_list:{}" + ).format(dst_cluster.immute_domain, self.get_inst_list(src_storageinstances)) + ) + RedisCCTopoOperator(dst_cluster).transfer_instances_to_cluster_module(src_storageinstances) + + return True diff --git a/dbm-ui/backend/flow/utils/redis/redis_module_operate.py b/dbm-ui/backend/flow/utils/redis/redis_module_operate.py new file mode 100644 index 0000000000..25a996dd7d --- /dev/null +++ b/dbm-ui/backend/flow/utils/redis/redis_module_operate.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +import logging + +from backend.configuration.constants import DBType +from backend.flow.utils.base.cc_topo_operate import CCTopoOperator + +logger = logging.getLogger("flow") + + +class RedisCCTopoOperator(CCTopoOperator): + db_type = DBType.Redis.value + + def init_instances_service(self, machine_type, instances=None): + """ + Redis 转移实例到对应的集群模块下,并判断是否需要添加服务实例 + """ + if instances: + first_instance = instances[0] + if first_instance.cluster.first() is None: + logger.warning("属于构造场景的临时节点,没有集群归属信息,不需要创建服务实例") + # 属于构造场景的临时节点,没有集群归属信息,不需要创建服务实例 + return + # 创建 CMDB 服务实例 + super(RedisCCTopoOperator, self).init_instances_service(machine_type, instances) diff --git a/dbm-ui/backend/flow/utils/redis/redis_proxy_util.py b/dbm-ui/backend/flow/utils/redis/redis_proxy_util.py index 94dfc31acc..dc0da319a0 100644 --- a/dbm-ui/backend/flow/utils/redis/redis_proxy_util.py +++ b/dbm-ui/backend/flow/utils/redis/redis_proxy_util.py @@ -8,8 +8,21 @@ 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. """ +import hashlib +import logging.config +from collections import defaultdict from typing import Dict, List, Tuple +from backend.components import DBConfigApi, DRSApi +from backend.components.dbconfig.constants import FormatType, LevelName +from backend.constants import IP_PORT_DIVIDER +from backend.db_meta.enums import ClusterType, InstanceRole +from backend.db_meta.models import Cluster +from backend.db_services.redis.util import is_predixy_proxy_type, is_redis_instance_type, is_twemproxy_proxy_type +from backend.flow.consts import DEFAULT_DB_MODULE_ID, ConfigFileEnum, ConfigTypeEnum + +logger = logging.getLogger("flow") + class TwemproxyBackendItem: def __init__(self, addr: str, app: str, seg_start: int, seg_end: int, weight: int): @@ -119,3 +132,178 @@ def decode_predixy_info_servers(info_str): elif list01[0] == "RecvBytes": item.recv_bytes = int(list01[1]) return rets + + +def check_cluster_proxy_backends_consistent(cluster_id: int, cluster_password: str): + cluster: Cluster = None + try: + cluster = Cluster.objects.get(id=cluster_id) + except Cluster.DoesNotExist: + raise Exception("src_cluster {} does not exist".format(cluster_id)) + + if cluster_password == "": + proxy_conf = DBConfigApi.query_conf_item( + params={ + "bk_biz_id": str(cluster.bk_biz_id), + "level_name": LevelName.CLUSTER.value, + "level_value": cluster.immute_domain, + "level_info": {"module": str(cluster.db_module_id)}, + "conf_file": cluster.proxy_version, + "conf_type": ConfigTypeEnum.ProxyConf, + "namespace": cluster.cluster_type, + "format": FormatType.MAP, + } + ) + proxy_content = proxy_conf.get("content", {}) + cluster_password = proxy_content.get("password", "") + + proxy_addrs = [] + proxys_backend_md5 = [] + if is_twemproxy_proxy_type(cluster.cluster_type): + # twemproxy 集群 + for proxy in cluster.proxyinstance_set.all(): + proxy_addrs.append(proxy.machine.ip + ":" + str(proxy.port + 1000)) + resp = DRSApi.twemproxy_rpc( + { + "addresses": proxy_addrs, + "db_num": 0, + "password": "", + "command": "get nosqlproxy servers", + "bk_cloud_id": cluster.bk_cloud_id, + } + ) + for ele in resp: + backends_ret, _ = decode_twemproxy_backends(ele["result"]) + sorted_backends = sorted(backends_ret, key=lambda x: x.segment_start) + sorted_str = "" + for bck in sorted_backends: + sorted_str += bck.string_without_app() + "\n" + # 求sorted_str的md5值 + md5 = hashlib.md5(sorted_str.encode("utf-8")).hexdigest() + proxys_backend_md5.append( + { + "proxy_addr": ele["address"], + "backend_md5": md5, + } + ) + elif is_predixy_proxy_type(cluster.cluster_type): + # predixy 集群 + for proxy in cluster.proxyinstance_set.all(): + proxy_addrs.append(proxy.machine.ip + ":" + str(proxy.port)) + resp = DRSApi.redis_rpc( + { + "addresses": proxy_addrs, + "db_num": 0, + "password": cluster_password, + "command": "info servers", + "bk_cloud_id": cluster.bk_cloud_id, + } + ) + for ele in resp: + backends_ret = decode_predixy_info_servers(ele["result"]) + sorted_backends = sorted(backends_ret, key=lambda x: x.server) + sorted_str = "" + for bck in sorted_backends: + sorted_str += bck.__str__() + "\n" + # 求sorted_str的md5值 + md5 = hashlib.md5(sorted_str.encode("utf-8")).hexdigest() + proxys_backend_md5.append( + { + "proxy_addr": ele["address"], + "backend_md5": md5, + } + ) + # 检查md5是否一致 + sorted_md5 = sorted(proxys_backend_md5, key=lambda x: x["backend_md5"]) + if sorted_md5[0]["backend_md5"] != sorted_md5[-1]["backend_md5"]: + logger.error( + "proxy[{}->{}] backends is not same".format(sorted_md5[0]["proxy_addr"], sorted_md5[-1]["proxy_addr"]) + ) + raise Exception( + "proxy[{}->{}] backends is not same".format(sorted_md5[0]["proxy_addr"], sorted_md5[-1]["proxy_addr"]) + ) + + +def get_twemproxy_cluster_server_shards(bk_biz_id: int, cluster_id: int, other_to_master: dict) -> dict: + """ + 获取twemproxy集群的server_shards + :param bk_biz_id: 业务id + :param cluster_id: 集群id + :param other_to_master: other实例 到 master实例的映射关系,格式为{a.a.a.a:30000 : b.b.b.b:30000} + """ + cluster = Cluster.objects.get(id=cluster_id, bk_biz_id=bk_biz_id) + if not is_twemproxy_proxy_type(cluster.cluster_type): + return {} + twemproxy_server_shards = defaultdict(dict) + ipport_to_segment = {} + for row in cluster.nosqlstoragesetdtl_set.all(): + ipport = row.instance.machine.ip + IP_PORT_DIVIDER + str(row.instance.port) + ipport_to_segment[ipport] = row.seg_range + + for master_obj in cluster.storageinstance_set.filter(instance_role=InstanceRole.REDIS_MASTER.value): + if master_obj.as_ejector and master_obj.as_ejector.first(): + slave_obj = master_obj.as_ejector.get().receiver + master_ipport = master_obj.machine.ip + IP_PORT_DIVIDER + str(master_obj.port) + slave_ipport = slave_obj.machine.ip + IP_PORT_DIVIDER + str(slave_obj.port) + + twemproxy_server_shards[master_obj.machine.ip][master_ipport] = ipport_to_segment[master_ipport] + twemproxy_server_shards[slave_obj.machine.ip][slave_ipport] = ipport_to_segment[master_ipport] + + for other_ipport, master_ipport in other_to_master.items(): + if master_ipport not in ipport_to_segment: + raise Exception( + "master ipport {} not found seg_range, not belong cluster:{}??".format( + master_ipport, cluster.immute_domain + ) + ) + other_list = other_ipport.split(IP_PORT_DIVIDER) + other_ip = other_list[0] + twemproxy_server_shards[other_ip][other_ipport] = ipport_to_segment[master_ipport] + return twemproxy_server_shards + + +def get_cache_backup_mode(bk_biz_id: int, cluster_id: int) -> str: + """ + 获取集群的cache_backup_mode + :param bk_biz_id: 业务id + :param cluster_id: 集群id + """ + query_params = { + "bk_biz_id": str(bk_biz_id), + "level_name": LevelName.CLUSTER.value, + "level_value": "", + "level_info": {"module": str(DEFAULT_DB_MODULE_ID)}, + "conf_file": ConfigFileEnum.FullBackup.value, + "conf_type": ConfigTypeEnum.Config.value, + "namespace": ClusterType.TendisTwemproxyRedisInstance.value, + "format": FormatType.MAP.value, + } + if cluster_id == 0: + # 获取业务级别的配置 + query_params["level_name"] = LevelName.APP.value + query_params["level_value"] = str(bk_biz_id) + resp = DBConfigApi.query_conf_item(params=query_params) + if resp["content"]: + return resp["content"].get("cache_backup_mode", "") + cluster: Cluster = None + try: + cluster = Cluster.objects.get(id=cluster_id, bk_biz_id=bk_biz_id) + except Cluster.DoesNotExist: + # 获取业务级别的配置 + query_params["level_name"] = LevelName.APP.value + query_params["level_value"] = str(bk_biz_id) + resp = DBConfigApi.query_conf_item(params=query_params) + if resp["content"]: + return resp["content"].get("cache_backup_mode", "") + if not is_redis_instance_type(cluster.cluster_type): + return "" + # 获取集群级别的配置 + query_params["level_name"] = LevelName.CLUSTER.value + query_params["level_value"] = cluster.immute_domain + query_params["namespace"] = cluster.cluster_type + try: + resp = DBConfigApi.query_conf_item(params=query_params) + if resp["content"]: + return resp["content"].get("cache_backup_mode", "") + except Exception: + return "aof" diff --git a/dbm-ui/backend/flow/utils/redis/redis_util.py b/dbm-ui/backend/flow/utils/redis/redis_util.py index 662808c74f..d3c951e1fc 100644 --- a/dbm-ui/backend/flow/utils/redis/redis_util.py +++ b/dbm-ui/backend/flow/utils/redis/redis_util.py @@ -16,3 +16,10 @@ def domain_without_port(domain): if end_port_reg.search(domain): return end_port_reg.sub("", domain) return domain + + +def check_domain(domain): + match = re.search(r"^[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62}){2,8}\.*(#(\d+))?$", domain) + if match: + return True + return False diff --git a/dbm-ui/backend/flow/utils/riak/__init__.py b/dbm-ui/backend/flow/utils/riak/__init__.py new file mode 100644 index 0000000000..aa5085c628 --- /dev/null +++ b/dbm-ui/backend/flow/utils/riak/__init__.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" diff --git a/dbm-ui/backend/flow/utils/riak/riak_act_dataclass.py b/dbm-ui/backend/flow/utils/riak/riak_act_dataclass.py index 6043873ddb..caf68183e6 100644 --- a/dbm-ui/backend/flow/utils/riak/riak_act_dataclass.py +++ b/dbm-ui/backend/flow/utils/riak/riak_act_dataclass.py @@ -65,6 +65,15 @@ class DownloadMediaKwargs(DownloadMediaBaseKwargs): exec_ip: Optional[Any] = None # 表示执行的ip,多个ip传入list类型,当个ip传入str类型,空则传入None,针对手输ip场景 +@dataclass() +class DownloadMediaKwargsFromTrans(DownloadMediaBaseKwargs): + """ + 针对资源池获取IP的场景 + """ + + get_trans_data_ip_var: str = None # 表示在上下文获取ip信息的变量名称。空则传入None + + @dataclass() class DBMetaFuncKwargs: """ diff --git a/dbm-ui/backend/flow/utils/riak/riak_act_payload.py b/dbm-ui/backend/flow/utils/riak/riak_act_payload.py index 5a48cf6059..0a2112df69 100644 --- a/dbm-ui/backend/flow/utils/riak/riak_act_payload.py +++ b/dbm-ui/backend/flow/utils/riak/riak_act_payload.py @@ -1,6 +1,16 @@ +import logging +import os.path + +from backend import env from backend.configuration.constants import DBType +from backend.configuration.models import SystemSettings +from backend.db_meta.enums import MachineType +from backend.db_meta.models import Cluster, Machine, StorageInstance from backend.db_package.models import Package from backend.flow.consts import DBActuatorTypeEnum, MediumEnum, RiakActuatorActionEnum +from backend.ticket.constants import TicketType + +logger = logging.getLogger("flow") class RiakActPayload(object): @@ -10,7 +20,8 @@ class RiakActPayload(object): def __init__(self, ticket_data: dict, cluster: dict): self.riak_pkg = None - self.bk_biz_id = str(ticket_data["bk_biz_id"]) + self.mysql_crond_pkg = None # riak使用mysql-crond实现定时任务 + self.riak_monitor_pkg = None self.ticket_data = ticket_data self.cluster = cluster @@ -28,7 +39,9 @@ def get_deploy_payload(self, **kwargs) -> dict: """ 部署节点 """ - self.riak_pkg = Package.get_latest_package(version="2.2.1", pkg_type=MediumEnum.Riak, db_type=DBType.Riak) + self.riak_pkg = Package.get_latest_package( + version=self.ticket_data["db_version"], pkg_type=MediumEnum.Riak, db_type=DBType.Riak.value + ) return { "db_type": DBActuatorTypeEnum.Riak.value, "action": RiakActuatorActionEnum.Deploy.value, @@ -37,7 +50,36 @@ def get_deploy_payload(self, **kwargs) -> dict: "extend": { "distributed_cookie": self.cluster["distributed_cookie"], "ring_size": self.cluster["ring_size"], - "db_module_id": self.ticket_data["db_module_id"], + "leveldb.expiration": self.cluster["leveldb.expiration"], + "leveldb.expiration.mode": self.cluster["leveldb.expiration.mode"], + "leveldb.expiration.retention_time": self.cluster["leveldb.expiration.retention_time"], + "pkg": { + "name": self.riak_pkg.name, + "md5": self.riak_pkg.md5, + }, + }, + }, + } + + def get_deploy_trans_payload(self, **kwargs) -> dict: + """ + 部署节点 + """ + self.riak_pkg = Package.get_latest_package( + version=self.ticket_data["db_version"], pkg_type=MediumEnum.Riak, db_type=DBType.Riak.value + ) + configs = kwargs["trans_data"]["configs"] + return { + "db_type": DBActuatorTypeEnum.Riak.value, + "action": RiakActuatorActionEnum.Deploy.value, + "payload": { + "general": {}, + "extend": { + "distributed_cookie": configs["distributed_cookie"], + "ring_size": configs["ring_size"], + "leveldb.expiration": configs["leveldb.expiration"], + "leveldb.expiration.mode": configs["leveldb.expiration.mode"], + "leveldb.expiration.retention_time": configs["leveldb.expiration.retention_time"], "pkg": { "name": self.riak_pkg.name, "md5": self.riak_pkg.md5, @@ -63,6 +105,39 @@ def get_join_cluster_payload(self, **kwargs) -> dict: }, } + def get_join_cluster_trans_payload(self, **kwargs) -> dict: + """ + 添加节点 + """ + configs = kwargs["trans_data"]["configs"] + return { + "db_type": DBActuatorTypeEnum.Riak.value, + "action": RiakActuatorActionEnum.JoinCluster.value, + "payload": { + "general": {}, + "extend": { + "distributed_cookie": configs["distributed_cookie"], + "ring_size": configs["ring_size"], + "base_node": kwargs["trans_data"]["base_node"], + }, + }, + } + + def get_remove_node_payload(self, **kwargs) -> dict: + """ + 剔除节点 + """ + return { + "db_type": DBActuatorTypeEnum.Riak.value, + "action": RiakActuatorActionEnum.RemoveNode.value, + "payload": { + "general": {}, + "extend": { + "operate_nodes": kwargs["trans_data"]["operate_nodes"], + }, + }, + } + def get_commit_cluster_change_payload(self, **kwargs) -> dict: """ 集群变更生效 @@ -78,9 +153,35 @@ def get_commit_cluster_change_payload(self, **kwargs) -> dict: }, } + def get_transfer_payload(self, **kwargs) -> dict: + """ + 集群数据搬迁进度 + """ + return { + "db_type": DBActuatorTypeEnum.Riak.value, + "action": RiakActuatorActionEnum.Transfer.value, + "payload": { + "general": {}, + "extend": {"auto_stop": self.ticket_data["ticket_type"] == TicketType.RIAK_CLUSTER_SCALE_IN}, + }, + } + + def get_check_connections_payload(self, **kwargs) -> dict: + """ + 检查连接 + """ + return { + "db_type": DBActuatorTypeEnum.Riak.value, + "action": RiakActuatorActionEnum.CheckConnections.value, + "payload": { + "general": {}, + "extend": {}, + }, + } + def get_init_bucket_type_payload(self, **kwargs) -> dict: """ - 添加节点 + 初始化bucket_type """ return { "db_type": DBActuatorTypeEnum.Riak.value, @@ -92,3 +193,211 @@ def get_init_bucket_type_payload(self, **kwargs) -> dict: }, }, } + + def get_config_payload(self, **kwargs) -> dict: + """ + 系统配置初始化 + """ + return { + "db_type": DBActuatorTypeEnum.Riak.value, + "action": RiakActuatorActionEnum.GetConfig.value, + "payload": {"general": {}, "extend": {}}, + } + + def get_uninstall_payload(self, **kwargs) -> dict: + """ + 下架 + """ + return { + "db_type": DBActuatorTypeEnum.Riak.value, + "action": RiakActuatorActionEnum.UnInstall.value, + "payload": { + "general": {}, + "extend": {"stopped": self.ticket_data["ticket_type"] == TicketType.RIAK_CLUSTER_DESTROY}, + }, + } + + def get_stop_payload(self, **kwargs) -> dict: + """ + 禁用 + """ + return { + "db_type": DBActuatorTypeEnum.Riak.value, + "action": RiakActuatorActionEnum.Stop.value, + "payload": { + "general": {}, + "extend": {}, + }, + } + + def get_start_payload(self, **kwargs) -> dict: + """ + 启用 + """ + return { + "db_type": DBActuatorTypeEnum.Riak.value, + "action": RiakActuatorActionEnum.Start.value, + "payload": { + "general": {}, + "extend": {}, + }, + } + + def get_install_monitor_payload(self, **kwargs) -> dict: + """ + 启用 + """ + self.mysql_crond_pkg = Package.get_latest_package(version=MediumEnum.Latest, pkg_type=MediumEnum.MySQLCrond) + self.riak_monitor_pkg = Package.get_latest_package( + version=MediumEnum.Latest, pkg_type=MediumEnum.RiakMonitor, db_type=DBType.Riak.value + ) + machine = Machine.objects.get(ip=kwargs["ip"]) + if machine.machine_type != MachineType.RIAK.value: + logger.error( + "install monitor error. Machine type is {} not {}".format(machine.machine_type, MachineType.RIAK.value) + ) + storage = StorageInstance.objects.filter(machine__ip=kwargs["ip"])[0] + + # 监控自定义上报配置通过SystemSettings表获取 + bkm_dbm_report = SystemSettings.get_setting_value(key="BKM_DBM_REPORT") + # 设置定时任务和调度计划 + schedules = [ + {"name": "riak-err-notice", "expression": "@every 1m"}, + {"name": "riak-load-health", "expression": "@every 1m"}, + {"name": "riak-ring-status", "expression": "@every 10s"}, + {"name": "riak-monitor-hardcode", "expression": "@every 10s"}, + ] + if self.ticket_data["ticket_type"] == TicketType.RIAK_CLUSTER_SCALE_OUT: + cluster = Cluster.objects.get(id=self.ticket_data["cluster_id"]) + domain = cluster.immute_domain + else: + domain = self.ticket_data["domain"] + + return { + "db_type": DBActuatorTypeEnum.Riak.value, + "action": RiakActuatorActionEnum.DeployMonitor.value, + "payload": { + "general": {}, + "extend": { + "crond_pkg": { + "name": self.mysql_crond_pkg.name, + "md5": self.mysql_crond_pkg.md5, + }, + "monitor_pkg": { + "name": self.riak_monitor_pkg.name, + "md5": self.riak_monitor_pkg.md5, + }, + "crond_config": { + "ip": kwargs["ip"], + "bk_cloud_id": self.ticket_data["bk_cloud_id"], + "event_data_id": bkm_dbm_report["event"]["data_id"], + "event_data_token": bkm_dbm_report["event"]["token"], + "metrics_data_id": bkm_dbm_report["metric"]["data_id"], + "metrics_data_token": bkm_dbm_report["metric"]["token"], + "log_path": "logs", + "beat_path": env.MYSQL_CROND_BEAT_PATH, + "agent_address": env.MYSQL_CROND_AGENT_ADDRESS, + }, + "monitor_config": { + "bk_biz_id": int(self.ticket_data["bk_biz_id"]), + "ip": kwargs["ip"], + "port": storage.port, + "bk_instance_id": storage.bk_instance_id, + "immute_domain": domain, + "machine_type": MachineType.RIAK.value, + "bk_cloud_id": self.ticket_data["bk_cloud_id"], + "log_path": "logs", + "items_config_file": "items-config.yaml", + "interact_timeout": 2, + }, + "monitor_items": create_riak_monitor_items(schedules), + "jobs_config": { + "bk_biz_id": int(self.ticket_data["bk_biz_id"]), + "jobs": create_crond_jobs(schedules), + }, + }, + }, + } + + def get_stop_monitor_payload(self, **kwargs) -> dict: + """ + 关闭定时和监控 + """ + return { + "db_type": DBActuatorTypeEnum.Riak.value, + "action": RiakActuatorActionEnum.StopMonitor.value, + "payload": { + "general": {}, + "extend": {}, + }, + } + + def get_start_monitor_payload(self, **kwargs) -> dict: + """ + 启用定时和监控 + """ + return { + "db_type": DBActuatorTypeEnum.Riak.value, + "action": RiakActuatorActionEnum.StartMonitor.value, + "payload": { + "general": {}, + "extend": {}, + }, + } + + +def create_crond_jobs(self: list) -> list: + monitor_path = "/data/monitor/riak-monitor" + jobs = [] + for schedule in self: + cmd = "run" + items = schedule["name"] + if "hardcode" in schedule["name"]: + cmd = "hardcode-run" + items = "db-up,riak_monitor_heart_beat" + job = { + "name": "{}{}".format(schedule["name"], schedule["expression"]), + "enable": True, + "command": os.path.join(monitor_path, "riak-monitor"), + "args": [ + cmd, + "--items", + items, + "-c", + os.path.join(monitor_path, "runtime.yaml"), + ], + "schedule": schedule["expression"], + "creator": "admin", + "work_dir": "", + } + jobs.append(job) + return jobs + + +def create_riak_monitor_items(self: list) -> list: + items = [] + for schedule in self: + if "hardcode" in schedule["name"]: + item = { + "name": "db-up", + "enable": True, + "schedule": schedule["expression"], + "machine_type": [MachineType.RIAK.value], + } + items.append(item) + item = { + "name": "riak_monitor_heart_beat", + "enable": True, + "schedule": schedule["expression"], + "machine_type": [MachineType.RIAK.value], + } + items.append(item) + continue + item = { + "name": schedule["name"], + "enable": True, + "schedule": schedule["expression"], + "machine_type": [MachineType.RIAK.value], + } + items.append(item) + return items diff --git a/dbm-ui/backend/flow/utils/riak/riak_context_dataclass.py b/dbm-ui/backend/flow/utils/riak/riak_context_dataclass.py index 152468a303..3d9bc7d387 100644 --- a/dbm-ui/backend/flow/utils/riak/riak_context_dataclass.py +++ b/dbm-ui/backend/flow/utils/riak/riak_context_dataclass.py @@ -25,12 +25,13 @@ class RiakActKwargs: file_list: list = field(default_factory=list) # 传入文件传输节点的文件名称列表,默认空字典 cluster: dict = field(default_factory=dict) # 集群信息 run_as_system_user: str = None # 表示执行job的api的操作用户, None 默认是用root用户 + get_trans_data_ip_var: str = None # 表示在上下文获取ip信息的变量名称。空则传入None @dataclass() class ApplyManualContext: """ - 定义单节点申请的上下文dataclass类(手输ip模式) + 定义申请集群的上下文dataclass类(手输ip模式) """ nodes: list = field(default_factory=list) # 手工输入的所有ip @@ -39,10 +40,60 @@ class ApplyManualContext: @dataclass() -class AddNodeManualContext: +class ScaleOutManualContext: """ - 定义单节点申请的上下文dataclass类(手输ip模式) + 定义扩容的上下文dataclass类(手输ip模式) """ - base_node: str = None # 选取集群中已存在的一个ip为操作节点 - operate_nodes: list = field(default_factory=list) # 手工输入的所有ip + nodes: list = field(default_factory=list) + base_node: str = None # 集群中已存在的一个节点 + operate_nodes: list = field(default_factory=list) # 新增节点 + configs: dict = None + + @staticmethod + def get_nodes_var_name() -> str: + return "nodes" + + @staticmethod + def get_base_node_var_name() -> str: + return "base_node" + + @staticmethod + def get_operate_nodes_var_name() -> str: + return "operate_nodes" + + +@dataclass() +class ScaleInManualContext: + """ + 定义缩容的上下文dataclass类(手输ip模式) + """ + + nodes: list = field(default_factory=list) + base_node: str = None # 集群中已存在的一个节点 + operate_nodes: list = field(default_factory=list) # 待剔除的节点 + + @staticmethod + def get_base_node_var_name() -> str: + return "base_node" + + @staticmethod + def get_operate_nodes_var_name() -> str: + return "operate_nodes" + + @staticmethod + def get_nodes_var_name() -> str: + return "nodes" + + +@dataclass() +class NodesContext: + """ + 定义销毁、启用、禁用集群的上下文dataclass类 + """ + + nodes: list = field(default_factory=list) + + @staticmethod + def get_nodes_var_name() -> str: + return "nodes" diff --git a/dbm-ui/backend/flow/utils/riak/riak_db_meta.py b/dbm-ui/backend/flow/utils/riak/riak_db_meta.py index fdc637896d..9bf21a3385 100644 --- a/dbm-ui/backend/flow/utils/riak/riak_db_meta.py +++ b/dbm-ui/backend/flow/utils/riak/riak_db_meta.py @@ -8,13 +8,13 @@ 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. """ -import copy import logging from django.db.transaction import atomic from backend.db_meta import api from backend.db_meta.enums import InstanceRole, MachineType +from backend.flow.consts import DEFAULT_DB_MODULE_ID, DEFAULT_RIAK_PORT logger = logging.getLogger("flow") @@ -33,29 +33,22 @@ def __init__(self, ticket_data: dict, cluster: dict): self.ticket_data = ticket_data self.cluster = cluster - def riak_deploy_node(self) -> bool: - if self.ticket_data["ticket_type"] == "RIAK_APPLY": - ips = self.cluster.nodes - elif self.ticket_data["ticket_type"] == "RIAK_ADD_NODE": - ips = self.cluster.operate_nodes - else: - logger.error("not supported ticket type for adding db meta") - return False - + def riak_cluster_apply(self) -> bool: + ips = self.cluster.nodes machines = [ {"ip": ip, "bk_biz_id": int(self.ticket_data["bk_biz_id"]), "machine_type": MachineType.RIAK.value} for ip in ips ] - - instances = [{"ip": ip, "port": 8087, "instance_role": InstanceRole.RIAK_NODE.value} for ip in ips] - + instances = [ + {"ip": ip, "port": DEFAULT_RIAK_PORT, "instance_role": InstanceRole.RIAK_NODE.value} for ip in ips + ] cluster = { "bk_cloud_id": self.ticket_data["bk_cloud_id"], "bk_biz_id": int(self.ticket_data["bk_biz_id"]), "name": self.ticket_data["cluster_name"], "alias": self.ticket_data["cluster_alias"], "immute_domain": self.ticket_data["domain"], - "db_module_id": self.ticket_data["db_module_id"], + "db_module_id": DEFAULT_DB_MODULE_ID, "storages": instances, "creator": self.ticket_data["created_by"], "major_version": self.ticket_data["db_version"], @@ -70,3 +63,46 @@ def riak_deploy_node(self) -> bool: api.storage_instance.create(instances=instances, creator=self.ticket_data["created_by"]) api.cluster.riak.create(**cluster) return True + + def riak_scale_out(self) -> bool: + ips = self.cluster.operate_nodes + machines = [ + {"ip": ip, "bk_biz_id": int(self.ticket_data["bk_biz_id"]), "machine_type": MachineType.RIAK.value} + for ip in ips + ] + instances = [ + {"ip": ip, "port": DEFAULT_RIAK_PORT, "instance_role": InstanceRole.RIAK_NODE.value} for ip in ips + ] + cluster = { + "cluster_id": self.ticket_data["cluster_id"], + "storages": instances, + } + + with atomic(): + api.machine.create( + machines=machines, + bk_cloud_id=self.ticket_data["bk_cloud_id"], + creator=self.ticket_data["created_by"], + ) + api.storage_instance.create(instances=instances, creator=self.ticket_data["created_by"]) + api.cluster.riak.scale_out(**cluster) + + def riak_scale_in(self) -> bool: + ips = self.cluster.operate_nodes + instances = [ + {"ip": ip, "port": DEFAULT_RIAK_PORT, "instance_role": InstanceRole.RIAK_NODE.value} for ip in ips + ] + cluster = { + "cluster_id": self.ticket_data["cluster_id"], + "storages": instances, + } + api.cluster.riak.scale_in(**cluster) + + def riak_cluster_destroy(self) -> bool: + api.cluster.riak.destroy(self.ticket_data["cluster_id"]) + + def riak_cluster_disable(self) -> bool: + api.cluster.riak.disable(self.ticket_data["cluster_id"]) + + def riak_cluster_enable(self) -> bool: + api.cluster.riak.enable(self.ticket_data["cluster_id"]) diff --git a/dbm-ui/backend/flow/utils/riak/riak_module_operate.py b/dbm-ui/backend/flow/utils/riak/riak_module_operate.py new file mode 100644 index 0000000000..de30bb1956 --- /dev/null +++ b/dbm-ui/backend/flow/utils/riak/riak_module_operate.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 backend.configuration.constants import DBType +from backend.flow.utils.base.cc_topo_operate import CCTopoOperator + + +class RiakCCTopoOperator(CCTopoOperator): + db_type = DBType.Riak.value diff --git a/dbm-ui/backend/flow/utils/spider/__init__.py b/dbm-ui/backend/flow/utils/spider/__init__.py new file mode 100644 index 0000000000..aa5085c628 --- /dev/null +++ b/dbm-ui/backend/flow/utils/spider/__init__.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" diff --git a/dbm-ui/backend/flow/utils/spider/get_spider_incr.py b/dbm-ui/backend/flow/utils/spider/get_spider_incr.py index 56a3a13a8d..1ab7ddba49 100644 --- a/dbm-ui/backend/flow/utils/spider/get_spider_incr.py +++ b/dbm-ui/backend/flow/utils/spider/get_spider_incr.py @@ -13,7 +13,6 @@ from django.utils.translation import ugettext_lazy as _ from backend.components import DRSApi -from backend.constants import IP_PORT_DIVIDER from backend.db_meta.models import Cluster from backend.flow.consts import MAX_SPIDER_MASTER_COUNT from backend.flow.engine.bamboo.scene.spider.common.exceptions import FailedToAssignIncrException @@ -27,8 +26,7 @@ def get_spider_master_incr(cluster: Cluster, add_spiders: list) -> list: 每个spider节点每个分配到的值目前阶段必须小于等于37 """ new_add_spiders = copy.deepcopy(add_spiders) - spider_instance = cluster.proxyinstance_set.first() # 随便拿一个接入层 - ctl_address = "{}{}{}".format(spider_instance.machine.ip, IP_PORT_DIVIDER, spider_instance.admin_port) + ctl_address = cluster.tendbcluster_ctl_primary_address() # 随便拿一个spider-master接入层 logger.info("ctl address: {}".format(ctl_address)) res = DRSApi.rpc( @@ -45,11 +43,11 @@ def get_spider_master_incr(cluster: Cluster, add_spiders: list) -> list: message=_("select spider_auto_increment failed: {}".format(res[0]["error_msg"])) ) - if res[0]["cmd_results"][1]["table_data"]: + if not res[0]["cmd_results"][1]["table_data"]: raise FailedToAssignIncrException(message=_("select spider_auto_increment is null, check ")) # 生成对比list - tmp_list = [info["SPIDER_AUTO_INCREMENT_MODE_VALUE"] for info in res[0]["cmd_results"][1]["table_data"]] + tmp_list = [int(info["SPIDER_AUTO_INCREMENT_MODE_VALUE"]) for info in res[0]["cmd_results"][1]["table_data"]] # incr_number 从1开始寻找,如果已使用则跳过,直至到未使用则赋值给对应的待加入的spider-master节点,且跳出 start = 0 diff --git a/dbm-ui/backend/flow/utils/spider/spider_act_dataclass.py b/dbm-ui/backend/flow/utils/spider/spider_act_dataclass.py index b4685cb863..71df2981fc 100644 --- a/dbm-ui/backend/flow/utils/spider/spider_act_dataclass.py +++ b/dbm-ui/backend/flow/utils/spider/spider_act_dataclass.py @@ -35,3 +35,35 @@ class AddSpiderRoutingKwargs: add_spider_role: TenDBClusterSpiderRole user: str passwd: str + + +@dataclass +class CtlSwitchToSlaveKwargs: + """ + 定义ctl集群切换的私有变量结构体 + """ + + cluster_id: int + reduce_ctl_primary: str # 待回收的ctl,格式是ip:port + new_ctl_primary: str # 待提升主的ctl,格式是ip:port + + +@dataclass +class CtlDropRoutingKwargs: + """ + 定义ctl节点路由删除的私有变量结构体 + """ + + cluster_id: int + reduce_ctl: str # 待回收的ctl,格式是ip:port + + +@dataclass +class DropSpiderRoutingKwargs: + """ + 定义spider节点路由删除的私有变量结构体 + """ + + cluster_id: int + reduce_spiders: list # 待下架的spider列表,每个元素的格式是字典 + is_safe: bool # 是否做安全检测 diff --git a/dbm-ui/backend/flow/utils/spider/spider_db_meta.py b/dbm-ui/backend/flow/utils/spider/spider_db_meta.py index 38d622771a..5cfdca1398 100644 --- a/dbm-ui/backend/flow/utils/spider/spider_db_meta.py +++ b/dbm-ui/backend/flow/utils/spider/spider_db_meta.py @@ -8,10 +8,14 @@ specific language governing permissions and limitations under the License. """ import logging +from typing import Optional + +from django.db import transaction from backend.db_meta.api.cluster.tendbcluster.handler import TenDBClusterClusterHandler -from backend.db_meta.enums import ClusterEntryRole -from backend.db_meta.models import Cluster +from backend.db_meta.api.cluster.tendbcluster.remotedb_node_migrate import TenDBClusterMigrateRemoteDb +from backend.db_meta.enums import ClusterEntryRole, MachineType, TenDBClusterSpiderRole +from backend.db_meta.models import Cluster, StorageInstance from backend.flow.utils.dict_to_dataclass import dict_to_dataclass from backend.flow.utils.spider.spider_act_dataclass import ShardInfo @@ -58,7 +62,6 @@ def tendb_cluster_apply(self): "creator": self.global_data["created_by"], "time_zone": self.cluster["time_zone_info"]["time_zone"], "bk_cloud_id": int(self.global_data["bk_cloud_id"]), - "deploy_plan_id": int(self.global_data["deploy_plan_id"]), "resource_spec": self.global_data["resource_spec"], "shard_infos": shard_infos, "region": self.global_data["city"], @@ -77,39 +80,202 @@ def tendb_cluster_destroy(self): def tendb_cluster_slave_apply(self): """ - 对已有的TenDB cluster集群 (spider集群)添加从集群(只读集群) + 对已有的TenDB cluster集群 (spider集群)添加从集群(只读接入层) """ kwargs = { "cluster_id": self.global_data["cluster_id"], "creator": self.global_data["created_by"], "spider_version": self.global_data["spider_version"], - "slave_domain": self.global_data["slave_domain"], - "spider_slaves": self.global_data["spider_slave_ip_list"], - "is_create": True, + "domain": self.global_data["slave_domain"], + "add_spiders": self.global_data["spider_slave_ip_list"], + "spider_role": TenDBClusterSpiderRole.SPIDER_SLAVE, + "resource_spec": self.global_data["resource_spec"], + "is_slave_cluster_create": True, } - TenDBClusterClusterHandler.add_spider_slaves(**kwargs) + TenDBClusterClusterHandler.add_spiders(**kwargs) return True - def add_spider_slave_nodes_apply(self): + def add_spider_nodes(self, spider_role: Optional[TenDBClusterSpiderRole], domain: str = None): """ - 对已有的TenDB cluster集群 (spider集群)扩容spider-slave节点 + 对已有的TenDB cluster集群 (spider集群)扩容写入的公共方法 """ - cluster = Cluster.objects.get(id=self.global_data["cluster_id"]) - slave_dns = cluster.clusterentry_set.get(role=ClusterEntryRole.SLAVE_ENTRY).entry + # 兼容spider mnt不使用资源池的情况 + default_spider_spec = {MachineType.SPIDER.value: {"id": 0}} kwargs = { "cluster_id": self.global_data["cluster_id"], "creator": self.global_data["created_by"], "spider_version": self.global_data["spider_version"], - "slave_domain": slave_dns, - "spider_slaves": self.global_data["spider_ip_list"], - "is_create": False, + "domain": domain, + "add_spiders": self.global_data["spider_ip_list"], + "spider_role": spider_role, + "resource_spec": self.global_data.get("resource_spec", default_spider_spec), + "is_slave_cluster_create": False, } - TenDBClusterClusterHandler.add_spider_slaves(**kwargs) + TenDBClusterClusterHandler.add_spiders(**kwargs) return True + def add_spider_slave_nodes_apply(self): + """ + 对已有的TenDB cluster集群 (spider集群)扩容spider-slave节点 + """ + cluster = Cluster.objects.get(id=self.global_data["cluster_id"]) + slave_dns = cluster.clusterentry_set.get(role=ClusterEntryRole.SLAVE_ENTRY).entry + self.add_spider_nodes(spider_role=TenDBClusterSpiderRole.SPIDER_SLAVE, domain=slave_dns) + def add_spider_master_nodes_apply(self): """ 对已有的TenDB cluster集群 (spider集群)扩容spider-master节点 - todo 后续tdbctl新版本出现在补齐 """ + cluster = Cluster.objects.get(id=self.global_data["cluster_id"]) + master_dns = cluster.clusterentry_set.get(role=ClusterEntryRole.MASTER_ENTRY).entry + self.add_spider_nodes(spider_role=TenDBClusterSpiderRole.SPIDER_MASTER, domain=master_dns) + + def reduce_spider_nodes_apply(self): + """ + 对已有的TenDB cluster集群 (spider集群)缩容spider节点,这里不区分spider角色 + """ + TenDBClusterClusterHandler.reduce_spider( + cluster_id=self.global_data["cluster_id"], + spiders=self.global_data["reduce_spiders"], + ) return True + + def add_spider_mnt(self): + """ + 对已有的TenDB cluster集群 (spider集群)扩容spider-mnt节点 + """ + self.add_spider_nodes(spider_role=TenDBClusterSpiderRole.SPIDER_MNT, domain=None) + + def remote_switch(self): + """ + 对已执行remote互切/主故障切换后的集群做元数据的调整 + """ + TenDBClusterClusterHandler.remote_switch( + cluster_id=self.global_data["cluster_id"], + switch_tuples=self.global_data["switch_tuples"], + ) + return True + + def remotedb_migrate_add_install_nodes(self): + """ + remotedb 成对迁移添加初始化节点元数据 + """ + TenDBClusterMigrateRemoteDb.storage_create( + cluster_id=self.cluster["cluster_id"], + master_ip=self.cluster["new_master_ip"], + slave_ip=self.cluster["new_slave_ip"], + ports=self.cluster["ports"], + creator=self.global_data["created_by"], + mysql_version=self.cluster["version"], + resource_spec=self.global_data["resource_spec"], + ) + return True + + def remotedb_migrate_add_storage_tuple(self): + """ + 写入真实的主从对应关系 + 新从库->新主库 + 新主库->旧主库(这条关系链在切换完毕后需要断开) + """ + new_slave_to_new_master = { + "master": {"ip": self.cluster["new_master_ip"], "port": self.cluster["new_master_port"]}, + "slave": {"ip": self.cluster["new_slave_ip"], "port": self.cluster["new_slave_port"]}, + } + TenDBClusterMigrateRemoteDb.add_storage_tuple( + cluster_id=self.cluster["cluster_id"], storage=new_slave_to_new_master + ) + new_master_to_old_master = { + "slave": {"ip": self.cluster["new_master_ip"], "port": self.cluster["new_master_port"]}, + "master": {"ip": self.cluster["master_ip"], "port": self.cluster["master_port"]}, + } + TenDBClusterMigrateRemoteDb.add_storage_tuple( + cluster_id=self.cluster["cluster_id"], storage=new_master_to_old_master + ) + # todo 是否修改new_master角色为中继状态 + + def remotedb_migrate_switch(self): + for port in self.cluster["ports"]: + source = { + "master": {"ip": self.cluster["master_ip"], "port": port}, + "slave": {"ip": self.cluster["slave_ip"], "port": port}, + } + target = { + "master": {"ip": self.cluster["new_master_ip"], "port": port}, + "slave": {"ip": self.cluster["new_slave_ip"], "port": port}, + } + TenDBClusterMigrateRemoteDb.switch_remote_node( + cluster_id=self.cluster["cluster_id"], source=source, target=target + ) + + def remotedb_migrate_remove_storage(self): + TenDBClusterMigrateRemoteDb.uninstall_storage( + cluster_id=self.cluster["cluster_id"], ip=self.cluster["uninstall_ip"] + ) + + @transaction.atomic + def tendb_remotedb_rebalance_switch(self): + for node in self.cluster["shards"].values(): + source = { + "master": {"ip": node["master"]["ip"], "port": node["master"]["port"]}, + "slave": {"ip": node["slave"]["ip"], "port": node["slave"]["port"]}, + } + target = { + "master": {"ip": node["new_master"]["ip"], "port": node["new_master"]["port"]}, + "slave": {"ip": node["new_slave"]["ip"], "port": node["new_slave"]["port"]}, + } + TenDBClusterMigrateRemoteDb.switch_remote_node( + cluster_id=self.cluster["cluster_id"], source=source, target=target + ) + + def tendb_cluster_slave_destroy(self): + """ + 清理只读接入层剩余元数据信息 + """ + kwargs = { + "cluster_id": self.global_data["cluster_id"], + } + TenDBClusterClusterHandler.clear_clusterentry(**kwargs) + return True + + def tendb_slave_recover_add_nodes(self): + """ + remotedb 成对迁移添加初始化节点元数据 + """ + TenDBClusterMigrateRemoteDb.storage_create( + cluster_id=self.cluster["cluster_id"], + slave_ip=self.cluster["new_slave_ip"], + ports=self.cluster["ports"], + creator=self.global_data["created_by"], + mysql_version=self.cluster["version"], + resource_spec=self.global_data["resource_spec"], + ) + return True + + def tendb_slave_recover_add_tuple(self): + new_slave_to_old_master = { + "master": {"ip": self.cluster["master_ip"], "port": self.cluster["master_port"]}, + "slave": {"ip": self.cluster["new_slave_ip"], "port": self.cluster["new_slave_port"]}, + } + TenDBClusterMigrateRemoteDb.add_storage_tuple( + cluster_id=self.cluster["cluster_id"], storage=new_slave_to_old_master + ) + # todo 是否修改new_master角色为中继状态 + + def tendb_modify_storage_status(self): + storage = StorageInstance.objects.get(self.cluster["storage_id"]) + storage.status = self.cluster["storage_status"] + storage.save() + + def tendb_slave_recover_switch(self): + for node in self.cluster["my_shards"].values(): + source = { + "master": {"ip": node["master"]["ip"], "port": node["master"]["port"]}, + "slave": {"ip": node["slave"]["ip"], "port": node["slave"]["port"]}, + } + target = { + "master": {"ip": node["master"]["ip"], "port": node["master"]["port"]}, + "slave": {"ip": node["new_slave"]["ip"], "port": node["new_slave"]["port"]}, + } + TenDBClusterMigrateRemoteDb.switch_remote_node( + cluster_id=self.cluster["cluster_id"], source=source, target=target + ) diff --git a/dbm-ui/backend/flow/utils/spider/tendb_cluster_info.py b/dbm-ui/backend/flow/utils/spider/tendb_cluster_info.py new file mode 100644 index 0000000000..668237e6f8 --- /dev/null +++ b/dbm-ui/backend/flow/utils/spider/tendb_cluster_info.py @@ -0,0 +1,143 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import copy + +from backend.db_meta.models import Cluster, StorageInstance + + +def get_remotedb_info(ip: str, bk_cloud_id: int) -> list: + nodes = [] + storage_instances = StorageInstance.objects.filter(machine__ip=ip, machine__bk_cloud_id=bk_cloud_id) + for one in storage_instances: + # storage = one.__dict__ + storage = { + "version": one.version, + "port": one.port, + "bk_biz_id": one.bk_biz_id, + "status": one.status, + "instance_role": one.instance_role, + "phase": one.phase, + "bk_instance_id": one.bk_instance_id, + "db_module_id": one.db_module_id, + "ip": ip, + "bk_cloud_id": bk_cloud_id, + } + nodes.append(storage) + return nodes + + +def get_rollback_clusters_info( + source_cluster_id: int, + target_cluster_id: int, +): + ip_list = [] + cluster_info = {"shards": {}, "source_spiders": [], "target_spiders": []} + source_obj = Cluster.objects.get(id=source_cluster_id) + target_obj = Cluster.objects.get(id=target_cluster_id) + source_spiders = source_obj.proxyinstance_set.filter() + target_spiders = target_obj.proxyinstance_set.filter() + for spider in source_spiders: + cluster_info["source_spiders"].append(spider.simple_desc) + for spider in target_spiders: + cluster_info["target_spiders"].append(spider.simple_desc) + ip_list.append(spider.machine.ip) + + cluster_info["bk_cloud_id"] = source_obj.bk_cloud_id + cluster_info["source"] = source_obj.to_dict() + cluster_info["target"] = target_obj.to_dict() + shards = source_obj.tendbclusterstorageset_set.filter() + new_shards = target_obj.tendbclusterstorageset_set.filter() + if len(shards) != len(new_shards): + return None + for shard in shards: + master_obj = StorageInstance.objects.get(id=shard.storage_instance_tuple.ejector_id) + slave_obj = StorageInstance.objects.get(id=shard.storage_instance_tuple.receiver_id) + shards_info = {"master": master_obj.simple_desc, "slave": slave_obj.simple_desc} + cluster_info["shards"][shard.shard_id] = shards_info + + for shard in new_shards: + master_obj = StorageInstance.objects.get(id=shard.storage_instance_tuple.ejector_id) + slave_obj = StorageInstance.objects.get(id=shard.storage_instance_tuple.receiver_id) + shards_info = {"new_master": master_obj.simple_desc, "new_slave": slave_obj.simple_desc} + ip_list.append(master_obj.machine.ip) + ip_list.append(slave_obj.machine.ip) + if shard.shard_id in cluster_info["shards"]: + cluster_info["shards"][shard.shard_id].update(shards_info) + else: + return None + ip_list = list(set(ip_list)) + cluster_info["ip_list"] = ip_list + return cluster_info + + +def get_cluster_info(cluster_id: int): + """ + 获取集群相关信息 + """ + cluster_info = { + "shards": {}, + "spiders": [], + "shard_ids": [], + "masters": [], + "slaves": [], + "master_slave_map": {}, + } + source_obj = Cluster.objects.get(id=cluster_id) + source_spiders = source_obj.proxyinstance_set.filter() + for spider in source_spiders: + cluster_info["spiders"].append(spider.simple_desc) + cluster_info["cluster"] = source_obj.to_dict() + cluster_info["cluster"]["cluster_id"] = source_obj.id + cluster_info["cluster_id"] = source_obj.id + cluster_info["bk_cloud_id"] = source_obj.bk_cloud_id + cluster_info["bk_biz_id"] = source_obj.bk_biz_id + cluster_info["db_module_id"] = source_obj.db_module_id + cluster_info["cluster_type"] = source_obj.cluster_type + shards = source_obj.tendbclusterstorageset_set.filter() + for shard in shards: + master_obj = StorageInstance.objects.get(id=shard.storage_instance_tuple.ejector_id) + slave_obj = StorageInstance.objects.get(id=shard.storage_instance_tuple.receiver_id) + shards_info = {"master": master_obj.simple_desc, "slave": slave_obj.simple_desc} + shards_info["master"]["id"] = master_obj.id + shards_info["slave"]["id"] = slave_obj.id + cluster_info["shards"][shard.shard_id] = shards_info + cluster_info["shard_ids"].append(shard.shard_id) + cluster_info["masters"].append(master_obj.machine.ip) + cluster_info["slaves"].append(slave_obj.machine.ip) + cluster_info["master_slave_map"][master_obj.machine.ip] = slave_obj.machine.ip + cluster_info["shard_ids"].sort() + cluster_info["masters"] = list(set(copy.deepcopy(cluster_info["masters"]))) + cluster_info["slaves"] = list(set(copy.deepcopy(cluster_info["slaves"]))) + cluster_info["masters"].sort() + cluster_info["slaves"].sort() + return cluster_info + + +def get_slave_recover_info(cluster_id: int, ip: str): + cluster_info = get_cluster_info(cluster_id) + cluster_info["my_shards"] = {} + if ip in cluster_info["slaves"]: + for key, val in cluster_info["shards"].items(): + if val["slave"]["ip"] == ip: + cluster_info["my_shards"][key] = val + return cluster_info + + +def get_slave_local_recover_info(cluster_id: int, storage_id: int): + cluster_info = get_cluster_info(cluster_id) + cluster_info["my_shards"] = {} + storage = StorageInstance.objects.get(id=storage_id) + cluster_info["target_ip"] = storage.machine.ip + for key, val in cluster_info["shards"].items(): + if val["slave"]["id"] == storage.id: + cluster_info["my_shards"][key] = val + break + return cluster_info diff --git a/dbm-ui/backend/flow/utils/tbinlogdumper/context_dataclass.py b/dbm-ui/backend/flow/utils/tbinlogdumper/context_dataclass.py new file mode 100644 index 0000000000..e83a73bbd5 --- /dev/null +++ b/dbm-ui/backend/flow/utils/tbinlogdumper/context_dataclass.py @@ -0,0 +1,49 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 dataclasses import dataclass, field + + +@dataclass() +class TBinlogDumperAddContext: + """ + 定义添加Tbinlogdumper的可交互上下文dataclass类 + """ + + master_ip_sync_info: dict = field(default_factory=dict) # 代表获取到master的主从复制位点信息 + backup_info: dict = field(default_factory=dict) # 代表做全量同步时备份信息 + + @staticmethod + def get_sync_info_var_name() -> str: + return "master_ip_sync_info" + + +@dataclass +class StopSlaveKwargs: + """ + 定义tbinlogdumper 执行stop slave的私有变量 + """ + + bk_cloud_id: int + tbinlogdumper_ip: str + tbinlogdumper_port: int + is_safe: bool + + +@dataclass +class TBinlogDumperFullSyncDataKwargs: + """ + 定义为tbinlogdumper实例同步的私有变量 + """ + + bk_cloud_id: int + backup_ip: str + backup_port: int + backup_role: str + module_id: int diff --git a/dbm-ui/backend/flow/utils/tbinlogdumper/tbinlogdumper_act_payload.py b/dbm-ui/backend/flow/utils/tbinlogdumper/tbinlogdumper_act_payload.py new file mode 100644 index 0000000000..830991f2c0 --- /dev/null +++ b/dbm-ui/backend/flow/utils/tbinlogdumper/tbinlogdumper_act_payload.py @@ -0,0 +1,196 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging + +from backend.components import DBConfigApi +from backend.components.dbconfig.constants import FormatType, LevelName +from backend.db_meta.enums import ClusterType, InstanceRole +from backend.db_meta.models import Cluster +from backend.db_package.models import Package +from backend.flow.consts import DBActuatorActionEnum, DBActuatorTypeEnum, MediumEnum +from backend.flow.engine.bamboo.scene.common.get_real_version import get_mysql_real_version + +logger = logging.getLogger("flow") + + +class TBinlogDumperActPayload(object): + @staticmethod + def get_tbinlogdumper_config(bk_biz_id: int, module_id: int): + data = DBConfigApi.query_conf_item( + { + "bk_biz_id": str(bk_biz_id), + "level_name": LevelName.MODULE, + "level_value": str(module_id), + "conf_file": "latest", + "conf_type": "tbinlogdumper", + "namespace": ClusterType.TenDBHA, + "format": FormatType.MAP_LEVEL, + } + ) + return data["content"] + + def install_tbinlogdumper_payload(self, **kwargs): + """ + 安装tbinlogdumper实例,字符集是通过master实例获取 + """ + + pkg = Package.get_latest_package(version=MediumEnum.Latest, pkg_type=MediumEnum.TBinlogDumper) + version_no = get_mysql_real_version(pkg.name) + + # 计算这次安装tbinlogdumper实例端口,并且计算每个端口安装配置 + mycnf_configs = {} + dumper_configs = {} + + for conf in self.ticket_data["add_conf_list"]: + mycnf_configs[conf["port"]] = self.get_tbinlogdumper_config( + bk_biz_id=self.ticket_data["bk_biz_id"], module_id=conf["module_id"] + ) + dumper_configs[conf["port"]] = {"dumper_id": conf["area_name"], "area_name": conf["area_name"]} + + drs_account, dbha_account = self.get_super_account() + return { + "db_type": DBActuatorTypeEnum.TBinlogDumper.value, + "action": DBActuatorActionEnum.Deploy.value, + "payload": { + "general": {"runtime_account": self.account}, + "extend": { + "host": kwargs["ip"], + "pkg": pkg.name, + "pkg_md5": pkg.md5, + "mysql_version": version_no, + "charset": self.ticket_data["charset"], + "inst_mem": 0, + "ports": [conf["port"] for conf in self.ticket_data["add_conf_list"]], + "super_account": drs_account, + "dbha_account": dbha_account, + "mycnf_configs": mycnf_configs, + "dumper_configs": dumper_configs, + }, + }, + } + + def uninstall_tbinlogdumper_payload(self, **kwargs) -> dict: + """ + 卸载tbinlogdumper进程的payload参数 + """ + return { + "db_type": DBActuatorTypeEnum.TBinlogDumper.value, + "action": DBActuatorActionEnum.UnInstall.value, + "payload": { + "general": {"runtime_account": self.account}, + "extend": { + "host": kwargs["ip"], + "force": True, + "ports": self.cluster["listen_ports"], + }, + }, + } + + def tbinlogdumper_sync_data_payload(self, **kwargs): + """ + TBinlogDumper建立数据同步 + """ + return { + "db_type": DBActuatorTypeEnum.MySQL.value, + "action": DBActuatorActionEnum.ChangeMaster.value, + "payload": { + "general": {"runtime_account": self.account}, + "extend": { + "host": kwargs["ip"], + "port": self.cluster["listen_port"], + "master_host": self.cluster["master_ip"], + "master_port": self.cluster["master_port"], + "is_gtid": False, + "max_tolerate_delay": 0, + "force": False, + "bin_file": self.cluster["bin_file"], + "bin_position": self.cluster["bin_position"], + }, + }, + } + + def tbinlogdumper_backup_demand_payload(self, **kwargs): + return { + "db_type": DBActuatorTypeEnum.TBinlogDumper.value, + "action": DBActuatorActionEnum.MySQLBackupDemand.value, + "payload": { + "general": {"runtime_account": self.account}, + "extend": { + "host": kwargs["ip"], + "port": self.ticket_data["port"], + "role": self.ticket_data["role"], + "backup_type": "logical", + "backup_gsd": ["data"], + "regex": self.ticket_data["db_table_filter_regex"], + "backup_id": self.ticket_data["backup_id"].__str__(), + "bill_id": str(self.ticket_data["uid"]), + "custom_backup_dir": "tbinlogdumper", + }, + }, + } + + def tbinlogdumper_restore_payload(self, **kwargs): + """ + tbinlogdumper 恢复备份数据(不包括表结构) + """ + cluster = Cluster.objects.get(id=self.ticket_data["cluster_id"]) + master = cluster.storageinstance_set.get(instance_role=InstanceRole.BACKEND_MASTER) + index_file = "" + for result in kwargs["trans_data"]["backup_info"]["report_result"]: + if result["file_type"] == "index": + index_file = result["file_name"] + break + + return { + "db_type": DBActuatorTypeEnum.MySQL.value, + "action": DBActuatorActionEnum.RestoreSlave.value, + "payload": { + "general": {"runtime_account": self.account}, + "extend": { + "work_dir": kwargs["trans_data"]["backup_info"]["backup_dir"], + "backup_dir": kwargs["trans_data"]["backup_info"]["backup_dir"], + "backup_files": { + "index": [index_file], + }, + "tgt_instance": { + "host": kwargs["ip"], + "port": self.ticket_data["add_tbinlogdumper_conf"]["port"], + "user": self.account["admin_user"], + "pwd": self.account["admin_pwd"], + "socket": None, + "charset": "", + "options": "", + }, + "src_instance": {"host": master.machine.ip, "port": master.port}, + "change_master": True, + "work_id": "", + }, + }, + } + + def tbinlogdumper_load_schema_payload(self, **kwargs): + """ + TbinlogDumper导入表结构 + """ + cluster = Cluster.objects.get(id=self.ticket_data["cluster_id"]) + master = cluster.storageinstance_set.get(instance_role=InstanceRole.BACKEND_MASTER) + return { + "db_type": DBActuatorTypeEnum.TBinlogDumper.value, + "action": DBActuatorActionEnum.DumpSchema.value, + "payload": { + "general": {"runtime_account": self.account}, + "extend": { + "host": kwargs["ip"], + "port": master.port, + "tbinlogdumper_port": self.ticket_data["add_tbinlogdumper_conf"]["port"], + "charset": "default", + }, + }, + } diff --git a/dbm-ui/backend/flow/views/base.py b/dbm-ui/backend/flow/views/base.py index 76cf1ba7f8..991093825e 100644 --- a/dbm-ui/backend/flow/views/base.py +++ b/dbm-ui/backend/flow/views/base.py @@ -32,8 +32,6 @@ def as_view(cls, **initkwargs): def get_permissions(self): # 暂时只对超级用户或者DEBUG模式开放 - """ if not self.request.user.is_superuser and not settings.DEBUG: raise PermissionDenied(_("权限不足,无法访问!")) - """ return [] diff --git a/dbm-ui/backend/flow/views/mysql_open_area.py b/dbm-ui/backend/flow/views/mysql_open_area.py new file mode 100644 index 0000000000..ed94aed17d --- /dev/null +++ b/dbm-ui/backend/flow/views/mysql_open_area.py @@ -0,0 +1,31 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging +import uuid + +from rest_framework.response import Response + +from backend.flow.engine.controller.mysql import MySQLController +from backend.flow.views.base import FlowTestView + +logger = logging.getLogger("root") + + +class MysqlOpenAreaSceneApiView(FlowTestView): + """ + api: /apis/v1/flow/scene/mysql_open_area + params: + """ + + def post(self, request): + root_id = uuid.uuid1().hex + test = MySQLController(root_id=root_id, ticket_data=request.data) + test.mysql_open_area_scene() + return Response({"root_id": root_id}) diff --git a/dbm-ui/backend/flow/views/name_service.py b/dbm-ui/backend/flow/views/name_service.py index 273c980d56..a320c42ed7 100644 --- a/dbm-ui/backend/flow/views/name_service.py +++ b/dbm-ui/backend/flow/views/name_service.py @@ -19,7 +19,7 @@ logger = logging.getLogger("root") -class NameServiceClbCreateSceneApiView(FlowTestView): +class ClbCreateSceneApiView(FlowTestView): """ 名字服务clb创建api接口 """ @@ -34,7 +34,7 @@ def post(request): return Response({"root_id": root_id}) -class NameServiceClbDeleteSceneApiView(FlowTestView): +class ClbDeleteSceneApiView(FlowTestView): """ 名字服务clb删除api接口 """ @@ -49,7 +49,37 @@ def post(request): return Response({"root_id": root_id}) -class NameServicePolarisCreateSceneApiView(FlowTestView): +class DomainBindClbIpSceneApiView(FlowTestView): + """ + 主域名绑定clb ip api接口 + """ + + @staticmethod + def post(request): + """ + 主域名绑定clb ip + """ + root_id = uuid.uuid1().hex + NameServiceController(root_id=root_id, ticket_data=request.data).immute_domain_bind_clb_ip() + return Response({"root_id": root_id}) + + +class DomainUnBindClbIpSceneApiView(FlowTestView): + """ + 主域名解绑clb ip api接口 + """ + + @staticmethod + def post(request): + """ + 主域名解绑clb ip + """ + root_id = uuid.uuid1().hex + NameServiceController(root_id=root_id, ticket_data=request.data).immute_domain_unbind_clb_ip() + return Response({"root_id": root_id}) + + +class PolarisCreateSceneApiView(FlowTestView): """ 名字服务polaris创建api接口 """ @@ -64,7 +94,7 @@ def post(request): return Response({"root_id": root_id}) -class NameServicePolarisDeleteSceneApiView(FlowTestView): +class PolarisDeleteSceneApiView(FlowTestView): """ 名字服务polaris删除api接口 """ diff --git a/dbm-ui/backend/flow/views/redis_cluster.py b/dbm-ui/backend/flow/views/redis_cluster.py index 685e058f19..eeb58ba37d 100644 --- a/dbm-ui/backend/flow/views/redis_cluster.py +++ b/dbm-ui/backend/flow/views/redis_cluster.py @@ -227,26 +227,21 @@ class RedisProxyScaleSceneApiView(FlowTestView): params: { "uid":"2022051612120001", + "bk_biz_id": 3, "created_by":"xxxx", - "bk_biz_id":2005000002, - "ticket_type":"PROXY_SCALE", - "cluster_id": 82, - "ip_source": "manual_input", - "add_proxy_list": [ - { - "ip": "127.0.0.1", - "bk_cloud_id": 0, - "bk_biz_id": 4, - "bk_host_id": 2 - } - ], - "del_proxy_list": [ - { - "ip": "127.0.0.1", - "bk_cloud_id": 0, - "bk_biz_id": 4, - "bk_host_id": 2 - } + "ticket_type":"PROXY_SCALE_UP/PROXY_SCALE_DOWN", + "infos": [ + { + "cluster_id": 1, + "target_proxy_count": 1, + // 缩容 + "online_switch_type":"user_confirm/no_confirm", + // 扩容 + "proxy_scale_up_hosts": [ + {"ip": "3.3.3.1", "bk_cloud_id": 0, "bk_host_id": 2}, + {"ip": "3.3.3.2", "bk_cloud_id": 0, "bk_host_id": 2}, + ] + } ] } """ @@ -258,48 +253,84 @@ def post(request): return Response({"root_id": root_id}) -class RedisClusterDtsSceneApiView(FlowTestView): +class RedisBackendScaleSceneApiView(FlowTestView): + """ + api: /apis/v1/flow/scene/redis_backend_scale + params{ + "bk_biz_id": "", + "ticket_type":"", + "infos":[ + "bk_cloud_id":", + "online_switch_type":"", + "cluster_id": "", # 必须 + "db_version": "Redis-7", # 可选 + "group_num": 4, + "shard_num": 40, # 需要保证分片数能整除机器组数。并且与老集群架构的分片数是一样的 + "backend_group":[ + { + "master":"1.1.1.1", + "slave":"2.2.2.1" + }, + { + "master":"1.1.1.2", + "slave":"2.2.2.2" + }, + { + "master":"1.1.1.3", + "slave":"2.2.2.3" + }, + { + "master":"1.1.1.4", + "slave":"2.2.2.4" + }], + "deploy_plan_id":3, + "resource_spec":{} + }] """ - api: /apis/v1/flow/scene/redis_cluster_dts + + @staticmethod + def post(request): + root_id = uuid.uuid1().hex + RedisController(root_id=root_id, ticket_data=request.data).redis_backend_scale() + return Response({"root_id": root_id}) + + +class RedisClusterDataCopySceneApiView(FlowTestView): + """ + api: /apis/v1/flow/scene/redis_cluster_data_copy params: - { - "uid":"2022051612120001", - "created_by":"xxxx", + { + "uid":1111, "bk_biz_id":2005000194, - "ticket_type":"REDIS_NEW_DTS_JOB", - "dts_bill_type":"cluster_nodes_num_update/cluster_type_update/cluster_data_copy", - # dts_copy_type值为: - # - one_app_diff_cluster - # - diff_app_diff_cluster - # - copy_from_rollback_temp - # - copy_to_other_system - # - user_built_to_dbm + "ticket_type":"REDIS_CLUSTER_DATA_COPY", + # dts 复制类型: 业务内 "dts_copy_type":"one_app_diff_cluster", - "online_switch":{ - "type":"auto/user_confirm" + # write_mode值为: + # - delete_and_write_to_redis 先删除同名redis key, 在执行写入 (如: del $key + hset $key) + # - keep_and_append_to_redis 保留同名redis key,追加写入 + # - flushall_and_write_to_redis 先清空目标集群所有数据,在写入 + "write_mode": "delete_and_write_to_redis", + # 同步断开设置 + "sync_disconnect_setting":{ + # type 值为: + # - auto_disconnect_after_replication: 数据复制完成后自动断开同步关系 + # - keep_sync_with_reminder: 数据复制完成后保持同步关系,定时发送断开同步提醒 + "type": "auto_disconnect_after_replication" + "reminder_frequency": "once_daily/once_weekly" + }, + "data_check_repair_setting":{ + # type值为: + # - data_check_and_repair: 数据校验并修复 + # - data_check_only: 仅进行数据校验,不进行修复 + # - no_check_no_repair: 不校验不修复 + "type": "data_check_and_repair", + # execution_frequency 执行频次 + "execution_frequency": "once_after_replication/once_every_three_days/once_weekly" } - "datacheck":true/false, - "datarepair":true/false, - "datarepair_mode":"auto/user_confirm", - "rules":[ + "infos":[ { - #dts_copy_type=user_built_to_dbm,src_cluster值就是源redis addr信息,如 1.1.1.1:6379; 其他情况下填源redis的domain; - "src_cluster":"cache.luketest101.dba.db:50000", - #dts_copy_type=user_built_to_dbm,src_cluster_type值必须为 RedisInstance or RedisCluster;其他时候值为空; - "src_cluster_type":"", - #dts_copy_type=user_built_to_dbm,src_cluster_password必须填值;否则为空; - "src_cluster_password":"", - #dts_copy_type=copy_from_rollback_temp时,src_rolback_bill_id才有值,其余为空 - "src_rolback_bill_id":0, - #dts_copy_type=copy_from_rollback_temp时,src_rollback_instances才有值,其余为空 - "src_rollback_instances":"", - #目的集群业务id,默认和bk_biz_id保持一致,只有当dts_copy_type=diff_app_diff_cluster时,才不同; - "dst_bk_biz_id": 0, - #dts_copy_type=sync_to_other_system,dst_cluster值就是目的redis addr信息, - #如2.2.2.2:6379; 其他情况下填目的redis的domain; - "dst_cluster":"tendisplus.luketest201.dba.db:50000", - #dts_copy_type=sync_to_other_system必须填值;否则为空; - "dst_cluster_password":"", + "src_cluster":"1111", + "dst_cluster":"2222", "key_white_regex":"*", #包含key "key_black_regex":"", #排除key } @@ -310,5 +341,200 @@ class RedisClusterDtsSceneApiView(FlowTestView): @staticmethod def post(request): root_id = uuid.uuid1().hex - RedisController(root_id=root_id, ticket_data=request.data).redis_dts() + RedisController(root_id=root_id, ticket_data=request.data).redis_cluster_data_copy() + return Response({"root_id": root_id}) + + +class RedisClusterShardNumUpdateSceneApiView(FlowTestView): + """""" + + @staticmethod + def post(request): + root_id = uuid.uuid1().hex + RedisController(root_id=root_id, ticket_data=request.data).redis_cluster_shard_num_update() + return Response({"root_id": root_id}) + + +class RedisClusterTypeUpdateSceneApiView(FlowTestView): + """""" + + @staticmethod + def post(request): + root_id = uuid.uuid1().hex + RedisController(root_id=root_id, ticket_data=request.data).redis_cluster_type_update() + return Response({"root_id": root_id}) + + +class RedisClusterDataCheckRepairApiView(FlowTestView): + """ + api: /apis/v1/flow/scene/redis_cluster_data_check_repair + params: + { + "bk_biz_id": 3, + "ticket_type":"REDIS_DATACOPY_CHECK_REPAIR", + # execute_mode 执行模式 + # - auto_execution 自动执行 + # - scheduled_execution 定时执行 + "execute_mode":"auto_execution", + "specified_execution_time":"2023-06-20 00:00:00", # 定时执行,指定执行时间 + "global_timeout": "never/3h/6h/24h/48h/168h", # 全局超时时间, + "data_repair_enabled":true/false, # 是否修复数据 + "repair_mode":"auto_repair/manual_confirm", + "infos":[ + { + "bill_id":11111, #关联的(数据复制)单据ID + "src_cluster":"cache.src.testapp.db:50000", #源集群,来自于数据复制记录 + "src_instances":["all"], # 源实例列表 + "dst_cluster": "cache.dst.testapp.db:50001",#目的集群,来自于数据复制记录 + "key_white_regex":"*", #包含key + "key_black_regex":"", #排除key + } + ] + } + """ + + @staticmethod + def post(request): + root_id = uuid.uuid1().hex + RedisController(root_id=root_id, ticket_data=request.data).redis_cluster_data_check_repair() + return Response({"root_id": root_id}) + + +class RedisAddDtsServerSceneApiView(FlowTestView): + """ + api: /apis/v1/flow/scene/redis_add_dts_server + params: + { + "uid":"2022051612120001", + "created_by":"xxxx", + "bk_biz_id":3, + "ticket_type":"REDIS_ADD_DTS_SERVER", + "infos":[ + {"ip": "3.3.3.1", "bk_cloud_id": 0, "bk_host_id": 2,"bk_city_id":1,"bk_city_name":"上海"}, + {"ip": "3.3.3.2", "bk_cloud_id": 0, "bk_host_id": 2,"bk_city_id":2,"bk_city_name":"南京"} + ] + } + """ + + @staticmethod + def post(request): + root_id = uuid.uuid1().hex + RedisController(root_id=root_id, ticket_data=request.data).redis_add_dts_server() + return Response({"root_id": root_id}) + + +class RedisRemoveDtsServerSceneApiView(FlowTestView): + """ + api: /apis/v1/flow/scene/redis_remove_dts_server + params: + { + "uid":"2022051612120001", + "created_by":"xxxx", + "bk_biz_id":3, + "ticket_type":"REDIS_REMOVE_DTS_SERVER", + "infos":[ + {"ip": "3.3.3.1", "bk_cloud_id": 0}, + {"ip": "3.3.3.2", "bk_cloud_id": 0} + ] + } + """ + + @staticmethod + def post(request): + root_id = uuid.uuid1().hex + RedisController(root_id=root_id, ticket_data=request.data).redis_remove_dts_server() + return Response({"root_id": root_id}) + + +class RedisDataStructureSceneApiView(FlowTestView): + """ + api: /apis/v1/flow/scene/redis_data_structure + params: + { + "bk_biz_id": 3, + "uid": "2022051612120001", + "created_by":"admin", + "ticket_type":"REDIS_DATA_STRUCTURE", + "infos": [ + { + "cluster_id": 1, + "master_instance":[ + "127.0.0.1:30000", "127.0.0.1:30002" + ], + "recovery_time_point": "2022-12-12 11:11:11", + "redis": [ + {"ip": "3.3.3.1", "bk_cloud_id": 0, "bk_host_id": 2}, + {"ip": "3.3.3.2", "bk_cloud_id": 0, "bk_host_id": 2}, + ] + } + ] + } + """ + + @staticmethod + def post(request): + root_id = uuid.uuid1().hex + RedisController(root_id=root_id, ticket_data=request.data).redis_data_structure() + return Response({"root_id": root_id}) + + +class RedisDataStructureTaskDeleteSceneApiView(FlowTestView): + """ + api: /apis/v1/flow/scene/redis_data_structure + params: + { + "bk_biz_id": 3, + "uid": "2022051612120001", + "created_by":"admin", + "ticket_type":"REDIS_DATA_STRUCTURE", + "infos": [ + { + "cluster_id": 1, + "master_instance":[ + "127.0.0.1:30000", "127.0.0.1:30002" + ], + "recovery_time_point": "2022-12-12 11:11:11", + "redis": [ + {"ip": "3.3.3.1", "bk_cloud_id": 0, "bk_host_id": 2}, + {"ip": "3.3.3.2", "bk_cloud_id": 0, "bk_host_id": 2}, + ] + } + ] + } + """ + + @staticmethod + def post(request): + root_id = uuid.uuid1().hex + RedisController(root_id=root_id, ticket_data=request.data).redis_data_structure_task_delete() + return Response({"root_id": root_id}) + + +class RedisClusterAddSlaveApiView(FlowTestView): + """ + api: /apis/v1/flow/scene/redis_cluster_add_slave + params: + { + "bk_biz_id": 3, + "ticket_type":"REDIS_CLUSTER_ADD_SLAVE", + "created_by":"admin", + "uid":"1111", + "infos": [ + { + "cluster_id": 1, + "pairs": [ + { + "redis_master": {"ip": "2.2.3.4", "bk_cloud_id": 0, "bk_host_id": 123}, + "redis_slave": [{"ip": "2.2.3.4", "bk_cloud_id": 0, "bk_host_id": 123}] + }, + ] + } + ] + } + """ + + @staticmethod + def post(request): + root_id = uuid.uuid1().hex + RedisController(root_id=root_id, ticket_data=request.data).redis_cluster_add_slave() return Response({"root_id": root_id}) diff --git a/dbm-ui/backend/flow/views/redis_scene.py b/dbm-ui/backend/flow/views/redis_scene.py index fff525c3d6..7419dbedeb 100644 --- a/dbm-ui/backend/flow/views/redis_scene.py +++ b/dbm-ui/backend/flow/views/redis_scene.py @@ -20,63 +20,67 @@ logger = logging.getLogger("flow") -class RedisClusterSlaveCutOffSceneApiView(FlowTestView): +class RedisClusterCompleteReplaceSceneApiView(FlowTestView): """ - /apis/v1/flow/scene/cutoff/redis_cluster_slave + /apis/v1/flow/scene/cutoff/redis_cluster params: { - "cluster_id":111, # 必须有 - "domain_name":"xxx.abc.dba.db", - "cluster_type":"xxxxx", - "db_version":"yyyyyy", - "bk_biz_id":"", - "bk_cloud_id":11, - "hosts":[1.1.1.1,2.2.2.2], # 必须有 - "region":"xxxyw", # 可选 - "device_class":"S5.Large8" # 可选 - "ticket_type": "REDIS_CLUSTER_SLAVE_CUTOFF" - "ip_source": "manual_input", # 手动输入/自动匹配资源池 - "assign_hosts":{"1.1.1.1":"6.6.6.6","2.2.2.2":"7.7.7.7,8.8.8.8"} # 可选 + "bk_biz_id": 3, + "uid": "2022051612120001", + "created_by":"vitox", + "ticket_type":"REDIS_CLUSTER_CUTOFF", + "infos": [ + { + "cluster_id": 1, + "redis_proxy": [ + { + "old": {"ip": "2.2.a.4", "bk_cloud_id": 0, "bk_host_id": 123}, + "new": {"ip": "2.2.a.4", "bk_cloud_id": 0, "bk_host_id": 123} + }], + "redis_slave": [ + { + "old": {"ip": "2.b.3.4", "bk_cloud_id": 0, "bk_host_id": 123}, + "new": {"ip": "2.b.3.4", "bk_cloud_id": 0, "bk_host_id": 123} + }], + "redis_master": [ + { + "old": {"ip": "2.2.3.c", "bk_cloud_id": 0, "bk_host_id": 123}, + "new": [{"ip": "2.2.3.c", "bk_cloud_id": 0, "bk_host_id": 123},] + }] + } + ] } """ @staticmethod def post(request): root_id = uuid.uuid1().hex - RedisController(root_id=root_id, ticket_data=request.data).redis_cluster_slave_cutoff_scene() + RedisController(root_id=root_id, ticket_data=request.data).redis_cluster_cutoff_scene() return Response({"root_id": root_id}) -class RedisClusterMasterCutOffSceneApiView(FlowTestView): +class RedisInstallDbmonSceneApiView(FlowTestView): """ - /apis/v1/flow/scene/cutoff/redis_cluster_master + /apis/v1/flow/scene/install/dbmon params: { - "cluster_id":111, # 必须有 - "domain_name":"xxx.abc.dba.db", - "cluster_type":"xxxxx", - "db_version":"yyyyyy", - "bk_biz_id":"", - "bk_cloud_id":11, - "hosts":[1.1.1.1,2.2.2.2], # 必须有 - "region":"xxxyw", # 可选 - "device_class":"S5.Large8" # 可选 - "ticket_type": "REDIS_CLUSTER_MASTER_CUTOFF" - "ip_source": "manual_input", # 手动输入/自动匹配资源池 - "assign_hosts":{"1.1.1.1":"6.6.6.6","2.2.2.2":"7.7.7.7,8.8.8.8"} # 可选 + "bk_biz_id":"", # 必须有 + "bk_cloud_id":11, # 必须有 + "hosts":[1.1.1.1,2.2.2.2], + "ticket_type": "REDIS_INSTALL_DBMON" } """ @staticmethod def post(request): root_id = uuid.uuid1().hex - RedisController(root_id=root_id, ticket_data=request.data).redis_cluster_master_cutoff_scene() + RedisController(root_id=root_id, ticket_data=request.data).redis_install_dbmon_scene() return Response({"root_id": root_id}) -class RedisInstallDbmonSceneApiView(FlowTestView): +class RedisClusterMSSwitchSceneApiView(FlowTestView): """ - /apis/v1/flow/scene/install/dbmon + /apis/v1/flow/scene/switch/redis_cluster params: { "bk_biz_id":"", # 必须有 @@ -89,5 +93,5 @@ class RedisInstallDbmonSceneApiView(FlowTestView): @staticmethod def post(request): root_id = uuid.uuid1().hex - RedisController(root_id=root_id, ticket_data=request.data).redis_install_dbmon_scene() + RedisController(root_id=root_id, ticket_data=request.data).redis_cluster_failover_scene() return Response({"root_id": root_id}) diff --git a/dbm-ui/backend/flow/views/riak_apply.py b/dbm-ui/backend/flow/views/riak_apply.py index 4858fd3660..70c9b444b9 100644 --- a/dbm-ui/backend/flow/views/riak_apply.py +++ b/dbm-ui/backend/flow/views/riak_apply.py @@ -17,7 +17,8 @@ class RiakApplySceneApiView(FlowTestView): - def post(self, request): + @staticmethod + def post(request): root_id = uuid.uuid1().hex flow = RiakController(root_id=root_id, ticket_data=request.data) flow.riak_cluster_apply_scene() diff --git a/dbm-ui/backend/flow/views/riak_destroy.py b/dbm-ui/backend/flow/views/riak_destroy.py new file mode 100644 index 0000000000..3e1e18f745 --- /dev/null +++ b/dbm-ui/backend/flow/views/riak_destroy.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import uuid + +from rest_framework.response import Response + +from backend.flow.engine.controller.riak import RiakController +from backend.flow.views.base import FlowTestView + + +class RiakClusterDestroyApiView(FlowTestView): + @staticmethod + def post(request): + root_id = uuid.uuid1().hex + flow = RiakController(root_id=root_id, ticket_data=request.data) + flow.riak_cluster_destroy_scene() + return Response({"root_id": root_id}) diff --git a/dbm-ui/backend/flow/views/riak_disable.py b/dbm-ui/backend/flow/views/riak_disable.py new file mode 100644 index 0000000000..97b37a052c --- /dev/null +++ b/dbm-ui/backend/flow/views/riak_disable.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import uuid + +from rest_framework.response import Response + +from backend.flow.engine.controller.riak import RiakController +from backend.flow.views.base import FlowTestView + + +class RiakClusterDisableApiView(FlowTestView): + @staticmethod + def post(request): + root_id = uuid.uuid1().hex + flow = RiakController(root_id=root_id, ticket_data=request.data) + flow.riak_cluster_disable_scene() + return Response({"root_id": root_id}) diff --git a/dbm-ui/backend/flow/views/riak_enable.py b/dbm-ui/backend/flow/views/riak_enable.py new file mode 100644 index 0000000000..5f55e80a9f --- /dev/null +++ b/dbm-ui/backend/flow/views/riak_enable.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import uuid + +from rest_framework.response import Response + +from backend.flow.engine.controller.riak import RiakController +from backend.flow.views.base import FlowTestView + + +class RiakClusterEnableApiView(FlowTestView): + @staticmethod + def post(request): + root_id = uuid.uuid1().hex + flow = RiakController(root_id=root_id, ticket_data=request.data) + flow.riak_cluster_enable_scene() + return Response({"root_id": root_id}) diff --git a/dbm-ui/backend/flow/views/riak_scale_in.py b/dbm-ui/backend/flow/views/riak_scale_in.py new file mode 100644 index 0000000000..233c52223b --- /dev/null +++ b/dbm-ui/backend/flow/views/riak_scale_in.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import uuid + +from rest_framework.response import Response + +from backend.flow.engine.controller.riak import RiakController +from backend.flow.views.base import FlowTestView + + +class RiakClusterScaleInApiView(FlowTestView): + @staticmethod + def post(request): + root_id = uuid.uuid1().hex + flow = RiakController(root_id=root_id, ticket_data=request.data) + flow.riak_cluster_scale_in_scene() + return Response({"root_id": root_id}) diff --git a/dbm-ui/backend/flow/views/riak_scale_out.py b/dbm-ui/backend/flow/views/riak_scale_out.py new file mode 100644 index 0000000000..6b6a207efe --- /dev/null +++ b/dbm-ui/backend/flow/views/riak_scale_out.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import uuid + +from rest_framework.response import Response + +from backend.flow.engine.controller.riak import RiakController +from backend.flow.views.base import FlowTestView + + +class RiakClusterScaleOutApiView(FlowTestView): + @staticmethod + def post(request): + root_id = uuid.uuid1().hex + flow = RiakController(root_id=root_id, ticket_data=request.data) + flow.riak_cluster_scale_out_scene() + return Response({"root_id": root_id}) diff --git a/dbm-ui/backend/flow/views/rollback_pipeline.py b/dbm-ui/backend/flow/views/rollback_pipeline.py deleted file mode 100644 index 873ad7bd03..0000000000 --- a/dbm-ui/backend/flow/views/rollback_pipeline.py +++ /dev/null @@ -1,184 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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. -""" -import base64 -import logging -import re -from enum import Enum -from typing import List - -import requests -from blue_krill.data_types.enum import EnumField, StructuredEnum -from django.test import Client -from django.utils.translation import ugettext_lazy as _ -from jinja2 import Environment -from rest_framework import serializers -from rest_framework.response import Response - -from backend import env -from backend.components import CCApi, GcsDnsApi, JobApi -from backend.db_meta.api import common -from backend.db_meta.models import Cluster, ClusterEntry, Machine, ProxyInstance, StorageInstance -from backend.flow.engine.bamboo.engine import BambooEngine -from backend.flow.models import FlowNode, FlowTree -from backend.flow.views.base import FlowTestView - -client = Client() -logger = logging.getLogger("root") - - -class ScriptTypeEnum(Enum): - Bash = 1 - Python = 2 - Perl = 3 - - -class RollbackPipelineApiView(FlowTestView): - def post(self, request): - serializer = RollbackPipelineSerializer(data=request.data) - if not serializer.is_valid(): - return Response(f"failed: {serializer.errors}") - root_id = request.data["root_id"] - cluster_type = request.data["cluster_type"] - engine = BambooEngine(root_id=root_id) - exists = FlowTree.objects.filter(root_id=root_id).exists() - if not exists: - return Response("success") - metas_data = [] - nodes = FlowNode.objects.filter(root_id=root_id) - for node in nodes: - data = engine.get_execution_data(node.node_id).data - try: - node_name = data["inputs"]["kwargs"]["node_name"] - if re.match(r"TenDB", node_name): - metas_data.append(data) - except Exception: - clean_hosts(20000, 10000, []) - for meta in metas_data: - kwargs = meta["inputs"]["kwargs"] - logger.info(f"kwargs: {kwargs}") - cluster_info = kwargs["cluster"] - bk_biz_id = cluster_info["bk_biz_id"] - bk_cloud_id = cluster_info["bk_cloud_id"] - domain_names = [{"domain_name": cluster_info["immute_domain"]}] - if cluster_info.get("slave_domain"): - domain_names.append({"domain_name": cluster_info["slave_domain"]}) - name = cluster_info["name"] - script_hosts = [] - hosts = [] - proxies = [] - storages = [] - mysql_port = 20000 - proxy_port = 10000 - for host in kwargs["machines"]: - hosts.append(host["ip"]) - script_hosts.append({"ip": host["ip"], "bk_cloud_id": 0}) - if host["machine_type"] == "proxy": - proxy_port = cluster_info["proxies"][0]["port"] - proxies.append({"ip": host["ip"], "port": proxy_port}) - else: - try: - mysql_port = cluster_info["storages"][0]["port"] - storages.append({"ip": host["ip"], "port": mysql_port}) - except KeyError: - mysql_port = cluster_info["storage"]["port"] - storages.append(cluster_info["storage"]) - # clean hosts - clean_hosts(mysql_port, proxy_port, script_hosts) - # delete ClusterEntry - try: - cluster = Cluster.objects.filter(name=name, bk_biz_id=bk_biz_id, cluster_type=cluster_type) - ClusterEntry.objects.filter(cluster__in=cluster).delete() - except Cluster.DoesNotExist: - logger.warning("not found cluster") - pass - # delete Cluster - Cluster.objects.filter(name=name, bk_biz_id=bk_biz_id, cluster_type=cluster_type).delete() - # delete ProxyInstance - proxy_objs = common.filter_out_instance_obj(proxies, ProxyInstance.objects.all()) - proxy_objs.delete() - # delete StorageInstance - storage_objs = common.filter_out_instance_obj(storages, StorageInstance.objects.all()) - storage_objs.delete() - # delete Machine - Machine.objects.filter(ip__in=hosts).delete() - # 删除DNS记录 - resp = CCApi.search_business( - { - "fields": ["bk_biz_id", "bk_biz_name", "db_app_abbr"], - "biz_property_filter": { - "condition": "AND", - "rules": [{"field": "bk_biz_id", "operator": "equal", "value": int(bk_biz_id)}], - }, - "page": {"start": 0, "limit": 10, "sort": ""}, - }, - raw=True, - use_admin=True, - ) - logger.info(f"business response: {resp}") - try: - app = resp["data"]["info"][0]["db_app_abbr"] - logger.info(f"appname: {app}") - resp = GcsDnsApi.delete_domain({"app": app, "domains": domain_names, "bk_cloud_id": bk_cloud_id}) - logger.info(f"delete domains response: {resp}") - except IndexError or KeyError: - logger.warning(f"not found app with bk_biz_id: {bk_biz_id}") - # 回收主机到空闲模块 - bk_host_ids = [] - for storage in storage_objs: - m = storage.machine - bk_host_ids.append(m.bk_host_id) - for proxy in proxy_objs: - m = proxy.machine - bk_host_ids.append(m.bk_host_id) - logger.info(f"ready transfer host to idle module with hosts: {bk_host_ids}") - if len(bk_host_ids) > 0: - resp = CCApi.transfer_host_to_idlemodule( - {"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "bk_host_id": bk_host_ids} - ) - logger.info(f"transfer_host_to_idlemodule response: {resp}") - # 重新导入主机至资源池 - url = "" - resp = requests.post(url=url, json={"apply_for": "", "ips": hosts, "labels": {}}) - if resp.status_code >= 400: - return Response(_("导入资源池失败")) - return Response("success") - - -def clean_hosts(mysql_port: int, proxy_port: int, hosts: List): - jinja_env = Environment() - template = jinja_env.from_string("") - script_plain = template.render({"mysql_port": mysql_port, "proxy_port": proxy_port}) - body = { - "bk_biz_id": env.JOB_BLUEKING_BIZ_ID, - "script_content": str(base64.b64encode(script_plain.encode("utf-8")), "utf-8"), - "task_name": "rollback_pipeline", - "account_alias": "root", - "script_language": ScriptTypeEnum.Bash.value, - "target_server": {"ip_list": hosts}, - } - JobApi.fast_execute_script(body, raw=True, use_admin=True) - - -class RollbackPipelineClusterTypeEnum(StructuredEnum): - TenDBHA = EnumField("tendbha", _("高可用架构")) - TenDBSingle = EnumField("tendbsingle", _("单实例架构")) - - -class RollbackPipelineSerializer(serializers.Serializer): - root_id = serializers.CharField(required=True) - cluster_type = serializers.ChoiceField(required=True, choices=RollbackPipelineClusterTypeEnum.get_choices()) - - -class PipelineTreeApiView(FlowTestView): - def get(self, request, root_id): - engine = BambooEngine(root_id=root_id) - tree = engine.get_pipeline_tree_states() - return Response(tree) diff --git a/dbm-ui/backend/flow/views/spider_add_mnt.py b/dbm-ui/backend/flow/views/spider_add_mnt.py new file mode 100644 index 0000000000..64107213e4 --- /dev/null +++ b/dbm-ui/backend/flow/views/spider_add_mnt.py @@ -0,0 +1,26 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging +import uuid + +from rest_framework.response import Response + +from backend.flow.engine.controller.spider import SpiderController +from backend.flow.views.base import FlowTestView + +logger = logging.getLogger("root") + + +class AddSpiderMNTSceneApiView(FlowTestView): + def post(self, request): + root_id = uuid.uuid1().hex + test = SpiderController(root_id=root_id, ticket_data=request.data) + test.add_spider_mnt_scene() + return Response({"root_id": root_id}) diff --git a/dbm-ui/backend/flow/views/spider_add_tmp_node.py b/dbm-ui/backend/flow/views/spider_add_tmp_node.py deleted file mode 100644 index a413689730..0000000000 --- a/dbm-ui/backend/flow/views/spider_add_tmp_node.py +++ /dev/null @@ -1,17 +0,0 @@ -import logging -import uuid - -from rest_framework.response import Response - -from backend.flow.engine.controller.spider import SpiderController -from backend.flow.views.base import FlowTestView - -logger = logging.getLogger("root") - - -class AddTmpSpiderSceneApiView(FlowTestView): - def post(self, request): - root_id = uuid.uuid1().hex - test = SpiderController(root_id=root_id, ticket_data=request.data) - test.spider_add_tmp_node_scene() - return Response({"root_id": root_id}) diff --git a/dbm-ui/backend/flow/views/spider_cluster_flashback.py b/dbm-ui/backend/flow/views/spider_cluster_flashback.py new file mode 100644 index 0000000000..7172e3263c --- /dev/null +++ b/dbm-ui/backend/flow/views/spider_cluster_flashback.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging +import uuid + +from django.utils.translation import ugettext as _ +from rest_framework.response import Response + +from backend.flow.engine.controller.spider import SpiderController +from backend.flow.views.base import FlowTestView + +logger = logging.getLogger("root") + + +class TenDBClusterFlashbackView(FlowTestView): + @staticmethod + def post(request): + logger.info(_("开始TenDBCluster Flashback场景")) + + root_id = uuid.uuid1().hex + logger.info("define root_id: {}".format(root_id)) + + c = SpiderController(root_id=root_id, ticket_data=request.data) + c.flashback() + return Response({"root_id": root_id}) diff --git a/dbm-ui/backend/flow/views/spider_cluster_truncate_database.py b/dbm-ui/backend/flow/views/spider_cluster_truncate_database.py index bbf447efa6..9e1c172a35 100644 --- a/dbm-ui/backend/flow/views/spider_cluster_truncate_database.py +++ b/dbm-ui/backend/flow/views/spider_cluster_truncate_database.py @@ -22,7 +22,7 @@ class TenDBClusterTruncateDatabaseView(FlowTestView): """ - api: api: /apis/v1/flow/scene/tendbcluster_truncate_database + api: api: /apis/v1/flow/scene/tendbcluster_truncate_data """ @staticmethod diff --git a/dbm-ui/backend/flow/views/spider_migrage_remotedb.py b/dbm-ui/backend/flow/views/spider_migrage_remotedb.py new file mode 100644 index 0000000000..632fcb97af --- /dev/null +++ b/dbm-ui/backend/flow/views/spider_migrage_remotedb.py @@ -0,0 +1,25 @@ +import logging +import uuid + +from rest_framework.response import Response + +from backend.flow.engine.controller.spider import SpiderController +from backend.flow.views.base import FlowTestView + +logger = logging.getLogger("root") + + +class SpiderMigrateRemoteDbSceneApiView(FlowTestView): + """ + api: /apis/v1/flow/scene/install_spider_cluster + params: + } + """ + + @staticmethod + def post(request): + # logger.info("spider remotedb 成对迁移") + root_id = uuid.uuid1().hex + test = SpiderController(root_id=root_id, ticket_data=request.data) + test.migrate_remotedb() + return Response({"root_id": root_id}) diff --git a/dbm-ui/backend/flow/views/spider_reduce_mnt.py b/dbm-ui/backend/flow/views/spider_reduce_mnt.py new file mode 100644 index 0000000000..3a10689484 --- /dev/null +++ b/dbm-ui/backend/flow/views/spider_reduce_mnt.py @@ -0,0 +1,32 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +import logging +import uuid + +from rest_framework.response import Response + +from backend.flow.engine.controller.spider import SpiderController +from backend.flow.views.base import FlowTestView + +logger = logging.getLogger("root") + + +class ReduceSpiderMNTSceneApiView(FlowTestView): + """ + api: /apis/v1/flow/scene/reduce_spider_mnt + params: + """ + + def post(self, request): + root_id = uuid.uuid1().hex + test = SpiderController(root_id=root_id, ticket_data=request.data) + test.reduce_spider_mnt_scene() + return Response({"root_id": root_id}) diff --git a/dbm-ui/backend/flow/views/spider_reduce_nodes.py b/dbm-ui/backend/flow/views/spider_reduce_nodes.py new file mode 100644 index 0000000000..ed513a276c --- /dev/null +++ b/dbm-ui/backend/flow/views/spider_reduce_nodes.py @@ -0,0 +1,32 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +import logging +import uuid + +from rest_framework.response import Response + +from backend.flow.engine.controller.spider import SpiderController +from backend.flow.views.base import FlowTestView + +logger = logging.getLogger("root") + + +class ReduceSpiderNodesSceneApiView(FlowTestView): + """ + api: /apis/v1/flow/scene/reduce_spider_nodes + params: + """ + + def post(self, request): + root_id = uuid.uuid1().hex + test = SpiderController(root_id=root_id, ticket_data=request.data) + test.reduce_spider_nodes_scene() + return Response({"root_id": root_id}) diff --git a/dbm-ui/backend/flow/views/spider_slave_destroy.py b/dbm-ui/backend/flow/views/spider_slave_destroy.py new file mode 100644 index 0000000000..dcb5208da3 --- /dev/null +++ b/dbm-ui/backend/flow/views/spider_slave_destroy.py @@ -0,0 +1,32 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging +import uuid + +from rest_framework.response import Response + +from backend.flow.engine.controller.spider import SpiderController +from backend.flow.views.base import FlowTestView + +logger = logging.getLogger("root") + + +class DestroySpiderSlaveClusterSceneApiView(FlowTestView): + """ + api: /apis/v1/flow/scene/destroy_tendb_slave_cluster + params: + } + """ + + def post(self, request): + root_id = uuid.uuid1().hex + test = SpiderController(root_id=root_id, ticket_data=request.data) + test.destroy_tendb_slave_cluster() + return Response({"root_id": root_id}) diff --git a/dbm-ui/backend/flow/views/tbinlogdumper_add.py b/dbm-ui/backend/flow/views/tbinlogdumper_add.py new file mode 100644 index 0000000000..85cb313f3c --- /dev/null +++ b/dbm-ui/backend/flow/views/tbinlogdumper_add.py @@ -0,0 +1,24 @@ +import logging +import uuid + +from rest_framework.response import Response + +from backend.flow.engine.controller.tbinlogdumper import TBinlogDumperController +from backend.flow.views.base import FlowTestView + +logger = logging.getLogger("root") + + +class InstallTBinlogDumperSceneApiView(FlowTestView): + """ + api: /apis/v1/flow/scene/install_tbinlogumper + params: + } + """ + + def post(self, request): + # logger.info("开始部署tenDB cluster HA场景") + root_id = uuid.uuid1().hex + test = TBinlogDumperController(root_id=root_id, ticket_data=request.data) + test.add_nodes_scene() + return Response({"root_id": root_id}) diff --git a/dbm-ui/backend/flow/views/tbinlogdumper_reduce.py b/dbm-ui/backend/flow/views/tbinlogdumper_reduce.py new file mode 100644 index 0000000000..b82d52e526 --- /dev/null +++ b/dbm-ui/backend/flow/views/tbinlogdumper_reduce.py @@ -0,0 +1,24 @@ +import logging +import uuid + +from rest_framework.response import Response + +from backend.flow.engine.controller.tbinlogdumper import TBinlogDumperController +from backend.flow.views.base import FlowTestView + +logger = logging.getLogger("root") + + +class ReduceTBinlogDumperSceneApiView(FlowTestView): + """ + api: /apis/v1/flow/scene/reduce_tbinlogumper + params: + } + """ + + def post(self, request): + # 卸载TBinlogDumper实例 + root_id = uuid.uuid1().hex + test = TBinlogDumperController(root_id=root_id, ticket_data=request.data) + test.reduce_nodes_scene() + return Response({"root_id": root_id}) diff --git a/dbm-ui/backend/flow/views/tbinlogdumper_switch.py b/dbm-ui/backend/flow/views/tbinlogdumper_switch.py new file mode 100644 index 0000000000..d869a0277d --- /dev/null +++ b/dbm-ui/backend/flow/views/tbinlogdumper_switch.py @@ -0,0 +1,24 @@ +import logging +import uuid + +from rest_framework.response import Response + +from backend.flow.engine.controller.tbinlogdumper import TBinlogDumperController +from backend.flow.views.base import FlowTestView + +logger = logging.getLogger("root") + + +class SwitchTBinlogDumperSceneApiView(FlowTestView): + """ + api: /apis/v1/flow/scene/swtich_tbinlogumper + params: + } + """ + + def post(self, request): + # 迁移部署TBinlogDumper实例 + root_id = uuid.uuid1().hex + test = TBinlogDumperController(root_id=root_id, ticket_data=request.data) + test.switch_nodes_scene() + return Response({"root_id": root_id}) diff --git a/dbm-ui/backend/flow/views/tendb_cluster_remote_fail_over.py b/dbm-ui/backend/flow/views/tendb_cluster_remote_fail_over.py new file mode 100644 index 0000000000..c96e57135e --- /dev/null +++ b/dbm-ui/backend/flow/views/tendb_cluster_remote_fail_over.py @@ -0,0 +1,32 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +import logging +import uuid + +from rest_framework.response import Response + +from backend.flow.engine.controller.spider import SpiderController +from backend.flow.views.base import FlowTestView + +logger = logging.getLogger("root") + + +class RemoteFailOverSceneApiView(FlowTestView): + """ + api: /apis/v1/flow/scene/tendb_cluster_remote_fail_over + params: + """ + + def post(self, request): + root_id = uuid.uuid1().hex + test = SpiderController(root_id=root_id, ticket_data=request.data) + test.tendbcluster_remote_fail_over_scene() + return Response({"root_id": root_id}) diff --git a/dbm-ui/backend/flow/views/tendb_cluster_remote_rebalance.py b/dbm-ui/backend/flow/views/tendb_cluster_remote_rebalance.py new file mode 100644 index 0000000000..791d5b65b8 --- /dev/null +++ b/dbm-ui/backend/flow/views/tendb_cluster_remote_rebalance.py @@ -0,0 +1,32 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +import logging +import uuid + +from rest_framework.response import Response + +from backend.flow.engine.controller.spider import SpiderController +from backend.flow.views.base import FlowTestView + +logger = logging.getLogger("root") + + +class RemoteRebalanceSceneApiView(FlowTestView): + """ + api: /apis/v1/flow/scene/tendb_cluster_rollback_data + params: + """ + + def post(self, request): + root_id = uuid.uuid1().hex + test = SpiderController(root_id=root_id, ticket_data=request.data) + test.tendb_cluster_remote_rebalance() + return Response({"root_id": root_id}) diff --git a/dbm-ui/backend/flow/views/tendb_cluster_remote_switch.py b/dbm-ui/backend/flow/views/tendb_cluster_remote_switch.py new file mode 100644 index 0000000000..fe383ab79a --- /dev/null +++ b/dbm-ui/backend/flow/views/tendb_cluster_remote_switch.py @@ -0,0 +1,32 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +import logging +import uuid + +from rest_framework.response import Response + +from backend.flow.engine.controller.spider import SpiderController +from backend.flow.views.base import FlowTestView + +logger = logging.getLogger("root") + + +class RemoteSwitchSceneApiView(FlowTestView): + """ + api: /apis/v1/flow/scene/tendb_cluster_remote_switch + params: + """ + + def post(self, request): + root_id = uuid.uuid1().hex + test = SpiderController(root_id=root_id, ticket_data=request.data) + test.tendb_cluster_remote_switch_scene() + return Response({"root_id": root_id}) diff --git a/dbm-ui/backend/flow/views/tendb_cluster_rollback_data.py b/dbm-ui/backend/flow/views/tendb_cluster_rollback_data.py new file mode 100644 index 0000000000..112645ca64 --- /dev/null +++ b/dbm-ui/backend/flow/views/tendb_cluster_rollback_data.py @@ -0,0 +1,32 @@ +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +import logging +import uuid + +from rest_framework.response import Response + +from backend.flow.engine.controller.spider import SpiderController +from backend.flow.views.base import FlowTestView + +logger = logging.getLogger("root") + + +class TendbClusterRollbackDataSceneApiView(FlowTestView): + """ + api: /apis/v1/flow/scene/tendb_cluster_rollback_data + params: + """ + + def post(self, request): + root_id = uuid.uuid1().hex + test = SpiderController(root_id=root_id, ticket_data=request.data) + test.tendb_cluster_rollback_data() + return Response({"root_id": root_id}) diff --git a/dbm-ui/backend/flow/views/tendb_ha_standardize.py b/dbm-ui/backend/flow/views/tendb_ha_standardize.py new file mode 100644 index 0000000000..1f1f4612da --- /dev/null +++ b/dbm-ui/backend/flow/views/tendb_ha_standardize.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging +import uuid + +from django.utils.translation import ugettext as _ +from rest_framework.response import Response + +from backend.flow.engine.controller.mysql import MySQLController +from backend.flow.views.base import FlowTestView + +logger = logging.getLogger("root") + + +class TenDBHAStandardizeView(FlowTestView): + """ + api: /apis/v1/flow/scene/tendbha_standardize + """ + + @staticmethod + def post(request): + logger.info(_("开始TenDBHA标准化")) + + root_id = uuid.uuid1().hex + logger.info("define root_id: {}".format(root_id)) + + c = MySQLController(root_id=root_id, ticket_data=request.data) + c.mysql_ha_standardize_scene() + + return Response({"root_id": root_id}) diff --git a/dbm-ui/backend/iam_app/handlers/drf_perm.py b/dbm-ui/backend/iam_app/handlers/drf_perm.py index fcca213f5c..c8372f8709 100644 --- a/dbm-ui/backend/iam_app/handlers/drf_perm.py +++ b/dbm-ui/backend/iam_app/handlers/drf_perm.py @@ -150,25 +150,13 @@ def _fetch_biz_id(self, request, view): return bk_biz_id -class IsAuthenticatedOrAPIGateWayPermission(permissions.BasePermission): +class IsAuthenticatedPermission(permissions.BasePermission): """ - 用户认证或apigw鉴权 + 用户认证 """ - @classmethod - def verify_jwt_valid(cls, request): - request.jwt = JWTClient(request) - if not request.jwt.is_valid: - logger.error(_("JWT鉴权错误,错误信息: {}").format(request.jwt.error_message)) - return False - - return True - def has_authenticated_permission(self, request, view): return permissions.IsAuthenticated().has_permission(request, view) def has_permission(self, request, view): - if not self.has_authenticated_permission(request, view): - return self.verify_jwt_valid(request) - - return True + return permissions.IsAuthenticated().has_permission(request, view) diff --git a/dbm-ui/backend/tests/db_meta/api/cluster/tendbha/test_handler.py b/dbm-ui/backend/tests/db_meta/api/cluster/tendbha/test_handler.py index a8bccd7cb9..32f7107c35 100644 --- a/dbm-ui/backend/tests/db_meta/api/cluster/tendbha/test_handler.py +++ b/dbm-ui/backend/tests/db_meta/api/cluster/tendbha/test_handler.py @@ -27,12 +27,10 @@ class TestHandler: - @patch("backend.db_meta.api.db_module.apis.CCApi", CCApiMock()) @patch("backend.db_meta.api.machine.apis.CCApi", CCApiMock()) - @patch("backend.db_meta.api.cluster.tendbha.create_cluster.CCApi", CCApiMock()) + @patch("backend.db_meta.models.app.CCApi", CCApiMock()) @patch("backend.db_meta.api.common.common.CCApi", CCApiMock()) - @patch("backend.db_meta.api.cluster.tendbha.handler.create_bk_module_for_cluster_id", lambda **kwargs: None) - @patch("backend.db_meta.api.cluster.tendbha.handler.transfer_host_in_cluster_module", lambda **kwargs: None) + @patch("backend.flow.utils.cc_manage.CCApi", CCApiMock()) def test_create_success(self, init_db_module, create_city): cluster_name = "test" clusters = [ diff --git a/dbm-ui/backend/tests/db_meta/api/db_module/test_apis.py b/dbm-ui/backend/tests/db_meta/api/db_module/test_apis.py index 769b6eadb5..18f525636d 100644 --- a/dbm-ui/backend/tests/db_meta/api/db_module/test_apis.py +++ b/dbm-ui/backend/tests/db_meta/api/db_module/test_apis.py @@ -22,7 +22,6 @@ class TestCreateDBModule: - @patch("backend.db_meta.api.db_module.apis.CCApi", CCApiMock()) def test_create_success(self): """创建成功""" db_module_name = "hello" @@ -31,7 +30,6 @@ def test_create_success(self): ) assert DBModule.objects.filter(bk_biz_id=constant.BK_BIZ_ID, db_module_name=db_module_name).exists() - @patch("backend.db_meta.api.db_module.apis.CCApi", CCApiMock()) def test_create_with_exception(self): """重复创建异常""" self.test_create_success() diff --git a/dbm-ui/backend/tests/db_periodic_task/test_register_tasks.py b/dbm-ui/backend/tests/db_periodic_task/test_register_tasks.py new file mode 100644 index 0000000000..a6faa5a273 --- /dev/null +++ b/dbm-ui/backend/tests/db_periodic_task/test_register_tasks.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging +from unittest.mock import patch + +import pytest + +from backend.db_periodic_task.constants import PeriodicTaskType +from backend.db_periodic_task.models import DBPeriodicTask +from backend.tests.mock_data.components.celery_service import REMOTE_API_LIST, CeleryServiceApiMock + +pytestmark = pytest.mark.django_db +logger = logging.getLogger("test") + + +class TestRegisterRemoteTasks: + @patch("backend.components.celery_service.client.CeleryServiceApi", CeleryServiceApiMock) + def test_register_remote_tasks(self): + from backend.db_periodic_task.remote_tasks.register import register_from_remote, registered_remote_tasks + + register_from_remote() + assert len(registered_remote_tasks) == len(REMOTE_API_LIST) + assert DBPeriodicTask.objects.filter(task_type=PeriodicTaskType.REMOTE.value).count() == len(REMOTE_API_LIST) + + def test_register_local_tasks(self): + from backend.db_periodic_task.local_tasks import register_periodic_task + + @register_periodic_task(run_every=1) + def demo_task(): + return "hello, world!" + + assert DBPeriodicTask.objects.filter(task_type=PeriodicTaskType.LOCAL, name__contains="demo_task").count() diff --git a/dbm-ui/backend/tests/db_services/mysql/instance/test_handler.py b/dbm-ui/backend/tests/db_services/mysql/instance/test_handler.py index 9d8b3de068..553159ffb1 100644 --- a/dbm-ui/backend/tests/db_services/mysql/instance/test_handler.py +++ b/dbm-ui/backend/tests/db_services/mysql/instance/test_handler.py @@ -12,7 +12,7 @@ from mock.mock import patch from backend.constants import IP_PORT_DIVIDER -from backend.db_services.mysql.instance.handlers import InstanceHandler +from backend.db_services.dbbase.instances.handlers import InstanceHandler from backend.tests.mock_data.components.cc import CCApiMock from backend.tests.mock_data.components.gse import GseApiMock diff --git a/dbm-ui/backend/tests/db_services/mysql/permission/test_account_handler.py b/dbm-ui/backend/tests/db_services/mysql/permission/test_account_handler.py index 82da235ee5..7635c41875 100644 --- a/dbm-ui/backend/tests/db_services/mysql/permission/test_account_handler.py +++ b/dbm-ui/backend/tests/db_services/mysql/permission/test_account_handler.py @@ -17,6 +17,7 @@ from backend.core.encrypt.constants import RSAConfigType from backend.core.encrypt.handlers import RSAHandler from backend.core.encrypt.models import RSAKey +from backend.db_services.mysql.permission.constants import AccountType from backend.db_services.mysql.permission.db_account.dataclass import AccountMeta, AccountRuleMeta from backend.db_services.mysql.permission.db_account.handlers import AccountHandler from backend.tests.mock_data.components.mysql_priv_manager import MySQLPrivManagerApiMock @@ -49,31 +50,31 @@ class TestAccountHandler: @pytest.mark.parametrize("password", VALID_PASSWORD_LIST + INVALID_PASSWORD_LIST) def test_check_password_strength__valid(self, password): - is_pwd_valid, _ = AccountHandler._check_pwd_strength(password, rule_data=copy.deepcopy(POLICY_DATA)) + is_pwd_valid, _ = AccountHandler._check_password_strength(password, rule_data=copy.deepcopy(POLICY_DATA)) assert is_pwd_valid == (password in VALID_PASSWORD_LIST) @patch("backend.db_services.mysql.permission.db_account.handlers.MySQLPrivManagerApi", MySQLPrivManagerApiMock) def test_create_account(self, query_fixture): account = AccountMeta(**ACCOUNT) - data = AccountHandler(bk_biz_id=1).create_account(account) + data = AccountHandler(bk_biz_id=1, account_type=AccountType.MYSQL).create_account(account) assert data["password"] == ACCOUNT["password"] @patch("backend.db_services.mysql.permission.db_account.handlers.MySQLPrivManagerApi", MySQLPrivManagerApiMock) def test_update_account(self, query_fixture): account = AccountMeta(**ACCOUNT) - data = AccountHandler(bk_biz_id=1).update_password(account) + data = AccountHandler(bk_biz_id=1, account_type=AccountType.MYSQL).update_password(account) assert data["password"] == ACCOUNT["password"] @patch("backend.db_services.mysql.permission.db_account.handlers.MySQLPrivManagerApi", MySQLPrivManagerApiMock) def test_delete_account(self, query_fixture): account = AccountMeta(**ACCOUNT) - data = AccountHandler(bk_biz_id=1).delete_account(account) + data = AccountHandler(bk_biz_id=1, account_type=AccountType.MYSQL).delete_account(account) assert not data @patch("backend.db_services.mysql.permission.db_account.handlers.MySQLPrivManagerApi", MySQLPrivManagerApiMock) def test_list_account_rules(self, query_fixture): account_rule = AccountRuleMeta(**ACCOUNT_RULE) - data = AccountHandler(bk_biz_id=1).list_account_rules(account_rule) + data = AccountHandler(bk_biz_id=1, account_type=AccountType.MYSQL).list_account_rules(account_rule) assert data["count"] == 1 @pytest.mark.parametrize("password", VALID_PASSWORD_LIST + INVALID_PASSWORD_LIST) @@ -81,5 +82,7 @@ def test_verify_password_strength__valid(self, password): rsa = RSAHandler.get_or_generate_rsa_in_db(name=RSAConfigType.MYSQL.value) account = AccountMeta(password=RSAHandler.encrypt_password(rsa.rsa_public_key.content, password, None)) - is_strength = AccountHandler(bk_biz_id=1).verify_password_strength(account)["is_strength"] + is_strength = AccountHandler(bk_biz_id=1, account_type=AccountType.MYSQL).verify_password_strength(account)[ + "is_strength" + ] assert is_strength == (password in VALID_PASSWORD_LIST) diff --git a/dbm-ui/backend/tests/db_services/mysql/permission/test_clone_handler.py b/dbm-ui/backend/tests/db_services/mysql/permission/test_clone_handler.py index 83b6699f14..5173946e2a 100644 --- a/dbm-ui/backend/tests/db_services/mysql/permission/test_clone_handler.py +++ b/dbm-ui/backend/tests/db_services/mysql/permission/test_clone_handler.py @@ -36,7 +36,7 @@ class TestCloneHandler: CloneHandler的测试类 """ - handler = CloneHandler(constant.BK_BIZ_ID, "admin", "client") + handler = CloneHandler(constant.BK_BIZ_ID, "admin", "client", "mysql") @patch("backend.db_services.mysql.permission.clone.handlers.MySQLPrivManagerApi", MySQLPrivManagerApiMock) def test_pre_check_clone(self): diff --git a/dbm-ui/backend/tests/db_services/mysql/test_sql_inport_handler.py b/dbm-ui/backend/tests/db_services/mysql/test_sql_inport_handler.py index edff15bb61..a646ded3ae 100644 --- a/dbm-ui/backend/tests/db_services/mysql/test_sql_inport_handler.py +++ b/dbm-ui/backend/tests/db_services/mysql/test_sql_inport_handler.py @@ -33,7 +33,6 @@ class TestSQLImportHandler: @patch("backend.db_services.mysql.sql_import.handlers.SQLImportApi", SQLImportApiMock) def test_grammar_check(self): sql_content = "select * from user" - sql = SQLMeta(sql_content=sql_content) - check_info = self.handler.grammar_check(sql) + check_info = self.handler.grammar_check(sql_content) assert len(check_info) == 1 diff --git a/dbm-ui/backend/tests/flow/components/collections/mysql/test_exec_actuator_script.py b/dbm-ui/backend/tests/flow/components/collections/mysql/test_exec_actuator_script.py index b6dbd01d65..38497da9e2 100644 --- a/dbm-ui/backend/tests/flow/components/collections/mysql/test_exec_actuator_script.py +++ b/dbm-ui/backend/tests/flow/components/collections/mysql/test_exec_actuator_script.py @@ -10,7 +10,7 @@ """ import logging -from typing import Type +from typing import List, Type import mock import pytest @@ -23,6 +23,8 @@ from backend.flow.plugins.components.collections.mysql.exec_actuator_script import ExecuteDBActuatorScriptComponent from backend.flow.utils.mysql.mysql_act_playload import MysqlActPayload from backend.flow.utils.mysql.mysql_context_dataclass import SingleApplyAutoContext +from backend.tests.flow.components.collections.base import BaseComponentPatcher as Patcher +from backend.tests.flow.components.collections.mysql.test_mysql_db_meta import TestMySQLDBMetaComponent from backend.tests.flow.components.collections.mysql.utils import MySQLSingleApplyComponentTest from backend.tests.mock_data.components import cc from backend.tests.mock_data.components.dbconfig import DBConfigApiMock @@ -34,8 +36,23 @@ class TestExecActuatorScriptComponent(MySQLSingleApplyComponentTest, TestCase): def component_cls(self) -> Type[Component]: - with mock.patch(target="backend.flow.utils.mysql.mysql_act_playload.DBConfigApi", new=DBConfigApiMock): - return ExecuteDBActuatorScriptComponent + return ExecuteDBActuatorScriptComponent + + def get_patchers(self) -> List[Patcher]: + patchers = super(MySQLSingleApplyComponentTest, self).get_patchers() + patchers.extend( + [ + Patcher( + target="backend.flow.utils.mysql.mysql_act_playload.DBConfigApi", + new=DBConfigApiMock, + ), + Patcher( + target="backend.flow.utils.base.payload_handler.DBConfigApi", + new=DBConfigApiMock, + ), + ] + ) + return patchers def to_mock_path_list(self): path_list = super(TestExecActuatorScriptComponent, self).to_mock_path_list() @@ -44,8 +61,7 @@ def to_mock_path_list(self): @classmethod def _set_trans_data(cls) -> None: - with mock.patch(target="backend.flow.utils.mysql.mysql_act_playload.DBConfigApi", new=DBConfigApiMock): - cls.trans_data = SingleApplyAutoContext(new_ip=cc.NORMAL_IP) + cls.trans_data = SingleApplyAutoContext(new_ip=cc.NORMAL_IP) @classmethod def _set_kwargs(cls) -> None: diff --git a/dbm-ui/backend/tests/flow/components/collections/mysql/test_mysql_db_meta.py b/dbm-ui/backend/tests/flow/components/collections/mysql/test_mysql_db_meta.py index 5bf79f547a..b56e6c84aa 100644 --- a/dbm-ui/backend/tests/flow/components/collections/mysql/test_mysql_db_meta.py +++ b/dbm-ui/backend/tests/flow/components/collections/mysql/test_mysql_db_meta.py @@ -19,7 +19,6 @@ from backend.constants import DEFAULT_TIME_ZONE from backend.db_meta.api.cluster.tendbsingle.create_cluster import create as tendbsingle_create -from backend.db_meta.api.common.common import add_service_instance from backend.db_meta.api.machine.apis import create as api_create from backend.flow.plugins.components.collections.mysql.mysql_db_meta import MySQLDBMetaComponent from backend.flow.utils.mysql.mysql_act_dataclass import DBMetaOPKwargs @@ -46,7 +45,6 @@ def to_mock_path_list(self) -> List[str]: MySQLDBMeta.__module__, api_create.__module__, tendbsingle_create.__module__, - add_service_instance.__module__, ] ) return mock_path_list diff --git a/dbm-ui/backend/tests/mock_data/components/cc.py b/dbm-ui/backend/tests/mock_data/components/cc.py index 8eecef9a01..3e4afbd4db 100644 --- a/dbm-ui/backend/tests/mock_data/components/cc.py +++ b/dbm-ui/backend/tests/mock_data/components/cc.py @@ -13,6 +13,8 @@ from backend.tests.mock_data import constant MOCK_SEARCH_BUSINESS_RETURN = {"info": [{"bk_biz_id": constant.BK_BIZ_ID, "bk_biz_name": "蓝鲸"}], "count": 1} +MOCK_SEARCH_SET_RETURN = {"info": [{"bk_set_id": constant.BK_SET_ID, "bk_set_name": "mock集群"}], "count": 1} +MOCK_SEARCH_MODULE_RETURN = {"info": [{"bk_module_id": constant.DB_MODULE_ID, "bk_biz_name": "mock模块"}], "count": 1} MOCK_SEARCH_BUSINESS_WITH_MULTI_BIZ_RETURN = { "info": [ @@ -21,6 +23,18 @@ ], "count": 2, } +MOCK_FIND_HOST_BIZ_RELATIONS_RETURN = [ + {"bk_host_id": 1, "bk_biz_id": constant.BK_BIZ_ID, "bk_module_id": constant.BK_MODULE_ID} +] + +MOCK_GET_BIZ_INTERNAL_MODULE_RETURN = { + "bk_set_id": constant.BK_SET_ID, + "bk_set_name": "空闲机池", + "module": [ + {"default": 1, "bk_module_id": constant.BK_MODULE_ID}, + {"default": 2, "bk_module_id": constant.BK_MODULE_ID2}, + ], +} NORMAL_IP = "127.0.0.1" NORMAL_IP2 = "127.0.0.2" @@ -138,19 +152,38 @@ class CCApiMock(object): search_business_return = copy.deepcopy(MOCK_SEARCH_BUSINESS_RETURN) list_hosts_without_biz_return = copy.deepcopy(MOCK_LIST_HOSTS_WITHOU_BIZ_RETURN) + search_set_return = copy.deepcopy(MOCK_SEARCH_SET_RETURN) + search_module_return = copy.deepcopy(MOCK_SEARCH_MODULE_RETURN) + find_host_biz_relations_return = copy.deepcopy(MOCK_FIND_HOST_BIZ_RELATIONS_RETURN) + get_biz_internal_module_return = copy.deepcopy(MOCK_GET_BIZ_INTERNAL_MODULE_RETURN) def __init__( self, search_business_return=None, list_hosts_without_biz_return=None, + search_set_return=None, + search_module_return=None, + find_host_biz_relations_return=None, ): # 提供接口默认返回值,可根据不同的需求进行构造 self.search_business_return = search_business_return or self.search_business_return self.list_hosts_without_biz_return = list_hosts_without_biz_return or self.list_hosts_without_biz_return + self.search_set_return = search_set_return or self.search_set_return + self.search_module_return = search_module_return or self.search_module_return + self.find_host_biz_relations_return = find_host_biz_relations_return or self.find_host_biz_relations_return def search_business(self, *args, **kwargs): return self.search_business_return + def search_set(self, *args, **kwargs): + return self.search_set_return + + def search_module(self, *args, **kwargs): + return self.search_module_return + + def find_host_biz_relations(self, *args, **kwargs): + return self.find_host_biz_relations_return + def search_cloud_area(self, *args, **kwargs): return {"result": True, "count": len(MOCK_CLOUD_AREA), "info": MOCK_CLOUD_AREA} @@ -185,3 +218,15 @@ def add_label_for_service_instance(*args, **kwargs): @staticmethod def create_service_instance(*args, **kwargs): return [1000000000] + + @staticmethod + def get_biz_internal_module(*args, **kwargs): + return MOCK_GET_BIZ_INTERNAL_MODULE_RETURN + + @staticmethod + def transfer_host_across_biz(*args, **kwargs): + return + + @staticmethod + def transfer_host_to_idlemodule(*args, **kwargs): + return diff --git a/dbm-ui/backend/tests/mock_data/components/celery_service.py b/dbm-ui/backend/tests/mock_data/components/celery_service.py new file mode 100644 index 0000000000..7ce5398dee --- /dev/null +++ b/dbm-ui/backend/tests/mock_data/components/celery_service.py @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +REMOTE_API_LIST = [ + { + "cluster_type": "TendbCluster", + "empty_param": [], + "enable": True, + "name": "shell-echo", + "url": "tendb-cluster/shell-echo", + }, + { + "cluster_type": "TendbCluster", + "empty_param": [], + "enable": True, + "name": "shell-counter", + "url": "tendb-cluster/shell-counter", + }, + { + "cluster_type": "TendbHA", + "empty_param": {"counter": 0}, + "enable": True, + "name": "Counter", + "url": "tendb-ha/counter", + }, +] + +ASYNC_QUERY_DATA = [ + { + "id": "test_id", + "message": "", + "error": "unexpected end of JSON input", + "done": True, + "start_at": "2023-08-14T09:16:37.961224+08:00", + } +] + +SESSION_ID = "test_session_id" + + +class CeleryServiceApiMock(object): + """ + gse 的 mock 接口 + """ + + base_info = {"result": True, "code": 0, "message": "success"} + + @classmethod + def list(cls, *args, **kwargs): + return REMOTE_API_LIST + + @classmethod + def async_list(cls, *args, **kwargs): + return REMOTE_API_LIST + + @classmethod + def async_query(cls, *args, **kwargs): + return ASYNC_QUERY_DATA + + @classmethod + def async_kill(cls, *args, **kwargs): + return "" + + @classmethod + def _mock_celery_api1(cls, *args, **kwargs): + return f"{SESSION_ID}_1" + + @classmethod + def _mock_celery_api2(cls, *args, **kwargs): + return f"{SESSION_ID}_2" + + @classmethod + def _mock_celery_api3(cls, *args, **kwargs): + return f"{SESSION_ID}_3" diff --git a/dbm-ui/backend/tests/mock_data/flow/components/collections/mysql.py b/dbm-ui/backend/tests/mock_data/flow/components/collections/mysql.py index fe80e19844..c90b8c33e4 100644 --- a/dbm-ui/backend/tests/mock_data/flow/components/collections/mysql.py +++ b/dbm-ui/backend/tests/mock_data/flow/components/collections/mysql.py @@ -29,6 +29,7 @@ "mysql_ports": [20000], "clusters": [{"name": "kkksc", "master": "blueking-moduledb.kkksc.blueking.db", "mysql_port": 20000}], "bk_cloud_id": 0, + "resource_spec": {"backend": {"id": 1}, "proxy": {"id": 1}}, } RESOURCE_POLL_NODES = [ diff --git a/dbm-ui/backend/tests/mock_data/ticket/ticket_params_data.py b/dbm-ui/backend/tests/mock_data/ticket/ticket_params_data.py index 21f1b91738..cabf1521ba 100644 --- a/dbm-ui/backend/tests/mock_data/ticket/ticket_params_data.py +++ b/dbm-ui/backend/tests/mock_data/ticket/ticket_params_data.py @@ -8,7 +8,7 @@ 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 backend.db_services.mysql.permission.constants import CloneClusterType from backend.ticket.constants import TicketType BASE_FLOW_PARAMS = { @@ -36,6 +36,7 @@ MYSQL_CLONE_FLOW_PARAMS = { **BASE_FLOW_PARAMS, + "clone_cluster_type": CloneClusterType.MYSQL, "ticket_type": TicketType.MYSQL_CLIENT_CLONE_RULES.value, "clone_data": [ {"source": "127.0.0.1:", "target": "127.0.1.1", "bk_cloud_id": 0}, diff --git a/dbm-ui/backend/ticket/builders/__init__.py b/dbm-ui/backend/ticket/builders/__init__.py index e76a52e597..6e2fced8da 100644 --- a/dbm-ui/backend/ticket/builders/__init__.py +++ b/dbm-ui/backend/ticket/builders/__init__.py @@ -13,19 +13,17 @@ import json import logging import os -from typing import Callable, Dict, List +from typing import Callable, Dict -from django.conf import settings from django.utils.translation import ugettext as _ from rest_framework import serializers from backend import env +from backend.configuration.constants import SystemSettingsEnum from backend.configuration.models import DBAdministrator, SystemSettings -from backend.configuration.models.system import SystemSettingsEnum -from backend.db_meta.models import AppCache from backend.db_services.dbbase.constants import IpSource from backend.dbm_init.services import Services -from backend.ticket.constants import FlowType +from backend.ticket.constants import FlowRetryType, FlowType from backend.ticket.models import Flow, Ticket logger = logging.getLogger("root") @@ -153,12 +151,12 @@ def get_params(self): { "name": _("单据链接"), "type": "LINK", - "value": f"{env.BK_SAAS_HOST}/self-service/my-tickets?id={self.ticket.id}", + "value": f"{env.BK_SAAS_HOST}/self-service/my-tickets/all?id={self.ticket.id}", }, { "name": _("需求信息"), "type": "LINK", - "value": f"{env.BK_SAAS_HOST}/self-service/my-tickets?id={self.ticket.id}&isFullscreen=true", + "value": f"{env.BK_SAAS_HOST}/self-service/my-tickets/all?id={self.ticket.id}&isFullscreen=true", }, ], "meta": { @@ -248,6 +246,8 @@ class TicketFlowBuilder: # resource_apply_builder和resource_batch_apply_builder只能存在其一,表示是资源池单次申请还是批量申请 resource_apply_builder: ResourceApplyParamBuilder = None resource_batch_apply_builder: ResourceApplyParamBuilder = None + # inner flow互斥的重试类型,默认为自动重试 + retry_type: FlowRetryType = FlowRetryType.AUTO_RETRY def __init__(self, ticket: Ticket): self.ticket = ticket @@ -345,12 +345,13 @@ def init_ticket_flows(self): flow_type=FlowType.INNER_FLOW.value, details=self.inner_flow_builder(self.ticket).get_params(), flow_alias=self.inner_flow_name, + retry_type=self.retry_type, ) ) # 如果使用资源池,则在最后需要进行资源交付 if self.need_resource_pool: - flow_type = FlowType.RESOURCE_DELIVERY if self.resource_apply_builder else FlowType.RESOURCE_BATCH_APPLY + flow_type = FlowType.RESOURCE_DELIVERY if self.resource_apply_builder else FlowType.RESOURCE_BATCH_DELIVERY flows.append(Flow(ticket=self.ticket, flow_type=flow_type)) Flow.objects.bulk_create(flows) @@ -362,14 +363,38 @@ def patch_ticket_detail(self): class BuilderFactory: + # 单据的注册器类集合 registry = {} + # 部署类单据集合 + apply_ticket_type = [] + # 单据与集群状态的映射 + ticket_type__cluster_phase = {} + # 单据和集群类型的映射 + ticket_type__cluster_type = {} @classmethod - def register(cls, ticket_type: str) -> Callable: + def register(cls, ticket_type: str, **kwargs) -> Callable: + """ + 将单据构造类注册到注册器中 + @param ticket_type: 单据类型 + @param kwargs: 单据注册的额外信息,主要是将单据归为不同的集合中,目前有这几种类型 + 1. is_apply: bool ---- 表示单据是否是部署类单据(类似集群的部署,扩容,替换等) + 2. phase: ClusterPhase ---- 表示单据与集群状态的映射 + 3. cluster_type: ClusterType ---- 表示单据与集群类型的映射 + """ + def inner_wrapper(wrapped_class: TicketFlowBuilder) -> TicketFlowBuilder: if ticket_type in cls.registry: logger.warning(f"Builder [{ticket_type}] already exists. Will replace it") cls.registry[ticket_type] = wrapped_class + + if kwargs.get("is_apply") and kwargs.get("is_apply") not in cls.apply_ticket_type: + cls.apply_ticket_type.append(ticket_type) + if kwargs.get("phase"): + cls.ticket_type__cluster_phase[ticket_type] = kwargs["phase"] + if kwargs.get("cluster_type"): + cls.ticket_type__cluster_type[ticket_type] = kwargs["cluster_type"] + return wrapped_class return inner_wrapper @@ -410,7 +435,6 @@ def register_all_builders(path=os.path.dirname(__file__), module_path="backend.t try: module_name = name.replace(".py", "") import_path = ".".join([module_path, module_name]) - print(f"register_all_builders: {import_path}") importlib.import_module(import_path) except ModuleNotFoundError as e: logger.warning(e) diff --git a/dbm-ui/backend/ticket/builders/common/base.py b/dbm-ui/backend/ticket/builders/common/base.py index 56442541fc..060e67d045 100644 --- a/dbm-ui/backend/ticket/builders/common/base.py +++ b/dbm-ui/backend/ticket/builders/common/base.py @@ -11,25 +11,62 @@ import operator import re from functools import reduce -from typing import Dict, List, Set, Tuple, Union +from typing import Any, Dict, List, Set, Tuple, Union from django.db.models import F, Q from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from backend.configuration.constants import MASTER_DOMAIN_INITIAL_VALUE from backend.db_meta.enums import AccessLayer, ClusterType, InstanceInnerRole from backend.db_meta.models import Cluster, ProxyInstance, StorageInstance from backend.db_services.ipchooser.query.resource import ResourceQueryHelper from backend.db_services.mysql.cluster.handlers import ClusterServiceHandler from backend.db_services.mysql.remote_service.handlers import RemoteServiceHandler -from backend.ticket.constants import TICKET_TYPE__CLUSTER_TYPE_MAP, TicketType +from backend.flow.utils.mysql.db_table_filter import DbTableFilter +from backend.ticket import builders +from backend.ticket.builders import BuilderFactory +from backend.ticket.builders.common.constants import MAX_DOMAIN_LEN_LIMIT +from backend.ticket.constants import TicketType + + +def fetch_cluster_ids(details: Dict[str, Any]) -> List[int]: + def _find_cluster_id(_cluster_ids: List[int], _info: Dict): + if "cluster_id" in _info: + _cluster_ids.append(_info["cluster_id"]) + elif "cluster_ids" in _info: + _cluster_ids.extend(_info["cluster_ids"]) + + cluster_ids = [] + _find_cluster_id(cluster_ids, details) + if isinstance(details.get("infos"), dict): + _find_cluster_id(cluster_ids, details.get("infos")) + elif isinstance(details.get("infos"), list): + for info in details.get("infos"): + _find_cluster_id(cluster_ids, info) + + return cluster_ids + + +def remove_useless_spec(attrs: Dict[str, Any]) -> Dict[str, Any]: + # 只保存有意义的规格资源申请 + real_resource_spec = {} + if "resource_spec" not in attrs: + return + + for role, spec in attrs["resource_spec"].items(): + if spec and spec["count"]: + real_resource_spec[role] = spec + + attrs["resource_spec"] = real_resource_spec + return attrs class HostInfoSerializer(serializers.Serializer): bk_cloud_id = serializers.IntegerField(help_text=_("云区域ID")) ip = serializers.CharField(help_text=_("IP地址")) bk_host_id = serializers.IntegerField(help_text=_("主机ID")) - bk_biz_id = serializers.IntegerField(help_text=_("业务ID")) + bk_biz_id = serializers.IntegerField(help_text=_("业务ID"), required=False) class InstanceInfoSerializer(HostInfoSerializer): @@ -53,7 +90,7 @@ def to_representation(self, instance): class CommonValidate(object): """存放单据的公共校验逻辑""" - db_name_pattern = re.compile(r"^[a-z][a-zA-Z0-9_-]{0,39}$") + domain_pattern = re.compile(r"^[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62}){2,8}\.*(#(\d+))?$") @classmethod def validate_hosts_from_idle_pool(cls, bk_biz_id: int, host_list: List[int]) -> Set[int]: @@ -122,37 +159,41 @@ def validate_instance_related_clusters( bk_biz_id = inst["bk_biz_id"] intersected_host_ids = [ info["bk_host_id"] - for info in ClusterServiceHandler(bk_biz_id).get_intersected_machines_from_clusters(cluster_ids, role) + for info in ClusterServiceHandler(bk_biz_id).get_intersected_machines_from_clusters( + cluster_ids, role, False + ) ] return inst["bk_host_id"] in intersected_host_ids - @classmethod - def validate_db_name(cls, db_name: str) -> Tuple[bool, str]: - """校验新db name是否合法""" - # 1. [a-zA-z][a-zA-Z0-9_-]{0,39} - # 2. 不以 dba_rollback 结尾 - # 3. 不以 stage_truncate 开头 - - if not re.match(cls.db_name_pattern, db_name): - message = _("新DB名字{}格式不合法,请保证数据库名以小写字母开头且只能包含字母、数字、连接符-和下划线_,并且长度在1到39字符之间").format(db_name) - return False, message - - if db_name.startswith("stage_truncate"): - return False, _("DB名{}不能以stage_truncate开头").format(db_name) - - if db_name.endswith("dba_rollback"): - return False, _("DB名{}不能以dba_rollback结尾").format(db_name) - - return True, "" - @classmethod def validate_duplicate_cluster_name(cls, bk_biz_id, ticket_type, cluster_name): - cluster_type = TICKET_TYPE__CLUSTER_TYPE_MAP.get(ticket_type, ticket_type) + cluster_type = BuilderFactory.ticket_type__cluster_type.get(ticket_type, ticket_type) if Cluster.objects.filter(bk_biz_id=bk_biz_id, cluster_type=cluster_type, name=cluster_name).exists(): raise serializers.ValidationError( _("业务{}下已经存在同类型: {}, 同名: {} 集群,请重新命名").format(bk_biz_id, cluster_type, cluster_name) ) + @classmethod + def _validate_domain_valid(cls, domain): + if not cls.domain_pattern.match(domain): + raise serializers.ValidationError(_("[{}]集群无法通过正则性校验{}").format(domain, cls.domain_pattern)) + + if len(domain) > MAX_DOMAIN_LEN_LIMIT: + raise serializers.ValidationError(_("[{}]集群域名长度过长,请不要让域名长度超过{}").format(domain, MAX_DOMAIN_LEN_LIMIT)) + + @classmethod + def validate_generate_domain(cls, cluster_domain_prefix, cluster_name, db_app_abbr): + """校验域名是否合法,仅适用于{cluster_domain_prefix}.{cluster_name}.{db_app_abbr}.db""" + domain = f"{cluster_domain_prefix}.{cluster_name}.{db_app_abbr}.db" + cls._validate_domain_valid(domain) + + @classmethod + def validate_mysql_domain(cls, db_module_name, db_app_abbr, cluster_name): + mysql_domain = MASTER_DOMAIN_INITIAL_VALUE.format( + db_module_name=db_module_name, db_app_abbr=db_app_abbr, cluster_name=cluster_name + ) + cls._validate_domain_valid(mysql_domain) + @classmethod def _validate_single_database_table_selector( cls, @@ -166,36 +207,14 @@ def _validate_single_database_table_selector( ) -> Tuple[bool, str]: """校验库表选择器中的单个数据是否合法""" - all_patterns_list = [db_patterns, ignore_dbs, table_patterns, ignore_tables] - for patterns in all_patterns_list: - for ele in patterns: - if ele == "%" or ("*" in ele and len(ele) > 1): - return False, _("不允许%单独使用,不允许*组合使用") - - if (("%" in ele) or ("?" in ele) or ("*" in ele)) and len(patterns) != 1: - return False, _("包含通配符时,每一个输入框只能允许单一对象") - - if not ele.strip(): - return False, _("字符不允许只包含空格") - - if not db_patterns: - return False, _("DB选择框不允许为空") - - if not is_only_db_operate and not table_patterns: - return False, _("table选择框不允许为空") - - if not is_only_db_operate and (bool(ignore_tables) ^ bool(ignore_dbs)): - return False, _("忽略DB选择框和忽略table选择框要么同时为空,要么同时不为空") - + # 库表选择器校验 + DbTableFilter(db_patterns, table_patterns, ignore_dbs, ignore_tables) + # 数据库存在性校验 db_name_list = [*db_patterns, *ignore_dbs] for db_name in db_name_list: if ("%" in db_name) or ("?" in db_name) or ("*" in db_name): continue - is_valid_db_name, message = cls.validate_db_name(db_name) - if not is_valid_db_name: - return False, message - if db_name not in dbs_in_cluster_map.get(cluster_id, []): return False, _("数据库{}不在所属集群{}中,请重新查验").format(db_name, cluster_id) @@ -203,12 +222,17 @@ def _validate_single_database_table_selector( @classmethod def validate_database_table_selector( - cls, bk_biz_id: int, infos: Dict, is_only_db_operate_list: List[bool] = None + cls, bk_biz_id: int, infos: Dict, role_key: None, is_only_db_operate_list: List[bool] = None ) -> Tuple[bool, str]: """校验库表选择器的数据是否合法""" cluster_ids = [info["cluster_id"] for info in infos] - dbs_in_cluster = RemoteServiceHandler(bk_biz_id).show_databases(cluster_ids) + # 如果想验证特定角色的库表,则传入集群ID与角色映射表 + cluster_id__role_map = {} + if role_key: + cluster_id__role_map = {info["cluster_id"]: info[role_key] for info in infos} + + dbs_in_cluster = RemoteServiceHandler(bk_biz_id).show_databases(cluster_ids, cluster_id__role_map) dbs_in_cluster_map = {db["cluster_id"]: db["databases"] for db in dbs_in_cluster} if not is_only_db_operate_list: is_only_db_operate_list = [False] * len(infos) @@ -268,10 +292,8 @@ def patch_ticket_detail(self): class MySQLTicketFlowBuilderPatchMixin(object): def patch_ticket_detail(self): """补充MySQL的集群信息和实例信息""" - from backend.ticket.builders.mysql.base import MySQLBaseOperateDetailSerializer - details = self.ticket.details - cluster_ids = MySQLBaseOperateDetailSerializer.fetch_cluster_ids(details) + cluster_ids = fetch_cluster_ids(details) self.ticket.update_details( clusters={cluster.id: cluster.to_dict() for cluster in Cluster.objects.filter(id__in=cluster_ids)} @@ -295,3 +317,22 @@ def patch_ticket_detail(self): return self.ticket.update_details(instances=self.get_instances(self.ticket.ticket_type, self.ticket.details)) + + +class BaseOperateResourceParamBuilder(builders.ResourceApplyParamBuilder): + def format(self): + # 对每个info补充云区域ID和业务ID + cluster_ids = fetch_cluster_ids(self.ticket_data) + clusters = Cluster.objects.filter(id__in=cluster_ids) + for info in self.ticket_data["infos"]: + # 如果已经存在则跳过 + if info.get("bk_cloud_id") and info.get("bk_biz_id"): + continue + + # 默认从集群中获取云区域ID和业务ID + cluster_id = info.get("cluster_id") or info.get("cluster_ids")[0] + bk_cloud_id = clusters.get(id=cluster_id).bk_cloud_id + info.update(bk_cloud_id=bk_cloud_id, bk_biz_id=self.ticket.bk_biz_id) + + def post_callback(self): + pass diff --git a/dbm-ui/backend/ticket/builders/common/bigdata.py b/dbm-ui/backend/ticket/builders/common/bigdata.py index f1cebb2216..d23aee3ac7 100644 --- a/dbm-ui/backend/ticket/builders/common/bigdata.py +++ b/dbm-ui/backend/ticket/builders/common/bigdata.py @@ -9,7 +9,6 @@ specific language governing permissions and limitations under the License. """ -import itertools from typing import Dict from django.utils.translation import ugettext_lazy as _ @@ -18,20 +17,19 @@ from backend.configuration.constants import DBType from backend.db_meta.enums.cluster_phase import ClusterPhase -from backend.db_meta.models import Group from backend.db_meta.models.cluster import Cluster from backend.db_meta.models.instance import StorageInstance from backend.db_meta.models.machine import Machine from backend.db_services.dbbase.constants import IpSource from backend.ticket import builders -from backend.ticket.builders import TicketFlowBuilder +from backend.ticket.builders import BuilderFactory, TicketFlowBuilder from backend.ticket.builders.common.base import ( BigDataTicketFlowBuilderPatchMixin, CommonValidate, InfluxdbTicketFlowBuilderPatchMixin, + remove_useless_spec, ) -from backend.ticket.builders.common.constants import BigDataRole -from backend.ticket.constants import TICKET_TYPE__CLUSTER_PHASE_MAP, TICKET_TYPE__CLUSTER_TYPE_MAP +from backend.ticket.builders.common.constants import MAX_DOMAIN_LEN_LIMIT, BigDataRole class BigDataDetailsSerializer(serializers.Serializer): @@ -42,6 +40,7 @@ def to_representation(self, instance): @classmethod def validate_hosts_from_idle_pool(cls, bk_biz_id, nodes: Dict): + """校验主机是否都来自空闲机池""" hosts_set = set() for role in nodes: role_host_list = [node["bk_host_id"] for node in nodes[role] if node.get("bk_host_id")] @@ -53,6 +52,7 @@ def validate_hosts_from_idle_pool(cls, bk_biz_id, nodes: Dict): @classmethod def validate_hosts_not_in_db_meta(cls, nodes: Dict): + """校验主机是否不存在于dbmeta中""" for role in nodes: role_host_list = [node["bk_host_id"] for node in nodes[role] if node.get("bk_host_id")] exist_host_ids = Machine.objects.filter(bk_host_id__in=role_host_list) @@ -63,8 +63,14 @@ def validate_hosts_not_in_db_meta(cls, nodes: Dict): @classmethod def validate_duplicate_cluster_name(cls, bk_biz_id, ticket_type, cluster_name): + """校验是否有重复集群名""" CommonValidate.validate_duplicate_cluster_name(bk_biz_id, ticket_type, cluster_name) + @classmethod + def validate_domain(cls, cluster_domain_prefix, cluster_name, db_app_abbr): + """校验域名是否合法""" + CommonValidate.validate_generate_domain(cluster_domain_prefix, cluster_name, db_app_abbr) + class BigDataSingleClusterOpsDetailsSerializer(BigDataDetailsSerializer): cluster_id = serializers.IntegerField(help_text=_("集群ID")) @@ -76,7 +82,7 @@ def validate_cluster_id(self, value): ticket_type = self.context["ticket_type"] cluster = Cluster.objects.get(id=value) - ticket_cluster_phase = TICKET_TYPE__CLUSTER_PHASE_MAP.get(ticket_type) + ticket_cluster_phase = BuilderFactory.ticket_type__cluster_phase.get(ticket_type) if not ClusterPhase.cluster_status_transfer_valid(cluster.phase, ticket_cluster_phase): raise ValidationError( _("集群{}状态转移不合法:{}--->{} is invalid").format(cluster.name, cluster.phase, ticket_cluster_phase) @@ -121,6 +127,8 @@ def get_node_count(self, attrs, role): if attrs["ip_source"] == IpSource.MANUAL_INPUT: return len(attrs["nodes"].get(role) or []) else: + if role not in attrs["resource_spec"]: + return 0 return attrs["resource_spec"][role]["count"] def validate(self, attrs): @@ -134,6 +142,7 @@ def validate(self, attrs): # 判断主机是否来自手工输入,从资源池拿到的主机不需要校验 if attrs["ip_source"] == IpSource.RESOURCE_POOL: + remove_useless_spec(attrs) return attrs # 判断主机角色是否互斥 diff --git a/dbm-ui/backend/ticket/builders/common/constants.py b/dbm-ui/backend/ticket/builders/common/constants.py index 40570f5622..946968fcf2 100644 --- a/dbm-ui/backend/ticket/builders/common/constants.py +++ b/dbm-ui/backend/ticket/builders/common/constants.py @@ -28,6 +28,8 @@ REDIS_PROXY_MIN = 2 +MAX_DOMAIN_LEN_LIMIT = 255 + MYSQL_BINLOG_ROLLBACK = "/home/mysql/dba-toolkit/mysqlbinlog_rollback" MYSQL_CHECKSUM_TABLE = "checksum_history" @@ -80,6 +82,15 @@ class MySQLChecksumTicketMode(str, StructuredEnum): MANUAL = EnumField("manual", _("人工确认")) +class TendbChecksumScope(str, StructuredEnum): + """ + tendbcluster集群校验的范围 + """ + + ALL = EnumField("all", _("整个集群")) + PARTIAL = EnumField("partial", _("部分实例")) + + class MySQLDataRepairTriggerMode(str, StructuredEnum): """ 数据修复触发类型 diff --git a/dbm-ui/backend/ticket/builders/es/es_apply.py b/dbm-ui/backend/ticket/builders/es/es_apply.py index 9ed6ea362b..2b01a60ca1 100644 --- a/dbm-ui/backend/ticket/builders/es/es_apply.py +++ b/dbm-ui/backend/ticket/builders/es/es_apply.py @@ -14,7 +14,9 @@ from django.utils.translation import ugettext_lazy as _ from rest_framework import serializers +from backend.db_meta.enums import ClusterType from backend.db_services.dbbase.constants import ES_DEFAULT_PORT, IpSource +from backend.flow.consts import ES_DEFAULT_INSTANCE_NUM from backend.flow.engine.controller.es import EsController from backend.ticket import builders from backend.ticket.builders.common import constants @@ -33,23 +35,25 @@ def validate(self, attrs): es上架限制: 1. 主机角色互斥 2. master数量为>=3的奇数台,通常配置为三台 - 3. hot, cold和client节点数量无要求,但是如果配置了client节点则一定有hot节点或者cold节点 + 3. hot/clod 至少存在一台 """ # 判断主机角色是否互斥 super().validate(attrs) + # 判断域名 + super().validate_domain(ClusterType.Es, attrs["cluster_name"], attrs["db_app_abbr"]) + # 判断master节点是否为3台 master_node_count = self.get_node_count(attrs, BigDataRole.Es.MASTER.value) - if master_node_count != constants.ES_MASTER_NEED: - raise serializers.ValidationError(_("master节点数不为3台! 请保证master的部署节点为3")) + if not (master_node_count >= constants.ES_MASTER_NEED and (master_node_count & 1)): + raise serializers.ValidationError(_("请保证master的部署节点至少为3,且为奇数")) # 保证在client存在情况下,存在hot/cold节点 - client_node_count = self.get_node_count(attrs, BigDataRole.Es.CLIENT.value) hot_node_count = self.get_node_count(attrs, BigDataRole.Es.HOT.value) cold_node_count = self.get_node_count(attrs, BigDataRole.Es.COLD.value) - if client_node_count and (not (hot_node_count + cold_node_count)): - raise serializers.ValidationError(_("请保证在部署client节点的情况下,部署至少一台hot/cold节点")) + if not (hot_node_count + cold_node_count): + raise serializers.ValidationError(_("请保证部署至少一台hot/cold节点")) return attrs @@ -115,16 +119,25 @@ def format_ticket_data(self): class EsApplyResourceParamBuilder(builders.ResourceApplyParamBuilder): - pass + @classmethod + def fill_instance_num(cls, next_flow_data, ticket_data, nodes_key): + """对es的hot和cold角色填充实例数""" + for role in ["hot", "cold"]: + if role not in next_flow_data[nodes_key]: + continue + + for node in next_flow_data["nodes"][role]: + node["instance_num"] = ticket_data["resource_spec"][role].get("instance_num", ES_DEFAULT_INSTANCE_NUM) + def post_callback(self): + next_flow = self.ticket.next_flow() + self.fill_instance_num(next_flow.details["ticket_data"], self.ticket_data, nodes_key="nodes") + next_flow.save(update_fields=["details"]) -@builders.BuilderFactory.register(TicketType.ES_APPLY) + +@builders.BuilderFactory.register(TicketType.ES_APPLY, is_apply=True, cluster_type=ClusterType.Es) class EsApplyFlowBuilder(BaseEsTicketFlowBuilder): serializer = EsApplyDetailSerializer inner_flow_builder = EsApplyFlowParamBuilder inner_flow_name = _("ES集群部署") resource_apply_builder = EsApplyResourceParamBuilder - - @property - def need_itsm(self): - return False diff --git a/dbm-ui/backend/ticket/builders/es/es_destroy.py b/dbm-ui/backend/ticket/builders/es/es_destroy.py index 3863fa4736..75ce17fe71 100644 --- a/dbm-ui/backend/ticket/builders/es/es_destroy.py +++ b/dbm-ui/backend/ticket/builders/es/es_destroy.py @@ -13,6 +13,7 @@ from django.utils.translation import ugettext_lazy as _ +from backend.db_meta.enums import ClusterPhase from backend.flow.engine.controller.es import EsController from backend.ticket import builders from backend.ticket.builders.common.bigdata import BaseEsTicketFlowBuilder, BigDataTakeDownDetailSerializer @@ -29,7 +30,7 @@ class EsDestroyFlowParamBuilder(builders.FlowParamBuilder): controller = EsController.es_destroy_scene -@builders.BuilderFactory.register(TicketType.ES_DESTROY) +@builders.BuilderFactory.register(TicketType.ES_DESTROY, phase=ClusterPhase.DESTROY) class EsDestroyFlowBuilder(BaseEsTicketFlowBuilder): serializer = EsDestroyDetailSerializer inner_flow_builder = EsDestroyFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/es/es_disable.py b/dbm-ui/backend/ticket/builders/es/es_disable.py index ff927d9ca9..7422cc6a9f 100644 --- a/dbm-ui/backend/ticket/builders/es/es_disable.py +++ b/dbm-ui/backend/ticket/builders/es/es_disable.py @@ -13,6 +13,7 @@ from django.utils.translation import ugettext_lazy as _ +from backend.db_meta.enums import ClusterPhase from backend.flow.engine.controller.es import EsController from backend.ticket import builders from backend.ticket.builders.common.bigdata import BaseEsTicketFlowBuilder, BigDataTakeDownDetailSerializer @@ -29,7 +30,7 @@ class EsDisableFlowParamBuilder(builders.FlowParamBuilder): controller = EsController.es_disable_scene -@builders.BuilderFactory.register(TicketType.ES_DISABLE) +@builders.BuilderFactory.register(TicketType.ES_DISABLE, phase=ClusterPhase.OFFLINE) class EsDisableFlowBuilder(BaseEsTicketFlowBuilder): serializer = EsDisableDetailSerializer inner_flow_builder = EsDisableFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/es/es_enable.py b/dbm-ui/backend/ticket/builders/es/es_enable.py index 3bb50ce399..1abb0a7453 100644 --- a/dbm-ui/backend/ticket/builders/es/es_enable.py +++ b/dbm-ui/backend/ticket/builders/es/es_enable.py @@ -13,6 +13,7 @@ from django.utils.translation import ugettext_lazy as _ +from backend.db_meta.enums import ClusterPhase from backend.flow.engine.controller.es import EsController from backend.ticket import builders from backend.ticket.builders.common.bigdata import BaseEsTicketFlowBuilder, BigDataTakeDownDetailSerializer @@ -29,7 +30,7 @@ class EsEnableFlowParamBuilder(builders.FlowParamBuilder): controller = EsController.es_enable_scene -@builders.BuilderFactory.register(TicketType.ES_ENABLE) +@builders.BuilderFactory.register(TicketType.ES_ENABLE, phase=ClusterPhase.ONLINE) class EsEnableFlowBuilder(BaseEsTicketFlowBuilder): serializer = EsEnableDetailSerializer inner_flow_builder = EsEnableFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/es/es_replace.py b/dbm-ui/backend/ticket/builders/es/es_replace.py index 27d8469545..6dbb06b185 100644 --- a/dbm-ui/backend/ticket/builders/es/es_replace.py +++ b/dbm-ui/backend/ticket/builders/es/es_replace.py @@ -20,6 +20,7 @@ BigDataReplaceDetailSerializer, BigDataReplaceResourceParamBuilder, ) +from backend.ticket.builders.es.es_apply import EsApplyResourceParamBuilder from backend.ticket.constants import TicketType logger = logging.getLogger("root") @@ -36,13 +37,19 @@ def format_ticket_data(self): super().format_ticket_data() -class EsApplyResourceParamBuilder(BigDataReplaceResourceParamBuilder): - pass +class EsReplaceResourceParamBuilder(BigDataReplaceResourceParamBuilder): + def post_callback(self): + next_flow = self.ticket.next_flow() + EsApplyResourceParamBuilder.fill_instance_num( + next_flow.details["ticket_data"], self.ticket_data, nodes_key="nodes" + ) + next_flow.details["ticket_data"]["new_nodes"] = next_flow.details["ticket_data"].pop("nodes") + next_flow.save(update_fields=["details"]) -@builders.BuilderFactory.register(TicketType.ES_REPLACE) +@builders.BuilderFactory.register(TicketType.ES_REPLACE, is_apply=True) class EsReplaceFlowBuilder(BaseEsTicketFlowBuilder): serializer = EsReplaceDetailSerializer inner_flow_builder = EsReplaceFlowParamBuilder inner_flow_name = _("ES集群替换") - resource_apply_builder = EsApplyResourceParamBuilder + resource_apply_builder = EsReplaceResourceParamBuilder diff --git a/dbm-ui/backend/ticket/builders/es/es_scale_up.py b/dbm-ui/backend/ticket/builders/es/es_scale_up.py index b21c2908f2..e7bce4b43f 100644 --- a/dbm-ui/backend/ticket/builders/es/es_scale_up.py +++ b/dbm-ui/backend/ticket/builders/es/es_scale_up.py @@ -21,6 +21,7 @@ BigDataScaleDetailSerializer, BigDataScaleUpResourceParamBuilder, ) +from backend.ticket.builders.es.es_apply import EsApplyResourceParamBuilder from backend.ticket.constants import TicketType logger = logging.getLogger("root") @@ -30,7 +31,7 @@ class EsScaleUpDetailSerializer(BigDataScaleDetailSerializer): def validate(self, attrs): attrs = super().validate(attrs) - if attrs["ip_resource"] == IpSource.RESOURCE_POOL: + if attrs["ip_source"] == IpSource.RESOURCE_POOL: return attrs role_nodes_list = list(attrs["nodes"].values()) @@ -47,7 +48,12 @@ def validate(self, attrs): class EsScaleUpResourceParamBuilder(BigDataScaleUpResourceParamBuilder): - pass + def post_callback(self): + next_flow = self.ticket.next_flow() + EsApplyResourceParamBuilder.fill_instance_num( + next_flow.details["ticket_data"], self.ticket_data, nodes_key="nodes" + ) + next_flow.save(update_fields=["details"]) class EsScaleUpFlowParamBuilder(builders.FlowParamBuilder): @@ -91,7 +97,7 @@ def format_ticket_data(self): super().format_ticket_data() -@builders.BuilderFactory.register(TicketType.ES_SCALE_UP) +@builders.BuilderFactory.register(TicketType.ES_SCALE_UP, is_apply=True) class EsScaleUpFlowBuilder(BaseEsTicketFlowBuilder): serializer = EsScaleUpDetailSerializer inner_flow_builder = EsScaleUpFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/hdfs/hdfs_apply.py b/dbm-ui/backend/ticket/builders/hdfs/hdfs_apply.py index ed32a05db6..585fcbc164 100644 --- a/dbm-ui/backend/ticket/builders/hdfs/hdfs_apply.py +++ b/dbm-ui/backend/ticket/builders/hdfs/hdfs_apply.py @@ -12,6 +12,7 @@ from django.utils.translation import ugettext as _ from rest_framework import serializers +from backend.db_meta.enums import ClusterType from backend.db_services.dbbase.constants import HDFS_DEFAULT_HTTP_PORT, HDFS_DEFAULT_RPC_PORT, IpSource from backend.flow.engine.controller.hdfs import HdfsController from backend.ticket import builders @@ -34,6 +35,9 @@ def validate(self, attrs): # 判断Datanode是否与Namenode/ZooKeeper/JournalNode互斥 super().validate(attrs=attrs) + # 判断域名 + super().validate_domain(ClusterType.Hdfs, attrs["cluster_name"], attrs["db_app_abbr"]) + # 判断datanode是不是>=2台 datanode_count = self.get_node_count(attrs, BigDataRole.Hdfs.DATANODE.value) if datanode_count < constants.HDFS_DATANODE_MIN: @@ -46,8 +50,12 @@ def validate(self, attrs): # 判断zk/jn数量是否为3 zk_jn_node_count = self.get_node_count(attrs, BigDataRole.Hdfs.ZK_JN.value) - if zk_jn_node_count != constants.HDFS_ZK_JN_NEED: - raise serializers.ValidationError(_("ZooKeeper/JournalNode节点数量不等于3台,请确保ZooKeeper/JournalNode数量为3台")) + if attrs["ip_source"] == IpSource.RESOURCE_POOL: + if zk_jn_node_count < 1 or zk_jn_node_count > 3: + raise serializers.ValidationError(_("资源池部署ZooKeeper/JournalNode的角色数量为1-3台")) + else: + if zk_jn_node_count != constants.HDFS_ZK_JN_NEED: + raise serializers.ValidationError(_("ZooKeeper/JournalNode节点数量不等于3台,请确保ZooKeeper/JournalNode数量为3台")) return attrs @@ -132,7 +140,7 @@ def post_callback(self): next_flow.save(update_fields=["details"]) -@builders.BuilderFactory.register(TicketType.HDFS_APPLY) +@builders.BuilderFactory.register(TicketType.HDFS_APPLY, is_apply=True, cluster_type=ClusterType.Hdfs) class HdfsApplyFlowBuilder(BaseHdfsTicketFlowBuilder): serializer = HdfsApplyDetailSerializer inner_flow_builder = HdfsApplyFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/hdfs/hdfs_destroy.py b/dbm-ui/backend/ticket/builders/hdfs/hdfs_destroy.py index c57068f384..359cfb7903 100644 --- a/dbm-ui/backend/ticket/builders/hdfs/hdfs_destroy.py +++ b/dbm-ui/backend/ticket/builders/hdfs/hdfs_destroy.py @@ -13,6 +13,7 @@ from django.utils.translation import ugettext as _ +from backend.db_meta.enums import ClusterPhase from backend.flow.engine.controller.hdfs import HdfsController from backend.ticket import builders from backend.ticket.builders.common.bigdata import BaseHdfsTicketFlowBuilder, BigDataTakeDownDetailSerializer @@ -29,7 +30,7 @@ class HdfsDestroyFlowParamBuilder(builders.FlowParamBuilder): controller = HdfsController.hdfs_destroy_scene -@builders.BuilderFactory.register(TicketType.HDFS_DESTROY) +@builders.BuilderFactory.register(TicketType.HDFS_DESTROY, phase=ClusterPhase.DESTROY) class HdfsDestroyFlowBuilder(BaseHdfsTicketFlowBuilder): serializer = HdfsDestroyDetailSerializer inner_flow_builder = HdfsDestroyFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/hdfs/hdfs_disable.py b/dbm-ui/backend/ticket/builders/hdfs/hdfs_disable.py index 1a4cf0e0ca..ece1fa6fbf 100644 --- a/dbm-ui/backend/ticket/builders/hdfs/hdfs_disable.py +++ b/dbm-ui/backend/ticket/builders/hdfs/hdfs_disable.py @@ -13,6 +13,7 @@ from django.utils.translation import ugettext_lazy as _ +from backend.db_meta.enums import ClusterPhase from backend.flow.engine.controller.hdfs import HdfsController from backend.ticket import builders from backend.ticket.builders.common.bigdata import BaseHdfsTicketFlowBuilder, BigDataTakeDownDetailSerializer @@ -29,7 +30,7 @@ class HdfsDisableFlowParamBuilder(builders.FlowParamBuilder): controller = HdfsController.hdfs_disable_scene -@builders.BuilderFactory.register(TicketType.HDFS_DISABLE) +@builders.BuilderFactory.register(TicketType.HDFS_DISABLE, phase=ClusterPhase.OFFLINE) class HdfsDisableFlowBuilder(BaseHdfsTicketFlowBuilder): serializer = HdfsDisableDetailSerializer inner_flow_builder = HdfsDisableFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/hdfs/hdfs_enable.py b/dbm-ui/backend/ticket/builders/hdfs/hdfs_enable.py index 350efd8f1b..6b1c624186 100644 --- a/dbm-ui/backend/ticket/builders/hdfs/hdfs_enable.py +++ b/dbm-ui/backend/ticket/builders/hdfs/hdfs_enable.py @@ -13,6 +13,7 @@ from django.utils.translation import ugettext as _ +from backend.db_meta.enums import ClusterPhase from backend.flow.engine.controller.hdfs import HdfsController from backend.ticket import builders from backend.ticket.builders.common.bigdata import BaseHdfsTicketFlowBuilder, BigDataTakeDownDetailSerializer @@ -29,7 +30,7 @@ class HdfsEnableFlowParamBuilder(builders.FlowParamBuilder): controller = HdfsController.hdfs_enable_scene -@builders.BuilderFactory.register(TicketType.HDFS_ENABLE) +@builders.BuilderFactory.register(TicketType.HDFS_ENABLE, phase=ClusterPhase.ONLINE) class HdfsEnableFlowBuilder(BaseHdfsTicketFlowBuilder): serializer = HdfsEnableDetailSerializer inner_flow_builder = HdfsEnableFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/hdfs/hdfs_replace.py b/dbm-ui/backend/ticket/builders/hdfs/hdfs_replace.py index e1a29c7d74..22e898e9fd 100644 --- a/dbm-ui/backend/ticket/builders/hdfs/hdfs_replace.py +++ b/dbm-ui/backend/ticket/builders/hdfs/hdfs_replace.py @@ -50,7 +50,7 @@ class HdfsResourceParamBuilder(BigDataReplaceResourceParamBuilder): pass -@builders.BuilderFactory.register(TicketType.HDFS_REPLACE) +@builders.BuilderFactory.register(TicketType.HDFS_REPLACE, is_apply=True) class HdfsReplaceFlowBuilder(BaseHdfsTicketFlowBuilder): serializer = HdfsReplaceDetailSerializer inner_flow_builder = HdfsReplaceFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/hdfs/hdfs_scale_up.py b/dbm-ui/backend/ticket/builders/hdfs/hdfs_scale_up.py index 3eb5a6adaf..0c4d8f7775 100644 --- a/dbm-ui/backend/ticket/builders/hdfs/hdfs_scale_up.py +++ b/dbm-ui/backend/ticket/builders/hdfs/hdfs_scale_up.py @@ -57,7 +57,7 @@ class HdfsScaleUpResourceParamBuilder(BigDataScaleUpResourceParamBuilder): pass -@builders.BuilderFactory.register(TicketType.HDFS_SCALE_UP) +@builders.BuilderFactory.register(TicketType.HDFS_SCALE_UP, is_apply=True) class HdfsScaleUpFlowBuilder(BaseHdfsTicketFlowBuilder): serializer = HdfsScaleUpDetailSerializer inner_flow_builder = HdfsScaleUpFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/influxdb/influxdb_apply.py b/dbm-ui/backend/ticket/builders/influxdb/influxdb_apply.py index 01f2d1aef6..d097443815 100644 --- a/dbm-ui/backend/ticket/builders/influxdb/influxdb_apply.py +++ b/dbm-ui/backend/ticket/builders/influxdb/influxdb_apply.py @@ -14,6 +14,7 @@ from django.utils.translation import ugettext as _ from rest_framework import serializers +from backend.db_meta.enums import ClusterType from backend.db_meta.models import Group from backend.db_services.dbbase.constants import IpSource from backend.flow.engine.controller.influxdb import InfluxdbController @@ -68,7 +69,7 @@ class InfluxApplyDBResourceParamBuilder(builders.ResourceApplyParamBuilder): pass -@builders.BuilderFactory.register(TicketType.INFLUXDB_APPLY) +@builders.BuilderFactory.register(TicketType.INFLUXDB_APPLY, is_apply=True, cluster_type=ClusterType.Influxdb) class InfluxDBApplyFlowBuilder(BaseInfluxDBTicketFlowBuilder): serializer = InfluxDBApplyDetailSerializer inner_flow_builder = InfluxDBApplyFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/influxdb/influxdb_destroy.py b/dbm-ui/backend/ticket/builders/influxdb/influxdb_destroy.py index de5c5e223b..f9fa3495cc 100644 --- a/dbm-ui/backend/ticket/builders/influxdb/influxdb_destroy.py +++ b/dbm-ui/backend/ticket/builders/influxdb/influxdb_destroy.py @@ -14,6 +14,7 @@ from django.utils.translation import ugettext as _ +from backend.db_meta.enums import ClusterPhase from backend.flow.engine.controller.influxdb import InfluxdbController from backend.ticket import builders from backend.ticket.builders.common.bigdata import BaseInfluxDBOpsDetailSerializer, BaseInfluxDBTicketFlowBuilder @@ -30,7 +31,7 @@ class InfluxDBDestroyFlowParamBuilder(builders.FlowParamBuilder): controller = InfluxdbController.influxdb_destroy_scene -@builders.BuilderFactory.register(TicketType.INFLUXDB_DESTROY) +@builders.BuilderFactory.register(TicketType.INFLUXDB_DESTROY, phase=ClusterPhase.DESTROY) class InfluxDBDestroyFlowBuilder(BaseInfluxDBTicketFlowBuilder): serializer = InfluxDBDestroyDetailSerializer inner_flow_builder = InfluxDBDestroyFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/influxdb/influxdb_disable.py b/dbm-ui/backend/ticket/builders/influxdb/influxdb_disable.py index 0fdf054ea4..b68025ff08 100644 --- a/dbm-ui/backend/ticket/builders/influxdb/influxdb_disable.py +++ b/dbm-ui/backend/ticket/builders/influxdb/influxdb_disable.py @@ -14,6 +14,7 @@ from django.utils.translation import ugettext as _ +from backend.db_meta.enums import ClusterPhase from backend.flow.engine.controller.influxdb import InfluxdbController from backend.ticket import builders from backend.ticket.builders.common.bigdata import BaseInfluxDBOpsDetailSerializer, BaseInfluxDBTicketFlowBuilder @@ -30,7 +31,7 @@ class InfluxDBDisableFlowParamBuilder(builders.FlowParamBuilder): controller = InfluxdbController.influxdb_disable_scene -@builders.BuilderFactory.register(TicketType.INFLUXDB_DISABLE) +@builders.BuilderFactory.register(TicketType.INFLUXDB_DISABLE, phase=ClusterPhase.OFFLINE) class InfluxDBDisableFlowBuilder(BaseInfluxDBTicketFlowBuilder): serializer = InfluxDBDisableDetailSerializer inner_flow_builder = InfluxDBDisableFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/influxdb/influxdb_enable.py b/dbm-ui/backend/ticket/builders/influxdb/influxdb_enable.py index f5fa4a69c6..ddae43e251 100644 --- a/dbm-ui/backend/ticket/builders/influxdb/influxdb_enable.py +++ b/dbm-ui/backend/ticket/builders/influxdb/influxdb_enable.py @@ -14,6 +14,7 @@ from django.utils.translation import ugettext as _ +from backend.db_meta.enums import ClusterPhase from backend.flow.engine.controller.influxdb import InfluxdbController from backend.ticket import builders from backend.ticket.builders.common.bigdata import BaseInfluxDBOpsDetailSerializer, BaseInfluxDBTicketFlowBuilder @@ -30,7 +31,7 @@ class InfluxDBEnableFlowParamBuilder(builders.FlowParamBuilder): controller = InfluxdbController.influxdb_enable_scene -@builders.BuilderFactory.register(TicketType.INFLUXDB_ENABLE) +@builders.BuilderFactory.register(TicketType.INFLUXDB_ENABLE, phase=ClusterPhase.ONLINE) class InfluxDBEnableFlowBuilder(BaseInfluxDBTicketFlowBuilder): serializer = InfluxDBEnableDetailSerializer inner_flow_builder = InfluxDBEnableFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/influxdb/influxdb_replace.py b/dbm-ui/backend/ticket/builders/influxdb/influxdb_replace.py index 1c6f790a03..898c3e9c18 100644 --- a/dbm-ui/backend/ticket/builders/influxdb/influxdb_replace.py +++ b/dbm-ui/backend/ticket/builders/influxdb/influxdb_replace.py @@ -39,7 +39,7 @@ def format(self): self.ticket_data["bk_cloud_id"] = self.ticket_data["old_nodes"]["influxdb"][0]["bk_cloud_id"] -@builders.BuilderFactory.register(TicketType.INFLUXDB_REPLACE) +@builders.BuilderFactory.register(TicketType.INFLUXDB_REPLACE, is_apply=True) class InfluxDBReplaceFlowBuilder(BaseInfluxDBTicketFlowBuilder): serializer = InfluxDBReplaceDetailSerializer inner_flow_builder = InfluxDBReplaceFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/kafka/kafka_apply.py b/dbm-ui/backend/ticket/builders/kafka/kafka_apply.py index 7fc2eba9eb..903ed7d3cf 100644 --- a/dbm-ui/backend/ticket/builders/kafka/kafka_apply.py +++ b/dbm-ui/backend/ticket/builders/kafka/kafka_apply.py @@ -13,6 +13,7 @@ from django.utils.translation import ugettext_lazy as _ from rest_framework import serializers +from backend.db_meta.enums import ClusterType from backend.db_services.dbbase.constants import KAFKA_DEFAULT_PORT, IpSource from backend.flow.engine.controller.kafka import KafkaController from backend.ticket import builders @@ -99,6 +100,9 @@ def validate(self, attrs): # 判断主机角色是否互斥 super().validate(attrs) + # 判断域名 + super().validate_domain(ClusterType.Kafka, attrs["cluster_name"], attrs["db_app_abbr"]) + # 判断zookeeper数量固定为3 zookeeper_node_count = self.get_node_count(attrs, BigDataRole.Kafka.ZOOKEEPER.value) if zookeeper_node_count != constants.KAFKA_ZOOKEEPER_NEED: @@ -130,7 +134,7 @@ class KafkaApplyResourceParamBuilder(builders.ResourceApplyParamBuilder): pass -@builders.BuilderFactory.register(TicketType.KAFKA_APPLY) +@builders.BuilderFactory.register(TicketType.KAFKA_APPLY, is_apply=True, cluster_type=ClusterType.Kafka) class KafkaApplyFlowBuilder(BaseKafkaTicketFlowBuilder): serializer = KafkaApplyDetailSerializer inner_flow_builder = KafkaApplyFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/kafka/kafka_destroy.py b/dbm-ui/backend/ticket/builders/kafka/kafka_destroy.py index af867af64c..35e80e99c3 100644 --- a/dbm-ui/backend/ticket/builders/kafka/kafka_destroy.py +++ b/dbm-ui/backend/ticket/builders/kafka/kafka_destroy.py @@ -14,6 +14,7 @@ from django.utils.translation import ugettext as _ +from backend.db_meta.enums import ClusterPhase from backend.flow.engine.controller.kafka import KafkaController from backend.ticket import builders from backend.ticket.builders.common.bigdata import BaseKafkaTicketFlowBuilder, BigDataTakeDownDetailSerializer @@ -30,7 +31,7 @@ class KafkaDestroyFlowParamBuilder(builders.FlowParamBuilder): controller = KafkaController.kafka_destroy_scene -@builders.BuilderFactory.register(TicketType.KAFKA_DESTROY) +@builders.BuilderFactory.register(TicketType.KAFKA_DESTROY, phase=ClusterPhase.DESTROY) class KafkaDestroyFlowBuilder(BaseKafkaTicketFlowBuilder): serializer = KafkaDestroyDetailSerializer inner_flow_builder = KafkaDestroyFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/kafka/kafka_disable.py b/dbm-ui/backend/ticket/builders/kafka/kafka_disable.py index 4a23909c75..27a6a183da 100644 --- a/dbm-ui/backend/ticket/builders/kafka/kafka_disable.py +++ b/dbm-ui/backend/ticket/builders/kafka/kafka_disable.py @@ -14,6 +14,7 @@ from django.utils.translation import ugettext as _ +from backend.db_meta.enums import ClusterPhase from backend.flow.engine.controller.kafka import KafkaController from backend.ticket import builders from backend.ticket.builders.common.bigdata import BaseKafkaTicketFlowBuilder, BigDataTakeDownDetailSerializer @@ -30,7 +31,7 @@ class KafkaDisableFlowParamBuilder(builders.FlowParamBuilder): controller = KafkaController.kafka_disable_scene -@builders.BuilderFactory.register(TicketType.KAFKA_DISABLE) +@builders.BuilderFactory.register(TicketType.KAFKA_DISABLE, phase=ClusterPhase.OFFLINE) class KafkaDisableFlowBuilder(BaseKafkaTicketFlowBuilder): serializer = KafkaDisableDetailSerializer inner_flow_builder = KafkaDisableFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/kafka/kafka_enable.py b/dbm-ui/backend/ticket/builders/kafka/kafka_enable.py index f3b775f8d6..6dc57063c8 100644 --- a/dbm-ui/backend/ticket/builders/kafka/kafka_enable.py +++ b/dbm-ui/backend/ticket/builders/kafka/kafka_enable.py @@ -14,6 +14,7 @@ from django.utils.translation import ugettext as _ +from backend.db_meta.enums import ClusterPhase from backend.flow.engine.controller.kafka import KafkaController from backend.ticket import builders from backend.ticket.builders.common.bigdata import BaseKafkaTicketFlowBuilder, BigDataTakeDownDetailSerializer @@ -30,7 +31,7 @@ class KafkaEnableFlowParamBuilder(builders.FlowParamBuilder): controller = KafkaController.kafka_enable_scene -@builders.BuilderFactory.register(TicketType.KAFKA_ENABLE) +@builders.BuilderFactory.register(TicketType.KAFKA_ENABLE, phase=ClusterPhase.ONLINE) class KafkaEnableFlowBuilder(BaseKafkaTicketFlowBuilder): serializer = KafkaEnableDetailSerializer inner_flow_builder = KafkaEnableFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/kafka/kafka_replace.py b/dbm-ui/backend/ticket/builders/kafka/kafka_replace.py index b58f0adc2b..be583f2a8d 100644 --- a/dbm-ui/backend/ticket/builders/kafka/kafka_replace.py +++ b/dbm-ui/backend/ticket/builders/kafka/kafka_replace.py @@ -41,7 +41,7 @@ class KafkaReplaceResourceParamBuilder(BigDataReplaceResourceParamBuilder): pass -@builders.BuilderFactory.register(TicketType.KAFKA_REPLACE) +@builders.BuilderFactory.register(TicketType.KAFKA_REPLACE, is_apply=True) class KafkaReplaceFlowBuilder(BaseKafkaTicketFlowBuilder): serializer = KafkaReplaceDetailSerializer inner_flow_builder = KafkaReplaceFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/kafka/kafka_scale_up.py b/dbm-ui/backend/ticket/builders/kafka/kafka_scale_up.py index 7af0baacb9..546f14ff2b 100644 --- a/dbm-ui/backend/ticket/builders/kafka/kafka_scale_up.py +++ b/dbm-ui/backend/ticket/builders/kafka/kafka_scale_up.py @@ -58,7 +58,7 @@ class KafkaScaleUpResourceParamBuilder(BigDataScaleUpResourceParamBuilder): pass -@builders.BuilderFactory.register(TicketType.KAFKA_SCALE_UP) +@builders.BuilderFactory.register(TicketType.KAFKA_SCALE_UP, is_apply=True) class KafkaScaleUpFlowBuilder(BaseKafkaTicketFlowBuilder): serializer = KafkaScaleUpDetailSerializer inner_flow_builder = KafkaScaleUpFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/mysql/base.py b/dbm-ui/backend/ticket/builders/mysql/base.py index c228b788a7..6a024b8031 100644 --- a/dbm-ui/backend/ticket/builders/mysql/base.py +++ b/dbm-ui/backend/ticket/builders/mysql/base.py @@ -8,30 +8,38 @@ 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. """ - +import re from typing import Any, Dict, List, Union -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import ugettext as _ from rest_framework import serializers from rest_framework.serializers import ValidationError from backend.configuration.constants import DBType from backend.db_meta.enums import AccessLayer, ClusterDBHAStatusFlags, ClusterType, InstanceInnerRole from backend.db_meta.models.cluster import Cluster, ClusterPhase +from backend.flow.consts import SYSTEM_DBS +from backend.flow.utils.mysql.db_table_filter.exception import DbTableFilterValidateException +from backend.flow.utils.mysql.db_table_filter.tools import glob_check from backend.ticket import builders -from backend.ticket.builders import TicketFlowBuilder +from backend.ticket.builders import BuilderFactory, TicketFlowBuilder from backend.ticket.builders.common.base import ( CommonValidate, MySQLTicketFlowBuilderPatchMixin, SkipToRepresentationMixin, + fetch_cluster_ids, ) -from backend.ticket.constants import TICKET_TYPE__CLUSTER_PHASE_MAP, TicketType +from backend.ticket.constants import TicketType class BaseMySQLTicketFlowBuilder(MySQLTicketFlowBuilderPatchMixin, TicketFlowBuilder): group = DBType.MySQL.value +class MySQLBasePauseParamBuilder(builders.PauseParamBuilder): + pass + + class MySQLBaseOperateDetailSerializer(SkipToRepresentationMixin, serializers.Serializer): """ mysql操作的基类,主要功能: @@ -40,7 +48,7 @@ class MySQLBaseOperateDetailSerializer(SkipToRepresentationMixin, serializers.Se """ # 实例不可用时,还能正常提单类型的白名单 - SLAVE_UNAVAILABLE_CAN_ACCESS = [ + SLAVE_UNAVAILABLE_WHITELIST = [ TicketType.MYSQL_IMPORT_SQLFILE.value, TicketType.MYSQL_CLIENT_CLONE_RULES.value, TicketType.MYSQL_ROLLBACK_CLUSTER.value, @@ -50,26 +58,14 @@ class MySQLBaseOperateDetailSerializer(SkipToRepresentationMixin, serializers.Se TicketType.MYSQL_RESTORE_SLAVE.value, TicketType.MYSQL_HA_TRUNCATE_DATA.value, ] - MASTER_UNAVAILABLE_CAN_ACCESS = [] - PROXY_UNAVAILABLE_CAN_ACCESS = [TicketType.get_values()] - - @classmethod - def fetch_cluster_ids(cls, details: Dict[str, Any]) -> List[int]: - def _find_cluster_id(_cluster_ids: List[int], _info: Dict): - if "cluster_id" in _info: - _cluster_ids.append(_info["cluster_id"]) - elif "cluster_ids" in _info: - _cluster_ids.extend(_info["cluster_ids"]) - - cluster_ids = [] - _find_cluster_id(cluster_ids, details) - if isinstance(details.get("infos"), dict): - _find_cluster_id(cluster_ids, details.get("infos")) - elif isinstance(details.get("infos"), list): - for info in details.get("infos"): - _find_cluster_id(cluster_ids, info) - - return cluster_ids + MASTER_UNAVAILABLE_WHITELIST = [] + PROXY_UNAVAILABLE_WHITELIST = [TicketType.get_values()] + # 集群的flag状态与白名单的映射表 + unavailable_whitelist__status_flag = { + ClusterDBHAStatusFlags.ProxyUnavailable: PROXY_UNAVAILABLE_WHITELIST, + ClusterDBHAStatusFlags.BackendSlaveUnavailable: SLAVE_UNAVAILABLE_WHITELIST, + ClusterDBHAStatusFlags.BackendMasterUnavailable: MASTER_UNAVAILABLE_WHITELIST, + } @classmethod def fetch_obj_by_keys(cls, obj_dict: Dict, keys: List[str]): @@ -88,26 +84,21 @@ def fetch_obj_by_keys(cls, obj_dict: Dict, keys: List[str]): def validate_cluster_can_access(self, attrs): """校验集群状态是否可以提单""" - clusters = Cluster.objects.filter(id__in=self.fetch_cluster_ids(details=attrs)) + clusters = Cluster.objects.filter(id__in=fetch_cluster_ids(details=attrs)) ticket_type = self.context["ticket_type"] + for cluster in clusters: - if ( - cluster.status_flag & ClusterDBHAStatusFlags.BackendMasterUnavailable - and ticket_type not in self.MASTER_UNAVAILABLE_CAN_ACCESS - ): - raise serializers.ValidationError(_("matser实例状态异常,暂时无法执行该单据类型:{}").format(ticket_type)) - - elif ( - cluster.status_flag & ClusterDBHAStatusFlags.BackendSlaveUnavailable - and ticket_type not in self.SLAVE_UNAVAILABLE_CAN_ACCESS - ): - raise serializers.ValidationError(_("slave实例状态异常,暂时无法执行该单据类型:{}").format(ticket_type)) - - elif ( - cluster.status_flag & ClusterDBHAStatusFlags.ProxyUnavailable - and ticket_type not in self.PROXY_UNAVAILABLE_CAN_ACCESS - ): - raise serializers.ValidationError(_("proxy实例状态异常,暂时无法执行该单据类型:{}").format(ticket_type)) + if cluster.cluster_type == ClusterType.TenDBSingle: + # 如果单节点异常,则直接报错 + if cluster.status_flag: + raise serializers.ValidationError(_("单节点实例状态异常,暂时无法执行该单据类型:{}").format(ticket_type)) + continue + + for status_flag, whitelist in self.unavailable_whitelist__status_flag.items(): + if cluster.status_flag & status_flag and ticket_type not in whitelist: + raise serializers.ValidationError( + _("高可用实例状态异常:{},暂时无法执行该单据类型:{}").format(status_flag.flag_text(), ticket_type) + ) return attrs @@ -130,7 +121,7 @@ def validate_instance_role(self, attrs, instance_key: List[str], role: Union[Acc def validate_cluster_type(self, attrs, cluster_type: ClusterType): """校验集群类型为高可用""" - cluster_ids = self.fetch_cluster_ids(attrs) + cluster_ids = fetch_cluster_ids(attrs) if not CommonValidate.validate_cluster_type(cluster_ids, cluster_type): raise serializers.ValidationError( _("请保证所选集群{}都是{}集群").format(cluster_ids, ClusterType.get_choice_label(cluster_type)) @@ -147,10 +138,13 @@ def validate_instance_related_clusters( if not CommonValidate.validate_instance_related_clusters(inst, cluster_ids, role): raise serializers.ValidationError(_("请保证所选实例{}的关联集群为{}").format(inst, cluster_ids)) - def validate_database_table_selector(self, attrs, is_only_db_operate_list: List[bool] = None): + def validate_database_table_selector(self, attrs, role_key=None, is_only_db_operate_list: List[bool] = None): """校验库表选择器的数据是否合法""" is_valid, message = CommonValidate.validate_database_table_selector( - bk_biz_id=self.context["bk_biz_id"], infos=attrs["infos"], is_only_db_operate_list=is_only_db_operate_list + bk_biz_id=self.context["bk_biz_id"], + infos=attrs["infos"], + role_key=role_key, + is_only_db_operate_list=is_only_db_operate_list, ) if not is_valid: raise serializers.ValidationError(message) @@ -169,7 +163,7 @@ class MySQLClustersTakeDownDetailsSerializer(SkipToRepresentationMixin, serializ def clusters_status_transfer_valid(cls, cluster_ids: List[int], ticket_type: str): cluster_list = Cluster.objects.filter(id__in=cluster_ids) for cluster in cluster_list: - ticket_cluster_phase = TICKET_TYPE__CLUSTER_PHASE_MAP.get(ticket_type) + ticket_cluster_phase = BuilderFactory.ticket_type__cluster_phase.get(ticket_type) if not ClusterPhase.cluster_status_transfer_valid(cluster.phase, ticket_cluster_phase): raise ValidationError( _("集群{}状态转移不合法:{}--->{} is invalid").format(cluster.name, cluster.phase, ticket_cluster_phase) @@ -182,9 +176,9 @@ def validate_cluster_ids(self, value): class MySQLBaseOperateResourceParamBuilder(builders.ResourceApplyParamBuilder): def format(self): - cluster_ids = MySQLBaseOperateDetailSerializer.fetch_cluster_ids(self.ticket_data) + cluster_ids = fetch_cluster_ids(self.ticket_data) clusters = Cluster.objects.filter(id__in=cluster_ids) - # 对每个info补充bk_cloud_id + # 对每个info补充bk_cloud_id和bk_biz_id for info in self.ticket_data["infos"]: cluster_id = info.get("cluster_id") or info.get("cluster_ids")[0] bk_cloud_id = clusters.get(id=cluster_id).bk_cloud_id @@ -192,3 +186,52 @@ def format(self): def post_callback(self): pass + + +class DBTableField(serializers.CharField): + """ + 库表备份的专属字段 + """ + + # 库表匹配正则 + db_tb_pattern = re.compile("^[-_a-zA-Z0-9\*\?%]{0,35}$") + # 是否为库 + db_field = False + + def __init__(self, **kwargs): + self.db_field = kwargs.pop("db_field", False) + super().__init__(**kwargs) + + def to_internal_value(self, data): + """ + 库表字段校验规则: + 1. 库表只能由`[0-9],[a-z],[A-Z],-,_` 组成,(某些单据)支持*/%/?通配符 + 2. % ? 不能独立使用 + 3. * 只能独立使用 + 4. 如果为库字段,则不允许出现系统库 + """ + data = super().to_internal_value(data) + + # 库表字符集校验 + if not self.db_tb_pattern.match(data): + raise serializers.ValidationError(_("【库表字段校验】库表只能由`[0-9],[a-z],[A-Z],-,_` 组成,(某些单据)支持*/%/?通配符。库表长度最大为35")) + + # 库表通配符规则校验 + try: + glob_check(patterns=[data]) + except DbTableFilterValidateException as e: + raise serializers.ValidationError(_("【库表字段校验】{}").format(e.message)) + + # 系统库字段校验 + if self.db_field: + if data in SYSTEM_DBS and data != "test": + raise serializers.ValidationError(_("【库表字段校验】不允许系统库(除test)做任何变更类操作")) + if data.startswith("stage_truncate"): + raise serializers.ValidationError(_("【库表字段校验】DB名{}不能以stage_truncate开头").format(data)) + if data.endswith("dba_rollback"): + raise serializers.ValidationError(_("【库表字段校验】DB名{}不能以dba_rollback结尾").format(data)) + + return data + + def to_representation(self, value): + return super().to_representation(value) diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_add_slave.py b/dbm-ui/backend/ticket/builders/mysql/mysql_add_slave.py index a348835dfc..80e91fa3ff 100644 --- a/dbm-ui/backend/ticket/builders/mysql/mysql_add_slave.py +++ b/dbm-ui/backend/ticket/builders/mysql/mysql_add_slave.py @@ -16,15 +16,10 @@ from backend.db_services.dbbase.constants import IpSource from backend.flow.engine.controller.mysql import MySQLController from backend.ticket import builders -from backend.ticket.builders.common.base import HostInfoSerializer +from backend.ticket.builders.common.base import BaseOperateResourceParamBuilder, HostInfoSerializer from backend.ticket.builders.common.constants import MySQLBackupSource -from backend.ticket.builders.mysql.base import ( - BaseMySQLTicketFlowBuilder, - MySQLBaseOperateDetailSerializer, - MySQLBaseOperateResourceParamBuilder, -) -from backend.ticket.constants import FlowRetryType, FlowType, TicketType -from backend.ticket.models import Flow +from backend.ticket.builders.mysql.base import BaseMySQLTicketFlowBuilder, MySQLBaseOperateDetailSerializer +from backend.ticket.constants import TicketType class MysqlAddSlaveDetailSerializer(MySQLBaseOperateDetailSerializer): @@ -69,7 +64,7 @@ def format_ticket_data(self): info["new_slave_ip"] = info["new_slave"]["ip"] -class MysqlAddSlaveResourceParamBuilder(MySQLBaseOperateResourceParamBuilder): +class MysqlAddSlaveResourceParamBuilder(BaseOperateResourceParamBuilder): def post_callback(self): next_flow = self.ticket.next_flow() ticket_data = next_flow.details["ticket_data"] @@ -80,7 +75,7 @@ def post_callback(self): next_flow.save(update_fields=["details"]) -@builders.BuilderFactory.register(TicketType.MYSQL_ADD_SLAVE) +@builders.BuilderFactory.register(TicketType.MYSQL_ADD_SLAVE, is_apply=True) class MysqlAddSlaveFlowBuilder(BaseMySQLTicketFlowBuilder): serializer = MysqlAddSlaveDetailSerializer inner_flow_builder = MysqlAddSlaveParamBuilder diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_checksum.py b/dbm-ui/backend/ticket/builders/mysql/mysql_checksum.py index 3691721120..0ef72ac222 100644 --- a/dbm-ui/backend/ticket/builders/mysql/mysql_checksum.py +++ b/dbm-ui/backend/ticket/builders/mysql/mysql_checksum.py @@ -22,7 +22,11 @@ from backend.ticket import builders from backend.ticket.builders.common.base import InstanceInfoSerializer from backend.ticket.builders.common.constants import MySQLChecksumTicketMode, MySQLDataRepairTriggerMode -from backend.ticket.builders.mysql.base import BaseMySQLTicketFlowBuilder, MySQLBaseOperateDetailSerializer +from backend.ticket.builders.mysql.base import ( + BaseMySQLTicketFlowBuilder, + DBTableField, + MySQLBaseOperateDetailSerializer, +) from backend.ticket.constants import FlowRetryType, FlowType, TicketFlowStatus, TicketType from backend.ticket.models import Flow @@ -30,10 +34,10 @@ class MySQLChecksumDetailSerializer(MySQLBaseOperateDetailSerializer): class ChecksumDataInfoSerializer(serializers.Serializer): slaves = serializers.ListField(help_text=_("slave信息列表"), child=InstanceInfoSerializer()) - db_patterns = serializers.ListField(help_text=_("匹配DB列表"), child=serializers.CharField()) - ignore_dbs = serializers.ListField(help_text=_("忽略DB列表"), child=serializers.CharField()) - table_patterns = serializers.ListField(help_text=_("匹配Table列表"), child=serializers.CharField()) - ignore_tables = serializers.ListField(help_text=_("忽略Table列表"), child=serializers.CharField()) + db_patterns = serializers.ListField(help_text=_("匹配DB列表"), child=DBTableField(db_field=True)) + ignore_dbs = serializers.ListField(help_text=_("忽略DB列表"), child=DBTableField(db_field=True)) + table_patterns = serializers.ListField(help_text=_("匹配Table列表"), child=DBTableField()) + ignore_tables = serializers.ListField(help_text=_("忽略Table列表"), child=DBTableField()) cluster_id = serializers.IntegerField(help_text=_("集群ID")) runtime_hour = serializers.IntegerField(help_text=_("超时时间")) @@ -68,29 +72,30 @@ class MySQLChecksumFlowParamBuilder(builders.FlowParamBuilder): def format_ticket_data(self): pass - def post_callback(self): - """根据数据校验的结果,填充上下文信息给数据修复""" - + def skip_data_repair(self, data_repair_name, pause_name): + """是否跳过数据修复""" # 如果flow的状态不为成功,则不更新 if self.ticket.current_flow().status != TicketFlowStatus.SUCCEEDED: - return + return True # 如果校验结果一致则跳过人工确认和数据修复 if set(self.ticket_data["is_consistent_list"].values()) == {True}: skip_filters = Q( ticket=self.ticket, - details__controller_info__func_name=MySQLController.mysql_pt_table_sync_scene.__name__, + details__controller_info__func_name=data_repair_name, ) if self.ticket_data["data_repair"]["mode"] == MySQLChecksumTicketMode.MANUAL: - skip_filters |= Q(ticket=self.ticket, details__pause_type=TicketType.MYSQL_CHECKSUM) + skip_filters |= Q(ticket=self.ticket, details__pause_type=pause_name) Flow.objects.filter(skip_filters).update(status=TicketFlowStatus.SKIPPED) - return + return True + + return False + def make_repair_data(self, data_repair_name): + """构造数据修复的数据""" # 更新每个实例的数据校验结果 - table_sync_flow = Flow.objects.get( - ticket=self.ticket, details__controller_info__func_name=MySQLController.mysql_pt_table_sync_scene.__name__ - ) + table_sync_flow = Flow.objects.get(ticket=self.ticket, details__controller_info__func_name=data_repair_name) address__inst_map = {} for info in table_sync_flow.details["ticket_data"]["infos"]: address__inst_map.update({f"{slave['ip']}:{slave['port']}": slave for slave in info["slaves"]}) @@ -104,6 +109,13 @@ def post_callback(self): ) table_sync_flow.save(update_fields=["details"]) + def post_callback(self): + """根据数据校验的结果,填充上下文信息给数据修复""" + if self.skip_data_repair(MySQLController.mysql_pt_table_sync_scene.__name__, TicketType.MYSQL_CHECKSUM): + return + + self.make_repair_data(MySQLController.mysql_pt_table_sync_scene.__name__) + class MySQLChecksumPauseParamBuilder(builders.PauseParamBuilder): """MySQL 数据修复人工确认执行的单据参数""" @@ -112,7 +124,7 @@ def format(self): self.params["pause_type"] = TicketType.MYSQL_CHECKSUM -class MySQLDataRepairFlowParamBuilder(MySQLChecksumFlowParamBuilder): +class MySQLDataRepairFlowParamBuilder(builders.FlowParamBuilder): """MySQL 数据修复执行的单据参数""" controller = MySQLController.mysql_pt_table_sync_scene @@ -125,6 +137,10 @@ def format_ticket_data(self): @builders.BuilderFactory.register(TicketType.MYSQL_CHECKSUM) class MySQLChecksumFlowBuilder(BaseMySQLTicketFlowBuilder): serializer = MySQLChecksumDetailSerializer + # 流程构造类 + checksum_flow_builder = MySQLChecksumFlowParamBuilder + pause_flow_builder = MySQLChecksumPauseParamBuilder + data_repair_flow_builder = MySQLDataRepairFlowParamBuilder def patch_ticket_detail(self): super().patch_ticket_detail() @@ -152,7 +168,7 @@ def custom_ticket_flows(self): Flow( ticket=self.ticket, flow_type=FlowType.INNER_FLOW.value, - details=MySQLChecksumFlowParamBuilder(self.ticket).get_params(), + details=self.checksum_flow_builder(self.ticket).get_params(), flow_alias=_("数据校验执行"), retry_type=FlowRetryType.MANUAL_RETRY.value, ), @@ -164,7 +180,7 @@ def custom_ticket_flows(self): Flow( ticket=self.ticket, flow_type=FlowType.PAUSE.value, - details=MySQLChecksumPauseParamBuilder(self.ticket).get_params(), + details=self.pause_flow_builder(self.ticket).get_params(), flow_alias=_("人工确认"), ), ) @@ -179,7 +195,7 @@ def custom_ticket_flows(self): ticket=self.ticket, flow_type=FlowType.INNER_FLOW.value, retry_type=retry_type, - details=MySQLDataRepairFlowParamBuilder(self.ticket).get_params(), + details=self.data_repair_flow_builder(self.ticket).get_params(), flow_alias=_("数据修复{}执行").format(is_auto_describe), ), ) diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_clone_rules.py b/dbm-ui/backend/ticket/builders/mysql/mysql_clone_rules.py index 16d9b7c8a7..d070f1e402 100644 --- a/dbm-ui/backend/ticket/builders/mysql/mysql_clone_rules.py +++ b/dbm-ui/backend/ticket/builders/mysql/mysql_clone_rules.py @@ -14,19 +14,36 @@ from rest_framework import serializers from backend import env -from backend.db_services.mysql.permission.constants import CloneType -from backend.db_services.mysql.permission.exceptions import CloneDataHasExpiredException +from backend.db_meta.enums import ClusterType +from backend.db_services.mysql.permission.constants import CloneClusterType, CloneType +from backend.db_services.mysql.permission.exceptions import CloneDataHasExpiredException, DBPermissionBaseException from backend.flow.engine.controller.mysql import MySQLController from backend.ticket import builders from backend.ticket.builders.common.base import SkipToRepresentationMixin from backend.ticket.builders.mysql.base import BaseMySQLTicketFlowBuilder -from backend.ticket.constants import FlowRetryType, FlowType, TicketType -from backend.ticket.models import Flow +from backend.ticket.constants import TicketType + + +class MySQLCloneRulesPluginSerializer(serializers.Serializer): + """这里是专用于插件权限克隆的serializer""" + + source = serializers.CharField(help_text=_("源IP")) + target = serializers.ListField(help_text=_("目标IP列表"), child=serializers.CharField()) + bk_cloud_id = serializers.IntegerField(help_text=_("云区域ID")) + user = serializers.CharField(help_text=_("用户名")) + target_instances = serializers.ListField(help_text=_("目标域名"), child=serializers.CharField()) class MySQLCloneRulesSerializer(SkipToRepresentationMixin, serializers.Serializer): - clone_uid = serializers.CharField(help_text=_("权限克隆数据缓存uid")) + clone_plugin_infos = serializers.ListSerializer( + help_text=_("插件的权限克隆信息"), child=MySQLCloneRulesPluginSerializer(), required=False + ) + + clone_uid = serializers.CharField(help_text=_("权限克隆数据缓存uid"), required=False) clone_type = serializers.ChoiceField(help_text=_("权限克隆类型"), choices=CloneType.get_choices()) + clone_cluster_type = serializers.ChoiceField( + help_text=_("克隆集群类型"), choices=ClusterType.get_choices(), required=False, default=CloneClusterType.TENDB + ) class MySQLCloneRulesFlowParamBuilder(builders.FlowParamBuilder): @@ -48,24 +65,28 @@ def post_callback(self): @builders.BuilderFactory.register(TicketType.MYSQL_CLIENT_CLONE_RULES) class MySQLClientCloneRulesFlowBuilder(BaseMySQLTicketFlowBuilder): serializer = MySQLCloneRulesSerializer - class_alias = _("客户端权限克隆执行") inner_flow_builder = MySQLCloneRulesFlowParamBuilder - inner_flow_name = class_alias + inner_flow_name = _("客户端权限克隆执行") @property def need_itsm(self): return False def patch_ticket_detail(self): - clone_uid = self.ticket.details["clone_uid"] - data = cache.get(clone_uid) - - if not data: - raise CloneDataHasExpiredException(_("权限克隆数据已过期,请重新提交权限克隆表单或excel文件")) - - self.ticket.update_details(clone_data=data) + if "clone_uid" in self.ticket.details: + clone_uid = self.ticket.details["clone_uid"] + data = cache.get(clone_uid) + if not data: + raise CloneDataHasExpiredException(_("权限克隆数据已过期,请重新提交权限克隆表单或excel文件")) + self.ticket.update_details(clone_data=data) + elif "clone_plugin_infos" in self.ticket.details: + # 权限克隆克隆插件专用 + clone_plugin_infos = self.ticket.details.pop("clone_plugin_infos") + self.ticket.update_details(clone_data=clone_plugin_infos) + else: + raise DBPermissionBaseException(_("权限克隆数据不合法!请检查")) @builders.BuilderFactory.register(TicketType.MYSQL_INSTANCE_CLONE_RULES) class MySQLInstanceCloneRulesFlowBuilder(MySQLClientCloneRulesFlowBuilder): - class_alias = _("DB实例权限克隆执行") + inner_flow_name = _("DB实例权限克隆执行") diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_fixpoint_rollback.py b/dbm-ui/backend/ticket/builders/mysql/mysql_fixpoint_rollback.py index b41f786086..8decd98b4e 100644 --- a/dbm-ui/backend/ticket/builders/mysql/mysql_fixpoint_rollback.py +++ b/dbm-ui/backend/ticket/builders/mysql/mysql_fixpoint_rollback.py @@ -17,7 +17,11 @@ from backend.flow.engine.controller.mysql import MySQLController from backend.ticket import builders from backend.ticket.builders.common.constants import MySQLBackupSource -from backend.ticket.builders.mysql.base import BaseMySQLTicketFlowBuilder, MySQLBaseOperateDetailSerializer +from backend.ticket.builders.mysql.base import ( + BaseMySQLTicketFlowBuilder, + DBTableField, + MySQLBaseOperateDetailSerializer, +) from backend.ticket.constants import FlowRetryType, FlowType, TicketType from backend.ticket.models import Flow from backend.utils.time import str2datetime @@ -34,30 +38,34 @@ class FixPointRollbackSerializer(serializers.Serializer): backupinfo = serializers.DictField( help_text=_("备份文件信息"), required=False, allow_null=True, allow_empty=True, default={} ) - databases = serializers.ListField(help_text=_("目标库列表"), child=serializers.CharField()) - databases_ignore = serializers.ListField(help_text=_("忽略库列表"), child=serializers.CharField()) - tables = serializers.ListField(help_text=_("目标table列表"), child=serializers.CharField()) - tables_ignore = serializers.ListField(help_text=_("忽略table列表"), child=serializers.CharField()) + databases = serializers.ListField(help_text=_("目标库列表"), child=DBTableField(db_field=True)) + databases_ignore = serializers.ListField(help_text=_("忽略库列表"), child=DBTableField(db_field=True)) + tables = serializers.ListField(help_text=_("目标table列表"), child=DBTableField()) + tables_ignore = serializers.ListField(help_text=_("忽略table列表"), child=DBTableField()) infos = serializers.ListSerializer(help_text=_("定点回档信息"), child=FixPointRollbackSerializer()) + @classmethod + def validate_rollback_info(cls, info, now): + # 校验rollback_time和backupinfo参数至少存在一个 + if not info["rollback_time"] and not info["backupinfo"]: + raise serializers.ValidationError(_("请保证rollback_time或backupinfo参数至少存在一个")) + + if not info["rollback_time"]: + return + + # 校验定点回档时间不能大于当前时间 + rollback_time = str2datetime(info["rollback_time"]) + if rollback_time > now: + raise serializers.ValidationError(_("定点时间{}不能晚于当前时间{}").format(rollback_time, now)) + def validate(self, attrs): # 校验集群是否可用 super().validate_cluster_can_access(attrs) now = datetime.datetime.now() for info in attrs["infos"]: - # 校验rollback_time和backupinfo参数至少存在一个 - if not info["rollback_time"] and not info["backupinfo"]: - raise serializers.ValidationError(_("请保证rollback_time或backupinfo参数至少存在一个")) - - if not info["rollback_time"]: - continue - - # 校验定点回档时间不能大于当前时间 - rollback_time = str2datetime(info["rollback_time"]) - if rollback_time > now: - raise serializers.ValidationError(_("定点时间{}不能晚于当前时间{}").format(rollback_time, now)) + self.validate_rollback_info(info, now) # TODO: 库表校验 @@ -79,3 +87,4 @@ class MysqlFixPointRollbackFlowBuilder(BaseMySQLTicketFlowBuilder): serializer = MySQLFixPointRollbackDetailSerializer inner_flow_builder = MySQLFixPointRollbackFlowParamBuilder inner_flow_name = _("定点回档执行") + retry_type = FlowRetryType.MANUAL_RETRY diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_flashback.py b/dbm-ui/backend/ticket/builders/mysql/mysql_flashback.py index 075622ff77..66907c5bc1 100644 --- a/dbm-ui/backend/ticket/builders/mysql/mysql_flashback.py +++ b/dbm-ui/backend/ticket/builders/mysql/mysql_flashback.py @@ -13,10 +13,15 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from backend.db_services.mysql.remote_service.handlers import RemoteServiceHandler from backend.flow.engine.controller.mysql import MySQLController from backend.ticket import builders from backend.ticket.builders.common.constants import MYSQL_BINLOG_ROLLBACK -from backend.ticket.builders.mysql.base import BaseMySQLTicketFlowBuilder, MySQLBaseOperateDetailSerializer +from backend.ticket.builders.mysql.base import ( + BaseMySQLTicketFlowBuilder, + DBTableField, + MySQLBaseOperateDetailSerializer, +) from backend.ticket.constants import FlowRetryType, FlowType, TicketType from backend.ticket.models import Flow from backend.utils.time import str2datetime @@ -27,21 +32,19 @@ class FlashbackSerializer(serializers.Serializer): cluster_id = serializers.IntegerField(help_text=_("集群ID")) start_time = serializers.CharField(help_text=_("开始时间")) end_time = serializers.CharField(help_text=_("结束时间")) - databases = serializers.ListField(help_text=_("目标库列表"), child=serializers.CharField()) - databases_ignore = serializers.ListField(help_text=_("忽略库列表"), child=serializers.CharField()) - tables = serializers.ListField(help_text=_("目标table列表"), child=serializers.CharField()) - tables_ignore = serializers.ListField(help_text=_("忽略table列表"), child=serializers.CharField()) + databases = serializers.ListField(help_text=_("目标库列表"), child=DBTableField(db_field=True)) + databases_ignore = serializers.ListField(help_text=_("忽略库列表"), child=DBTableField(db_field=True)) + tables = serializers.ListField(help_text=_("目标table列表"), child=DBTableField()) + tables_ignore = serializers.ListField(help_text=_("忽略table列表"), child=DBTableField()) mysqlbinlog_rollback = serializers.CharField( help_text=_("flashback工具地址"), default=MYSQL_BINLOG_ROLLBACK, required=False ) recored_file = serializers.CharField(help_text=_("记录文件"), required=False, default="") infos = serializers.ListSerializer(help_text=_("flashback信息"), child=FlashbackSerializer(), allow_empty=False) + force = serializers.BooleanField(help_text=_("是否强制执行"), required=False, default=False) - def validate(self, attrs): - # 校验集群是否可用,集群类型为高可用 - super(MySQLFlashbackDetailSerializer, self).validate_cluster_can_access(attrs) - + def validate_flash_time(self, attrs): # 校验start time和end time的合法性 for info in attrs["infos"]: start_time, end_time = str2datetime(info["start_time"]), str2datetime(info["start_time"]) @@ -51,6 +54,14 @@ def validate(self, attrs): _("flash的起止时间{}--{}不合法,请保证开始时间小于结束时间,并且二者不大于当前时间").format(start_time, end_time) ) + def validate(self, attrs): + # 校验集群是否可用,集群类型为高可用 + super(MySQLFlashbackDetailSerializer, self).validate_cluster_can_access(attrs) + # 校验闪回的时间 + self.validate_flash_time(attrs) + # 校验库表是否存在 + RemoteServiceHandler(bk_biz_id=self.context["bk_biz_id"]).check_flashback_database(attrs["infos"]) + return attrs @@ -66,3 +77,4 @@ class MysqlSingleDestroyFlowBuilder(BaseMySQLTicketFlowBuilder): serializer = MySQLFlashbackDetailSerializer inner_flow_builder = MySQLFlashbackFlowParamBuilder inner_flow_name = _("闪回执行") + retry_type = FlowRetryType.MANUAL_RETRY diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_ha_apply.py b/dbm-ui/backend/ticket/builders/mysql/mysql_ha_apply.py index fa51e6653b..fde3d2e4c0 100644 --- a/dbm-ui/backend/ticket/builders/mysql/mysql_ha_apply.py +++ b/dbm-ui/backend/ticket/builders/mysql/mysql_ha_apply.py @@ -44,8 +44,8 @@ class MysqlHAApplyDetailSerializer(MysqlSingleApplyDetailSerializer): ) def validate(self, attrs): + super().validate(attrs) # 验证输入的机器数量是否预期 - if attrs["ip_source"] == IpSource.RESOURCE_POOL: return attrs @@ -119,7 +119,7 @@ def post_callback(self): next_flow.save(update_fields=["details"]) -@BuilderFactory.register(TicketType.MYSQL_HA_APPLY) +@BuilderFactory.register(TicketType.MYSQL_HA_APPLY, is_apply=True, cluster_type=ClusterType.TenDBHA) class MysqlHAApplyFlowBuilder(BaseMySQLTicketFlowBuilder): serializer = MysqlHAApplyDetailSerializer inner_flow_builder = MysqlHAApplyFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_ha_backup.py b/dbm-ui/backend/ticket/builders/mysql/mysql_ha_backup.py index 254b4a8d64..b0f4b5f907 100644 --- a/dbm-ui/backend/ticket/builders/mysql/mysql_ha_backup.py +++ b/dbm-ui/backend/ticket/builders/mysql/mysql_ha_backup.py @@ -18,7 +18,11 @@ from backend.db_meta.models import Cluster from backend.flow.engine.controller.mysql import MySQLController from backend.ticket import builders -from backend.ticket.builders.mysql.base import BaseMySQLTicketFlowBuilder, MySQLBaseOperateDetailSerializer +from backend.ticket.builders.mysql.base import ( + BaseMySQLTicketFlowBuilder, + DBTableField, + MySQLBaseOperateDetailSerializer, +) from backend.ticket.constants import FlowRetryType, FlowType, TicketType from backend.ticket.models import Flow @@ -26,10 +30,10 @@ class MySQLHaBackupDetailSerializer(MySQLBaseOperateDetailSerializer): class BackupDataInfoSerializer(serializers.Serializer): cluster_id = serializers.IntegerField(help_text=_("集群ID")) - db_patterns = serializers.ListField(help_text=_("匹配DB列表"), child=serializers.CharField()) - ignore_dbs = serializers.ListField(help_text=_("忽略DB列表"), child=serializers.CharField()) - table_patterns = serializers.ListField(help_text=_("匹配Table列表"), child=serializers.CharField()) - ignore_tables = serializers.ListField(help_text=_("忽略Table列表"), child=serializers.CharField()) + db_patterns = serializers.ListField(help_text=_("匹配DB列表"), child=DBTableField(db_field=True)) + ignore_dbs = serializers.ListField(help_text=_("忽略DB列表"), child=DBTableField(db_field=True)) + table_patterns = serializers.ListField(help_text=_("匹配Table列表"), child=DBTableField()) + ignore_tables = serializers.ListField(help_text=_("忽略Table列表"), child=DBTableField()) # 废弃backup_on参数,暂时不需要传递 # backup_on = serializers.ChoiceField(choices=InstanceInnerRole.get_choices(), help_text=_("备份源")) @@ -56,3 +60,4 @@ class MySQLHaBackupFlowBuilder(BaseMySQLTicketFlowBuilder): serializer = MySQLHaBackupDetailSerializer inner_flow_builder = MySQLHaBackupFlowParamBuilder inner_flow_name = _("库表备份执行") + retry_type = FlowRetryType.MANUAL_RETRY diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_ha_clear.py b/dbm-ui/backend/ticket/builders/mysql/mysql_ha_clear.py index 92d3b4ff14..246cd658ac 100644 --- a/dbm-ui/backend/ticket/builders/mysql/mysql_ha_clear.py +++ b/dbm-ui/backend/ticket/builders/mysql/mysql_ha_clear.py @@ -19,7 +19,11 @@ from backend.flow.engine.controller.mysql import MySQLController from backend.ticket import builders from backend.ticket.builders.common.base import CommonValidate, SkipToRepresentationMixin -from backend.ticket.builders.mysql.base import BaseMySQLTicketFlowBuilder, MySQLBaseOperateDetailSerializer +from backend.ticket.builders.mysql.base import ( + BaseMySQLTicketFlowBuilder, + DBTableField, + MySQLBaseOperateDetailSerializer, +) from backend.ticket.constants import FlowRetryType, FlowType, TicketType from backend.ticket.models import Flow @@ -27,10 +31,10 @@ class MySQLHaClearDetailSerializer(MySQLBaseOperateDetailSerializer): class TruncateDataInfoSerializer(serializers.Serializer): cluster_id = serializers.IntegerField(help_text=_("集群ID")) - db_patterns = serializers.ListField(help_text=_("匹配DB列表"), child=serializers.CharField()) - ignore_dbs = serializers.ListField(help_text=_("忽略DB列表"), child=serializers.CharField()) - table_patterns = serializers.ListField(help_text=_("匹配Table列表"), child=serializers.CharField()) - ignore_tables = serializers.ListField(help_text=_("忽略Table列表"), child=serializers.CharField()) + db_patterns = serializers.ListField(help_text=_("匹配DB列表"), child=DBTableField(db_field=True)) + ignore_dbs = serializers.ListField(help_text=_("忽略DB列表"), child=DBTableField(db_field=True)) + table_patterns = serializers.ListField(help_text=_("匹配Table列表"), child=DBTableField()) + ignore_tables = serializers.ListField(help_text=_("忽略Table列表"), child=DBTableField()) truncate_data_type = serializers.ChoiceField(help_text=_("清档类型"), choices=TruncateDataTypeEnum.get_choices()) force = serializers.BooleanField(help_text=_("是否强制执行"), default=False) diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_ha_destroy.py b/dbm-ui/backend/ticket/builders/mysql/mysql_ha_destroy.py index f94161aa08..0a05430514 100644 --- a/dbm-ui/backend/ticket/builders/mysql/mysql_ha_destroy.py +++ b/dbm-ui/backend/ticket/builders/mysql/mysql_ha_destroy.py @@ -13,10 +13,11 @@ from django.utils.translation import ugettext_lazy as _ +from backend.db_meta.enums import ClusterPhase from backend.flow.engine.controller.mysql import MySQLController from backend.ticket import builders from backend.ticket.builders.mysql.base import BaseMySQLTicketFlowBuilder, MySQLClustersTakeDownDetailsSerializer -from backend.ticket.constants import TicketType +from backend.ticket.constants import FlowRetryType, TicketType class MysqlHADestroyDetailSerializer(MySQLClustersTakeDownDetailsSerializer): @@ -27,10 +28,11 @@ class MysqlHADestroyFlowParamBuilder(builders.FlowParamBuilder): controller = MySQLController.mysql_ha_destroy_scene -@builders.BuilderFactory.register(TicketType.MYSQL_HA_DESTROY) +@builders.BuilderFactory.register(TicketType.MYSQL_HA_DESTROY, phase=ClusterPhase.DESTROY) class MysqlHaDestroyFlowBuilder(BaseMySQLTicketFlowBuilder): """Mysql下架流程的构建基类""" serializer = MysqlHADestroyDetailSerializer inner_flow_builder = MysqlHADestroyFlowParamBuilder inner_flow_name = _("MySQL高可用销毁执行") + retry_type = FlowRetryType.MANUAL_RETRY diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_ha_disable.py b/dbm-ui/backend/ticket/builders/mysql/mysql_ha_disable.py index 8775ce28cc..8c279cd33d 100644 --- a/dbm-ui/backend/ticket/builders/mysql/mysql_ha_disable.py +++ b/dbm-ui/backend/ticket/builders/mysql/mysql_ha_disable.py @@ -13,10 +13,11 @@ from django.utils.translation import ugettext_lazy as _ +from backend.db_meta.enums import ClusterPhase from backend.flow.engine.controller.mysql import MySQLController from backend.ticket import builders from backend.ticket.builders.mysql.base import BaseMySQLTicketFlowBuilder, MySQLClustersTakeDownDetailsSerializer -from backend.ticket.constants import TicketType +from backend.ticket.constants import FlowRetryType, TicketType class MysqlHADisableDetailSerializer(MySQLClustersTakeDownDetailsSerializer): @@ -27,10 +28,11 @@ class MysqlHADisableFlowParamBuilder(builders.FlowParamBuilder): controller = MySQLController.mysql_ha_disable_scene -@builders.BuilderFactory.register(TicketType.MYSQL_HA_DISABLE) +@builders.BuilderFactory.register(TicketType.MYSQL_HA_DISABLE, phase=ClusterPhase.OFFLINE) class MysqlHaDisableFlowBuilder(BaseMySQLTicketFlowBuilder): """Mysql下架流程的构建基类""" serializer = MysqlHADisableDetailSerializer inner_flow_builder = MysqlHADisableFlowParamBuilder inner_flow_name = _("MySQL高可用禁用执行") + retry_type = FlowRetryType.MANUAL_RETRY diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_ha_enable.py b/dbm-ui/backend/ticket/builders/mysql/mysql_ha_enable.py index 9a650a8a55..d99d51115d 100644 --- a/dbm-ui/backend/ticket/builders/mysql/mysql_ha_enable.py +++ b/dbm-ui/backend/ticket/builders/mysql/mysql_ha_enable.py @@ -13,10 +13,11 @@ from django.utils.translation import ugettext_lazy as _ +from backend.db_meta.enums import ClusterPhase from backend.flow.engine.controller.mysql import MySQLController from backend.ticket import builders from backend.ticket.builders.mysql.base import BaseMySQLTicketFlowBuilder, MySQLClustersTakeDownDetailsSerializer -from backend.ticket.constants import TicketType +from backend.ticket.constants import FlowRetryType, TicketType class MysqlHAEnableDetailSerializer(MySQLClustersTakeDownDetailsSerializer): @@ -27,10 +28,11 @@ class MysqlHAEnableFlowParamBuilder(builders.FlowParamBuilder): controller = MySQLController.mysql_ha_enable_scene -@builders.BuilderFactory.register(TicketType.MYSQL_HA_ENABLE) +@builders.BuilderFactory.register(TicketType.MYSQL_HA_ENABLE, phase=ClusterPhase.ONLINE) class MysqlHaEnableFlowBuilder(BaseMySQLTicketFlowBuilder): """Mysql下架流程的构建基类""" serializer = MysqlHAEnableDetailSerializer inner_flow_builder = MysqlHAEnableFlowParamBuilder inner_flow_name = _("MySQL高可用启用执行") + retry_type = FlowRetryType.MANUAL_RETRY diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_ha_full_backup.py b/dbm-ui/backend/ticket/builders/mysql/mysql_ha_full_backup.py index cdd6556e13..384ff69850 100644 --- a/dbm-ui/backend/ticket/builders/mysql/mysql_ha_full_backup.py +++ b/dbm-ui/backend/ticket/builders/mysql/mysql_ha_full_backup.py @@ -45,3 +45,4 @@ class MySQLHaFullBackupFlowBuilder(BaseMySQLTicketFlowBuilder): serializer = MySQLHaFullBackupDetailSerializer inner_flow_builder = MySQLHaFullBackupFlowParamBuilder inner_flow_name = _("全库备份执行") + retry_type = FlowRetryType.MANUAL_RETRY diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_ha_rename.py b/dbm-ui/backend/ticket/builders/mysql/mysql_ha_rename.py index 5b718802ea..5eed29e1fe 100644 --- a/dbm-ui/backend/ticket/builders/mysql/mysql_ha_rename.py +++ b/dbm-ui/backend/ticket/builders/mysql/mysql_ha_rename.py @@ -16,11 +16,17 @@ from django.utils.translation import ugettext as _ from rest_framework import serializers +from backend.db_services.mysql.remote_service.handlers import RemoteServiceHandler from backend.flow.consts import SYSTEM_DBS from backend.flow.engine.controller.mysql import MySQLController +from backend.flow.utils.mysql.db_table_filter.tools import contain_glob from backend.ticket import builders from backend.ticket.builders.common.base import CommonValidate -from backend.ticket.builders.mysql.base import BaseMySQLTicketFlowBuilder, MySQLBaseOperateDetailSerializer +from backend.ticket.builders.mysql.base import ( + BaseMySQLTicketFlowBuilder, + DBTableField, + MySQLBaseOperateDetailSerializer, +) from backend.ticket.constants import FlowRetryType, FlowType, TicketType from backend.ticket.models import Flow @@ -28,11 +34,12 @@ class MySQLHaRenameSerializer(MySQLBaseOperateDetailSerializer): class RenameDatabaseInfoSerializer(serializers.Serializer): cluster_id = serializers.IntegerField(help_text=_("集群ID")) + # 源database无需校验,考虑将存量不合法的DB名重命名为合法DB名 from_database = serializers.CharField(help_text=_("源数据库名")) - to_database = serializers.CharField(help_text=_("目标数据库名")) - force = serializers.BooleanField(help_text=_("是否强制执行"), default=False) + to_database = DBTableField(help_text=_("目标数据库名"), db_field=True) infos = serializers.ListField(help_text=_("重命名数据库列表"), child=RenameDatabaseInfoSerializer()) + force = serializers.BooleanField(help_text=_("是否强制执行"), default=False) def validate(self, attrs): super().validate(attrs) @@ -45,10 +52,15 @@ def validate(self, attrs): cluster__db_name_map[db_info["cluster_id"]]["from_database"].append(db_info["from_database"]) cluster__db_name_map[db_info["cluster_id"]]["to_database"].append(db_info["to_database"]) - # 校验新db name是否合法 - is_valid, message = CommonValidate.validate_db_name(db_info["to_database"]) - if not is_valid: - raise serializers.ValidationError(message) + # 校验源dbname和新db那么不包含通配符 + if contain_glob(db_info["to_database"]) or contain_glob(db_info["from_database"]): + raise serializers.ValidationError(_("源DB名和新DB名不允许包含通配符")) + + # 校验源DB名是否存在于数据库 + database_info = RemoteServiceHandler(self.context["bk_biz_id"]).show_databases( + cluster_ids=list(cluster__db_name_map.keys()) + ) + cluster__databases = {info["cluster_id"]: info["databases"] for info in database_info} # 校验在同一个集群内,源DB名必须唯一,新DB名必须唯一,且源DB名不能出现在新DB名中 for cluster_id, name_info in cluster__db_name_map.items(): @@ -56,6 +68,9 @@ def validate(self, attrs): for db_name in from_database_list: if db_name in SYSTEM_DBS: raise serializers.ValidationError(_("系统内置数据库[{}],不允许重命名").format(db_name)) + if db_name not in cluster__databases[cluster_id]: + raise serializers.ValidationError(_("数据库[{}]不存在于集群{}中").format(db_name, cluster_id)) + to_database_list = name_info["to_database"] if len(set(from_database_list)) != len(from_database_list): raise serializers.ValidationError(_("请保证集群{}中源数据库名{}的名字唯一").format(cluster_id, from_database_list)) @@ -74,7 +89,8 @@ class MySQLHaRenameFlowParamBuilder(builders.FlowParamBuilder): controller = MySQLController.mysql_ha_rename_database_scene def format_ticket_data(self): - pass + for info in self.ticket_data["infos"]: + info["force"] = self.ticket_data["force"] @builders.BuilderFactory.register(TicketType.MYSQL_HA_RENAME_DATABASE) @@ -82,3 +98,4 @@ class MySQLHaRenameFlowBuilder(BaseMySQLTicketFlowBuilder): serializer = MySQLHaRenameSerializer inner_flow_builder = MySQLHaRenameFlowParamBuilder inner_flow_name = _("DB重命名执行") + retry_type = FlowRetryType.MANUAL_RETRY diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_import_sqlfile.py b/dbm-ui/backend/ticket/builders/mysql/mysql_import_sqlfile.py index d26a6461f0..8615e35aae 100644 --- a/dbm-ui/backend/ticket/builders/mysql/mysql_import_sqlfile.py +++ b/dbm-ui/backend/ticket/builders/mysql/mysql_import_sqlfile.py @@ -16,9 +16,9 @@ from rest_framework import serializers from backend import env +from backend.configuration.constants import DBType from backend.db_meta.models import Cluster from backend.db_services.mysql.sql_import.constants import SQLExecuteTicketMode -from backend.db_services.mysql.sql_import.dataclass import SemanticOperateMeta from backend.db_services.mysql.sql_import.handlers import SQLHandler from backend.flow.engine.bamboo.engine import BambooEngine from backend.flow.engine.controller.mysql import MySQLController @@ -51,46 +51,11 @@ def validate(self, attrs): class MysqlSqlImportItsmParamBuilder(builders.ItsmParamBuilder): """SQL导入审批单据参数""" - def format(self): - self.details.pop("execute_sql_files") - self.details.pop("execute_db_infos") - - self.details[_("字符集")] = self.details.pop("charset") - self.details[_("sql文件路径")] = self.details.pop("path") - self.details[_("集群ID")] = self.details.pop("cluster_ids") - self.details[_("执行模式")] = self.details.pop("ticket_mode") - self.details[_("sql导入模式")] = self.details.pop("import_mode") - self.details[_("模拟执行node_id")] = self.details.pop("semantic_node_id") - self.details[_("模拟执行root_id")] = self.details.pop("root_id") - self.details[_("业务ID")] = self.details.pop("bk_biz_id") - self.details[_("创建人")] = self.details.pop("created_by") - self.details[_("高危信息提示")] = self.details.pop("highrisk_warnings") - - execute_objects = self.details.pop("execute_objects") - for index, sql_obj in enumerate(execute_objects): - sql_obj[_("sql文件名")] = sql_obj.pop("sql_file") - sql_obj[_("目标变更db")] = sql_obj.pop("dbnames") - sql_obj[_("忽略db")] = sql_obj.pop("ignore_dbnames") - execute_objects[index] = json.dumps(sql_obj, ensure_ascii=False) - - sql_execute_info = "\n".join(execute_objects) - self.details[_("sql执行体信息")] = f"[\n{sql_execute_info}\n]" - - backup_objects = self.details.pop("backup", []) - for index, backup_obj in enumerate(backup_objects): - backup_obj[_("备份源")] = backup_obj.pop("backup_on") - backup_obj[_("备份匹配DB列表")] = backup_obj.pop("db_patterns") - backup_obj[_("备份匹配Table列表")] = backup_obj.pop("table_patterns") - backup_objects[index] = json.dumps(backup_obj, ensure_ascii=False) - - backup_info = "\n".join(backup_objects) - self.details[_("sql备份信息")] = f"[\n{backup_info}\n]" - def get_params(self): params = super().get_params() # 添加语义执行结果的链接 - root_id = self.details[_("模拟执行root_id")] + root_id = self.ticket.details["root_id"] semantic_url = f"{env.BK_SAAS_HOST}/database/{self.ticket.bk_biz_id}/mission-details/{root_id}/" params["dynamic_fields"].append({"name": _("模拟执行链接"), "type": "LINK", "value": semantic_url}) @@ -121,15 +86,16 @@ def format_ticket_data(self): class MysqlSqlImportFlowBuilder(BaseMySQLTicketFlowBuilder): serializer = MysqlSqlImportDetailSerializer - def patch_ticket_detail(self): + @classmethod + def patch_sqlimport_ticket_detail(cls, ticket, cluster_type): # 移除语义执行缓存 - root_id = self.ticket.details["root_id"] - handler = SQLHandler(bk_biz_id=self.ticket.bk_biz_id, context={"user": self.ticket.creator}) - handler.delete_user_semantic_tasks(semantic=SemanticOperateMeta(task_ids=[root_id])) + root_id = ticket.details["root_id"] + handler = SQLHandler(bk_biz_id=ticket.bk_biz_id, context={"user": ticket.creator}, cluster_type=cluster_type) + handler.delete_user_semantic_tasks(task_ids=[root_id]) # 为语义执行的FlowTree关联单据 flow_tree = FlowTree.objects.get(root_id=root_id) - flow_tree.uid = self.ticket.id + flow_tree.uid = ticket.id flow_tree.save() # 获取语义执行的details的输入数据 @@ -145,14 +111,17 @@ def patch_ticket_detail(self): # 补充集群信息和node_id cluster_ids = details["cluster_ids"] - semantic_node_id = handler._get_node_id_by_component(flow_tree, SemanticCheckComponent.code) + semantic_node_id = handler.get_node_id_by_component(flow_tree.tree, SemanticCheckComponent.code) details.update( semantic_node_id=semantic_node_id, clusters={cluster.id: cluster.to_dict() for cluster in Cluster.objects.filter(id__in=cluster_ids)}, ) - self.ticket.details.update(details) - self.ticket.save(update_fields=["details"]) + ticket.details.update(details) + ticket.save(update_fields=["details"]) + + def patch_ticket_detail(self): + self.patch_sqlimport_ticket_detail(ticket=self.ticket, cluster_type=DBType.MySQL) def init_ticket_flows(self): """ diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_master_fail_over.py b/dbm-ui/backend/ticket/builders/mysql/mysql_master_fail_over.py index b927a4ce38..bd3ebe6882 100644 --- a/dbm-ui/backend/ticket/builders/mysql/mysql_master_fail_over.py +++ b/dbm-ui/backend/ticket/builders/mysql/mysql_master_fail_over.py @@ -13,12 +13,12 @@ from backend.flow.engine.controller.mysql import MySQLController from backend.ticket import builders -from backend.ticket.builders.mysql.base import BaseMySQLTicketFlowBuilder +from backend.ticket.builders.mysql.base import BaseMySQLTicketFlowBuilder, MySQLBasePauseParamBuilder from backend.ticket.builders.mysql.mysql_master_slave_switch import ( MysqlMasterSlaveSwitchDetailSerializer, MysqlMasterSlaveSwitchParamBuilder, ) -from backend.ticket.constants import TicketType +from backend.ticket.constants import FlowRetryType, TicketType class MysqlMasterFailOverDetailSerializer(MysqlMasterSlaveSwitchDetailSerializer): @@ -38,6 +38,8 @@ class MysqlSingleDestroyFlowBuilder(BaseMySQLTicketFlowBuilder): serializer = MysqlMasterFailOverDetailSerializer inner_flow_builder = MysqlMasterFailOverParamBuilder inner_flow_name = _("主库故障切换执行") + retry_type = FlowRetryType.MANUAL_RETRY + pause_node_builder = MySQLBasePauseParamBuilder @property def need_manual_confirm(self): diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_master_slave_switch.py b/dbm-ui/backend/ticket/builders/mysql/mysql_master_slave_switch.py index ed5748d212..3df2ce7e05 100644 --- a/dbm-ui/backend/ticket/builders/mysql/mysql_master_slave_switch.py +++ b/dbm-ui/backend/ticket/builders/mysql/mysql_master_slave_switch.py @@ -17,7 +17,11 @@ from backend.flow.engine.controller.mysql import MySQLController from backend.ticket import builders from backend.ticket.builders.common.base import HostInfoSerializer -from backend.ticket.builders.mysql.base import BaseMySQLTicketFlowBuilder, MySQLBaseOperateDetailSerializer +from backend.ticket.builders.mysql.base import ( + BaseMySQLTicketFlowBuilder, + MySQLBaseOperateDetailSerializer, + MySQLBasePauseParamBuilder, +) from backend.ticket.constants import FlowRetryType, FlowType, TicketType from backend.ticket.models import Flow @@ -61,6 +65,8 @@ class MysqlMasterSlaveSwitchFlowBuilder(BaseMySQLTicketFlowBuilder): serializer = MysqlMasterSlaveSwitchDetailSerializer inner_flow_builder = MysqlMasterSlaveSwitchParamBuilder inner_flow_name = _("主从互换执行") + retry_type = FlowRetryType.MANUAL_RETRY + pause_node_builder = MySQLBasePauseParamBuilder @property def need_manual_confirm(self): diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_migrate_cluster.py b/dbm-ui/backend/ticket/builders/mysql/mysql_migrate_cluster.py index 01ef8045d5..5d0e1554ad 100644 --- a/dbm-ui/backend/ticket/builders/mysql/mysql_migrate_cluster.py +++ b/dbm-ui/backend/ticket/builders/mysql/mysql_migrate_cluster.py @@ -16,14 +16,9 @@ from backend.db_services.dbbase.constants import IpSource from backend.flow.engine.controller.mysql import MySQLController from backend.ticket import builders -from backend.ticket.builders.common.base import HostInfoSerializer -from backend.ticket.builders.mysql.base import ( - BaseMySQLTicketFlowBuilder, - MySQLBaseOperateDetailSerializer, - MySQLBaseOperateResourceParamBuilder, -) -from backend.ticket.constants import FlowRetryType, FlowType, TicketType -from backend.ticket.models import Flow +from backend.ticket.builders.common.base import BaseOperateResourceParamBuilder, HostInfoSerializer +from backend.ticket.builders.mysql.base import BaseMySQLTicketFlowBuilder, MySQLBaseOperateDetailSerializer +from backend.ticket.constants import FlowRetryType, TicketType class MysqlMigrateClusterDetailSerializer(MySQLBaseOperateDetailSerializer): @@ -67,7 +62,7 @@ def format_ticket_data(self): info["new_slave_ip"] = info["new_slave"]["ip"] -class MysqlMigrateClusterResourceParamBuilder(MySQLBaseOperateResourceParamBuilder): +class MysqlMigrateClusterResourceParamBuilder(BaseOperateResourceParamBuilder): def post_callback(self): next_flow = self.ticket.next_flow() ticket_data = next_flow.details["ticket_data"] @@ -78,13 +73,10 @@ def post_callback(self): next_flow.save(update_fields=["details"]) -@builders.BuilderFactory.register(TicketType.MYSQL_MIGRATE_CLUSTER) +@builders.BuilderFactory.register(TicketType.MYSQL_MIGRATE_CLUSTER, is_apply=True) class MysqlMigrateClusterFlowBuilder(BaseMySQLTicketFlowBuilder): serializer = MysqlMigrateClusterDetailSerializer inner_flow_builder = MysqlMigrateClusterParamBuilder inner_flow_name = _("克隆主从执行") resource_batch_apply_builder = MysqlMigrateClusterResourceParamBuilder - - @property - def need_itsm(self): - return False + retry_type = FlowRetryType.MANUAL_RETRY diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_partition.py b/dbm-ui/backend/ticket/builders/mysql/mysql_partition.py index 9aeea111ab..cfbab502ef 100644 --- a/dbm-ui/backend/ticket/builders/mysql/mysql_partition.py +++ b/dbm-ui/backend/ticket/builders/mysql/mysql_partition.py @@ -15,7 +15,11 @@ from backend.db_meta.models import Cluster from backend.flow.engine.controller.mysql import MySQLController from backend.ticket import builders -from backend.ticket.builders.mysql.base import BaseMySQLTicketFlowBuilder, MySQLBaseOperateDetailSerializer +from backend.ticket.builders.mysql.base import ( + BaseMySQLTicketFlowBuilder, + DBTableField, + MySQLBaseOperateDetailSerializer, +) from backend.ticket.constants import FlowRetryType, FlowType, TicketType from backend.ticket.models import Flow @@ -27,8 +31,8 @@ class InitPartitionSerializer(serializers.Serializer): class ExecuteObjectSerializer(serializers.Serializer): config_id = serializers.IntegerField(help_text=_("配置ID")) - dblike = serializers.CharField(help_text=_("库名匹配规则")) - tblike = serializers.CharField(help_text=_("表明匹配规则")) + dblike = DBTableField(help_text=_("库名匹配规则"), db_field=True) + tblike = DBTableField(help_text=_("表明匹配规则")) init_partition = serializers.ListField(help_text=_("初始化分区表"), child=InitPartitionSerializer()) add_partition = serializers.ListField(help_text=_("添加分区"), child=serializers.CharField()) drop_partition = serializers.ListField(help_text=_("删除分区"), child=serializers.CharField()) diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_proxy_add.py b/dbm-ui/backend/ticket/builders/mysql/mysql_proxy_add.py index ada838773b..4e10f9f03d 100644 --- a/dbm-ui/backend/ticket/builders/mysql/mysql_proxy_add.py +++ b/dbm-ui/backend/ticket/builders/mysql/mysql_proxy_add.py @@ -17,14 +17,9 @@ from backend.db_services.dbbase.constants import IpSource from backend.flow.engine.controller.mysql import MySQLController from backend.ticket import builders -from backend.ticket.builders.common.base import HostInfoSerializer -from backend.ticket.builders.mysql.base import ( - BaseMySQLTicketFlowBuilder, - MySQLBaseOperateDetailSerializer, - MySQLBaseOperateResourceParamBuilder, -) -from backend.ticket.constants import FlowRetryType, FlowType, TicketType -from backend.ticket.models import Flow +from backend.ticket.builders.common.base import BaseOperateResourceParamBuilder, HostInfoSerializer +from backend.ticket.builders.mysql.base import BaseMySQLTicketFlowBuilder, MySQLBaseOperateDetailSerializer +from backend.ticket.constants import TicketType class MysqlProxyAddDetailSerializer(MySQLBaseOperateDetailSerializer): @@ -66,7 +61,7 @@ def format_ticket_data(self): info["proxy_ip"] = info["new_proxy"] -class MysqlProxyAddResourceParamBuilder(MySQLBaseOperateResourceParamBuilder): +class MysqlProxyAddResourceParamBuilder(BaseOperateResourceParamBuilder): def post_callback(self): next_flow = self.ticket.next_flow() ticket_data = next_flow.details["ticket_data"] @@ -77,7 +72,7 @@ def post_callback(self): next_flow.save(update_fields=["details"]) -@builders.BuilderFactory.register(TicketType.MYSQL_PROXY_ADD) +@builders.BuilderFactory.register(TicketType.MYSQL_PROXY_ADD, is_apply=True) class MysqlProxyAddFlowBuilder(BaseMySQLTicketFlowBuilder): serializer = MysqlProxyAddDetailSerializer inner_flow_builder = MysqlProxyAddParamBuilder diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_proxy_switch.py b/dbm-ui/backend/ticket/builders/mysql/mysql_proxy_switch.py index 62fdfac3c0..757b33691f 100644 --- a/dbm-ui/backend/ticket/builders/mysql/mysql_proxy_switch.py +++ b/dbm-ui/backend/ticket/builders/mysql/mysql_proxy_switch.py @@ -17,14 +17,17 @@ from backend.db_services.dbbase.constants import IpSource from backend.flow.engine.controller.mysql import MySQLController from backend.ticket import builders -from backend.ticket.builders.common.base import HostInfoSerializer, InstanceInfoSerializer +from backend.ticket.builders.common.base import ( + BaseOperateResourceParamBuilder, + HostInfoSerializer, + InstanceInfoSerializer, +) from backend.ticket.builders.mysql.base import ( BaseMySQLTicketFlowBuilder, MySQLBaseOperateDetailSerializer, - MySQLBaseOperateResourceParamBuilder, + MySQLBasePauseParamBuilder, ) -from backend.ticket.constants import FlowRetryType, FlowType, TicketType -from backend.ticket.models import Flow +from backend.ticket.constants import FlowRetryType, TicketType class MysqlProxySwitchDetailSerializer(MySQLBaseOperateDetailSerializer): @@ -76,7 +79,7 @@ def format_ticket_data(self): info["target_proxy_ip"] = info["target_proxy"] -class MysqlProxySwitchResourceParamBuilder(MySQLBaseOperateResourceParamBuilder): +class MysqlProxySwitchResourceParamBuilder(BaseOperateResourceParamBuilder): def post_callback(self): next_flow = self.ticket.next_flow() ticket_data = next_flow.details["ticket_data"] @@ -87,12 +90,14 @@ def post_callback(self): next_flow.save(update_fields=["details"]) -@builders.BuilderFactory.register(TicketType.MYSQL_PROXY_SWITCH) +@builders.BuilderFactory.register(TicketType.MYSQL_PROXY_SWITCH, is_apply=True) class MysqlProxySwitchFlowBuilder(BaseMySQLTicketFlowBuilder): serializer = MysqlProxySwitchDetailSerializer inner_flow_builder = MysqlProxySwitchParamBuilder inner_flow_name = _("替换PROXY执行") resource_batch_apply_builder = MysqlProxySwitchResourceParamBuilder + retry_type = FlowRetryType.MANUAL_RETRY + pause_node_builder = MySQLBasePauseParamBuilder @property def need_manual_confirm(self): diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_restore_slave.py b/dbm-ui/backend/ticket/builders/mysql/mysql_restore_slave.py index 6bf287acaf..78cc4d9ccc 100644 --- a/dbm-ui/backend/ticket/builders/mysql/mysql_restore_slave.py +++ b/dbm-ui/backend/ticket/builders/mysql/mysql_restore_slave.py @@ -63,7 +63,7 @@ def format_ticket_data(self): info["old_slave_ip"], info["new_slave_ip"] = info["old_slave"]["ip"], info["new_slave"]["ip"] -@builders.BuilderFactory.register(TicketType.MYSQL_RESTORE_SLAVE) +@builders.BuilderFactory.register(TicketType.MYSQL_RESTORE_SLAVE, is_apply=True) class MysqlSingleDestroyFlowBuilder(BaseMySQLTicketFlowBuilder): serializer = MysqlRestoreSlaveDetailSerializer inner_flow_builder = MysqlRestoreSlaveParamBuilder diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_single_apply.py b/dbm-ui/backend/ticket/builders/mysql/mysql_single_apply.py index a09e024f0b..23ef8ecc85 100644 --- a/dbm-ui/backend/ticket/builders/mysql/mysql_single_apply.py +++ b/dbm-ui/backend/ticket/builders/mysql/mysql_single_apply.py @@ -26,11 +26,11 @@ from backend.exceptions import ValidationError from backend.flow.engine.controller.mysql import MySQLController from backend.ticket import builders -from backend.ticket.builders.common.base import SkipToRepresentationMixin +from backend.ticket.builders.common.base import CommonValidate +from backend.ticket.builders.common.constants import MAX_DOMAIN_LEN_LIMIT from backend.ticket.builders.mysql.base import BaseMySQLTicketFlowBuilder -from backend.ticket.constants import FlowType, TicketType +from backend.ticket.constants import TicketType from backend.ticket.exceptions import TicketParamsVerifyException -from backend.ticket.models import Flow class DomainSerializer(serializers.Serializer): @@ -81,14 +81,20 @@ def to_representation(self, instance): # TODO 缺少数据库版本和字符集,考虑封装 DBConfigHandler 来处理此类需求 return representation - def validate_domains(self, obj): - # 域名关键字不允许重复 + def validate(self, attrs): + # 校验域名是否重复 # TODO 校验存量的域名是否存在重复 - keys = [domain["key"] for domain in obj] + keys = [domain["key"] for domain in attrs["domains"]] duplicates = [item for item, count in collections.Counter(keys).items() if count > 1] if duplicates: raise ValidationError(_("不允许存在重复的域名关键字[{duplicates}]").format(duplicates=",".join(duplicates))) - return obj + + # 校验域名长度 + bk_biz_id = self.context["ticket_ctx"].db_module_id__biz_id_map.get(attrs["db_module_id"]) + db_app_abbr = self.context["ticket_ctx"].app_abbr_map.get(bk_biz_id, f"biz-{bk_biz_id}") + for key in keys: + CommonValidate.validate_mysql_domain(self.get_db_module_name(attrs), db_app_abbr, key) + return attrs def get_db_module_name(self, obj): db_module_id = obj["db_module_id"] @@ -138,7 +144,8 @@ def format_cluster_domains(self) -> List[Dict[str, str]]: @classmethod def insert_ip_into_apply_infos(cls, ticket_data, apply_infos: List[Dict]): - backend_nodes = ticket_data["nodes"]["backend"] + # 适配手动输入和资源池导入的角色类型 + backend_nodes = ticket_data["nodes"]["backend"] or ticket_data["nodes"]["single"] for index, apply_info in enumerate(apply_infos): apply_info["new_ip"] = backend_nodes[index] @@ -177,7 +184,7 @@ def post_callback(self): next_flow.save(update_fields=["details"]) -@builders.BuilderFactory.register(TicketType.MYSQL_SINGLE_APPLY) +@builders.BuilderFactory.register(TicketType.MYSQL_SINGLE_APPLY, is_apply=True, cluster_type=ClusterType.TenDBSingle) class MysqlSingleApplyFlowBuilder(BaseMySQLTicketFlowBuilder): serializer = MysqlSingleApplyDetailSerializer inner_flow_builder = MysqlSingleApplyFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_single_destroy.py b/dbm-ui/backend/ticket/builders/mysql/mysql_single_destroy.py index 48f9c02b1e..1c50c70761 100644 --- a/dbm-ui/backend/ticket/builders/mysql/mysql_single_destroy.py +++ b/dbm-ui/backend/ticket/builders/mysql/mysql_single_destroy.py @@ -11,6 +11,7 @@ from django.utils.translation import ugettext_lazy as _ +from backend.db_meta.enums import ClusterPhase from backend.flow.engine.controller.mysql import MySQLController from backend.ticket import builders from backend.ticket.builders.mysql.base import BaseMySQLTicketFlowBuilder, MySQLClustersTakeDownDetailsSerializer @@ -25,7 +26,7 @@ class MysqlSingleDestroyFlowParamBuilder(builders.FlowParamBuilder): controller = MySQLController.mysql_single_destroy_scene -@builders.BuilderFactory.register(TicketType.MYSQL_SINGLE_DESTROY) +@builders.BuilderFactory.register(TicketType.MYSQL_SINGLE_DESTROY, phase=ClusterPhase.DESTROY) class MysqlSingleDestroyFlowBuilder(BaseMySQLTicketFlowBuilder): serializer = MysqlSingleDestroyDetailSerializer inner_flow_builder = MysqlSingleDestroyFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_single_disable.py b/dbm-ui/backend/ticket/builders/mysql/mysql_single_disable.py index ceb2b4da15..26691b0120 100644 --- a/dbm-ui/backend/ticket/builders/mysql/mysql_single_disable.py +++ b/dbm-ui/backend/ticket/builders/mysql/mysql_single_disable.py @@ -11,6 +11,7 @@ from django.utils.translation import ugettext_lazy as _ +from backend.db_meta.enums import ClusterPhase from backend.flow.engine.controller.mysql import MySQLController from backend.ticket import builders from backend.ticket.builders.mysql.base import BaseMySQLTicketFlowBuilder, MySQLClustersTakeDownDetailsSerializer @@ -25,7 +26,7 @@ class MysqlSingleDisableFlowParamBuilder(builders.FlowParamBuilder): controller = MySQLController.mysql_single_disable_scene -@builders.BuilderFactory.register(TicketType.MYSQL_SINGLE_DISABLE) +@builders.BuilderFactory.register(TicketType.MYSQL_SINGLE_DISABLE, phase=ClusterPhase.OFFLINE) class MysqlSingleDisableFlowBuilder(BaseMySQLTicketFlowBuilder): serializer = MysqlSingleDisableDetailSerializer inner_flow_builder = MysqlSingleDisableFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_single_enable.py b/dbm-ui/backend/ticket/builders/mysql/mysql_single_enable.py index c2e67aa7ca..43e2d9f6e3 100644 --- a/dbm-ui/backend/ticket/builders/mysql/mysql_single_enable.py +++ b/dbm-ui/backend/ticket/builders/mysql/mysql_single_enable.py @@ -11,6 +11,7 @@ from django.utils.translation import ugettext_lazy as _ +from backend.db_meta.enums import ClusterPhase from backend.flow.engine.controller.mysql import MySQLController from backend.ticket import builders from backend.ticket.builders.mysql.base import BaseMySQLTicketFlowBuilder, MySQLClustersTakeDownDetailsSerializer @@ -25,7 +26,7 @@ class MysqlSingleEnableFlowParamBuilder(builders.FlowParamBuilder): controller = MySQLController.mysql_single_enable_scene -@builders.BuilderFactory.register(TicketType.MYSQL_SINGLE_ENABLE) +@builders.BuilderFactory.register(TicketType.MYSQL_SINGLE_ENABLE, phase=ClusterPhase.ONLINE) class MysqlSingleEnableFlowBuilder(BaseMySQLTicketFlowBuilder): serializer = MysqlSingleEnableDetailSerializer inner_flow_builder = MysqlSingleEnableFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/pulsar/pulsar_apply.py b/dbm-ui/backend/ticket/builders/pulsar/pulsar_apply.py index 3d4c0a5511..69ce38441e 100644 --- a/dbm-ui/backend/ticket/builders/pulsar/pulsar_apply.py +++ b/dbm-ui/backend/ticket/builders/pulsar/pulsar_apply.py @@ -14,6 +14,7 @@ from django.utils.translation import ugettext as _ from rest_framework import serializers +from backend.db_meta.enums import ClusterType from backend.db_services.dbbase.constants import IpSource from backend.flow.engine.controller.pulsar import PulsarController from backend.ticket import builders @@ -48,6 +49,9 @@ def validate(self, attrs): replication_num = attrs["replication_num"] ack_quorum = attrs["replication_num"] + # 判断域名 + super().validate_domain(ClusterType.Pulsar, attrs["cluster_name"], attrs["db_app_abbr"]) + # 判断bookkeeper节点是否至少为2台 bookkeeper_node_count = self.get_node_count(attrs, BigDataRole.Pulsar.BOOKKEEPER.value) if bookkeeper_node_count < constants.PULSAR_BOOKKEEPER_MIN: @@ -91,7 +95,7 @@ class PulsarApplyResourceParamBuilder(builders.ResourceApplyParamBuilder): pass -@builders.BuilderFactory.register(TicketType.PULSAR_APPLY) +@builders.BuilderFactory.register(TicketType.PULSAR_APPLY, is_apply=True, cluster_type=ClusterType.Pulsar) class PulsarApplyFlowBuilder(BasePulsarTicketFlowBuilder): serializer = PulsarApplyDetailSerializer inner_flow_builder = PulsarApplyFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/pulsar/pulsar_destroy.py b/dbm-ui/backend/ticket/builders/pulsar/pulsar_destroy.py index 8140659649..0099a38ac7 100644 --- a/dbm-ui/backend/ticket/builders/pulsar/pulsar_destroy.py +++ b/dbm-ui/backend/ticket/builders/pulsar/pulsar_destroy.py @@ -14,6 +14,7 @@ from django.utils.translation import ugettext as _ +from backend.db_meta.enums import ClusterPhase from backend.flow.engine.controller.pulsar import PulsarController from backend.ticket import builders from backend.ticket.builders.common.bigdata import BasePulsarTicketFlowBuilder, BigDataTakeDownDetailSerializer @@ -30,7 +31,7 @@ class PulsarDestroyFlowParamBuilder(builders.FlowParamBuilder): controller = PulsarController.pulsar_destroy_scene -@builders.BuilderFactory.register(TicketType.PULSAR_DESTROY) +@builders.BuilderFactory.register(TicketType.PULSAR_DESTROY, phase=ClusterPhase.DESTROY) class PulsarDestroyFlowBuilder(BasePulsarTicketFlowBuilder): serializer = PulsarDestroyDetailSerializer inner_flow_builder = PulsarDestroyFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/pulsar/pulsar_disable.py b/dbm-ui/backend/ticket/builders/pulsar/pulsar_disable.py index 6a973d5146..fb61626ebf 100644 --- a/dbm-ui/backend/ticket/builders/pulsar/pulsar_disable.py +++ b/dbm-ui/backend/ticket/builders/pulsar/pulsar_disable.py @@ -14,6 +14,7 @@ from django.utils.translation import ugettext as _ +from backend.db_meta.enums import ClusterPhase from backend.flow.engine.controller.pulsar import PulsarController from backend.ticket import builders from backend.ticket.builders.common.bigdata import BasePulsarTicketFlowBuilder, BigDataTakeDownDetailSerializer @@ -30,7 +31,7 @@ class PulsarDisableFlowParamBuilder(builders.FlowParamBuilder): controller = PulsarController.pulsar_disable_scene -@builders.BuilderFactory.register(TicketType.PULSAR_DISABLE) +@builders.BuilderFactory.register(TicketType.PULSAR_DISABLE, phase=ClusterPhase.OFFLINE) class PulsarDisableFlowBuilder(BasePulsarTicketFlowBuilder): serializer = PulsarDisableDetailSerializer inner_flow_builder = PulsarDisableFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/pulsar/pulsar_enable.py b/dbm-ui/backend/ticket/builders/pulsar/pulsar_enable.py index ce91ad9fd2..5c89e72739 100644 --- a/dbm-ui/backend/ticket/builders/pulsar/pulsar_enable.py +++ b/dbm-ui/backend/ticket/builders/pulsar/pulsar_enable.py @@ -14,6 +14,7 @@ from django.utils.translation import ugettext as _ +from backend.db_meta.enums import ClusterPhase from backend.flow.engine.controller.pulsar import PulsarController from backend.ticket import builders from backend.ticket.builders.common.bigdata import BasePulsarTicketFlowBuilder, BigDataTakeDownDetailSerializer @@ -30,7 +31,7 @@ class PulsarEnableFlowParamBuilder(builders.FlowParamBuilder): controller = PulsarController.pulsar_enable_scene -@builders.BuilderFactory.register(TicketType.PULSAR_ENABLE) +@builders.BuilderFactory.register(TicketType.PULSAR_ENABLE, phase=ClusterPhase.ONLINE) class PulsarEnableFlowBuilder(BasePulsarTicketFlowBuilder): serializer = PulsarEnableDetailSerializer inner_flow_builder = PulsarEnableFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/pulsar/pulsar_replace.py b/dbm-ui/backend/ticket/builders/pulsar/pulsar_replace.py index 9de40cd9a1..c864037f80 100644 --- a/dbm-ui/backend/ticket/builders/pulsar/pulsar_replace.py +++ b/dbm-ui/backend/ticket/builders/pulsar/pulsar_replace.py @@ -40,7 +40,7 @@ class PulsarReplaceResourceParamBuilder(BigDataReplaceResourceParamBuilder): pass -@builders.BuilderFactory.register(TicketType.PULSAR_REPLACE) +@builders.BuilderFactory.register(TicketType.PULSAR_REPLACE, is_apply=True) class PulsarReplaceFlowBuilder(BasePulsarTicketFlowBuilder): serializer = PulsarReplaceDetailSerializer inner_flow_builder = PulsarReplaceFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/pulsar/pulsar_scale_up.py b/dbm-ui/backend/ticket/builders/pulsar/pulsar_scale_up.py index 3c8b68d2e2..832f3cd8a8 100644 --- a/dbm-ui/backend/ticket/builders/pulsar/pulsar_scale_up.py +++ b/dbm-ui/backend/ticket/builders/pulsar/pulsar_scale_up.py @@ -40,7 +40,7 @@ class PulsarScaleUpResourceParamBuilder(BigDataScaleUpResourceParamBuilder): pass -@builders.BuilderFactory.register(TicketType.PULSAR_SCALE_UP) +@builders.BuilderFactory.register(TicketType.PULSAR_SCALE_UP, is_apply=True) class PulsarScaleUpFlowBuilder(BasePulsarTicketFlowBuilder): serializer = PulsarScaleUpDetailSerializer inner_flow_builder = PulsarScaleUpFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/redis/base.py b/dbm-ui/backend/ticket/builders/redis/base.py index bc43ab946a..10cb27f53c 100644 --- a/dbm-ui/backend/ticket/builders/redis/base.py +++ b/dbm-ui/backend/ticket/builders/redis/base.py @@ -16,11 +16,13 @@ from backend.configuration.constants import DBType from backend.core.storages.storage import get_storage +from backend.db_meta.enums import ClusterPhase from backend.db_meta.models import Cluster from backend.db_services.redis.constants import KeyDeleteType from backend.ticket import builders from backend.ticket.builders import TicketFlowBuilder from backend.ticket.builders.common.base import RedisTicketFlowBuilderPatchMixin +from backend.ticket.constants import CheckRepairFrequencyType, DataCheckRepairSettingType KEY_FILE_PREFIX = "/redis/keyfiles" @@ -44,7 +46,6 @@ def validate(self, attrs): # TODO: 暂时忽略单据互斥,后续可能改为执行互斥 try: cluster = Cluster.objects.get(id=attrs["cluster_id"]) - # 锁定检测 if Cluster.is_exclusive(cluster.id, self.context["ticket_type"]): raise serializers.ValidationError(_("集群【{}({})】锁定中,请等待").format(cluster.name, cluster.id)) @@ -100,3 +101,63 @@ class RedisBasePauseParamBuilder(builders.PauseParamBuilder): """人工确认""" pass + + +class DataCheckRepairSettingSerializer(serializers.Serializer): + type = serializers.ChoiceField(choices=DataCheckRepairSettingType.get_choices(), allow_null=True, allow_blank=True) + execution_frequency = serializers.ChoiceField( + choices=CheckRepairFrequencyType.get_choices(), allow_null=True, allow_blank=True + ) + + +class RedisUpdateApplyResourceParamBuilder(builders.ResourceApplyParamBuilder): + def post_callback(self): + next_flow = self.ticket.next_flow() + for info in next_flow.details["ticket_data"]["infos"]: + group_num = info["resource_spec"]["backend_group"]["count"] + shard_num = info["cluster_shard_num"] + + min_mem = min([g["master"]["bk_mem"] for g in info["backend_group"]]) + cluster_maxmemory = min_mem * group_num // shard_num + min_disk = min([g["master"]["bk_disk"] for g in info["backend_group"]]) + cluster_max_disk = min_disk * group_num // shard_num + + info.update( + # 分片大小, MB -> byte + maxmemory=int(int(cluster_maxmemory) * 1024 * 1024), + # 磁盘大小,单位是GB + max_disk=int(cluster_max_disk), + # 机器组数 + group_num=group_num, + # 分片数 + shard_num=shard_num, + ) + next_flow.save(update_fields=["details"]) + + +class ClusterValidateMixin(object): + """全局校验cluster状态""" + + @staticmethod + def check_cluster_phase(cluster_id): + + if not isinstance(cluster_id, int): + return cluster_id + + try: + cluster = Cluster.objects.get(pk=cluster_id) + if cluster.phase != ClusterPhase.ONLINE: + raise serializers.ValidationError(_("集群{}已被禁用,请先启用!").format(cluster.immute_domain)) + except Cluster.DoesNotExist: + raise serializers.ValidationError(_("集群{}不存在.").format(cluster_id)) + + return cluster_id + + def validate_cluster_id(self, cluster_id): + return self.check_cluster_phase(cluster_id) + + def validate_src_cluster(self, cluster_id): + return self.check_cluster_phase(cluster_id) + + def validate_dst_cluster(self, cluster_id): + return self.check_cluster_phase(cluster_id) diff --git a/dbm-ui/backend/ticket/builders/redis/plugin_create_clb.py b/dbm-ui/backend/ticket/builders/redis/plugin_create_clb.py index 370d61bc1c..d6ae9d4b03 100644 --- a/dbm-ui/backend/ticket/builders/redis/plugin_create_clb.py +++ b/dbm-ui/backend/ticket/builders/redis/plugin_create_clb.py @@ -40,3 +40,7 @@ class RedisPluginCreateCLBFlowBuilder(BaseRedisTicketFlowBuilder): serializer = RedisPluginCreateCLBDetailSerializer inner_flow_builder = RedisPluginCreateCLBFlowParamBuilder inner_flow_name = _("创建CLB") + + @property + def need_itsm(self): + return False diff --git a/dbm-ui/backend/ticket/builders/redis/plugin_create_polaris.py b/dbm-ui/backend/ticket/builders/redis/plugin_create_polaris.py index 518c1bc52c..5c858baf1c 100644 --- a/dbm-ui/backend/ticket/builders/redis/plugin_create_polaris.py +++ b/dbm-ui/backend/ticket/builders/redis/plugin_create_polaris.py @@ -40,3 +40,7 @@ class RedisPluginCreatePolarisFlowBuilder(BaseRedisTicketFlowBuilder): serializer = RedisPluginCreatePolarisDetailSerializer inner_flow_builder = RedisPluginCreatePolarisFlowParamBuilder inner_flow_name = _("创建Polaris") + + @property + def need_itsm(self): + return False diff --git a/dbm-ui/backend/ticket/builders/redis/plugin_delete_clb.py b/dbm-ui/backend/ticket/builders/redis/plugin_delete_clb.py index 55f9f0669d..37d438e7ea 100644 --- a/dbm-ui/backend/ticket/builders/redis/plugin_delete_clb.py +++ b/dbm-ui/backend/ticket/builders/redis/plugin_delete_clb.py @@ -40,3 +40,7 @@ class RedisPluginDeleteCLBFlowBuilder(BaseRedisTicketFlowBuilder): serializer = RedisPluginDeleteCLBDetailSerializer inner_flow_builder = RedisPluginDeleteCLBFlowParamBuilder inner_flow_name = _("删除CLB") + + @property + def need_itsm(self): + return False diff --git a/dbm-ui/backend/ticket/builders/redis/plugin_delete_polaris.py b/dbm-ui/backend/ticket/builders/redis/plugin_delete_polaris.py index 9adf68c58e..648735ed4d 100644 --- a/dbm-ui/backend/ticket/builders/redis/plugin_delete_polaris.py +++ b/dbm-ui/backend/ticket/builders/redis/plugin_delete_polaris.py @@ -40,3 +40,7 @@ class RedisPluginCreatePolarisFlowBuilder(BaseRedisTicketFlowBuilder): serializer = RedisPluginCreatePolarisDetailSerializer inner_flow_builder = RedisPluginDeletePolarisFlowParamBuilder inner_flow_name = _("删除Polaris") + + @property + def need_itsm(self): + return False diff --git a/dbm-ui/backend/ticket/builders/redis/redis_close.py b/dbm-ui/backend/ticket/builders/redis/redis_close.py index 341cf1b0a2..3403fa0637 100644 --- a/dbm-ui/backend/ticket/builders/redis/redis_close.py +++ b/dbm-ui/backend/ticket/builders/redis/redis_close.py @@ -10,6 +10,7 @@ """ from django.utils.translation import ugettext_lazy as _ +from backend.db_meta.enums import ClusterPhase from backend.flow.engine.controller.redis import RedisController from backend.ticket import builders from backend.ticket.builders.redis.base import BaseRedisTicketFlowBuilder, RedisSingleOpsBaseDetailSerializer @@ -38,7 +39,7 @@ def format_ticket_data(self): super().format_ticket_data() -@builders.BuilderFactory.register(TicketType.REDIS_CLOSE) +@builders.BuilderFactory.register(TicketType.REDIS_CLOSE, phase=ClusterPhase.OFFLINE) class RedisCloseFlowBuilder(BaseRedisTicketFlowBuilder): serializer = RedisCloseDetailSerializer inner_flow_builder = RedisCloseFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/redis/redis_cluster_apply.py b/dbm-ui/backend/ticket/builders/redis/redis_cluster_apply.py index dc6823c0f0..78d8747883 100644 --- a/dbm-ui/backend/ticket/builders/redis/redis_cluster_apply.py +++ b/dbm-ui/backend/ticket/builders/redis/redis_cluster_apply.py @@ -10,17 +10,17 @@ """ import humanize from django.utils.crypto import get_random_string -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import ugettext as _ from rest_framework import serializers from backend.db_meta.enums import ClusterType from backend.db_meta.models import Machine -from backend.db_meta.models.spec import ClusterDeployPlan from backend.db_services.dbbase.constants import IpSource +from backend.exceptions import ValidationError from backend.flow.engine.controller.redis import RedisController from backend.ticket import builders from backend.ticket.builders.common.base import CommonValidate -from backend.ticket.builders.common.constants import REDIS_PROXY_MIN, RedisRole +from backend.ticket.builders.common.constants import MAX_DOMAIN_LEN_LIMIT, REDIS_PROXY_MIN, RedisRole from backend.ticket.builders.redis.base import BaseRedisTicketFlowBuilder, RedisBasePauseParamBuilder from backend.ticket.constants import TicketType @@ -35,7 +35,7 @@ class RedisClusterApplyDetailSerializer(serializers.Serializer): city_name = serializers.SerializerMethodField(help_text=_("城市名")) cluster_type = serializers.CharField(help_text=_("集群类型")) db_version = serializers.CharField(help_text=_("版本号")) - cap_key = serializers.CharField(help_text=_("申请容量"), required=False) + cap_key = serializers.CharField(help_text=_("申请容量"), required=False, allow_blank=True, allow_null=True) cap_spec = serializers.SerializerMethodField(help_text=_("申请容量详情"), required=False) cluster_name = serializers.CharField(help_text=_("集群ID(英文数字及下划线)")) @@ -45,7 +45,7 @@ class RedisClusterApplyDetailSerializer(serializers.Serializer): nodes = serializers.JSONField(help_text=_("部署节点"), required=False) resource_spec = serializers.JSONField(help_text=_("proxy部署方案"), required=False) - resource_plan = serializers.JSONField(help_text=_("后台部署方案"), required=False) + cluster_shard_num = serializers.IntegerField(help_text=_("集群分片数"), required=False) def get_city_name(self, obj): city_code = obj["city_code"] @@ -205,6 +205,9 @@ def format_ticket_data(self): self.ticket_data["db_app_abbr"], ) + # 校验域名是否合法 + CommonValidate._validate_domain_valid(domain_name) + self.ticket_data.update( { "ip_source": self.ticket_data["ip_source"], @@ -232,7 +235,7 @@ def format_ticket_data(self): # 单位GB "max_disk": int(max_disk), # 机器组数 - "group_num": int(group_num), + "group_num": int(len(self.ticket_data["nodes"]["master"])), # 分片数 "shard_num": int(shard_num), } @@ -242,28 +245,28 @@ def format_ticket_data(self): class RedisApplyResourceParamBuilder(builders.ResourceApplyParamBuilder): def post_callback(self): next_flow = self.ticket.next_flow() - deploy_plan = ClusterDeployPlan.objects.get(id=self.ticket_data["resource_plan"]["resource_plan_id"]) - - min_mem = min([host["bk_mem"] for host in self.ticket_data["nodes"]["master"]]) - maxmemory = min_mem * deploy_plan.machine_pair_cnt // deploy_plan.shard_cnt + group_num = self.ticket_data["resource_spec"]["backend_group"]["count"] + shard_num = self.ticket_data["cluster_shard_num"] - min_disk = min([host["bk_disk"] for host in self.ticket_data["nodes"]["master"]]) - max_disk = min_disk * deploy_plan.machine_pair_cnt // deploy_plan.shard_cnt + min_mem = min([host["master"]["bk_mem"] for host in self.ticket_data["nodes"]["backend_group"]]) + cluster_maxmemory = min_mem * group_num // shard_num + min_disk = min([host["master"]["bk_disk"] for host in self.ticket_data["nodes"]["backend_group"]]) + cluster_max_disk = min_disk * group_num // shard_num next_flow.details["ticket_data"].update( - # 分片大小, GB -> byte - maxmemory=int(int(maxmemory) * 1024 * 1024 * 1024), - # 单位MB TODO: 需要转为GB - max_disk=int(max_disk), + # 分片大小, MB -> byte + maxmemory=int(int(cluster_maxmemory) * 1024 * 1024), + # 磁盘大小,单位是GB + max_disk=int(cluster_max_disk), # 机器组数 - group_num=deploy_plan.machine_pair_cnt, + group_num=group_num, # 分片数 - shard_num=deploy_plan.shard_cnt, + shard_num=shard_num, ) next_flow.save(update_fields=["details"]) -@builders.BuilderFactory.register(TicketType.REDIS_CLUSTER_APPLY) +@builders.BuilderFactory.register(TicketType.REDIS_CLUSTER_APPLY, is_apply=True) class RedisClusterApplyFlowBuilder(BaseRedisTicketFlowBuilder): serializer = RedisClusterApplyDetailSerializer inner_flow_builder = RedisClusterApplyFlowParamBuilder @@ -274,3 +277,7 @@ class RedisClusterApplyFlowBuilder(BaseRedisTicketFlowBuilder): @property def need_manual_confirm(self): return True + + @property + def need_itsm(self): + return False diff --git a/dbm-ui/backend/ticket/builders/redis/redis_destroy.py b/dbm-ui/backend/ticket/builders/redis/redis_destroy.py index fa68d1cd5f..af62cfbf8c 100644 --- a/dbm-ui/backend/ticket/builders/redis/redis_destroy.py +++ b/dbm-ui/backend/ticket/builders/redis/redis_destroy.py @@ -10,6 +10,7 @@ """ from django.utils.translation import ugettext_lazy as _ +from backend.db_meta.enums import ClusterPhase from backend.flow.engine.controller.redis import RedisController from backend.ticket import builders from backend.ticket.builders.redis.base import ( @@ -39,7 +40,7 @@ def format_ticket_data(self): super().format_ticket_data() -@builders.BuilderFactory.register(TicketType.REDIS_DESTROY) +@builders.BuilderFactory.register(TicketType.REDIS_DESTROY, phase=ClusterPhase.DESTROY) class RedisDestroyFlowBuilder(BaseRedisTicketFlowBuilder): serializer = RedisDestroyDetailSerializer inner_flow_builder = RedisDestroyFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/redis/redis_open.py b/dbm-ui/backend/ticket/builders/redis/redis_open.py index 75470ac8dd..d75830e3c7 100644 --- a/dbm-ui/backend/ticket/builders/redis/redis_open.py +++ b/dbm-ui/backend/ticket/builders/redis/redis_open.py @@ -10,6 +10,7 @@ """ from django.utils.translation import ugettext_lazy as _ +from backend.db_meta.enums import ClusterPhase from backend.flow.engine.controller.redis import RedisController from backend.ticket import builders from backend.ticket.builders.redis.base import BaseRedisTicketFlowBuilder, RedisSingleOpsBaseDetailSerializer @@ -35,7 +36,7 @@ def format_ticket_data(self): super().format_ticket_data() -@builders.BuilderFactory.register(TicketType.REDIS_OPEN) +@builders.BuilderFactory.register(TicketType.REDIS_OPEN, phase=ClusterPhase.ONLINE) class RedisOpenFlowBuilder(BaseRedisTicketFlowBuilder): serializer = RedisOpenDetailSerializer inner_flow_builder = RedisOpenFlowParamBuilder diff --git a/dbm-ui/backend/ticket/builders/redis/redis_toolbox_add_slave.py b/dbm-ui/backend/ticket/builders/redis/redis_toolbox_add_slave.py new file mode 100644 index 0000000000..c43a0ea63a --- /dev/null +++ b/dbm-ui/backend/ticket/builders/redis/redis_toolbox_add_slave.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 rest_framework import serializers + +from backend.db_meta.enums import InstanceRole, InstanceStatus +from backend.db_meta.models import Cluster, StorageInstanceTuple +from backend.db_services.dbbase.constants import IpSource +from backend.flow.engine.controller.redis import RedisController +from backend.ticket import builders +from backend.ticket.builders.redis.base import BaseRedisTicketFlowBuilder, ClusterValidateMixin +from backend.ticket.constants import TicketType + + +class RedisAddSlaveDetailSerializer(serializers.Serializer): + """新建从库""" + + class InfoSerializer(ClusterValidateMixin, serializers.Serializer): + cluster_id = serializers.IntegerField(help_text=_("集群ID")) + bk_cloud_id = serializers.IntegerField(help_text=_("云区域ID")) + pairs = serializers.ListField(help_text=_("主从切换对"), child=serializers.DictField()) + + def validate(self, attr): + """业务逻辑校验""" + cluster = Cluster.objects.get(id=attr.get("cluster_id")) + for pair in attr["pairs"]: + redis_master = pair["redis_master"]["bk_host_id"] + if StorageInstanceTuple.objects.filter( + ejector__machine__bk_host_id=redis_master, + receiver__instance_role=InstanceRole.REDIS_SLAVE, + receiver__status=InstanceStatus.RUNNING, + ).exists(): + raise serializers.ValidationError(_("集群{}已存在可用的从库主机,不允许一主多从").format(cluster.immute_domain)) + return attr + + ip_source = serializers.ChoiceField(help_text=_("主机来源"), choices=IpSource.get_choices()) + infos = serializers.ListField(help_text=_("批量操作参数列表"), child=InfoSerializer()) + + +class RedisAddSlaveParamBuilder(builders.FlowParamBuilder): + controller = RedisController.redis_cluster_add_slave + + def format_ticket_data(self): + super().format_ticket_data() + + +class RedisAddSlaveResourceParamBuilder(builders.ResourceApplyParamBuilder): + def post_callback(self): + next_flow = self.ticket.next_flow() + ticket_data = next_flow.details["ticket_data"] + + for info_index, info in enumerate(ticket_data["infos"]): + for pair in info["pairs"]: + pair["redis_slave"] = info.pop(pair["redis_master"]["ip"], []) + + next_flow.save(update_fields=["details"]) + super().post_callback() + + +@builders.BuilderFactory.register(TicketType.REDIS_CLUSTER_ADD_SLAVE, is_apply=True) +class RedisAddSlaveFlowBuilder(BaseRedisTicketFlowBuilder): + serializer = RedisAddSlaveDetailSerializer + inner_flow_builder = RedisAddSlaveParamBuilder + inner_flow_name = _("Redis 新建从库") + resource_batch_apply_builder = RedisAddSlaveResourceParamBuilder + + @property + def need_itsm(self): + return False + + def patch_ticket_detail(self): + """redis_master -> backend_group""" + + super().patch_ticket_detail() + + for info in self.ticket.details["infos"]: + info["resource_spec"] = {} + for pair in info["pairs"]: + info["resource_spec"][pair["redis_master"]["ip"]] = pair["redis_slave"] + + self.ticket.save(update_fields=["details"]) diff --git a/dbm-ui/backend/ticket/builders/redis/redis_toolbox_autofix.py b/dbm-ui/backend/ticket/builders/redis/redis_toolbox_autofix.py new file mode 100644 index 0000000000..4642fb0091 --- /dev/null +++ b/dbm-ui/backend/ticket/builders/redis/redis_toolbox_autofix.py @@ -0,0 +1,104 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 rest_framework import serializers + +from backend.db_services.dbbase.constants import IpSource +from backend.flow.engine.controller.redis import RedisController +from backend.ticket import builders +from backend.ticket.builders.redis.base import BaseRedisTicketFlowBuilder +from backend.ticket.constants import AffinityEnum, TicketType + + +class RedisClusterAutofixDetailSerializer(serializers.Serializer): + """故障自愈""" + + class InfoSerializer(serializers.Serializer): + class HostInfoSerializer(serializers.Serializer): + ip = serializers.IPAddressField() + spec_id = serializers.IntegerField() + + cluster_id = serializers.IntegerField(help_text=_("集群ID")) + bk_cloud_id = serializers.IntegerField(help_text=_("云区域ID")) + proxy = serializers.ListField(help_text=_("proxy列表"), child=HostInfoSerializer(), required=False) + redis_slave = serializers.ListField(help_text=_("slave列表"), child=HostInfoSerializer(), required=False) + + ip_source = serializers.ChoiceField(help_text=_("主机来源"), choices=IpSource.get_choices()) + infos = serializers.ListField(help_text=_("批量操作参数列表"), child=InfoSerializer()) + + +class RedisClusterAutofixParamBuilder(builders.FlowParamBuilder): + controller = RedisController.redis_cluster_auotfix_scene + + def format_ticket_data(self): + super().format_ticket_data() + + +class RedisClusterAutofixResourceParamBuilder(builders.ResourceApplyParamBuilder): + def post_callback(self): + nodes = self.ticket_data.pop("nodes", []) + + next_flow = self.ticket.next_flow() + ticket_data = next_flow.details["ticket_data"] + + for info_index, info in enumerate(self.ticket_data["infos"]): + for role in ["redis_master", "proxy", "redis_slave"]: + role_hosts = info.get(role) + if not role_hosts: + continue + + role_group = "backend_group" if role == "redis_master" else role + for role_host_index, role_host in enumerate(role_hosts): + role_host["target"] = nodes.get(f"{info_index}_{role_group}")[role_host_index] + + # 保留下个节点更完整的resource_spec + info["resource_spec"] = ticket_data["infos"][info_index]["resource_spec"] + info["resource_spec"].pop("backend_group", None) + + ticket_data["infos"][info_index] = info + + next_flow.save(update_fields=["details"]) + super().post_callback() + + +@builders.BuilderFactory.register(TicketType.REDIS_CLUSTER_AUTOFIX, is_apply=True) +class RedisClusterAutofixFlowBuilder(BaseRedisTicketFlowBuilder): + serializer = RedisClusterAutofixDetailSerializer + inner_flow_builder = RedisClusterAutofixParamBuilder + inner_flow_name = _("故障自愈") + resource_batch_apply_builder = RedisClusterAutofixResourceParamBuilder + + @property + def need_itsm(self): + return False + + def patch_ticket_detail(self): + """redis_master -> backend_group""" + + super().patch_ticket_detail() + + resource_spec = {} + for info in self.ticket.details["infos"]: + for role in ["redis_master", "proxy", "redis_slave"]: + role_hosts = info.get(role) + if not role_hosts: + continue + role_group = "backend_group" if role == "redis_master" else role + role_group_affinity = AffinityEnum.CROS_SUBZONE if role_group == "backend_group" else AffinityEnum.NONE + resource_spec[role_group] = { + "spec_id": info[role][0]["spec_id"], + "count": len(role_hosts), + "affinity": role_group_affinity.value, + } + info["resource_spec"] = resource_spec + + self.ticket.save(update_fields=["details"]) diff --git a/dbm-ui/backend/ticket/builders/redis/redis_toolbox_cut_off.py b/dbm-ui/backend/ticket/builders/redis/redis_toolbox_cut_off.py new file mode 100644 index 0000000000..c5462cd757 --- /dev/null +++ b/dbm-ui/backend/ticket/builders/redis/redis_toolbox_cut_off.py @@ -0,0 +1,105 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 rest_framework import serializers + +from backend.db_services.dbbase.constants import IpSource +from backend.flow.engine.controller.redis import RedisController +from backend.ticket import builders +from backend.ticket.builders.redis.base import BaseRedisTicketFlowBuilder, ClusterValidateMixin +from backend.ticket.constants import AffinityEnum, TicketType + + +class RedisClusterCutOffDetailSerializer(ClusterValidateMixin, serializers.Serializer): + """整机替换""" + + class InfoSerializer(serializers.Serializer): + class HostInfoSerializer(serializers.Serializer): + ip = serializers.IPAddressField() + spec_id = serializers.IntegerField() + + cluster_id = serializers.IntegerField(help_text=_("集群ID")) + bk_cloud_id = serializers.IntegerField(help_text=_("云区域ID")) + proxy = serializers.ListField(help_text=_("proxy列表"), child=HostInfoSerializer(), required=False) + redis_master = serializers.ListField(help_text=_("master列表"), child=HostInfoSerializer(), required=False) + redis_slave = serializers.ListField(help_text=_("slave列表"), child=HostInfoSerializer(), required=False) + + ip_source = serializers.ChoiceField(help_text=_("主机来源"), choices=IpSource.get_choices()) + infos = serializers.ListField(help_text=_("批量操作参数列表"), child=InfoSerializer()) + + +class RedisClusterCutOffParamBuilder(builders.FlowParamBuilder): + controller = RedisController.redis_cluster_cutoff_scene + + def format_ticket_data(self): + super().format_ticket_data() + + +class RedisClusterCutOffResourceParamBuilder(builders.ResourceApplyParamBuilder): + def post_callback(self): + nodes = self.ticket_data.pop("nodes", []) + + next_flow = self.ticket.next_flow() + ticket_data = next_flow.details["ticket_data"] + + for info_index, info in enumerate(self.ticket_data["infos"]): + for role in ["redis_master", "proxy", "redis_slave"]: + role_hosts = info.get(role) + if not role_hosts: + continue + + role_group = "backend_group" if role == "redis_master" else role + for role_host_index, role_host in enumerate(role_hosts): + role_host["target"] = nodes.get(f"{info_index}_{role_group}")[role_host_index] + + # 保留下个节点更完整的resource_spec + info["resource_spec"] = ticket_data["infos"][info_index]["resource_spec"] + info["resource_spec"].pop("backend_group", None) + + ticket_data["infos"][info_index] = info + + next_flow.save(update_fields=["details"]) + super().post_callback() + + +@builders.BuilderFactory.register(TicketType.REDIS_CLUSTER_CUTOFF, is_apply=True) +class RedisClusterCutOffFlowBuilder(BaseRedisTicketFlowBuilder): + serializer = RedisClusterCutOffDetailSerializer + inner_flow_builder = RedisClusterCutOffParamBuilder + inner_flow_name = _("整机替换") + resource_batch_apply_builder = RedisClusterCutOffResourceParamBuilder + + @property + def need_itsm(self): + return False + + def patch_ticket_detail(self): + """redis_master -> backend_group""" + + super().patch_ticket_detail() + + resource_spec = {} + for info in self.ticket.details["infos"]: + for role in ["redis_master", "proxy", "redis_slave"]: + role_hosts = info.get(role) + if not role_hosts: + continue + role_group = "backend_group" if role == "redis_master" else role + role_group_affinity = AffinityEnum.CROS_SUBZONE if role_group == "backend_group" else AffinityEnum.NONE + resource_spec[role_group] = { + "spec_id": info[role][0]["spec_id"], + "count": len(role_hosts), + "affinity": role_group_affinity.value, + } + info["resource_spec"] = resource_spec + + self.ticket.save(update_fields=["details"]) diff --git a/dbm-ui/backend/ticket/builders/redis/redis_toolbox_data_check_repair.py b/dbm-ui/backend/ticket/builders/redis/redis_toolbox_data_check_repair.py new file mode 100644 index 0000000000..d8f8140e14 --- /dev/null +++ b/dbm-ui/backend/ticket/builders/redis/redis_toolbox_data_check_repair.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 rest_framework import serializers + +from backend.db_services.redis.redis_dts.enums import DtsDataRepairMode, ExecuteMode +from backend.flow.engine.controller.redis import RedisController +from backend.ticket import builders +from backend.ticket.builders.redis.base import BaseRedisTicketFlowBuilder +from backend.ticket.constants import TicketType + + +class RedisDataCheckRepairDetailSerializer(serializers.Serializer): + """数据校验与修复""" + + class InfoSerializer(serializers.Serializer): + bill_id = serializers.IntegerField(help_text=_("任务ID")) + src_cluster = serializers.CharField(help_text=_("源集群访问入口")) + dst_cluster = serializers.CharField(help_text=_("目标集群访问入口")) + src_instances = serializers.ListField( + help_text=_("源实例列表"), allow_empty=False, child=serializers.CharField(help_text=_("IP:PORT")) + ) + key_white_regex = serializers.CharField(help_text=_("包含key"), allow_blank=True) + key_black_regex = serializers.CharField(help_text=_("排除key"), allow_blank=True) + + execute_mode = serializers.ChoiceField(help_text=_("执行模式"), choices=ExecuteMode.get_choices()) + specified_execution_time = serializers.CharField( + help_text=_("执行模式为定时执行时,需要设置执行时间"), required=False, allow_blank=True + ) + + keep_check_and_repair = serializers.BooleanField(help_text=_("是否保持校验")) + check_stop_time = serializers.CharField(help_text=_("校验终止时间,当不保持校验时,需要设置该时间"), required=False, allow_blank=True) + + data_repair_enabled = serializers.BooleanField(help_text=_("是否修复数据")) + repair_mode = serializers.ChoiceField(help_text=_("数据修复模式"), choices=DtsDataRepairMode.get_choices()) + + infos = serializers.ListField(help_text=_("批量校验与修复列表"), child=InfoSerializer(), allow_empty=False) + + def validate(self, attr): + """进一步校验info""" + key_white_regex_cnt = sum(map(lambda info: 1 if len(info["key_white_regex"]) else 0, attr["infos"])) + key_black_regex_cnt = sum(map(lambda info: 1 if len(info["key_black_regex"]) else 0, attr["infos"])) + if (key_white_regex_cnt + key_black_regex_cnt) < len(attr["infos"]): + raise serializers.ValidationError(_("请补齐缺少正则配置的行")) + + return attr + + +class RedisDataCheckRepairParamBuilder(builders.FlowParamBuilder): + controller = RedisController.redis_cluster_data_check_repair + + def format_ticket_data(self): + super().format_ticket_data() + + +class RedisDataCheckRepairResourceParamBuilder(builders.ResourceApplyParamBuilder): + def post_callback(self): + super().post_callback() + + +@builders.BuilderFactory.register(TicketType.REDIS_DATACOPY_CHECK_REPAIR) +class RedisDataCheckRepairFlowBuilder(BaseRedisTicketFlowBuilder): + serializer = RedisDataCheckRepairDetailSerializer + inner_flow_builder = RedisDataCheckRepairParamBuilder + inner_flow_name = _("Redis 数据校验与修复") + resource_batch_apply_builder = RedisDataCheckRepairResourceParamBuilder + + @property + def need_itsm(self): + return False diff --git a/dbm-ui/backend/ticket/builders/redis/redis_toolbox_data_copy.py b/dbm-ui/backend/ticket/builders/redis/redis_toolbox_data_copy.py new file mode 100644 index 0000000000..4e9a21c22e --- /dev/null +++ b/dbm-ui/backend/ticket/builders/redis/redis_toolbox_data_copy.py @@ -0,0 +1,138 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 rest_framework import serializers + +from backend.db_meta.models import Cluster +from backend.db_services.redis.redis_dts.enums import DtsCopyType +from backend.flow.engine.controller.redis import RedisController +from backend.ticket import builders +from backend.ticket.builders.redis.base import ( + BaseRedisTicketFlowBuilder, + ClusterValidateMixin, + DataCheckRepairSettingSerializer, +) +from backend.ticket.constants import RemindFrequencyType, SyncDisconnectSettingType, TicketType, WriteModeType + + +class RedisDataCopyDetailSerializer(serializers.Serializer): + """数据复制""" + + class SyncDisconnectSettingSerializer(serializers.Serializer): + type = serializers.ChoiceField(choices=SyncDisconnectSettingType.get_choices()) + reminder_frequency = serializers.ChoiceField( + choices=RemindFrequencyType.get_choices(), required=False, allow_blank=True + ) + + class BaseInfoSerializer(serializers.Serializer): + key_white_regex = serializers.CharField(help_text=_("包含key"), allow_blank=True) + key_black_regex = serializers.CharField(help_text=_("排除key"), allow_blank=True) + + class RedisDataCopyInnerInfoSerializer(BaseInfoSerializer): + """业务内""" + + src_cluster = serializers.IntegerField(help_text=_("集群ID")) + dst_cluster = serializers.IntegerField(help_text=_("集群ID")) + + class RedisDataCopyInnerTo3rdInfoSerializer(BaseInfoSerializer): + """业务到第三方""" + + src_cluster = serializers.IntegerField(help_text=_("集群ID")) + dst_cluster = serializers.CharField(help_text=_("集群IP端口")) + dst_cluster_password = serializers.CharField(help_text=_("集群访问密码")) + + class RedisDataCopy3rdToInnerInfoSerializer(BaseInfoSerializer): + """第三方到业务""" + + src_cluster = serializers.CharField(help_text=_("集群IP端口")) + src_cluster_password = serializers.CharField(help_text=_("集群访问密码")) + dst_cluster = serializers.IntegerField(help_text=_("集群ID")) + + class RedisDataCopyBetweenInnerInfoSerializer(BaseInfoSerializer): + """业务之间""" + + src_cluster = serializers.IntegerField(help_text=_("集群ID")) + dst_cluster = serializers.IntegerField(help_text=_("集群ID")) + + # 区分复制类型 + INFO_SERIALIZER_MAP = { + DtsCopyType.ONE_APP_DIFF_CLUSTER: RedisDataCopyInnerInfoSerializer, + DtsCopyType.DIFF_APP_DIFF_CLUSTER: RedisDataCopyBetweenInnerInfoSerializer, + DtsCopyType.COPY_TO_OTHER_SYSTEM: RedisDataCopyInnerTo3rdInfoSerializer, + DtsCopyType.USER_BUILT_TO_DBM: RedisDataCopy3rdToInnerInfoSerializer, + } + + dts_copy_type = serializers.ChoiceField(choices=DtsCopyType.get_choices()) + write_mode = serializers.ChoiceField(choices=WriteModeType.get_choices()) + sync_disconnect_setting = SyncDisconnectSettingSerializer() + data_check_repair_setting = DataCheckRepairSettingSerializer(required=False) + + infos = serializers.ListField(help_text=_("批量数据复制列表"), child=serializers.DictField(), allow_empty=False) + + def validate(self, attr): + """根据复制类型校验info""" + dts_copy_type = attr.get("dts_copy_type") + infos = attr.get("infos") + + info_serializer = self.INFO_SERIALIZER_MAP.get(dts_copy_type) + info_serializer(data=attr["infos"], many=True).is_valid(raise_exception=True) + + src_cluster_set = set() + for info in infos: + src_cluster = info.get("src_cluster") + dst_cluster = info.get("dst_cluster") + + ClusterValidateMixin.check_cluster_phase(src_cluster) + ClusterValidateMixin.check_cluster_phase(dst_cluster) + + if src_cluster == dst_cluster: + raise serializers.ValidationError(_("仅支持两个不同集群间的复制: {}").format(src_cluster)) + + if src_cluster in src_cluster_set: + raise serializers.ValidationError(_("源集群不能重复: {}").format(src_cluster)) + + if info["key_white_regex"] == "" and info["key_black_regex"] == "": + raise serializers.ValidationError(_("请补齐缺少正则配置的行")) + + if dts_copy_type in [DtsCopyType.ONE_APP_DIFF_CLUSTER, DtsCopyType.DIFF_APP_DIFF_CLUSTER]: + if not Cluster.objects.filter(id=src_cluster).exists(): + raise serializers.ValidationError(_("源集群{}不存在,请确认.").format(src_cluster)) + if not Cluster.objects.filter(id=dst_cluster).exists(): + raise serializers.ValidationError(_("目标集群{}不存在,请确认.").format(src_cluster)) + + src_cluster_set.add(src_cluster) + + return attr + + +class RedisDataCopyParamBuilder(builders.FlowParamBuilder): + controller = RedisController.redis_cluster_data_copy + + def format_ticket_data(self): + super().format_ticket_data() + + +class RedisDataCopyResourceParamBuilder(builders.ResourceApplyParamBuilder): + def post_callback(self): + super().post_callback() + + +@builders.BuilderFactory.register(TicketType.REDIS_CLUSTER_DATA_COPY) +class RedisDataCopyFlowBuilder(BaseRedisTicketFlowBuilder): + serializer = RedisDataCopyDetailSerializer + inner_flow_builder = RedisDataCopyParamBuilder + inner_flow_name = _("Redis 数据复制") + resource_batch_apply_builder = RedisDataCopyResourceParamBuilder + + @property + def need_itsm(self): + return False diff --git a/dbm-ui/backend/ticket/builders/redis/redis_toolbox_datastruct_task_delete.py b/dbm-ui/backend/ticket/builders/redis/redis_toolbox_datastruct_task_delete.py new file mode 100644 index 0000000000..e28c983dfe --- /dev/null +++ b/dbm-ui/backend/ticket/builders/redis/redis_toolbox_datastruct_task_delete.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 rest_framework import serializers + +from backend.db_meta.enums import DestroyedStatus +from backend.db_meta.models import Cluster +from backend.db_services.redis.rollback.models import TbTendisRollbackTasks +from backend.flow.engine.controller.redis import RedisController +from backend.ticket import builders +from backend.ticket.builders.redis.base import BaseRedisTicketFlowBuilder, RedisBasePauseParamBuilder +from backend.ticket.constants import SwitchConfirmType, TicketType + + +class RedisDataStructureTaskDeleteDetailSerializer(serializers.Serializer): + """数据构造与实例销毁""" + + class InfoSerializer(serializers.Serializer): + related_rollback_bill_id = serializers.CharField(help_text=_("关联单据ID")) + prod_cluster = serializers.CharField(help_text=_("集群域名")) + bk_cloud_id = serializers.IntegerField(help_text=_("云区域ID")) + + def validate(self, attr): + """业务逻辑校验""" + prod_cluster = attr.get("prod_cluster") + + if not Cluster.objects.filter(immute_domain=prod_cluster).exists(): + raise serializers.ValidationError(_("目标集群{}不存在,请确认.").format(prod_cluster)) + + if not TbTendisRollbackTasks.objects.filter( + related_rollback_bill_id=attr.get("related_rollback_bill_id"), + prod_cluster=prod_cluster, + bk_cloud_id=attr.get("bk_cloud_id"), + destroyed_status=DestroyedStatus.NOT_DESTROYED, + ).exists(): + raise serializers.ValidationError(_("集群{}: 没有找到未销毁的实例.").format(prod_cluster)) + + return attr + + infos = serializers.ListField(help_text=_("批量操作参数列表"), child=InfoSerializer()) + + +class RedisDataStructureTaskDeleteParamBuilder(builders.FlowParamBuilder): + controller = RedisController.redis_data_structure_task_delete + + +@builders.BuilderFactory.register(TicketType.REDIS_DATA_STRUCTURE_TASK_DELETE) +class RedisDataStructureTaskDeleteFlowBuilder(BaseRedisTicketFlowBuilder): + serializer = RedisDataStructureTaskDeleteDetailSerializer + inner_flow_builder = RedisDataStructureTaskDeleteParamBuilder + inner_flow_name = _("Redis 销毁构造实例") + pause_node_builder = RedisBasePauseParamBuilder + + @property + def need_manual_confirm(self): + return True + + @property + def need_itsm(self): + return False diff --git a/dbm-ui/backend/ticket/builders/redis/redis_toolbox_fixpoint_make.py b/dbm-ui/backend/ticket/builders/redis/redis_toolbox_fixpoint_make.py new file mode 100644 index 0000000000..c1cbb69c48 --- /dev/null +++ b/dbm-ui/backend/ticket/builders/redis/redis_toolbox_fixpoint_make.py @@ -0,0 +1,96 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import datetime + +from django.utils.translation import ugettext_lazy as _ +from rest_framework import serializers + +from backend.db_meta.enums import ClusterType +from backend.db_meta.models import Cluster +from backend.db_services.dbbase.constants import IpSource +from backend.flow.engine.controller.redis import RedisController +from backend.ticket import builders +from backend.ticket.builders.redis.base import BaseRedisTicketFlowBuilder, ClusterValidateMixin +from backend.ticket.constants import TicketType +from backend.utils.time import str2datetime + + +class RedisFixPointMakeDetailSerializer(serializers.Serializer): + """定点构造""" + + class InfoSerializer(ClusterValidateMixin, serializers.Serializer): + cluster_id = serializers.IntegerField(help_text=_("集群ID"), required=True) + bk_cloud_id = serializers.IntegerField(help_text=_("云区域ID")) + master_instances = serializers.ListField(help_text=_("master实例列表")) + resource_spec = serializers.JSONField(help_text=_("资源规格"), required=True) + recovery_time_point = serializers.CharField(help_text=_("待构造时间点")) + + def validate(self, attr): + """业务逻辑校验""" + master_instances = attr.get("master_instances") + recovery_time_point = attr.get("recovery_time_point") + resource_spec = attr.get("resource_spec") + cluster = Cluster.objects.get(id=attr.get("cluster_id")) + + redis_instances = [s.ip_port for s in cluster.storageinstance_set.all()] + host_count = resource_spec["redis"]["count"] + instance_count = len(master_instances) + if host_count > instance_count: + raise serializers.ValidationError( + _("集群{}: 主机数量({})不能大于实例数量({}).").format(cluster.immute_domain, host_count, instance_count) + ) + + # tendisplus 要求所有实例 + if ( + cluster.cluster_type + in [ + ClusterType.TendisPredixyTendisplusCluster, + ClusterType.TendisTwemproxyTendisplusIns, + ClusterType.TendisTendisplusInsance, + ClusterType.TendisTendisplusCluster, + ] + and len(master_instances) != len(redis_instances) / 2 + ): + raise serializers.ValidationError(_("集群{}: 不支持部分实例构造.").format(cluster.immute_domain)) + + now = datetime.datetime.now() + recovery_time_point = str2datetime(recovery_time_point) + if recovery_time_point >= now or now - recovery_time_point > datetime.timedelta(days=15): + raise serializers.ValidationError(_("集群{}: 构造时间最多向前追溯15天.").format(cluster.immute_domain)) + + return attr + + ip_source = serializers.ChoiceField(help_text=_("主机来源"), choices=IpSource.get_choices()) + infos = serializers.ListField(help_text=_("批量操作参数列表"), child=InfoSerializer()) + + +class RedisFixPointMakeParamBuilder(builders.FlowParamBuilder): + controller = RedisController.redis_data_structure + + def format_ticket_data(self): + super().format_ticket_data() + + +class RedisFixPointMakeResourceParamBuilder(builders.ResourceApplyParamBuilder): + def post_callback(self): + super().post_callback() + + +@builders.BuilderFactory.register(TicketType.REDIS_DATA_STRUCTURE, is_apply=True) +class RedisFixPointMakeFlowBuilder(BaseRedisTicketFlowBuilder): + serializer = RedisFixPointMakeDetailSerializer + inner_flow_builder = RedisFixPointMakeParamBuilder + inner_flow_name = _("Redis 定点构造") + resource_batch_apply_builder = RedisFixPointMakeResourceParamBuilder + + @property + def need_itsm(self): + return False diff --git a/dbm-ui/backend/ticket/builders/redis/redis_toolbox_instance_shutdown.py b/dbm-ui/backend/ticket/builders/redis/redis_toolbox_instance_shutdown.py new file mode 100644 index 0000000000..c32e283c55 --- /dev/null +++ b/dbm-ui/backend/ticket/builders/redis/redis_toolbox_instance_shutdown.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 rest_framework import serializers + +from backend.flow.engine.controller.redis import RedisController +from backend.ticket import builders +from backend.ticket.builders.redis.base import BaseRedisTicketFlowBuilder +from backend.ticket.constants import TicketType + + +class RedisClusterInstShutdownDetailSerializer(serializers.Serializer): + """实例下架""" + + class InfoSerializer(serializers.Serializer): + class HostInfoSerializer(serializers.Serializer): + ip = serializers.IPAddressField() + + cluster_id = serializers.IntegerField(help_text=_("集群ID")) + # bk_cloud_id = serializers.IntegerField(help_text=_("云区域ID")) + proxy = serializers.ListField(help_text=_("proxy列表"), child=HostInfoSerializer(), required=False) + redis_slave = serializers.ListField(help_text=_("slave列表"), child=HostInfoSerializer(), required=False) + + infos = serializers.ListField(help_text=_("批量操作参数列表"), child=InfoSerializer()) + + +class RedisClusterInstShutdownParamBuilder(builders.FlowParamBuilder): + controller = RedisController.redis_cluster_instance_shutdown + + def format_ticket_data(self): + super().format_ticket_data() + + +@builders.BuilderFactory.register(TicketType.REDIS_CLUSTER_INSTANCE_SHUTDOWN, is_apply=False) +class RedisClusterInstShutdownFlowBuilder(BaseRedisTicketFlowBuilder): + serializer = RedisClusterInstShutdownDetailSerializer + inner_flow_builder = RedisClusterInstShutdownParamBuilder + inner_flow_name = _("实例下架") + + @property + def need_itsm(self): + return False + + def patch_ticket_detail(self): + super().patch_ticket_detail() diff --git a/dbm-ui/backend/ticket/builders/redis/redis_toolbox_master_slave_switch.py b/dbm-ui/backend/ticket/builders/redis/redis_toolbox_master_slave_switch.py new file mode 100644 index 0000000000..b0764da083 --- /dev/null +++ b/dbm-ui/backend/ticket/builders/redis/redis_toolbox_master_slave_switch.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 rest_framework import serializers + +from backend.db_meta.models import Cluster +from backend.db_services.redis.toolbox.handlers import ToolboxHandler +from backend.flow.engine.controller.redis import RedisController +from backend.ticket import builders +from backend.ticket.builders.redis.base import BaseRedisTicketFlowBuilder, ClusterValidateMixin +from backend.ticket.constants import SwitchConfirmType, TicketType + + +class RedisMasterSlaveSwitchDetailSerializer(serializers.Serializer): + """主从故障切换""" + + class InfoSerializer(ClusterValidateMixin, serializers.Serializer): + class PairSerializer(serializers.Serializer): + redis_master = serializers.IPAddressField(help_text=_("master主机")) + redis_slave = serializers.IPAddressField(help_text=_("slave主机")) + + cluster_id = serializers.IntegerField(help_text=_("集群ID")) + pairs = serializers.ListField(help_text=_("主从切换对"), child=PairSerializer(), allow_empty=False) + online_switch_type = serializers.ChoiceField( + help_text=_("切换类型"), choices=SwitchConfirmType.get_choices(), default=SwitchConfirmType.NO_CONFIRM + ) + + def validate(self, attr): + """业务逻辑校验""" + cluster = Cluster.objects.get(id=attr.get("cluster_id")) + + master_slaves = { + pair["master_ip"]: pair["slave_ip"] + for pair in ToolboxHandler(self.context.get("bk_biz_id")).query_master_slave_pairs( + attr.get("cluster_id") + ) + } + + for pair in attr["pairs"]: + if master_slaves.get(pair["redis_master"]) != pair["redis_slave"]: + raise serializers.ValidationError( + _("集群{}的主从关系不匹配:{} -> {}.").format( + cluster.immute_domain, + pair["redis_master"], + master_slaves.get(pair["redis_master"]), + ) + ) + + return attr + + force = serializers.BooleanField(help_text=_("是否强制执行"), required=False, default=False) + infos = serializers.ListField(help_text=_("批量操作参数列表"), child=InfoSerializer()) + + +class RedisMasterSlaveSwitchParamBuilder(builders.FlowParamBuilder): + controller = RedisController.redis_cluster_failover_scene + + +@builders.BuilderFactory.register(TicketType.REDIS_MASTER_SLAVE_SWITCH) +class RedisMasterSlaveSwitchFlowBuilder(BaseRedisTicketFlowBuilder): + serializer = RedisMasterSlaveSwitchDetailSerializer + inner_flow_builder = RedisMasterSlaveSwitchParamBuilder + inner_flow_name = _("Redis 主从故障切换") + + @property + def need_itsm(self): + return False diff --git a/dbm-ui/backend/ticket/builders/redis/redis_toolbox_proxy_scale_down.py b/dbm-ui/backend/ticket/builders/redis/redis_toolbox_proxy_scale_down.py new file mode 100644 index 0000000000..5eb597214b --- /dev/null +++ b/dbm-ui/backend/ticket/builders/redis/redis_toolbox_proxy_scale_down.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 rest_framework import serializers + +from backend.db_services.dbbase.constants import IpSource +from backend.flow.engine.controller.redis import RedisController +from backend.ticket import builders +from backend.ticket.builders.redis.base import BaseRedisTicketFlowBuilder, ClusterValidateMixin +from backend.ticket.constants import SwitchConfirmType, TicketType + + +class ProxyScaleDownDetailSerializer(ClusterValidateMixin, serializers.Serializer): + """proxy缩容""" + + class InfoSerializer(serializers.Serializer): + cluster_id = serializers.IntegerField(help_text=_("集群ID")) + target_proxy_count = serializers.IntegerField(help_text=_("目标proxy数量"), min_value=2) + online_switch_type = serializers.ChoiceField( + help_text=_("切换类型"), choices=SwitchConfirmType.get_choices(), default=SwitchConfirmType.NO_CONFIRM + ) + + infos = serializers.ListField(help_text=_("批量操作参数列表"), child=InfoSerializer()) + + +class ProxyScaleDownParamBuilder(builders.FlowParamBuilder): + controller = RedisController.redis_proxy_scale + + def format_ticket_data(self): + super().format_ticket_data() + + +@builders.BuilderFactory.register(TicketType.PROXY_SCALE_DOWN) +class ProxyScaleDownFlowBuilder(BaseRedisTicketFlowBuilder): + serializer = ProxyScaleDownDetailSerializer + inner_flow_builder = ProxyScaleDownParamBuilder + inner_flow_name = _("Proxy缩容") + + @property + def need_itsm(self): + return False diff --git a/dbm-ui/backend/ticket/builders/redis/redis_toolbox_proxy_scale_up.py b/dbm-ui/backend/ticket/builders/redis/redis_toolbox_proxy_scale_up.py new file mode 100644 index 0000000000..ed1acfeb5c --- /dev/null +++ b/dbm-ui/backend/ticket/builders/redis/redis_toolbox_proxy_scale_up.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging + +from django.utils.translation import ugettext_lazy as _ +from rest_framework import serializers + +from backend.db_services.dbbase.constants import IpSource +from backend.flow.engine.controller.redis import RedisController +from backend.ticket import builders +from backend.ticket.builders.redis.base import BaseRedisTicketFlowBuilder, ClusterValidateMixin +from backend.ticket.constants import TicketType + +logger = logging.getLogger("root") + + +class ProxyScaleUpDetailSerializer(serializers.Serializer): + """proxy扩容""" + + class InfoSerializer(ClusterValidateMixin, serializers.Serializer): + cluster_id = serializers.IntegerField(help_text=_("集群ID")) + bk_cloud_id = serializers.IntegerField(help_text=_("云区域ID")) + target_proxy_count = serializers.IntegerField(help_text=_("目标proxy数量"), min_value=2) + resource_spec = serializers.JSONField(help_text=_("资源规格")) + + ip_source = serializers.ChoiceField(help_text=_("主机来源"), choices=IpSource.get_choices()) + infos = serializers.ListField(help_text=_("批量操作参数列表"), child=InfoSerializer()) + + +class ProxyScaleUpParamBuilder(builders.FlowParamBuilder): + controller = RedisController.redis_proxy_scale + + def format_ticket_data(self): + super().format_ticket_data() + + +class ProxyScaleUpResourceParamBuilder(builders.ResourceApplyParamBuilder): + def post_callback(self): + next_flow = self.ticket.next_flow() + ticket_data = next_flow.details["ticket_data"] + logger.info("ticket_data: %s", ticket_data) + super().post_callback() + + +@builders.BuilderFactory.register(TicketType.PROXY_SCALE_UP, is_apply=True) +class ProxyScaleUpFlowBuilder(BaseRedisTicketFlowBuilder): + serializer = ProxyScaleUpDetailSerializer + inner_flow_builder = ProxyScaleUpParamBuilder + inner_flow_name = _("Proxy扩容") + resource_batch_apply_builder = ProxyScaleUpResourceParamBuilder + + @property + def need_itsm(self): + return False diff --git a/dbm-ui/backend/ticket/builders/redis/redis_toolbox_redis_scale_updown.py b/dbm-ui/backend/ticket/builders/redis/redis_toolbox_redis_scale_updown.py new file mode 100644 index 0000000000..1502d91c25 --- /dev/null +++ b/dbm-ui/backend/ticket/builders/redis/redis_toolbox_redis_scale_updown.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 rest_framework import serializers + +from backend.db_services.dbbase.constants import IpSource +from backend.flow.engine.controller.redis import RedisController +from backend.ticket import builders +from backend.ticket.builders.redis.base import BaseRedisTicketFlowBuilder, ClusterValidateMixin +from backend.ticket.constants import AffinityEnum, SwitchConfirmType, TicketType + + +class RedisScaleUpDownDetailSerializer(serializers.Serializer): + """redis集群容量变更""" + + class InfoSerializer(ClusterValidateMixin, serializers.Serializer): + class ResourceSpecSerializer(serializers.Serializer): + class BackendGroupSerializer(serializers.Serializer): + spec_id = serializers.IntegerField(help_text=_("规格ID")) + count = serializers.IntegerField(help_text=_("数量")) + affinity = serializers.ChoiceField( + help_text=_("亲和性"), choices=AffinityEnum.get_choices(), default=AffinityEnum.NONE + ) + + backend_group = BackendGroupSerializer() + + cluster_id = serializers.IntegerField(help_text=_("集群ID")) + bk_cloud_id = serializers.IntegerField(help_text=_("云区域ID")) + shard_num = serializers.IntegerField(help_text=_("集群分片数")) + group_num = serializers.IntegerField(help_text=_("部署机器组数")) + db_version = serializers.CharField(help_text=_("版本号")) + online_switch_type = serializers.ChoiceField( + help_text=_("切换类型"), choices=SwitchConfirmType.get_choices(), default=SwitchConfirmType.NO_CONFIRM + ) + resource_spec = ResourceSpecSerializer() + + ip_source = serializers.ChoiceField(help_text=_("主机来源"), choices=IpSource.get_choices()) + infos = serializers.ListField(help_text=_("批量操作参数列表"), child=InfoSerializer()) + + +class RedisScaleUpDownParamBuilder(builders.FlowParamBuilder): + controller = RedisController.redis_backend_scale + + def format_ticket_data(self): + super().format_ticket_data() + + +class RedisScaleUpDownResourceParamBuilder(builders.ResourceApplyParamBuilder): + def post_callback(self): + super().post_callback() + + +@builders.BuilderFactory.register(TicketType.REDIS_SCALE_UPDOWN, is_apply=True) +class RedisScaleUpDownFlowBuilder(BaseRedisTicketFlowBuilder): + serializer = RedisScaleUpDownDetailSerializer + inner_flow_builder = RedisScaleUpDownParamBuilder + inner_flow_name = _("Redis 集群容量变更") + resource_batch_apply_builder = RedisScaleUpDownResourceParamBuilder + + @property + def need_itsm(self): + return False diff --git a/dbm-ui/backend/ticket/builders/redis/redis_toolbox_rollback_data_copy.py b/dbm-ui/backend/ticket/builders/redis/redis_toolbox_rollback_data_copy.py new file mode 100644 index 0000000000..10e3c4f792 --- /dev/null +++ b/dbm-ui/backend/ticket/builders/redis/redis_toolbox_rollback_data_copy.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 rest_framework import serializers + +from backend.db_meta.models import Cluster +from backend.db_services.redis.redis_dts.enums import DtsCopyType +from backend.db_services.redis.rollback.models import TbTendisRollbackTasks +from backend.flow.engine.controller.redis import RedisController +from backend.ticket import builders +from backend.ticket.builders.redis.base import BaseRedisTicketFlowBuilder, ClusterValidateMixin +from backend.ticket.constants import TicketType, WriteModeType + + +class RedisRollbackDataCopyDetailSerializer(serializers.Serializer): + class InfoSerializer(serializers.Serializer): + src_cluster = serializers.CharField(help_text=_("构造产物访问入口(ip:port)")) + dst_cluster = serializers.IntegerField(help_text=_("目标集群ID")) + key_white_regex = serializers.CharField(help_text=_("包含key"), allow_null=True) + key_black_regex = serializers.CharField(help_text=_("排除key"), allow_blank=True) + recovery_time_point = serializers.CharField(help_text=_("待构造时间点")) + + dts_copy_type = serializers.ChoiceField( + choices=( + ( + DtsCopyType.COPY_FROM_ROLLBACK_INSTANCE.value, + DtsCopyType.get_choice_label(DtsCopyType.COPY_FROM_ROLLBACK_INSTANCE.value), + ), + ) + ) + write_mode = serializers.ChoiceField(choices=WriteModeType.get_choices()) + infos = InfoSerializer(many=True, help_text=_("批量操作参数列表")) + + def validate(self, attr): + """根据复制类型校验info""" + + dst_cluster_set = set() + src_cluster_set = set() + for info in attr.get("infos"): + src_cluster = info.get("src_cluster") + dst_cluster = info.get("dst_cluster") + + ClusterValidateMixin.check_cluster_phase(dst_cluster) + + if not TbTendisRollbackTasks.objects.filter( + prod_cluster_id=dst_cluster, + temp_cluster_proxy=src_cluster, + recovery_time_point=info.get("recovery_time_point"), + ).exists(): + raise serializers.ValidationError(_("构造记录不存在,请确认: {}").format(src_cluster)) + + if dst_cluster in dst_cluster_set: + raise serializers.ValidationError(_("目标集群不能重复: {}").format(dst_cluster)) + + if src_cluster in src_cluster_set: + raise serializers.ValidationError(_("源集群不能重复: {}").format(src_cluster)) + + if info["key_white_regex"] == "" and info["key_black_regex"] == "": + raise serializers.ValidationError(_("请补齐缺少正则配置的行")) + + src_cluster_set.add(src_cluster) + dst_cluster_set.add(dst_cluster) + + return attr + + +class RedisRollbackDataCopyParamBuilder(builders.FlowParamBuilder): + controller = RedisController.redis_cluster_data_copy + + def format_ticket_data(self): + super().format_ticket_data() + + +@builders.BuilderFactory.register(TicketType.REDIS_CLUSTER_ROLLBACK_DATA_COPY) +class RedisRollbackDataCopyFlowBuilder(BaseRedisTicketFlowBuilder): + serializer = RedisRollbackDataCopyDetailSerializer + inner_flow_builder = RedisRollbackDataCopyParamBuilder + inner_flow_name = _("Redis 构造实例数据回写") + + @property + def need_itsm(self): + return False diff --git a/dbm-ui/backend/ticket/builders/redis/redis_toolbox_shard_update.py b/dbm-ui/backend/ticket/builders/redis/redis_toolbox_shard_update.py new file mode 100644 index 0000000000..602a0bbf50 --- /dev/null +++ b/dbm-ui/backend/ticket/builders/redis/redis_toolbox_shard_update.py @@ -0,0 +1,103 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 rest_framework import serializers + +from backend.db_meta.models import Cluster +from backend.db_services.dbbase.constants import IpSource +from backend.db_services.version.utils import query_versions_by_key +from backend.flow.engine.controller.redis import RedisController +from backend.ticket import builders +from backend.ticket.builders.redis.base import ( + BaseRedisTicketFlowBuilder, + ClusterValidateMixin, + DataCheckRepairSettingSerializer, + RedisUpdateApplyResourceParamBuilder, +) +from backend.ticket.builders.redis.redis_cluster_apply import RedisApplyResourceParamBuilder +from backend.ticket.constants import AffinityEnum, SwitchConfirmType, TicketType + + +class RedisShardUpdateDetailSerializer(serializers.Serializer): + """集群分片变更""" + + class InfoSerializer(ClusterValidateMixin, serializers.Serializer): + class ResourceSpecSerializer(serializers.Serializer): + class SpecSerializer(serializers.Serializer): + spec_id = serializers.IntegerField(help_text=_("规格ID")) + count = serializers.IntegerField(help_text=_("数量")) + affinity = serializers.ChoiceField( + help_text=_("亲和性"), choices=AffinityEnum.get_choices(), default=AffinityEnum.NONE + ) + + proxy = SpecSerializer(help_text=_("申请proxy资源")) + backend_group = SpecSerializer(help_text=_("申请redis主从资源")) + + src_cluster = serializers.IntegerField(help_text=_("集群ID")) + current_spec_id = serializers.IntegerField(help_text=_("当前规格ID")) + current_shard_num = serializers.IntegerField(help_text=_("当前分片数")) + cluster_shard_num = serializers.IntegerField(help_text=_("目标分片数")) + resource_spec = ResourceSpecSerializer(help_text=_("资源申请")) + db_version = serializers.CharField(help_text=_("版本号")) + online_switch_type = serializers.ChoiceField( + help_text=_("切换类型"), choices=SwitchConfirmType.get_choices(), default=SwitchConfirmType.NO_CONFIRM + ) + + def validate(self, attr): + """业务逻辑校验""" + cluster = Cluster.objects.get(id=attr.get("src_cluster")) + if attr.get("current_shard_num") == attr.get("cluster_shard_num"): + raise serializers.ValidationError( + _("集群({}):目标分片数({})和原始分片数({})相同.").format( + cluster.immute_domain, + attr.get("cluster_shard_num"), + attr.get("current_shard_num"), + ) + ) + + if attr.get("db_version") not in query_versions_by_key(cluster.cluster_type): + raise serializers.ValidationError( + _("集群({}):{} 类集群不支持版本 {}.").format( + cluster.immute_domain, + cluster.cluster_type, + attr.get("db_version"), + ) + ) + + return attr + + data_check_repair_setting = DataCheckRepairSettingSerializer() + ip_source = serializers.ChoiceField(help_text=_("主机来源"), choices=IpSource.get_choices()) + infos = serializers.ListField(help_text=_("批量操作参数列表"), child=InfoSerializer(), allow_empty=False) + + +class RedisShardUpdateParamBuilder(builders.FlowParamBuilder): + controller = RedisController.redis_cluster_shard_num_update + + def format_ticket_data(self): + super().format_ticket_data() + + +class RedisShardUpdateResourceParamBuilder(RedisUpdateApplyResourceParamBuilder): + pass + + +@builders.BuilderFactory.register(TicketType.REDIS_CLUSTER_SHARD_NUM_UPDATE, is_apply=True) +class RedisShardUpdateFlowBuilder(BaseRedisTicketFlowBuilder): + serializer = RedisShardUpdateDetailSerializer + inner_flow_builder = RedisShardUpdateParamBuilder + inner_flow_name = _("Redis 集群分片变更") + resource_batch_apply_builder = RedisShardUpdateResourceParamBuilder + + @property + def need_itsm(self): + return True diff --git a/dbm-ui/backend/ticket/builders/redis/redis_toolbox_type_update.py b/dbm-ui/backend/ticket/builders/redis/redis_toolbox_type_update.py new file mode 100644 index 0000000000..b87a7cc74f --- /dev/null +++ b/dbm-ui/backend/ticket/builders/redis/redis_toolbox_type_update.py @@ -0,0 +1,105 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 rest_framework import serializers + +from backend.db_meta.enums import ClusterType +from backend.db_meta.models import Cluster +from backend.db_services.dbbase.constants import IpSource +from backend.db_services.version.utils import query_versions_by_key +from backend.flow.engine.controller.redis import RedisController +from backend.ticket import builders +from backend.ticket.builders.redis.base import ( + BaseRedisTicketFlowBuilder, + ClusterValidateMixin, + DataCheckRepairSettingSerializer, + RedisUpdateApplyResourceParamBuilder, +) +from backend.ticket.constants import AffinityEnum, SwitchConfirmType, TicketType + + +class RedisTypeUpdateDetailSerializer(serializers.Serializer): + """集群类型变更""" + + class InfoSerializer(ClusterValidateMixin, serializers.Serializer): + class ResourceSpecSerializer(serializers.Serializer): + class SpecSerializer(serializers.Serializer): + spec_id = serializers.IntegerField(help_text=_("规格ID")) + count = serializers.IntegerField(help_text=_("数量")) + affinity = serializers.ChoiceField( + help_text=_("亲和性"), choices=AffinityEnum.get_choices(), default=AffinityEnum.NONE + ) + + proxy = SpecSerializer(help_text=_("申请proxy资源")) + backend_group = SpecSerializer(help_text=_("申请redis主从资源")) + + src_cluster = serializers.IntegerField(help_text=_("集群ID")) + current_spec_id = serializers.IntegerField(help_text=_("当前规格ID")) + current_shard_num = serializers.IntegerField(help_text=_("当前分片数")) + cluster_shard_num = serializers.IntegerField(help_text=_("目标分片数")) + current_cluster_type = serializers.ChoiceField(choices=ClusterType.get_choices(), help_text=_("当前集群类型")) + target_cluster_type = serializers.ChoiceField(choices=ClusterType.get_choices(), help_text=_("目标集群类型")) + resource_spec = ResourceSpecSerializer(help_text=_("资源申请")) + db_version = serializers.CharField(help_text=_("版本号")) + online_switch_type = serializers.ChoiceField( + help_text=_("切换类型"), choices=SwitchConfirmType.get_choices(), default=SwitchConfirmType.NO_CONFIRM + ) + + def validate(self, attr): + """业务逻辑校验""" + cluster = Cluster.objects.get(id=attr.get("src_cluster")) + if cluster.cluster_type == attr.get("target_cluster_type"): + raise serializers.ValidationError( + _("集群({}):目标类型({})和原始类型({})相同.").format( + cluster.immute_domain, + attr.get("target_cluster_type"), + cluster.cluster_type, + ) + ) + + if attr.get("db_version") not in query_versions_by_key(attr.get("target_cluster_type")): + raise serializers.ValidationError( + _("集群({}):{} 类集群不支持版本 {}.").format( + cluster.immute_domain, + attr.get("target_cluster_type"), + attr.get("db_version"), + ) + ) + + return attr + + data_check_repair_setting = DataCheckRepairSettingSerializer() + ip_source = serializers.ChoiceField(help_text=_("主机来源"), choices=IpSource.get_choices()) + infos = serializers.ListField(help_text=_("批量操作参数列表"), child=InfoSerializer(), allow_empty=False) + + +class RedisTypeUpdateParamBuilder(builders.FlowParamBuilder): + controller = RedisController.redis_cluster_type_update + + def format_ticket_data(self): + super().format_ticket_data() + + +class RedisTypeUpdateResourceParamBuilder(RedisUpdateApplyResourceParamBuilder): + pass + + +@builders.BuilderFactory.register(TicketType.REDIS_CLUSTER_TYPE_UPDATE, is_apply=True) +class RedisTypeUpdateFlowBuilder(BaseRedisTicketFlowBuilder): + serializer = RedisTypeUpdateDetailSerializer + inner_flow_builder = RedisTypeUpdateParamBuilder + inner_flow_name = _("Redis 集群类型变更") + resource_batch_apply_builder = RedisTypeUpdateResourceParamBuilder + + @property + def need_itsm(self): + return True diff --git a/dbm-ui/backend/ticket/builders/spider/base.py b/dbm-ui/backend/ticket/builders/spider/base.py deleted file mode 100644 index 9798582819..0000000000 --- a/dbm-ui/backend/ticket/builders/spider/base.py +++ /dev/null @@ -1,27 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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 backend.configuration.constants import DBType -from backend.ticket.builders import TicketFlowBuilder -from backend.ticket.builders.common.base import MySQLTicketFlowBuilderPatchMixin -from backend.ticket.builders.mysql.base import MySQLBaseOperateDetailSerializer, MySQLClustersTakeDownDetailsSerializer - - -class BaseTendbTicketFlowBuilder(MySQLTicketFlowBuilderPatchMixin, TicketFlowBuilder): - group = DBType.Tendb.value - - -class TendbBaseOperateDetailSerializer(MySQLBaseOperateDetailSerializer): - pass - - -class TendbClustersTakeDownDetailsSerializer(MySQLClustersTakeDownDetailsSerializer): - pass diff --git a/dbm-ui/backend/ticket/builders/spider/spider_destory.py b/dbm-ui/backend/ticket/builders/spider/spider_destory.py deleted file mode 100644 index 2f01bc1bdf..0000000000 --- a/dbm-ui/backend/ticket/builders/spider/spider_destory.py +++ /dev/null @@ -1,34 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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 backend.flow.engine.controller.spider import SpiderController -from backend.ticket import builders -from backend.ticket.builders.mysql.base import BaseMySQLTicketFlowBuilder -from backend.ticket.builders.spider.base import BaseTendbTicketFlowBuilder, TendbClustersTakeDownDetailsSerializer -from backend.ticket.constants import TicketType - - -class TendbDestroyDetailSerializer(TendbClustersTakeDownDetailsSerializer): - pass - - -class TendbDestroyFlowParamBuilder(builders.FlowParamBuilder): - controller = SpiderController.spider_cluster_disable_scene - - -@builders.BuilderFactory.register(TicketType.TENDB_CLUSTER_DESTROY) -class TendbDestroyFlowBuilder(BaseTendbTicketFlowBuilder): - - serializer = TendbDestroyDetailSerializer - inner_flow_builder = TendbDestroyFlowParamBuilder - inner_flow_name = _("TenDB Cluster 下架执行") diff --git a/dbm-ui/backend/ticket/builders/spider/spider_disable.py b/dbm-ui/backend/ticket/builders/spider/spider_disable.py deleted file mode 100644 index 37bcb7dc02..0000000000 --- a/dbm-ui/backend/ticket/builders/spider/spider_disable.py +++ /dev/null @@ -1,34 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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 backend.flow.engine.controller.spider import SpiderController -from backend.ticket import builders -from backend.ticket.builders.mysql.base import BaseMySQLTicketFlowBuilder -from backend.ticket.builders.spider.base import BaseTendbTicketFlowBuilder, TendbClustersTakeDownDetailsSerializer -from backend.ticket.constants import TicketType - - -class TendbDisableDetailSerializer(TendbClustersTakeDownDetailsSerializer): - pass - - -class TendbDisableFlowParamBuilder(builders.FlowParamBuilder): - controller = SpiderController.spider_cluster_disable_scene - - -@builders.BuilderFactory.register(TicketType.TENDB_CLUSTER_DISABLE) -class TendbEnableFlowBuilder(BaseTendbTicketFlowBuilder): - - serializer = TendbDisableDetailSerializer - inner_flow_builder = TendbDisableFlowParamBuilder - inner_flow_name = _("TenDB Cluster 禁用执行") diff --git a/dbm-ui/backend/ticket/builders/spider/spider_partition.py b/dbm-ui/backend/ticket/builders/spider/spider_partition.py deleted file mode 100644 index 6a8dd3c4f6..0000000000 --- a/dbm-ui/backend/ticket/builders/spider/spider_partition.py +++ /dev/null @@ -1,43 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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 gettext_lazy as _ - -from backend.flow.engine.controller.spider import SpiderController -from backend.ticket import builders -from backend.ticket.builders.mysql.base import BaseMySQLTicketFlowBuilder -from backend.ticket.builders.mysql.mysql_partition import MySQLPartitionDetailSerializer, MySQLPartitionParamBuilder -from backend.ticket.builders.spider.base import BaseTendbTicketFlowBuilder -from backend.ticket.constants import FlowRetryType, FlowType, TicketType -from backend.ticket.models import Flow - - -class SpiderPartitionDetailSerializer(MySQLPartitionDetailSerializer): - pass - - -class SpiderPartitionParamBuilder(builders.FlowParamBuilder): - controller = SpiderController.spider_partition - - def format_ticket_data(self): - pass - - -@builders.BuilderFactory.register(TicketType.SPIDER_PARTITION) -class SpiderPartitionFlowBuilder(BaseTendbTicketFlowBuilder): - serializer = SpiderPartitionDetailSerializer - inner_flow_builder = SpiderPartitionParamBuilder - inner_flow_name = _("分区管理执行") - - @property - def need_itsm(self): - # TODO:先不考虑执行的分区审批 - return False diff --git a/dbm-ui/backend/ticket/builders/spider/tendb_master_fail_over.py b/dbm-ui/backend/ticket/builders/spider/tendb_master_fail_over.py deleted file mode 100644 index 2d9d0ad7a6..0000000000 --- a/dbm-ui/backend/ticket/builders/spider/tendb_master_fail_over.py +++ /dev/null @@ -1,37 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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 gettext_lazy as _ - -from backend.ticket import builders -from backend.ticket.builders.spider.base import BaseTendbTicketFlowBuilder -from backend.ticket.builders.spider.tendb_master_slave_switch import TendbMasterSlaveSwitchDetailSerializer -from backend.ticket.constants import TicketType - - -class TendbMasterFailOverDetailSerializer(TendbMasterSlaveSwitchDetailSerializer): - pass - - -class TendbMasterFailOverParamBuilder(builders.FlowParamBuilder): - # controller = SpiderController.mysql_ha_master_fail_over_scene - controller = None - - -@builders.BuilderFactory.register(TicketType.SPIDER_MASTER_FAIL_OVER) -class TendbMasterFailOverFlowBuilder(BaseTendbTicketFlowBuilder): - serializer = TendbMasterFailOverDetailSerializer - inner_flow_builder = TendbMasterFailOverParamBuilder - inner_flow_name = _("TendbCluster 主故障切换") - - @property - def need_manual_confirm(self): - return True diff --git a/dbm-ui/backend/ticket/builders/spider/tendb_master_slave_switch.py b/dbm-ui/backend/ticket/builders/spider/tendb_master_slave_switch.py deleted file mode 100644 index b00cf1468d..0000000000 --- a/dbm-ui/backend/ticket/builders/spider/tendb_master_slave_switch.py +++ /dev/null @@ -1,50 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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 gettext_lazy as _ -from rest_framework import serializers - -from backend.ticket import builders -from backend.ticket.builders.common.base import HostInfoSerializer -from backend.ticket.builders.spider.base import BaseTendbTicketFlowBuilder, TendbBaseOperateDetailSerializer -from backend.ticket.constants import TicketType - - -class TendbMasterSlaveSwitchDetailSerializer(TendbBaseOperateDetailSerializer): - class InfoSerializer(serializers.Serializer): - master_ip = HostInfoSerializer(help_text=_("主库 IP")) - slave_ip = HostInfoSerializer(help_text=_("从库 IP")) - cluster_ids = serializers.ListField(help_text=_("集群ID列表"), child=serializers.IntegerField()) - - infos = serializers.ListField(help_text=_("单据信息"), child=InfoSerializer()) - is_check_proc = serializers.BooleanField(help_text=_("是否检测连接")) - is_check_delay = serializers.BooleanField(help_text=_("是否检测数据同步延时情况")) - is_check_checksum = serializers.BooleanField(help_text=_("是否检测历史数据检验结果")) - - def validate(self, attrs): - # super().validate(attrs) - return attrs - - -class TendbMasterSlaveSwitchParamBuilder(builders.FlowParamBuilder): - # controller = SpiderController.mysql_ha_switch_scene - controller = None - - -@builders.BuilderFactory.register(TicketType.SPIDER_MASTER_SLAVE_SWITCH) -class TendbMasterSlaveSwitchFlowBuilder(BaseTendbTicketFlowBuilder): - serializer = TendbMasterSlaveSwitchDetailSerializer - inner_flow_builder = TendbMasterSlaveSwitchParamBuilder - inner_flow_name = _("TendbCluster 主从互换执行") - - @property - def need_manual_confirm(self): - return True diff --git a/dbm-ui/backend/ticket/builders/tbinlogdumper/__init__.py b/dbm-ui/backend/ticket/builders/tbinlogdumper/__init__.py new file mode 100644 index 0000000000..aa5085c628 --- /dev/null +++ b/dbm-ui/backend/ticket/builders/tbinlogdumper/__init__.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" diff --git a/dbm-ui/backend/ticket/builders/tbinlogdumper/dumper_clear.py b/dbm-ui/backend/ticket/builders/tbinlogdumper/dumper_clear.py new file mode 100644 index 0000000000..8102decd07 --- /dev/null +++ b/dbm-ui/backend/ticket/builders/tbinlogdumper/dumper_clear.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 rest_framework import serializers + +from backend.flow.consts import TBinlogDumperAddType +from backend.flow.engine.controller.tbinlogdumper import TBinlogDumperController +from backend.ticket import builders +from backend.ticket.builders.tendbcluster.base import BaseTendbTicketFlowBuilder +from backend.ticket.constants import TicketType + + +class TbinlogdumperApplyDetailSerializer(serializers.Serializer): + class DumperInfoSerializer(serializers.Serializer): + class AddInfoSerializer(serializers.Serializer): + area_name = serializers.IntegerField(help_text=_("dumper安装的大区")) + module_id = serializers.IntegerField(help_text=_("dumper的模块")) + add_type = serializers.ChoiceField(help_text=_("dumper的安装方式"), choices=TBinlogDumperAddType.get_choices()) + + cluster_id = serializers.IntegerField(help_text=_("集群ID")) + add_infos = serializers.ListSerializer(help_text=_("dumper部署信息"), child=AddInfoSerializer()) + + infos = serializers.ListSerializer(child=DumperInfoSerializer()) + + +class TbinlogdumperApplyFlowParamBuilder(builders.FlowParamBuilder): + controller = TBinlogDumperController.add_nodes_scene + + +@builders.BuilderFactory.register(TicketType.TBINLOGDUMPER_INSTALL) +class TbinlogdumperApplyFlowBuilder(BaseTendbTicketFlowBuilder): + serializer = TbinlogdumperApplyDetailSerializer + inner_flow_builder = TbinlogdumperApplyFlowParamBuilder + inner_flow_name = _("Tbinlogdumper 上架") diff --git a/dbm-ui/backend/ticket/builders/tbinlogdumper/dumper_reduce_nodes.py b/dbm-ui/backend/ticket/builders/tbinlogdumper/dumper_reduce_nodes.py new file mode 100644 index 0000000000..735c0e6423 --- /dev/null +++ b/dbm-ui/backend/ticket/builders/tbinlogdumper/dumper_reduce_nodes.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 rest_framework import serializers + +from backend.flow.engine.controller.tbinlogdumper import TBinlogDumperController +from backend.ticket import builders +from backend.ticket.builders.tendbcluster.base import BaseTendbTicketFlowBuilder +from backend.ticket.constants import TicketType + + +class TbinlogdumperReduceNodesDetailSerializer(serializers.Serializer): + class DumperReduceInfoSerializer(serializers.Serializer): + cluster_id = serializers.IntegerField(help_text=_("集群ID")) + reduce_ids = serializers.ListField(help_text=_("dumper部署信息"), child=serializers.IntegerField()) + + infos = serializers.ListSerializer(child=DumperReduceInfoSerializer()) + + +class TbinlogdumperReduceNodesFlowParamBuilder(builders.FlowParamBuilder): + controller = TBinlogDumperController.reduce_nodes_scene + + +@builders.BuilderFactory.register(TicketType.TBINLOGDUMPER_REDUCE_NODES) +class TbinlogdumperApplyFlowBuilder(BaseTendbTicketFlowBuilder): + serializer = TbinlogdumperReduceNodesDetailSerializer + inner_flow_builder = TbinlogdumperReduceNodesFlowParamBuilder + inner_flow_name = _("Tbinlogdumper 上架") diff --git a/dbm-ui/backend/ticket/builders/tbinlogdumper/dumper_switch.py b/dbm-ui/backend/ticket/builders/tbinlogdumper/dumper_switch.py new file mode 100644 index 0000000000..888c9a03e0 --- /dev/null +++ b/dbm-ui/backend/ticket/builders/tbinlogdumper/dumper_switch.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 rest_framework import serializers + +from backend.flow.engine.controller.tbinlogdumper import TBinlogDumperController +from backend.ticket import builders +from backend.ticket.builders.tendbcluster.base import BaseTendbTicketFlowBuilder +from backend.ticket.constants import TicketType + + +class TbinlogdumperSwitchNodesDetailSerializer(serializers.Serializer): + class DumperSwitchInfoSerializer(serializers.Serializer): + class SwitchInstanceSerializer(serializers.Serializer): + host = serializers.CharField(help_text=_("主机IP")) + port = serializers.IntegerField(help_text=_("主机端口")) + repl_binlog_file = serializers.CharField(help_text=_("待切换后需要同步的binlog文件")) + repl_binlog_pos = serializers.IntegerField(help_text=_("待切换后需要同步的binlog文件的为位点")) + + cluster_id = serializers.IntegerField(help_text=_("集群ID")) + switch_instances = serializers.ListSerializer(help_text=_("dumper切换信息"), child=SwitchInstanceSerializer()) + + infos = serializers.ListSerializer(child=DumperSwitchInfoSerializer()) + + +class TbinlogdumperSwitchNodesFlowParamBuilder(builders.FlowParamBuilder): + controller = TBinlogDumperController.switch_nodes_scene + + +@builders.BuilderFactory.register(TicketType.TBINLOGDUMPER_SWITCH_NODES) +class TbinlogdumperSwitchNodesFlowBuilder(BaseTendbTicketFlowBuilder): + serializer = TbinlogdumperSwitchNodesDetailSerializer + inner_flow_builder = TbinlogdumperSwitchNodesFlowParamBuilder + inner_flow_name = _("Tbinlogdumper 切换") diff --git a/dbm-ui/backend/ticket/builders/tendbcluster/__init__.py b/dbm-ui/backend/ticket/builders/tendbcluster/__init__.py new file mode 100644 index 0000000000..aa5085c628 --- /dev/null +++ b/dbm-ui/backend/ticket/builders/tendbcluster/__init__.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" diff --git a/dbm-ui/backend/ticket/builders/tendbcluster/base.py b/dbm-ui/backend/ticket/builders/tendbcluster/base.py new file mode 100644 index 0000000000..0c8d419521 --- /dev/null +++ b/dbm-ui/backend/ticket/builders/tendbcluster/base.py @@ -0,0 +1,123 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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.db.models import Q +from django.utils.translation import ugettext as _ +from rest_framework import serializers + +from backend.configuration.constants import DBType +from backend.db_meta.enums import ClusterTenDBClusterStatusFlag, TenDBClusterSpiderRole +from backend.db_meta.models import Cluster +from backend.flow.consts import MAX_SPIDER_MASTER_COUNT, MIN_SPIDER_MASTER_COUNT, MIN_SPIDER_SLAVE_COUNT +from backend.ticket import builders +from backend.ticket.builders import TicketFlowBuilder +from backend.ticket.builders.common.base import MySQLTicketFlowBuilderPatchMixin, fetch_cluster_ids +from backend.ticket.builders.mysql.base import ( + MySQLBaseOperateDetailSerializer, + MySQLBaseOperateResourceParamBuilder, + MySQLClustersTakeDownDetailsSerializer, +) +from backend.ticket.constants import TicketType + + +class BaseTendbTicketFlowBuilder(MySQLTicketFlowBuilderPatchMixin, TicketFlowBuilder): + group = DBType.TenDBCluster.value + + +class TendbBasePauseParamBuilder(builders.PauseParamBuilder): + pass + + +class TendbBaseOperateDetailSerializer(MySQLBaseOperateDetailSerializer): + """ + tendbcluster操作的基类,主要功能: + 1. 屏蔽序列化的to_representation + 2. 存放tendbcluster操作的各种校验逻辑 + """ + + # 实例不可用时,还能正常提单类型的白名单 + SPIDER_UNAVAILABLE_WHITELIST = [] + REMOTE_MASTER_UNAVAILABLE_WHITELIST = [] + REMOTE_SLAVE_UNAVAILABLE_WHITELIST = [] + # 集群的flag状态与白名单的映射表 + unavailable_whitelist__status_flag = { + ClusterTenDBClusterStatusFlag.SpiderUnavailable: SPIDER_UNAVAILABLE_WHITELIST, + ClusterTenDBClusterStatusFlag.RemoteMasterUnavailable: REMOTE_MASTER_UNAVAILABLE_WHITELIST, + ClusterTenDBClusterStatusFlag.RemoteSlaveUnavailable: REMOTE_SLAVE_UNAVAILABLE_WHITELIST, + } + + @classmethod + def fetch_cluster_map(cls, attrs): + cluster_ids = fetch_cluster_ids(attrs) + clusters = Cluster.objects.prefetch_related("proxyinstance_set", "storageinstance_set").filter( + id__in=cluster_ids + ) + cluster_id__cluster = {cluster.id: cluster for cluster in clusters} + return cluster_id__cluster + + def validate_max_spider_master_mnt_count(self, attrs): + """校验部署后spider_master + spider_mnt的数量<37""" + cluster_id__cluster = self.fetch_cluster_map(attrs) + for info in attrs["infos"]: + cluster = cluster_id__cluster[info["cluster_id"]] + # 对于spider-slave的情况不校验 + if ( + self.context["ticket_type"] == TicketType.TENDBCLUSTER_SPIDER_ADD_NODES + and info["add_spider_role"] == TenDBClusterSpiderRole.SPIDER_SLAVE + ): + continue + + # 获取当前存在的spider master/spider mnt 节点数量 以及 新加入的节点数量 + spider_master_mnt_count = cluster.proxyinstance_set.filter( + Q(tendbclusterspiderext__spider_role=TenDBClusterSpiderRole.SPIDER_MASTER) + | Q(tendbclusterspiderext__spider_role=TenDBClusterSpiderRole.SPIDER_MNT) + ).count() + if self.context["ticket_type"] == TicketType.TENDBCLUSTER_SPIDER_ADD_NODES: + new_add_count = info["resource_spec"]["spider_ip_list"]["count"] + else: + new_add_count = len(info["spider_ip_list"]) + + if spider_master_mnt_count + new_add_count > MAX_SPIDER_MASTER_COUNT: + raise serializers.ValidationError(_("【{}】请保证集群部署的接入层主节点和运维节点的总和小于37").format(cluster.name)) + + def validate_min_spider_count(self, attrs): + """校验缩容后,spider节点能满足最小限度""" + cluster_id__cluster = self.fetch_cluster_map(attrs) + for info in attrs["infos"]: + cluster = cluster_id__cluster[info["cluster_id"]] + + spider_node_count = cluster.proxyinstance_set.filter( + tendbclusterspiderext__spider_role=info["reduce_spider_role"] + ).count() + if info["spider_reduced_to_count"] >= spider_node_count: + raise serializers.ValidationError(_("【{}】请保证缩容后的接入层数量小于当前节点数量").format(cluster.name)) + + role = info["reduce_spider_role"] + if ( + role == TenDBClusterSpiderRole.SPIDER_MASTER + and info["spider_reduced_to_count"] < MIN_SPIDER_MASTER_COUNT + ): + raise serializers.ValidationError(_("【{}】请保证缩容后的接入层spider master数量>1").format(cluster.name)) + + if ( + role == TenDBClusterSpiderRole.SPIDER_SLAVE + and info["spider_reduced_to_count"] < MIN_SPIDER_SLAVE_COUNT + ): + raise serializers.ValidationError(_("【{}】请保证缩容后的接入层spider master数量>0").format(cluster.name)) + + +class TendbClustersTakeDownDetailsSerializer(MySQLClustersTakeDownDetailsSerializer): + is_only_delete_slave_domain = serializers.BooleanField(help_text=_("是否只禁用只读接入层"), required=False, default=False) + is_only_add_slave_domain = serializers.BooleanField(help_text=_("是否只启用只读接入层"), required=False, default=False) + + +class TendbBaseOperateResourceParamBuilder(MySQLBaseOperateResourceParamBuilder): + pass diff --git a/dbm-ui/backend/ticket/builders/spider/tendb_apply.py b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_apply.py similarity index 78% rename from dbm-ui/backend/ticket/builders/spider/tendb_apply.py rename to dbm-ui/backend/ticket/builders/tendbcluster/tendb_apply.py index 119dde029d..4b1182dd63 100644 --- a/dbm-ui/backend/ticket/builders/spider/tendb_apply.py +++ b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_apply.py @@ -16,17 +16,18 @@ from backend.components import DBConfigApi from backend.components.dbconfig import constants as dbconf_const from backend.db_meta.enums import ClusterType -from backend.db_meta.models import ClusterDeployPlan, Spec from backend.db_services.dbbase.constants import IpSource from backend.db_services.ipchooser.query.resource import ResourceQueryHelper from backend.flow.engine.controller.spider import SpiderController from backend.ticket import builders -from backend.ticket.builders.spider.base import BaseTendbTicketFlowBuilder +from backend.ticket.builders.common.base import CommonValidate +from backend.ticket.builders.tendbcluster.base import BaseTendbTicketFlowBuilder from backend.ticket.constants import TicketType class TenDBClusterApplyDetailSerializer(serializers.Serializer): bk_cloud_id = serializers.IntegerField(help_text=_("云区域ID")) + db_app_abbr = serializers.CharField(help_text=_("业务英文缩写")) cluster_name = serializers.CharField(help_text=_("集群名")) city_code = serializers.CharField( help_text=_("城市代码"), required=False, allow_blank=True, allow_null=True, default="" @@ -36,9 +37,9 @@ class TenDBClusterApplyDetailSerializer(serializers.Serializer): help_text=_("主机来源"), choices=IpSource.get_choices(), default=IpSource.RESOURCE_POOL.value ) resource_spec = serializers.JSONField(help_text=_("部署规格")) - resource_plan = serializers.JSONField(help_text=_("部署方案")) spider_port = serializers.IntegerField(help_text=_("集群访问端口")) - immutable_domain = serializers.CharField(help_text=_("集群访问域名")) + cluster_shard_num = serializers.IntegerField(help_text=_("集群分片数")) + remote_shard_num = serializers.IntegerField(help_text=_("单机分片数")) # display fields bk_cloud_name = serializers.SerializerMethodField(help_text=_("云区域")) @@ -46,8 +47,7 @@ class TenDBClusterApplyDetailSerializer(serializers.Serializer): version = serializers.SerializerMethodField(help_text=_("数据库版本")) db_module_name = serializers.SerializerMethodField(help_text=_("DB模块名")) city_name = serializers.SerializerMethodField(help_text=_("城市名")) - cluster_shard_num = serializers.SerializerMethodField(help_text=_("集群分片数")) - machine_pair_cnt = serializers.SerializerMethodField(help_text=_("机器数")) + machine_pair_cnt = serializers.SerializerMethodField(help_text=_("机器组数")) def get_bk_cloud_name(self, obj): clouds = ResourceQueryHelper.search_cc_cloud(get_cache=True) @@ -67,13 +67,13 @@ def get_city_name(self, obj): city_code = obj["city_code"] return self.context["ticket_ctx"].city_map.get(city_code, city_code) - def get_cluster_shard_num(self, obj): - return obj["cluster_shard_num"] - def get_machine_pair_cnt(self, obj): - return obj["machine_pair_cnt"] + return obj["cluster_shard_num"] / obj["remote_shard_num"] def validate(self, attrs): + # 校验集群域名合法 + CommonValidate.validate_generate_domain("spider", attrs["cluster_name"], attrs["db_app_abbr"]) + CommonValidate.validate_generate_domain("spider-slave", attrs["cluster_name"], attrs["db_app_abbr"]) # TODO: spider集群部署校验 return attrs @@ -86,6 +86,7 @@ def format_ticket_data(self): self.ticket_data.update( module=str(self.ticket.details["db_module_id"]), city=self.ticket.details["city_code"], + immutable_domain=f"spider.{self.ticket_data['cluster_name']}.{self.ticket_data['db_app_abbr']}.db", ) @@ -93,20 +94,17 @@ class TenDBClusterApplyResourceParamBuilder(builders.ResourceApplyParamBuilder): def post_callback(self): next_flow = self.ticket.next_flow() nodes = next_flow.details["ticket_data"].pop("nodes") - spider_ip_list, mysql_ip_list = nodes["spider"], [*nodes["master"], *nodes["slave"]] - - # 补充remote的规格信息 resource_spec = next_flow.details["ticket_data"]["resource_spec"] - resource_spec["remote"] = resource_spec.pop("master") - resource_spec.pop("slave") + # 格式化后台角色信息 + resource_spec["remote"], __ = resource_spec.pop("master"), resource_spec.pop("slave") next_flow.details["ticket_data"].update( - spider_ip_list=spider_ip_list, mysql_ip_list=mysql_ip_list, resource_spec=resource_spec + spider_ip_list=nodes["spider"], remote_group=nodes["backend_group"], resource_spec=resource_spec ) next_flow.save(update_fields=["details"]) -@builders.BuilderFactory.register(TicketType.TENDB_CLUSTER_APPLY) +@builders.BuilderFactory.register(TicketType.TENDBCLUSTER_APPLY, is_apply=True, cluster_type=ClusterType.TenDBCluster) class TenDBClusterApplyFlowBuilder(BaseTendbTicketFlowBuilder): serializer = TenDBClusterApplyDetailSerializer inner_flow_builder = TenDBClusterApplyFlowParamBuilder @@ -116,15 +114,6 @@ class TenDBClusterApplyFlowBuilder(BaseTendbTicketFlowBuilder): def patch_ticket_detail(self): """补充spider申请的需求信息参数""" details = self.ticket.details - - # 补充部署方案信息 - deploy_plan = ClusterDeployPlan.objects.get(id=details["resource_plan"]["resource_plan_id"]) - details.update( - deploy_plan_name=deploy_plan.name, - cluster_shard_num=deploy_plan.shard_cnt, - machine_pair_cnt=deploy_plan.machine_pair_cnt, - ) - # 补充字符集和版本信息 db_config = DBConfigApi.query_conf_item( { @@ -141,10 +130,6 @@ def patch_ticket_detail(self): charset=db_config.get("charset"), db_version=db_config.get("db_version"), spider_version=db_config.get("spider_version"), - # TODO: 暂时为了测试用,后续可以删除这些参数 - start_mysql_port=21000, - ctl_charset=db_config.get("charset"), - spider_charset=db_config.get("charset"), ) self.ticket.save(update_fields=["details"]) diff --git a/dbm-ui/backend/ticket/builders/tendbcluster/tendb_authorize_rules.py b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_authorize_rules.py new file mode 100644 index 0000000000..52a13635ff --- /dev/null +++ b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_authorize_rules.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 backend.ticket import builders +from backend.ticket.builders.mysql.mysql_authorize_rules import ( + MySQLAuthorizeRulesFlowParamBuilder, + MySQLAuthorizeRulesSerializer, + MySQLExcelAuthorizeRulesSerializer, +) +from backend.ticket.builders.tendbcluster.base import BaseTendbTicketFlowBuilder +from backend.ticket.constants import TicketType + + +@builders.BuilderFactory.register(TicketType.TENDBCLUSTER_AUTHORIZE_RULES) +class TendbClusterAuthorizeRulesFlowBuilder(BaseTendbTicketFlowBuilder): + serializer = MySQLAuthorizeRulesSerializer + inner_flow_builder = MySQLAuthorizeRulesFlowParamBuilder + inner_flow_name = _("TenDB Cluster 授权执行") + + @property + def need_itsm(self): + return False + + +@builders.BuilderFactory.register(TicketType.TENDBCLUSTER_EXCEL_AUTHORIZE_RULES) +class TendbClusterAuthorizeRulesFlowBuilder(TendbClusterAuthorizeRulesFlowBuilder): + serializer = MySQLExcelAuthorizeRulesSerializer + inner_flow_name = _("TenDB Cluster 授权执行") diff --git a/dbm-ui/backend/ticket/builders/tendbcluster/tendb_backup.py b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_backup.py new file mode 100644 index 0000000000..51cf63d13d --- /dev/null +++ b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_backup.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 gettext_lazy as _ +from rest_framework import serializers + +from backend.flow.engine.controller.spider import SpiderController +from backend.ticket import builders +from backend.ticket.builders.mysql.base import DBTableField +from backend.ticket.builders.tendbcluster.base import BaseTendbTicketFlowBuilder, TendbBaseOperateDetailSerializer +from backend.ticket.builders.tendbcluster.tendb_full_backup import TendbFullBackUpDetailSerializer +from backend.ticket.constants import TicketType + + +class TendbBackUpDetailSerializer(TendbBaseOperateDetailSerializer): + class TendbBackUpItemSerializer(serializers.Serializer): + cluster_id = serializers.IntegerField(help_text=_("集群ID")) + backup_local = serializers.CharField(help_text=_("备份位置")) + db_patterns = serializers.ListField(help_text=_("匹配DB列表"), child=DBTableField(db_field=True)) + ignore_dbs = serializers.ListField(help_text=_("忽略DB列表"), child=DBTableField(db_field=True)) + table_patterns = serializers.ListField(help_text=_("匹配Table列表"), child=DBTableField()) + ignore_tables = serializers.ListField(help_text=_("忽略Table列表"), child=DBTableField()) + + infos = serializers.ListSerializer(help_text=_("库表备份信息"), child=TendbBackUpItemSerializer()) + + def validate(self, attrs): + for info in attrs["infos"]: + TendbFullBackUpDetailSerializer.get_backup_local_params(info) + + # 库表选择器校验 + super().validate_database_table_selector(attrs, role_key="backup_local") + return attrs + + +class TendbBackUpFlowParamBuilder(builders.FlowParamBuilder): + controller = SpiderController.database_table_backup + + +@builders.BuilderFactory.register(TicketType.TENDBCLUSTER_DB_TABLE_BACKUP) +class TendbBackUpFlowBuilder(BaseTendbTicketFlowBuilder): + serializer = TendbBackUpDetailSerializer + inner_flow_builder = TendbBackUpFlowParamBuilder + inner_flow_name = _("TenDB Cluster 库表备份") diff --git a/dbm-ui/backend/ticket/builders/tendbcluster/tendb_checksum.py b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_checksum.py new file mode 100644 index 0000000000..ca6d4b1e8e --- /dev/null +++ b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_checksum.py @@ -0,0 +1,198 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 typing import Any, Dict, List + +from django.utils.translation import gettext_lazy as _ +from rest_framework import serializers + +from backend.configuration.constants import DBType +from backend.db_meta.models import Cluster, StorageInstance, TenDBClusterStorageSet +from backend.flow.engine.controller.mysql import MySQLController +from backend.flow.engine.controller.spider import SpiderController +from backend.ticket import builders +from backend.ticket.builders.common.constants import ( + MySQLChecksumTicketMode, + MySQLDataRepairTriggerMode, + TendbChecksumScope, +) +from backend.ticket.builders.mysql.base import DBTableField +from backend.ticket.builders.mysql.mysql_checksum import ( + MySQLChecksumFlowBuilder, + MySQLChecksumFlowParamBuilder, + MySQLDataRepairFlowParamBuilder, +) +from backend.ticket.builders.tendbcluster.base import TendbBaseOperateDetailSerializer +from backend.ticket.constants import TicketType +from backend.ticket.models import Flow + + +class TendbChecksumDetailSerializer(TendbBaseOperateDetailSerializer): + class DataRepairSerializer(serializers.Serializer): + is_repair = serializers.BooleanField(help_text=_("是否修复")) + mode = serializers.ChoiceField(help_text=_("数据校验后修复执行类型"), choices=MySQLChecksumTicketMode.get_choices()) + + class ChecksumDataInfoSerializer(serializers.Serializer): + class BackupInfoSerializer(serializers.Serializer): + master = serializers.CharField(help_text=_("主库实例"), required=False, allow_null=True, allow_blank=True) + slave = serializers.CharField(help_text=_("从库实例"), required=False, allow_null=True, allow_blank=True) + db_patterns = serializers.ListField(help_text=_("匹配DB列表"), child=DBTableField(db_field=True)) + ignore_dbs = serializers.ListField(help_text=_("忽略DB列表"), child=DBTableField(db_field=True)) + table_patterns = serializers.ListField(help_text=_("匹配Table列表"), child=DBTableField()) + ignore_tables = serializers.ListField(help_text=_("忽略Table列表"), child=DBTableField()) + + cluster_id = serializers.IntegerField(help_text=_("集群ID")) + checksum_scope = serializers.ChoiceField(help_text=_("校验范围"), choices=TendbChecksumScope.get_choices()) + backup_infos = serializers.ListSerializer(help_text=_("备份信息"), child=BackupInfoSerializer()) + + data_repair = DataRepairSerializer(help_text=_("数据修复信息")) + runtime_hour = serializers.IntegerField(help_text=_("超时时间")) + timing = serializers.CharField(help_text=_("定时触发时间")) + infos = serializers.ListField(help_text=_("全备信息列表"), child=ChecksumDataInfoSerializer()) + is_sync_non_innodb = serializers.BooleanField(help_text=_("非innodb表是否修复"), required=False, default=False) + + def validate(self, attrs): + # super().validate(attrs) + return attrs + + +class TendbChecksumParamBuilder(MySQLChecksumFlowParamBuilder): + controller = SpiderController.spider_checksum + + def _get_backup_table_info(self, backup_info): + return { + "db_patterns": backup_info["db_patterns"], + "ignore_dbs": backup_info["ignore_dbs"], + "table_patterns": backup_info["table_patterns"], + "ignore_tables": backup_info["ignore_tables"], + } + + def _get_instance_related_info(self, inst): + return { + "id": inst.id, + "ip": inst.machine.ip, + "port": inst.port, + "instance_inner_role": inst.instance_inner_role, + } + + def fetch_cluster_shard_infos(self, cluster, backup_table_info): + storage_set = TenDBClusterStorageSet.objects.select_related("storage_instance_tuple").filter(cluster=cluster) + shard_infos: List[Dict[str, Any]] = [] + for shard in storage_set: + # 排除spider集群迁移的情况(这个放到具体场景下做)。正常checksum提单,一个分片只有一主一从 + master = self._get_instance_related_info(shard.storage_instance_tuple.ejector) + slave = self._get_instance_related_info(shard.storage_instance_tuple.receiver) + shard_infos.append({"shard_id": shard.shard_id, "master": master, "slaves": [slave], **backup_table_info}) + + return shard_infos + + def fetch_instance_shard_infos(self, cluster, master, backup_table_info): + master_inst = StorageInstance.find_insts_by_addresses([master]).first() + inst_tuple = master_inst.as_ejector.first() + master_info = self._get_instance_related_info(master_inst) + slave_info = self._get_instance_related_info(inst_tuple.receiver) + + shard_info = { + "shard_id": inst_tuple.tendbclusterstorageset.shard_id, + "master": master_info, + "slaves": [slave_info], + **backup_table_info, + } + + return shard_info + + def format_ticket_data(self): + cluster_ids = [info["cluster_id"] for info in self.ticket_data["infos"]] + cluster_id__cluster_map = {cluster.id: cluster for cluster in Cluster.objects.filter(id__in=cluster_ids)} + for info in self.ticket_data["infos"]: + cluster = cluster_id__cluster_map[info["cluster_id"]] + + # 如果校验范围为全库,则查询所有的分片信息。 否则根据实例查询对应分片信息 + if info["checksum_scope"] == TendbChecksumScope.ALL: + backup_table_info = self._get_backup_table_info(info["backup_infos"][0]) + shard_infos = self.fetch_cluster_shard_infos(cluster, backup_table_info) + else: + shard_infos: List[Dict[str, Any]] = [] + for backup_info in info["backup_infos"]: + backup_table_info = self._get_backup_table_info(backup_info) + sub_shard_info = self.fetch_instance_shard_infos(cluster, backup_info["master"], backup_table_info) + shard_infos.append(sub_shard_info) + + # 填充校验的分片信息,填充时区,域名和云区域 + info["shards"] = shard_infos + info["time_zone"] = cluster.time_zone + info["immute_domain"] = cluster.immute_domain + info["bk_cloud_id"] = cluster.bk_cloud_id + + def make_repair_data(self, data_repair_name): + """构造数据修复的数据""" + + # 获取每个实例的数据校验结果 + consistent_list = self.ticket_data["is_consistent_list"] + slave_address_list = list(consistent_list.keys()) + slaves = StorageInstance.find_insts_by_addresses(addresses=slave_address_list) + data_repair_infos = [ + { + "cluster_id": slave.cluster.first().id, + "master": self._get_instance_related_info(slave.as_receiver.first().ejector), + "slaves": [ + {**self._get_instance_related_info(slave), "is_consistent": consistent_list[slave.ip_port]} + ], + } + for slave in slaves + ] + + # 获取数据修复的flow + table_sync_flow = Flow.objects.get(ticket=self.ticket, details__controller_info__func_name=data_repair_name) + + # 更新校验表和触发类型 + table_sync_flow.details["ticket_data"].update( + checksum_table=self.ticket_data["checksum_table"], + trigger_type=MySQLDataRepairTriggerMode.MANUAL.value, + infos=data_repair_infos, + ) + table_sync_flow.save(update_fields=["details"]) + + def post_callback(self): + """根据数据校验的结果,填充上下文信息给数据修复""" + if self.skip_data_repair(MySQLController.mysql_pt_table_sync_scene.__name__, TicketType.TENDBCLUSTER_CHECKSUM): + return + + self.make_repair_data(MySQLController.mysql_pt_table_sync_scene.__name__) + + +class TendbChecksumPauseParamBuilder(builders.PauseParamBuilder): + """TendbCluster 数据修复人工确认执行的单据参数""" + + def format(self): + self.params["pause_type"] = TicketType.TENDBCLUSTER_CHECKSUM + + +class TendbDataRepairFlowParamBuilder(MySQLDataRepairFlowParamBuilder): + """TendbCluster 数据修复执行的单据参数""" + + pass + + +@builders.BuilderFactory.register(TicketType.TENDBCLUSTER_CHECKSUM) +class TendbChecksumFlowBuilder(MySQLChecksumFlowBuilder): + group = DBType.TenDBCluster.value + serializer = TendbChecksumDetailSerializer + # 流程构造类 + checksum_flow_builder = TendbChecksumParamBuilder + pause_flow_builder = TendbChecksumPauseParamBuilder + data_repair_flow_builder = TendbDataRepairFlowParamBuilder + + def patch_ticket_detail(self): + pass + + def custom_ticket_flows(self): + return super().custom_ticket_flows() diff --git a/dbm-ui/backend/ticket/builders/tendbcluster/tendb_clear.py b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_clear.py new file mode 100644 index 0000000000..566a005fcd --- /dev/null +++ b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_clear.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 backend.flow.engine.controller.spider import SpiderController +from backend.ticket import builders +from backend.ticket.builders.mysql.mysql_ha_clear import MySQLHaClearDetailSerializer +from backend.ticket.builders.tendbcluster.base import BaseTendbTicketFlowBuilder +from backend.ticket.constants import TicketType + + +class TendbClearDetailSerializer(MySQLHaClearDetailSerializer): + def validate(self, attrs): + return super().validate(attrs) + + +class TendbClearFlowParamBuilder(builders.FlowParamBuilder): + """TendbCluster清档执行单据参数""" + + controller = SpiderController.truncate_database + + def format_ticket_data(self): + pass + + +@builders.BuilderFactory.register(TicketType.TENDBCLUSTER_TRUNCATE_DATABASE) +class MySQLHaClearFlowBuilder(BaseTendbTicketFlowBuilder): + serializer = TendbClearDetailSerializer + inner_flow_builder = TendbClearFlowParamBuilder + inner_flow_name = _("TenDB Cluster 清档执行") diff --git a/dbm-ui/backend/ticket/builders/tendbcluster/tendb_clone_rules.py b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_clone_rules.py new file mode 100644 index 0000000000..714a9da07b --- /dev/null +++ b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_clone_rules.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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.core.cache import cache +from django.utils.translation import ugettext_lazy as _ + +from backend.configuration.constants import DBType +from backend.db_services.mysql.permission.exceptions import CloneDataHasExpiredException +from backend.ticket import builders +from backend.ticket.builders.mysql.mysql_clone_rules import ( + MySQLClientCloneRulesFlowBuilder, + MySQLCloneRulesFlowParamBuilder, + MySQLCloneRulesSerializer, +) +from backend.ticket.builders.tendbcluster.base import BaseTendbTicketFlowBuilder +from backend.ticket.constants import TicketType + + +class TendbClusterCloneRulesSerializer(MySQLCloneRulesSerializer): + pass + + +class TendbClusterCloneRulesFlowParamBuilder(MySQLCloneRulesFlowParamBuilder): + pass + + +@builders.BuilderFactory.register(TicketType.TENDBCLUSTER_CLIENT_CLONE_RULES) +class TendbClusterClientCloneRulesFlowBuilder(MySQLClientCloneRulesFlowBuilder): + group = DBType.TenDBCluster.value + serializer = TendbClusterCloneRulesSerializer + inner_flow_name = _("TenDB Cluster 客户端权限克隆执行") + inner_flow_builder = TendbClusterCloneRulesFlowParamBuilder + + @property + def need_itsm(self): + return False + + +@builders.BuilderFactory.register(TicketType.TENDBCLUSTER_INSTANCE_CLONE_RULES) +class TendbClusterInstanceCloneRulesFlowBuilder(TendbClusterClientCloneRulesFlowBuilder): + inner_flow_name = _("TenDB Cluster 实例权限克隆执行") diff --git a/dbm-ui/backend/ticket/builders/tendbcluster/tendb_data_repair.py b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_data_repair.py new file mode 100644 index 0000000000..6757f6fb43 --- /dev/null +++ b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_data_repair.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 typing import List + +from django.utils.translation import ugettext_lazy as _ +from rest_framework import serializers + +from backend.db_meta.models import Cluster +from backend.flow.engine.controller.mysql import MySQLController +from backend.ticket import builders +from backend.ticket.builders.common.constants import MySQLDataRepairTriggerMode +from backend.ticket.builders.mysql.base import BaseMySQLTicketFlowBuilder, MySQLBaseOperateDetailSerializer +from backend.ticket.builders.mysql.mysql_data_repair import MySQLDataRepairDetailSerializer +from backend.ticket.builders.tendbcluster.base import BaseTendbTicketFlowBuilder +from backend.ticket.constants import FlowRetryType, FlowType, TicketType +from backend.ticket.models import Flow + + +class TendbDataRepairDetailSerializer(MySQLDataRepairDetailSerializer): + pass + + +class TendbDataRepairFlowParamBuilder(builders.FlowParamBuilder): + """MySQL 数据校验执行单据参数""" + + controller = MySQLController.mysql_pt_table_sync_scene + + +@builders.BuilderFactory.register(TicketType.TENDBCLUSTER_DATA_REPAIR) +class TendbDataRepairFlowBuilder(BaseTendbTicketFlowBuilder): + serializer = TendbDataRepairDetailSerializer + inner_flow_builder = TendbDataRepairFlowParamBuilder + inner_flow_name = _("Tendb Cluster 数据修复执行") diff --git a/dbm-ui/backend/ticket/builders/tendbcluster/tendb_destroy.py b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_destroy.py new file mode 100644 index 0000000000..ea72426e3f --- /dev/null +++ b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_destroy.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 backend.db_meta.enums import ClusterPhase +from backend.flow.engine.controller.spider import SpiderController +from backend.ticket import builders +from backend.ticket.builders.tendbcluster.base import ( + BaseTendbTicketFlowBuilder, + TendbClustersTakeDownDetailsSerializer, +) +from backend.ticket.constants import TicketType + + +class TendbDestroyDetailSerializer(TendbClustersTakeDownDetailsSerializer): + pass + + +class TendbDestroyFlowParamBuilder(builders.FlowParamBuilder): + controller = SpiderController.spider_cluster_destroy_scene + + +@builders.BuilderFactory.register(TicketType.TENDBCLUSTER_DESTROY, phase=ClusterPhase.DESTROY) +class TendbDestroyFlowBuilder(BaseTendbTicketFlowBuilder): + + serializer = TendbDestroyDetailSerializer + inner_flow_builder = TendbDestroyFlowParamBuilder + inner_flow_name = _("TenDB Cluster 下架执行") diff --git a/dbm-ui/backend/ticket/builders/tendbcluster/tendb_disable.py b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_disable.py new file mode 100644 index 0000000000..3d7dec6418 --- /dev/null +++ b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_disable.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 backend.db_meta.enums import ClusterPhase +from backend.flow.engine.controller.spider import SpiderController +from backend.ticket import builders +from backend.ticket.builders.mysql.base import BaseMySQLTicketFlowBuilder +from backend.ticket.builders.tendbcluster.base import ( + BaseTendbTicketFlowBuilder, + TendbClustersTakeDownDetailsSerializer, +) +from backend.ticket.constants import TicketType + + +class TendbDisableDetailSerializer(TendbClustersTakeDownDetailsSerializer): + pass + + +class TendbDisableFlowParamBuilder(builders.FlowParamBuilder): + controller = SpiderController.spider_cluster_disable_scene + + +@builders.BuilderFactory.register(TicketType.TENDBCLUSTER_DISABLE, phase=ClusterPhase.OFFLINE) +class TendbEnableFlowBuilder(BaseTendbTicketFlowBuilder): + + serializer = TendbDisableDetailSerializer + inner_flow_builder = TendbDisableFlowParamBuilder + inner_flow_name = _("TenDB Cluster 禁用执行") diff --git a/dbm-ui/backend/ticket/builders/spider/tendb_enable.py b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_enable.py similarity index 84% rename from dbm-ui/backend/ticket/builders/spider/tendb_enable.py rename to dbm-ui/backend/ticket/builders/tendbcluster/tendb_enable.py index 4fbe86a118..0c6e912b6c 100644 --- a/dbm-ui/backend/ticket/builders/spider/tendb_enable.py +++ b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_enable.py @@ -11,10 +11,14 @@ from django.utils.translation import ugettext_lazy as _ +from backend.db_meta.enums import ClusterPhase from backend.flow.engine.controller.spider import SpiderController from backend.ticket import builders from backend.ticket.builders.mysql.base import BaseMySQLTicketFlowBuilder -from backend.ticket.builders.spider.base import BaseTendbTicketFlowBuilder, TendbClustersTakeDownDetailsSerializer +from backend.ticket.builders.tendbcluster.base import ( + BaseTendbTicketFlowBuilder, + TendbClustersTakeDownDetailsSerializer, +) from backend.ticket.constants import TicketType @@ -26,7 +30,7 @@ class TendbEnableFlowParamBuilder(builders.FlowParamBuilder): controller = SpiderController.spider_cluster_enable_scene -@builders.BuilderFactory.register(TicketType.TENDB_CLUSTER_ENABLE) +@builders.BuilderFactory.register(TicketType.TENDBCLUSTER_ENABLE, phase=ClusterPhase.ONLINE) class TendbEnableFlowBuilder(BaseTendbTicketFlowBuilder): serializer = TendbEnableDetailSerializer diff --git a/dbm-ui/backend/ticket/builders/tendbcluster/tendb_fixpoint_rollback.py b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_fixpoint_rollback.py new file mode 100644 index 0000000000..92f78cc7fb --- /dev/null +++ b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_fixpoint_rollback.py @@ -0,0 +1,214 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import datetime + +from django.utils.crypto import get_random_string +from django.utils.translation import gettext_lazy as _ +from rest_framework import serializers + +from backend.components import DBConfigApi +from backend.components.dbconfig import constants as dbconf_const +from backend.db_meta.enums import ClusterType, TenDBClusterSpiderRole +from backend.db_meta.enums.comm import SystemTagEnum, TagType +from backend.db_meta.models import AppCache, Cluster, Tag +from backend.db_services.dbbase.constants import IpSource +from backend.flow.engine.controller.spider import SpiderController +from backend.ticket import builders +from backend.ticket.builders.common.constants import FixpointRollbackType +from backend.ticket.builders.mysql.base import DBTableField +from backend.ticket.builders.mysql.mysql_fixpoint_rollback import MySQLFixPointRollbackDetailSerializer +from backend.ticket.builders.tendbcluster.base import BaseTendbTicketFlowBuilder, TendbBaseOperateDetailSerializer +from backend.ticket.builders.tendbcluster.tendb_apply import TenDBClusterApplyResourceParamBuilder +from backend.ticket.constants import FlowRetryType, FlowType, TicketType +from backend.ticket.models import ClusterOperateRecord, Flow +from backend.utils.time import date2str + + +class TendbFixPointRollbackDetailSerializer(TendbBaseOperateDetailSerializer): + cluster_id = serializers.IntegerField(help_text=_("集群ID")) + rollback_type = serializers.ChoiceField(help_text=_("回档类型"), choices=FixpointRollbackType.get_choices()) + rollback_time = serializers.CharField( + help_text=_("回档时间"), required=False, allow_blank=True, allow_null=True, default="" + ) + backupinfo = serializers.DictField( + help_text=_("备份文件信息"), required=False, allow_null=True, allow_empty=True, default={} + ) + databases = serializers.ListField(help_text=_("目标库列表"), child=DBTableField(db_field=True)) + databases_ignore = serializers.ListField(help_text=_("忽略库列表"), child=DBTableField(db_field=True), required=False) + tables = serializers.ListField(help_text=_("目标table列表"), child=DBTableField()) + tables_ignore = serializers.ListField(help_text=_("忽略table列表"), child=DBTableField(), required=False) + + def validate(self, attrs): + # 校验集群是否可用 + super().validate_cluster_can_access(attrs) + + # 校验回档信息 + MySQLFixPointRollbackDetailSerializer.validate_rollback_info(attrs, datetime.datetime.now()) + + return attrs + + +class TendbFixPointRollbackFlowParamBuilder(builders.FlowParamBuilder): + controller = SpiderController.tendb_cluster_rollback_data + + def format_ticket_data(self): + pass + + def pre_callback(self): + rollback_flow = self.ticket.current_flow() + ticket_data = rollback_flow.details["ticket_data"] + + # 为定点构造的flow填充临时集群信息 + source_cluster_id = ticket_data.pop("cluster_id") + # 对同一个集群同一天回档26^4才有可能重名, 暂时无需担心 + target_cluster = Cluster.objects.get(name=ticket_data["apply_details"]["cluster_name"]) + ticket_data.update(source_cluster_id=source_cluster_id, target_cluster_id=target_cluster.id) + rollback_flow.save(update_fields=["details"]) + + # 对临时集群记录变更 + temporary_tag, _ = Tag.objects.get_or_create( + bk_biz_id=self.ticket.bk_biz_id, name=SystemTagEnum.TEMPORARY.value, type=TagType.SYSTEM.value + ) + target_cluster.tag.add(temporary_tag) + target_cluster.save(update_fields=["tag"]) + ClusterOperateRecord.objects.get_or_create( + cluster_id=target_cluster.id, ticket=self.ticket, flow=rollback_flow + ) + + +class TendbApplyTemporaryFlowParamBuilder(builders.FlowParamBuilder): + controller = SpiderController.spider_cluster_apply_scene + + def format_ticket_data(self): + self.ticket_data = self.ticket_data["apply_details"] + # 填充common参数 + super().add_common_params() + + +class TenDBClusterApplyCopyResourceParamBuilder(TenDBClusterApplyResourceParamBuilder): + def format(self): + self.ticket_data = self.ticket_data["apply_details"] + + def post_callback(self): + super().post_callback() + + +@builders.BuilderFactory.register(TicketType.TENDBCLUSTER_ROLLBACK_CLUSTER, is_apply=True) +class MysqlFixPointRollbackFlowBuilder(BaseTendbTicketFlowBuilder): + serializer = TendbFixPointRollbackDetailSerializer + + def get_machine_spec(self, machine_infos): + # 获取机器的数量和规格,默认认为同一角色的规格是相同的 + machine_count = len(set([data["machine__bk_host_id"] for data in machine_infos])) + spec_id = machine_infos[0]["machine__spec_id"] + return machine_count, spec_id + + def get_cluster_config(self, cluster, details): + db_config = DBConfigApi.query_conf_item( + { + "bk_biz_id": str(cluster.bk_biz_id), + "level_name": dbconf_const.LevelName.MODULE, + "level_value": str(cluster.db_module_id), + "conf_file": dbconf_const.DEPLOY_FILE_NAME, + "conf_type": dbconf_const.ConfType.DEPLOY, + "namespace": ClusterType.TenDBCluster, + "format": dbconf_const.FormatType.MAP, + } + )["content"] + details.update( + charset=db_config.get("charset"), + db_version=db_config.get("db_version"), + spider_version=db_config.get("spider_version"), + ) + + def get_cluster_apply_spec(self, cluster, details): + # 获取集群分片数 + cluster_shard_num = cluster.tendbclusterstorageset_set.count() + # 获取spider的部署规格和机器数 + spider_machine_data = list( + cluster.proxyinstance_set.filter( + tendbclusterspiderext__spider_role=TenDBClusterSpiderRole.SPIDER_MASTER.value + ).values("machine__bk_host_id", "machine__spec_id") + ) + spider_machine_count, spider_spec_id = self.get_machine_spec(spider_machine_data) + # 获取remote的部署规格和机器组数 + remote_machine_data = [] + for storage_set in cluster.tendbclusterstorageset_set.all(): + master_machine = storage_set.storage_instance_tuple.ejector.machine + remote_machine_data.append( + {"machine__bk_host_id": master_machine.bk_host_id, "machine__spec_id": master_machine.spec_id} + ) + + remote_machine_count, remote_spec_id = self.get_machine_spec(remote_machine_data) + # 填充资源池规格信息和分片信息 + resource_spec = { + "spider": {"spec_id": spider_spec_id, "count": spider_machine_count}, + "backend_group": {"spec_id": remote_spec_id, "count": remote_machine_count}, + } + details.update( + resource_spec=resource_spec, + cluster_shard_num=cluster_shard_num, + remote_shard_num=int(cluster_shard_num / remote_machine_count), + ) + + def patch_ticket_detail(self): + cluster = Cluster.objects.get(id=self.ticket.details["cluster_id"]) + cluster_name = f"{cluster.name}-tmp{get_random_string(4).lower()}{date2str(datetime.date.today(), '%Y%m%d')}" + db_app_abbr = AppCache.get_app_attr(cluster.bk_biz_id) + + # 集群部署的基本信息 + apply_details = { + "bk_cloud_id": cluster.bk_cloud_id, + "db_app_abbr": db_app_abbr, + "cluster_name": cluster_name, + "city": cluster.region, + "module": cluster.db_module_id, + "immutable_domain": f"spider.{cluster_name}.{db_app_abbr}.db", + "ip_source": IpSource.RESOURCE_POOL, + "spider_port": cluster.proxyinstance_set.first().port, + } + # 填充配置信息 + self.get_cluster_config(cluster, apply_details) + # 填充集群部署规格信息 + self.get_cluster_apply_spec(cluster, apply_details) + + self.ticket.update_details(apply_details=apply_details) + super().patch_ticket_detail() + + def custom_ticket_flows(self): + flows = [ + Flow( + ticket=self.ticket, + flow_type=FlowType.RESOURCE_APPLY, + details=TenDBClusterApplyCopyResourceParamBuilder(self.ticket).get_params(), + flow_alias=_("资源申请"), + ), + Flow( + ticket=self.ticket, + flow_type=FlowType.INNER_FLOW.value, + details=TendbApplyTemporaryFlowParamBuilder(self.ticket).get_params(), + flow_alias=_("回档临时集群部署"), + retry_type=FlowRetryType.MANUAL_RETRY.value, + ), + Flow( + ticket=self.ticket, + flow_type=FlowType.INNER_FLOW.value, + details=TendbFixPointRollbackFlowParamBuilder(self.ticket).get_params(), + flow_alias=_("TenDBCluster 回档执行"), + retry_type=FlowRetryType.MANUAL_RETRY.value, + ), + Flow( + ticket=self.ticket, + flow_type=FlowType.RESOURCE_DELIVERY, + flow_alias=_("资源确认"), + ), + ] + return flows diff --git a/dbm-ui/backend/ticket/builders/tendbcluster/tendb_flashback.py b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_flashback.py new file mode 100644 index 0000000000..fb82b6c1e6 --- /dev/null +++ b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_flashback.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 gettext_lazy as _ + +from backend.db_services.mysql.remote_service.handlers import RemoteServiceHandler +from backend.flow.engine.controller.spider import SpiderController +from backend.ticket import builders +from backend.ticket.builders.mysql.mysql_flashback import MySQLFlashbackDetailSerializer +from backend.ticket.builders.tendbcluster.base import ( + BaseTendbTicketFlowBuilder, + TendbBaseOperateDetailSerializer, + TendbBasePauseParamBuilder, +) +from backend.ticket.constants import FlowRetryType, TicketType + + +class TendbFlashbackDetailSerializer(MySQLFlashbackDetailSerializer, TendbBaseOperateDetailSerializer): + def validate(self, attrs): + # 校验集群是否可用 + super(TendbBaseOperateDetailSerializer, self).validate_cluster_can_access(attrs) + # 校验闪回时间 + super().validate_flash_time(attrs) + # 校验flash的库表选择器 + RemoteServiceHandler(bk_biz_id=self.context["bk_biz_id"]).check_flashback_database(attrs["infos"]) + + return attrs + + +class TendbFlashbackFlowParamBuilder(builders.FlowParamBuilder): + controller = SpiderController.flashback + + def format_ticket_data(self): + pass + + +@builders.BuilderFactory.register(TicketType.TENDBCLUSTER_FLASHBACK) +class TendbFlashbackFlowBuilder(BaseTendbTicketFlowBuilder): + serializer = TendbFlashbackDetailSerializer + inner_flow_builder = TendbFlashbackFlowParamBuilder + inner_flow_name = _("TenDB Cluster 闪回执行") + retry_type = FlowRetryType.MANUAL_RETRY + pause_node_builder = TendbBasePauseParamBuilder + + @property + def need_manual_confirm(self): + return True diff --git a/dbm-ui/backend/ticket/builders/tendbcluster/tendb_full_backup.py b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_full_backup.py new file mode 100644 index 0000000000..4ea3d22eb1 --- /dev/null +++ b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_full_backup.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 gettext_lazy as _ +from rest_framework import serializers + +from backend.flow.consts import MySQLBackupFileTagEnum, MySQLBackupTypeEnum, TenDBBackUpLocation +from backend.flow.engine.controller.spider import SpiderController +from backend.ticket import builders +from backend.ticket.builders.tendbcluster.base import BaseTendbTicketFlowBuilder, TendbBaseOperateDetailSerializer +from backend.ticket.constants import TicketType + + +class TendbFullBackUpDetailSerializer(TendbBaseOperateDetailSerializer): + class FullBackUpItemSerializer(serializers.Serializer): + class FullBackUpClusterItemSerializer(serializers.Serializer): + cluster_id = serializers.IntegerField(help_text=_("集群ID")) + backup_local = serializers.CharField(help_text=_("备份位置信息")) + + backup_type = serializers.ChoiceField(help_text=_("备份选项"), choices=MySQLBackupTypeEnum.get_choices()) + file_tag = serializers.ChoiceField(help_text=_("备份保存时间"), choices=MySQLBackupFileTagEnum.get_choices()) + clusters = serializers.ListSerializer(help_text=_("集群备份信息"), child=FullBackUpClusterItemSerializer()) + + infos = FullBackUpItemSerializer() + + @classmethod + def get_backup_local_params(cls, info): + """ + 对备份位置进行提取, + 两种情况:remote/spider_mnt::127.0.0.1 + """ + if info["backup_local"] == TenDBBackUpLocation.REMOTE: + return info + + backup_local, spider_mnt_address = info["backup_local"].split("::") + info["backup_local"] = backup_local + info["spider_mnt_address"] = spider_mnt_address + + return info + + def validate(self, attrs): + for cluster_info in attrs["infos"]["clusters"]: + self.get_backup_local_params(cluster_info) + + for cluster in attrs["infos"]["clusters"]: + if cluster["backup_local"] == TenDBBackUpLocation.SPIDER_MNT and "spider_mnt_address" not in cluster: + raise serializers.ValidationError(_("备份位置选择spider_mnt时,请提供运维节点的地址")) + + return attrs + + +class TendbFullBackUpFlowParamBuilder(builders.FlowParamBuilder): + controller = SpiderController.full_backup + + +@builders.BuilderFactory.register(TicketType.TENDBCLUSTER_FULL_BACKUP) +class TendbFullBackUpFlowBuilder(BaseTendbTicketFlowBuilder): + serializer = TendbFullBackUpDetailSerializer + inner_flow_builder = TendbFullBackUpFlowParamBuilder + inner_flow_name = _("TenDB Cluster 全库备份") diff --git a/dbm-ui/backend/ticket/builders/tendbcluster/tendb_import_sqlfile.py b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_import_sqlfile.py new file mode 100644 index 0000000000..995814f273 --- /dev/null +++ b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_import_sqlfile.py @@ -0,0 +1,130 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +import json +import logging + +from django.utils.translation import ugettext as _ +from rest_framework import serializers + +from backend import env +from backend.configuration.constants import DBType +from backend.db_meta.models import Cluster +from backend.db_services.mysql.sql_import.constants import SQLExecuteTicketMode +from backend.db_services.mysql.sql_import.dataclass import SemanticOperateMeta +from backend.db_services.mysql.sql_import.handlers import SQLHandler +from backend.flow.engine.bamboo.engine import BambooEngine +from backend.flow.engine.controller.mysql import MySQLController +from backend.flow.engine.controller.spider import SpiderController +from backend.flow.models import FlowNode, FlowTree +from backend.flow.plugins.components.collections.mysql.semantic_check import SemanticCheckComponent +from backend.ticket import builders +from backend.ticket.builders.mysql.base import BaseMySQLTicketFlowBuilder, MySQLBaseOperateDetailSerializer +from backend.ticket.builders.mysql.mysql_import_sqlfile import ( + MysqlSqlImportBackUpFlowParamBuilder, + MysqlSqlImportDetailSerializer, + MysqlSqlImportFlowBuilder, + MysqlSqlImportFlowParamBuilder, + MysqlSqlImportItsmParamBuilder, +) +from backend.ticket.builders.tendbcluster.base import BaseTendbTicketFlowBuilder +from backend.ticket.builders.tendbcluster.tendb_full_backup import TendbFullBackUpDetailSerializer +from backend.ticket.constants import FlowRetryType, FlowType, TicketType +from backend.ticket.exceptions import TicketBaseException +from backend.ticket.models import Flow + +logger = logging.getLogger("root") + + +class TenDBClusterSqlImportDetailSerializer(MysqlSqlImportDetailSerializer): + pass + + +class TenDBClusterSqlImportItsmParamBuilder(MysqlSqlImportItsmParamBuilder): + pass + + +class TenDBClusterSqlImportBackUpFlowParamBuilder(MysqlSqlImportBackUpFlowParamBuilder): + controller = SpiderController.database_table_backup + + def format_ticket_data(self): + super().format_ticket_data() + for info in self.ticket_data["infos"]: + info["backup_local"] = info["backup_on"] + TendbFullBackUpDetailSerializer.get_backup_local_params(info) + + +class TenDBClusterSqlImportFlowParamBuilder(MysqlSqlImportFlowParamBuilder): + controller = SpiderController.spider_sql_import_scene + + def format_ticket_data(self): + super().format_ticket_data() + + +@builders.BuilderFactory.register(TicketType.TENDBCLUSTER_IMPORT_SQLFILE) +class TenDBClusterSqlImportFlowBuilder(BaseTendbTicketFlowBuilder): + serializer = TenDBClusterSqlImportDetailSerializer + + def patch_ticket_detail(self): + MysqlSqlImportFlowBuilder.patch_sqlimport_ticket_detail(ticket=self.ticket, cluster_type=DBType.MySQL) + + def init_ticket_flows(self): + """ + sql导入根据执行模式可分为三种执行流程: + 手动:语义检查-->单据审批-->手动确认-->(备份)--->sql导入 + 自动:语义检查-->单据审批-->(备份)--->sql导入 + 定时:语义检查-->单据审批-->定时触发-->(备份)--->sql导入 + """ + + flows = [ + Flow( + ticket=self.ticket, + flow_type=FlowType.DESCRIBE_TASK.value, + details=TenDBClusterSqlImportFlowParamBuilder(self.ticket).get_params(), + flow_alias=_("SQL模拟执行状态查询"), + ), + Flow( + ticket=self.ticket, + flow_type=FlowType.BK_ITSM.value, + details=TenDBClusterSqlImportItsmParamBuilder(self.ticket).get_params(), + flow_alias=_("单据审批"), + ), + ] + + mode = self.ticket.details["ticket_mode"]["mode"] + if mode == SQLExecuteTicketMode.MANUAL.value: + flows.append(Flow(ticket=self.ticket, flow_type=FlowType.PAUSE.value, flow_alias=_("人工确认执行"))) + elif mode == SQLExecuteTicketMode.TIMER.value: + flows.append(Flow(ticket=self.ticket, flow_type=FlowType.TIMER.value, flow_alias=_("定时执行"))) + + if self.ticket.details.get("backup"): + flows.append( + Flow( + ticket=self.ticket, + flow_type=FlowType.INNER_FLOW.value, + details=TenDBClusterSqlImportBackUpFlowParamBuilder(self.ticket).get_params(), + retry_type=FlowRetryType.MANUAL_RETRY.value, + flow_alias=_("库表备份"), + ) + ) + + flows.append( + Flow( + ticket=self.ticket, + flow_type=FlowType.INNER_FLOW.value, + details=TenDBClusterSqlImportFlowParamBuilder(self.ticket).get_params(), + retry_type=FlowRetryType.MANUAL_RETRY.value, + flow_alias=_("变更SQL执行"), + ) + ) + + Flow.objects.bulk_create(flows) + return list(Flow.objects.filter(ticket=self.ticket)) diff --git a/dbm-ui/backend/ticket/builders/tendbcluster/tendb_master_fail_over.py b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_master_fail_over.py new file mode 100644 index 0000000000..38a482e2e2 --- /dev/null +++ b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_master_fail_over.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 gettext_lazy as _ +from rest_framework import serializers + +from backend.flow.engine.controller.spider import SpiderController +from backend.ticket import builders +from backend.ticket.builders.tendbcluster.base import BaseTendbTicketFlowBuilder +from backend.ticket.builders.tendbcluster.tendb_master_slave_switch import TendbMasterSlaveSwitchDetailSerializer +from backend.ticket.constants import TicketType + + +class TendbMasterFailOverDetailSerializer(TendbMasterSlaveSwitchDetailSerializer): + force = serializers.BooleanField(help_text=_("是否强制执行(互切不强制,故障切强制)"), required=False, default=True) + serializers.BooleanField(help_text=_("是否检测数据同步延时情况")) + + def validate(self, attrs): + if not attrs["force"]: + raise serializers.ValidationError(_("主故障切换场景需要强制执行")) + + return attrs + + +class TendbMasterFailOverParamBuilder(builders.FlowParamBuilder): + controller = SpiderController.tendbcluster_remote_fail_over_scene + + +@builders.BuilderFactory.register(TicketType.TENDBCLUSTER_MASTER_FAIL_OVER) +class TendbMasterFailOverFlowBuilder(BaseTendbTicketFlowBuilder): + serializer = TendbMasterFailOverDetailSerializer + inner_flow_builder = TendbMasterFailOverParamBuilder + inner_flow_name = _("TendbCluster 主故障切换") diff --git a/dbm-ui/backend/ticket/builders/tendbcluster/tendb_master_slave_switch.py b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_master_slave_switch.py new file mode 100644 index 0000000000..f3466f735a --- /dev/null +++ b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_master_slave_switch.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 gettext_lazy as _ +from rest_framework import serializers + +from backend.flow.engine.controller.spider import SpiderController +from backend.ticket import builders +from backend.ticket.builders.common.base import HostInfoSerializer +from backend.ticket.builders.tendbcluster.base import BaseTendbTicketFlowBuilder, TendbBaseOperateDetailSerializer +from backend.ticket.constants import TicketType + + +class TendbMasterSlaveSwitchDetailSerializer(TendbBaseOperateDetailSerializer): + class InfoSerializer(serializers.Serializer): + class SwitchItemSerializer(serializers.Serializer): + master = HostInfoSerializer(help_text=_("主库信息")) + slave = HostInfoSerializer(help_text=_("从库信息")) + + switch_tuples = serializers.ListSerializer(help_text=_("切换的主从组"), child=SwitchItemSerializer()) + cluster_id = serializers.IntegerField(help_text=_("集群ID")) + + infos = serializers.ListField(help_text=_("单据信息"), child=InfoSerializer()) + force = serializers.BooleanField(help_text=_("是否强制执行(互切不强制,故障切强制)"), default=False, required=False) + is_check_process = serializers.BooleanField(help_text=_("是否检测连接")) + is_check_delay = serializers.BooleanField( + help_text=_("是否检测数据同步延时情况(互切单据延时属于强制检测,故必须传True)"), default=True, required=False + ) + is_verify_checksum = serializers.BooleanField(help_text=_("是否检测历史数据检验结果")) + + def validate(self, attrs): + if attrs["force"] or not attrs["is_check_delay"]: + raise serializers.ValidationError(_("主从互切场景:非强制执行,强制检查延时")) + + return attrs + + +class TendbMasterSlaveSwitchParamBuilder(builders.FlowParamBuilder): + controller = SpiderController.tendb_cluster_remote_switch_scene + + +@builders.BuilderFactory.register(TicketType.TENDBCLUSTER_MASTER_SLAVE_SWITCH) +class TendbMasterSlaveSwitchFlowBuilder(BaseTendbTicketFlowBuilder): + serializer = TendbMasterSlaveSwitchDetailSerializer + inner_flow_builder = TendbMasterSlaveSwitchParamBuilder + inner_flow_name = _("TendbCluster 主从互换执行") diff --git a/dbm-ui/backend/ticket/builders/tendbcluster/tendb_mnt_apply.py b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_mnt_apply.py new file mode 100644 index 0000000000..160517d02d --- /dev/null +++ b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_mnt_apply.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 gettext_lazy as _ +from rest_framework import serializers + +from backend.db_meta.models import Cluster +from backend.flow.engine.controller.spider import SpiderController +from backend.ticket import builders +from backend.ticket.builders.tendbcluster.base import BaseTendbTicketFlowBuilder, TendbBaseOperateDetailSerializer +from backend.ticket.constants import TicketType + + +class TendbMNTApplyDetailSerializer(TendbBaseOperateDetailSerializer): + class MNTApplySerializer(serializers.Serializer): + cluster_id = serializers.IntegerField(help_text=_("集群ID")) + bk_cloud_id = serializers.IntegerField(help_text=_("云区域ID")) + spider_ip_list = serializers.ListField(help_text=_("运维节点信息"), child=serializers.DictField()) + + infos = serializers.ListField(help_text=_("添加spider运维节点信息"), child=MNTApplySerializer()) + + def validate(self, attrs): + super().validate(attrs) + self.validate_max_spider_master_mnt_count(attrs) + return attrs + + +class TendbMNTApplyParamBuilder(builders.FlowParamBuilder): + controller = SpiderController.add_spider_mnt_scene + + def format_ticket_data(self): + cluster_ids = [info["cluster_id"] for info in self.ticket_data["infos"]] + cluster_id__domain = { + cluster.id: cluster.immute_domain for cluster in Cluster.objects.filter(id__in=cluster_ids) + } + for info in self.ticket_data["infos"]: + info.update(immutable_domain=cluster_id__domain[info["cluster_id"]]) + + +@builders.BuilderFactory.register(TicketType.TENDBCLUSTER_SPIDER_MNT_APPLY, is_apply=True) +class TendbMNTApplyFlowBuilder(BaseTendbTicketFlowBuilder): + serializer = TendbMNTApplyDetailSerializer + inner_flow_builder = TendbMNTApplyParamBuilder + inner_flow_name = _("TendbCluster 添加运维节点") diff --git a/dbm-ui/backend/ticket/builders/tendbcluster/tendb_mnt_destroy.py b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_mnt_destroy.py new file mode 100644 index 0000000000..49fff424ed --- /dev/null +++ b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_mnt_destroy.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 gettext_lazy as _ +from rest_framework import serializers + +from backend.db_meta.models import Cluster +from backend.flow.engine.controller.spider import SpiderController +from backend.ticket import builders +from backend.ticket.builders.tendbcluster.base import BaseTendbTicketFlowBuilder, TendbBaseOperateDetailSerializer +from backend.ticket.constants import TicketType + + +class TendbMNTDestroyDetailSerializer(TendbBaseOperateDetailSerializer): + class MNTDestroySerializer(serializers.Serializer): + cluster_id = serializers.IntegerField(help_text=_("集群ID")) + spider_ip_list = serializers.ListField(help_text=_("运维节点信息"), child=serializers.DictField()) + + infos = serializers.ListField(help_text=_("下架spider运维节点信息"), child=MNTDestroySerializer()) + is_safe = serializers.BooleanField(help_text=_("是否安全模式执行"), required=False, default=True) + + def validate(self, attrs): + super().validate(attrs) + return attrs + + +class TendbMNTDestroyParamBuilder(builders.FlowParamBuilder): + controller = SpiderController.reduce_spider_mnt_scene + + +@builders.BuilderFactory.register(TicketType.TENDBCLUSTER_SPIDER_MNT_DESTROY, is_apply=True) +class TendbMNTDestroyFlowBuilder(BaseTendbTicketFlowBuilder): + serializer = TendbMNTDestroyDetailSerializer + inner_flow_builder = TendbMNTDestroyParamBuilder + inner_flow_name = _("TendbCluster 下架运维节点") diff --git a/dbm-ui/backend/ticket/builders/tendbcluster/tendb_node_reblance.py b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_node_reblance.py new file mode 100644 index 0000000000..301fc744d0 --- /dev/null +++ b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_node_reblance.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 gettext_lazy as _ +from rest_framework import serializers + +from backend.db_meta.models import Cluster +from backend.db_services.dbbase.constants import IpSource +from backend.flow.engine.controller.spider import SpiderController +from backend.ticket import builders +from backend.ticket.builders.tendbcluster.base import BaseTendbTicketFlowBuilder, TendbBaseOperateDetailSerializer +from backend.ticket.constants import TicketType, TriggerChecksumType + + +class TendbNodeRebalanceDetailSerializer(TendbBaseOperateDetailSerializer): + class NodeRebalanceItemSerializer(serializers.Serializer): + cluster_id = serializers.IntegerField(help_text=_("集群ID")) + bk_cloud_id = serializers.IntegerField(help_text=_("云区域ID")) + cluster_shard_num = serializers.IntegerField(help_text=_("集群分片数")) + remote_shard_num = serializers.IntegerField(help_text=_("单机分片数")) + resource_spec = serializers.JSONField(help_text=_("规格要求")) + + infos = serializers.ListSerializer(help_text=_("集群扩缩容信息"), child=NodeRebalanceItemSerializer()) + ip_source = serializers.ChoiceField( + help_text=_("主机来源"), choices=IpSource.get_choices(), default=IpSource.RESOURCE_POOL.value + ) + need_checksum = serializers.BooleanField(help_text=_("执行前是否需要数据校验")) + trigger_checksum_type = serializers.ChoiceField(help_text=_("数据校验触发类型"), choices=TriggerChecksumType.get_choices()) + trigger_checksum_time = serializers.CharField(help_text=_("数据校验 触发时间")) + + def validate(self, attrs): + super().validate(attrs) + return attrs + + +class TendbNodeRebalanceFlowParamBuilderBuilder(builders.FlowParamBuilder): + controller = SpiderController.tendb_cluster_remote_rebalance + + def format_ticket_data(self): + cluster_ids = [info["cluster_id"] for info in self.ticket_data["infos"]] + cluster__module_id = { + cluster.id: cluster.db_module_id for cluster in Cluster.objects.filter(id__in=cluster_ids) + } + for info in self.ticket_data["infos"]: + info["db_module_id"] = cluster__module_id[info["cluster_id"]] + + +class TendbNodeRebalanceResourceParamBuilder(builders.ResourceApplyParamBuilder): + def post_callback(self): + next_flow = self.ticket.next_flow() + infos = next_flow.details["ticket_data"]["infos"] + for info in infos: + info["resource_spec"]["remote"], __ = info["resource_spec"].pop("master"), info["resource_spec"].pop( + "slave" + ) + info["remote_group"] = info.pop("backend_group") + + next_flow.save(update_fields=["details"]) + + +@builders.BuilderFactory.register(TicketType.TENDBCLUSTER_NODE_REBALANCE, is_apply=True) +class TendbMNTApplyFlowBuilder(BaseTendbTicketFlowBuilder): + serializer = TendbNodeRebalanceDetailSerializer + inner_flow_builder = TendbNodeRebalanceFlowParamBuilderBuilder + resource_batch_apply_builder = TendbNodeRebalanceResourceParamBuilder + inner_flow_name = _("TendbCluster 集群容量变更") diff --git a/dbm-ui/backend/ticket/builders/tendbcluster/tendb_partition.py b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_partition.py new file mode 100644 index 0000000000..f26c74e6a3 --- /dev/null +++ b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_partition.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 gettext_lazy as _ + +from backend.flow.engine.controller.spider import SpiderController +from backend.ticket import builders +from backend.ticket.builders.mysql.mysql_partition import MySQLPartitionDetailSerializer +from backend.ticket.builders.tendbcluster.base import BaseTendbTicketFlowBuilder +from backend.ticket.constants import TicketType + + +class SpiderPartitionDetailSerializer(MySQLPartitionDetailSerializer): + pass + + +class SpiderPartitionParamBuilder(builders.FlowParamBuilder): + controller = SpiderController.spider_partition + + def format_ticket_data(self): + pass + + +@builders.BuilderFactory.register(TicketType.TENDBCLUSTER_PARTITION) +class SpiderPartitionFlowBuilder(BaseTendbTicketFlowBuilder): + serializer = SpiderPartitionDetailSerializer + inner_flow_builder = SpiderPartitionParamBuilder + inner_flow_name = _("分区管理执行") + + @property + def need_itsm(self): + # TODO:先不考虑执行的分区审批 + return False diff --git a/dbm-ui/backend/ticket/builders/tendbcluster/tendb_rename.py b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_rename.py new file mode 100644 index 0000000000..2deeb0e765 --- /dev/null +++ b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_rename.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 as _ + +from backend.flow.engine.controller.spider import SpiderController +from backend.ticket import builders +from backend.ticket.builders.mysql.base import BaseMySQLTicketFlowBuilder +from backend.ticket.builders.mysql.mysql_ha_rename import MySQLHaRenameFlowParamBuilder, MySQLHaRenameSerializer +from backend.ticket.constants import FlowRetryType, TicketType + + +class TendbRenameSerializer(MySQLHaRenameSerializer): + pass + + +class TendbRenameFlowParamBuilder(MySQLHaRenameFlowParamBuilder): + controller = SpiderController.rename_database + + def format_ticket_data(self): + super().format_ticket_data() + + +@builders.BuilderFactory.register(TicketType.TENDBCLUSTER_RENAME_DATABASE) +class MySQLHaRenameFlowBuilder(BaseMySQLTicketFlowBuilder): + serializer = TendbRenameSerializer + inner_flow_builder = TendbRenameFlowParamBuilder + inner_flow_name = _("TenDBCluster Cluster 重命名执行") + retry_type = FlowRetryType.MANUAL_RETRY diff --git a/dbm-ui/backend/ticket/builders/tendbcluster/tendb_spider_add_nodes.py b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_spider_add_nodes.py new file mode 100644 index 0000000000..042f5ae8f7 --- /dev/null +++ b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_spider_add_nodes.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 gettext_lazy as _ +from rest_framework import serializers + +from backend.db_meta.enums import TenDBClusterSpiderRole +from backend.db_services.dbbase.constants import IpSource +from backend.flow.engine.controller.spider import SpiderController +from backend.ticket import builders +from backend.ticket.builders.tendbcluster.base import ( + BaseTendbTicketFlowBuilder, + TendbBaseOperateDetailSerializer, + TendbBaseOperateResourceParamBuilder, +) +from backend.ticket.constants import TicketType + + +class TendbSpiderAddNodesDetailSerializer(TendbBaseOperateDetailSerializer): + class SpiderNodesItemSerializer(serializers.Serializer): + cluster_id = serializers.IntegerField(help_text=_("集群ID")) + add_spider_role = serializers.ChoiceField(help_text=_("接入层类型"), choices=TenDBClusterSpiderRole.get_choices()) + resource_spec = serializers.DictField(help_text=_("规格参数")) + + ip_source = serializers.ChoiceField( + help_text=_("机器导入类型"), choices=IpSource.get_choices(), required=False, default=IpSource.RESOURCE_POOL + ) + infos = serializers.ListSerializer(help_text=_("扩容信息"), child=SpiderNodesItemSerializer()) + + def validate(self, attrs): + super().validate(attrs) + self.validate_max_spider_master_mnt_count(attrs) + return attrs + + +class TendbSpiderAddNodesFlowParamBuilder(builders.FlowParamBuilder): + controller = SpiderController.add_spider_nodes_scene + + def format_ticket_data(self): + pass + + +class TendbSpiderAddNodesResourceParamBuilder(TendbBaseOperateResourceParamBuilder): + def post_callback(self): + next_flow = self.ticket.next_flow() + for info in next_flow.details["ticket_data"]["infos"]: + # 格式化规格信息 + info["resource_spec"]["spider"] = info["resource_spec"].pop("spider_ip_list") + + next_flow.save(update_fields=["details"]) + + +@builders.BuilderFactory.register(TicketType.TENDBCLUSTER_SPIDER_ADD_NODES, is_apply=True) +class TendbSpiderAddNodesFlowBuilder(BaseTendbTicketFlowBuilder): + serializer = TendbSpiderAddNodesDetailSerializer + inner_flow_builder = TendbSpiderAddNodesFlowParamBuilder + inner_flow_name = _("TenDBCluster Cluster 接入层扩容") + resource_batch_apply_builder = TendbSpiderAddNodesResourceParamBuilder diff --git a/dbm-ui/backend/ticket/builders/tendbcluster/tendb_spider_reduce_nodes.py b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_spider_reduce_nodes.py new file mode 100644 index 0000000000..092652dcc7 --- /dev/null +++ b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_spider_reduce_nodes.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 gettext_lazy as _ +from rest_framework import serializers + +from backend.db_meta.enums import TenDBClusterSpiderRole +from backend.db_services.dbbase.constants import IpSource +from backend.flow.engine.controller.spider import SpiderController +from backend.ticket import builders +from backend.ticket.builders.tendbcluster.base import ( + BaseTendbTicketFlowBuilder, + TendbBaseOperateDetailSerializer, + TendbBaseOperateResourceParamBuilder, +) +from backend.ticket.constants import TicketType + + +class TendbSpiderReduceNodesDetailSerializer(TendbBaseOperateDetailSerializer): + class SpiderNodesItemSerializer(serializers.Serializer): + cluster_id = serializers.IntegerField(help_text=_("集群ID")) + spider_reduced_to_count = serializers.IntegerField(help_text=_("剩余spider数量")) + reduce_spider_role = serializers.ChoiceField( + help_text=_("缩容的角色"), choices=TenDBClusterSpiderRole.get_choices() + ) + + is_safe = serializers.BooleanField(help_text=_("是否做安全检测")) + infos = serializers.ListSerializer(help_text=_("缩容信息"), child=SpiderNodesItemSerializer()) + + def validate(self, attrs): + super().validate(attrs) + self.validate_min_spider_count(attrs) + return attrs + + +class TendbSpiderReduceNodesFlowParamBuilder(builders.FlowParamBuilder): + controller = SpiderController.reduce_spider_nodes_scene + + def format_ticket_data(self): + pass + + +@builders.BuilderFactory.register(TicketType.TENDBCLUSTER_SPIDER_REDUCE_NODES, is_apply=True) +class TendbSpiderReduceNodesFlowBuilder(BaseTendbTicketFlowBuilder): + serializer = TendbSpiderReduceNodesDetailSerializer + inner_flow_builder = TendbSpiderReduceNodesFlowParamBuilder + inner_flow_name = _("TenDB Cluster 接入层缩容") diff --git a/dbm-ui/backend/ticket/builders/tendbcluster/tendb_spider_slave_apply.py b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_spider_slave_apply.py new file mode 100644 index 0000000000..f025e9c9e7 --- /dev/null +++ b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_spider_slave_apply.py @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 gettext_lazy as _ +from rest_framework import serializers + +from backend.db_meta.enums import TenDBClusterSpiderRole +from backend.db_meta.models import AppCache, Cluster +from backend.db_services.dbbase.constants import IpSource +from backend.flow.engine.controller.spider import SpiderController +from backend.ticket import builders +from backend.ticket.builders.tendbcluster.base import ( + BaseTendbTicketFlowBuilder, + TendbBaseOperateDetailSerializer, + TendbBaseOperateResourceParamBuilder, +) +from backend.ticket.constants import TicketType + + +class SpiderSlaveApplyDetailSerializer(TendbBaseOperateDetailSerializer): + class SpiderNodesItemSerializer(serializers.Serializer): + cluster_id = serializers.IntegerField(help_text=_("集群ID")) + spider_slave_ip_list = serializers.ListField( + help_text=_("slave信息"), child=serializers.DictField(), required=False + ) + resource_spec = serializers.JSONField(help_text=_("资源规格参数"), required=False) + + infos = serializers.ListSerializer(help_text=_("扩容信息"), child=SpiderNodesItemSerializer()) + ip_source = serializers.ChoiceField( + help_text=_("机器导入类型"), choices=IpSource.get_choices(), required=False, default=IpSource.RESOURCE_POOL + ) + + def validate(self, attrs): + """校验集群是否已经存在只读接入层""" + clusters = Cluster.objects.prefetch_related("proxyinstance_set").filter( + id__in=[info["cluster_id"] for info in attrs["infos"]] + ) + for cluster in clusters: + if cluster.proxyinstance_set.filter( + tendbclusterspiderext__spider_role=TenDBClusterSpiderRole.SPIDER_SLAVE + ).exists(): + raise serializers.ValidationError(_("集群{}已经存在只读接入层,无法再次部署").format(cluster.name)) + + return attrs + + +class SpiderSlaveApplyFlowParamBuilder(builders.FlowParamBuilder): + controller = SpiderController.spider_slave_cluster_apply_scene + + def format_ticket_data(self): + # 补充从域名 + cluster_ids = [info["cluster_id"] for info in self.ticket_data["infos"]] + cluster_id__name = {cluster.id: cluster.name for cluster in Cluster.objects.filter(id__in=cluster_ids)} + db_app_abbr = AppCache.objects.get(bk_biz_id=self.ticket.bk_biz_id).db_app_abbr + for info in self.ticket_data["infos"]: + info.update(slave_domain=f"spider-slave.{cluster_id__name[info['cluster_id']]}.{db_app_abbr}.db") + + +class SpiderSlaveApplyResourceParamBuilder(TendbBaseOperateResourceParamBuilder): + def post_callback(self): + next_flow = self.ticket.next_flow() + for info in next_flow.details["ticket_data"]["infos"]: + # 格式化规格信息 + info["resource_spec"]["spider"] = info["resource_spec"].pop("spider_slave_ip_list") + + next_flow.save(update_fields=["details"]) + + +@builders.BuilderFactory.register(TicketType.TENDBCLUSTER_SPIDER_SLAVE_APPLY, is_apply=True) +class SpiderSlaveApplyFlowBuilder(BaseTendbTicketFlowBuilder): + serializer = SpiderSlaveApplyDetailSerializer + inner_flow_builder = SpiderSlaveApplyFlowParamBuilder + inner_flow_name = _("TenDB Cluster 部署只读接入层") + resource_batch_apply_builder = SpiderSlaveApplyResourceParamBuilder diff --git a/dbm-ui/backend/ticket/builders/tendbcluster/tendb_spider_slave_destroy.py b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_spider_slave_destroy.py new file mode 100644 index 0000000000..04cf6126db --- /dev/null +++ b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_spider_slave_destroy.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 gettext_lazy as _ +from rest_framework import serializers + +from backend.db_meta.models import AppCache, Cluster +from backend.db_services.dbbase.constants import IpSource +from backend.flow.engine.controller.spider import SpiderController +from backend.ticket import builders +from backend.ticket.builders.tendbcluster.base import ( + BaseTendbTicketFlowBuilder, + TendbBaseOperateDetailSerializer, + TendbBaseOperateResourceParamBuilder, +) +from backend.ticket.constants import TicketType + + +class SpiderSlaveDestroyDetailSerializer(TendbBaseOperateDetailSerializer): + is_safe = serializers.BooleanField(help_text=_("是否做安全检测"), required=False, default=True) + cluster_ids = serializers.ListField(help_text=_("集群ID列表"), child=serializers.IntegerField()) + + +class SpiderSlaveDestroyFlowParamBuilder(builders.FlowParamBuilder): + controller = SpiderController.destroy_tendb_slave_cluster + + +@builders.BuilderFactory.register(TicketType.TENDBCLUSTER_SPIDER_SLAVE_DESTROY, is_apply=True) +class SpiderSlaveApplyFlowBuilder(BaseTendbTicketFlowBuilder): + serializer = SpiderSlaveDestroyDetailSerializer + inner_flow_builder = SpiderSlaveDestroyFlowParamBuilder + inner_flow_name = _("TenDB Cluster 只读接入层下架") diff --git a/dbm-ui/backend/ticket/builders/tendbcluster/tendb_temporary_destroy.py b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_temporary_destroy.py new file mode 100644 index 0000000000..ba8e9d7a1f --- /dev/null +++ b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_temporary_destroy.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 as _ +from rest_framework import serializers + +from backend.db_meta.enums import ClusterPhase, ClusterStatus +from backend.db_meta.enums.comm import SystemTagEnum +from backend.db_meta.models import Cluster +from backend.flow.engine.controller.spider import SpiderController +from backend.ticket import builders +from backend.ticket.builders.tendbcluster.base import ( + BaseTendbTicketFlowBuilder, + TendbClustersTakeDownDetailsSerializer, +) +from backend.ticket.constants import FlowType, TicketType +from backend.ticket.models import Flow + + +class TendbTemporaryDestroyDetailSerializer(TendbClustersTakeDownDetailsSerializer): + def validate_cluster_ids(self, value): + clusters = Cluster.objects.filter(id__in=value, tag__name=SystemTagEnum.TEMPORARY.value) + if clusters.count() != len(value): + raise serializers.ValidationError(_("此单据只用于临时集群的销毁,请不要用于其他正常集群")) + + running_clusters = [cluster.id for cluster in clusters if cluster.phase == ClusterPhase.ONLINE] + if not running_clusters: + raise serializers.ValidationError(_("不存在状态为启用的临时集群,如果临时集群已禁用请在集群页面进行销毁")) + + return running_clusters + + +class TendbTemporaryDisableFlowParamBuilder(builders.FlowParamBuilder): + controller = SpiderController.spider_cluster_disable_scene + + +class TendbTemporaryDestroyFlowParamBuilder(builders.FlowParamBuilder): + controller = SpiderController.spider_cluster_destroy_scene + + +@builders.BuilderFactory.register(TicketType.TENDBCLUSTER_TEMPORARY_DESTROY) +class TendbDestroyFlowBuilder(BaseTendbTicketFlowBuilder): + serializer = TendbTemporaryDestroyDetailSerializer + + def custom_ticket_flows(self): + flows = [ + Flow( + ticket=self.ticket, + flow_type=FlowType.INNER_FLOW.value, + details=TendbTemporaryDisableFlowParamBuilder(self.ticket).get_params(), + flow_alias=_("TenDBCluster 临时集群下架"), + ), + Flow( + ticket=self.ticket, + flow_type=FlowType.INNER_FLOW.value, + details=TendbTemporaryDestroyFlowParamBuilder(self.ticket).get_params(), + flow_alias=_("TenDBCluster 临时集群销毁"), + ), + ] + return flows diff --git a/dbm-ui/backend/ticket/constants.py b/dbm-ui/backend/ticket/constants.py index 81dd66035c..a4fc3b7f2c 100644 --- a/dbm-ui/backend/ticket/constants.py +++ b/dbm-ui/backend/ticket/constants.py @@ -30,6 +30,7 @@ class TodoType(str, StructuredEnum): APPROVE = EnumField("APPROVE", _("主流程-人工确认")) INNER_APPROVE = EnumField("INNER_APPROVE", _("自动化流程-人工确认")) + RESOURCE_REPLENISH = EnumField("RESOURCE_REPLENISH", _("资源补货")) class CountType(str, StructuredEnum): @@ -54,6 +55,28 @@ class TodoStatus(str, StructuredEnum): DONE_FAILED = EnumField("DONE_FAILED", _("已终止")) +class AffinityEnum(str, StructuredEnum): + """ + 亲和性枚举类 + """ + + SAME_SUBZONE_CROSS_SWTICH = EnumField("SAME_SUBZONE_CROSS_SWTICH", _("同城同subzone跨交换机跨机架")) + SAME_SUBZONE = EnumField("SAME_SUBZONE", _("同城同subzone")) + CROS_SUBZONE = EnumField("CROS_SUBZONE", _("CROS_SUBZONE")) + NONE = EnumField("NONE", _("NONE")) + + +class ResourceApplyErrCode(int, StructuredEnum): + """ + 资源申请错误码 + """ + + RESOURCE_LAKE = EnumField(60001, _("资源不足")) + RESOURCE_LOCK_FAIL = EnumField(60002, _("获取资源所失败")) + RESOURCE_PARAMS_INVALID = EnumField(60003, _("参数合法性校验失败")) + RESOURCE_MACHINE_FAIL = EnumField(60004, _("锁定返回机器失败")) + + DONE_STATUS = [TodoStatus.DONE_SUCCESS, TodoStatus.DONE_FAILED] @@ -85,14 +108,13 @@ class TicketFlowStatus(str, StructuredEnum): StateType.FINISHED.value: TicketFlowStatus.SUCCEEDED.value, StateType.FAILED.value: TicketFlowStatus.FAILED.value, StateType.REVOKED.value: TicketFlowStatus.REVOKED.value, + StateType.RUNNING.value: TicketFlowStatus.RUNNING.value, } EXCLUSIVE_TICKET_EXCEL_PATH = "backend/ticket/exclusive_ticket.xlsx" class TicketType(str, StructuredEnum): - """单据类型枚举""" - @classmethod def get_choice_value(cls, label: str) -> str: """Get the value of field member by label""" @@ -138,21 +160,43 @@ def get_choice_value(cls, label: str) -> str: MYSQL_HA_FULL_BACKUP = EnumField("MYSQL_HA_FULL_BACKUP", _("MySQL 高可用全库备份")) MYSQL_SINGLE_TRUNCATE_DATA = EnumField("MYSQL_SINGLE_TRUNCATE_DATA", _("MySQL 单节点清档")) MYSQL_SINGLE_RENAME_DATABASE = EnumField("MYSQL_SINGLE_RENAME_DATABASE", _("MySQL 单节点DB重命名")) + MYSQL_HA_STANDARDIZE = EnumField("MYSQL_HA_STANDARDIZE", _("TendbHA 标准化")) - # SPIDER - SPIDER_CHECKSUM = EnumField("SPIDER_CHECKSUM", _("Spider 数据校验修复")) - SPIDER_PARTITION = EnumField("SPIDER_PARTITION", _("Spider 分区管理")) - SPIDER_DB_TABLE_BACKUP = EnumField("SPIDER_DB_TABLE_BACKUP", _("Spider 库表备份")) - SPIDER_RENAME_DATABASE = EnumField("SPIDER_RENAME_DATABASE", _("Spider 数据库重命名")) - SPIDER_TRUNCATE_DATABASE = EnumField("SPIDER_TRUNCATE_DATABASE", _("Spider 清档")) # SPIDER(TenDB Cluster) - SPIDER_MASTER_FAIL_OVER = EnumField("SPIDER_MASTER_FAIL_OVER", _("TenDB Cluster 主故障切换")) - SPIDER_MASTER_SLAVE_SWITCH = EnumField("SPIDER_MASTER_SLAVE_SWITCH", _("TenDB Cluster 主从互切")) - TENDB_CLUSTER_APPLY = EnumField("TENDB_CLUSTER_APPLY", _("TenDB Cluster 集群部署")) - TENDB_CLUSTER_ENABLE = EnumField("TENDB_CLUSTER_ENABLE", _("TenDB Cluster 集群启用")) - TENDB_CLUSTER_DISABLE = EnumField("TENDB_CLUSTER_DISABLE", _("TenDB Cluster 集群禁用")) - TENDB_CLUSTER_DESTROY = EnumField("TENDB_CLUSTER_DESTROY", _("TenDB Cluster 集群销毁")) - SPIDER_FULL_BACKUP = EnumField("SPIDER_FULL_BACKUP", _("Spider 全备")) + TENDBCLUSTER_CHECKSUM = EnumField("TENDBCLUSTER_CHECKSUM", _("TenDB Cluster 数据校验修复")) + TENDBCLUSTER_DATA_REPAIR = EnumField("TENDBCLUSTER_DATA_REPAIR", _("TenDB Cluster 数据修复")) + TENDBCLUSTER_PARTITION = EnumField("TENDBCLUSTER_PARTITION", _("TenDB Cluster 分区管理")) + TENDBCLUSTER_DB_TABLE_BACKUP = EnumField("TENDBCLUSTER_DB_TABLE_BACKUP", _("TenDB Cluster 库表备份")) + TENDBCLUSTER_RENAME_DATABASE = EnumField("TENDBCLUSTER_RENAME_DATABASE", _("TenDB Cluster 数据库重命名")) + TENDBCLUSTER_TRUNCATE_DATABASE = EnumField("TENDBCLUSTER_TRUNCATE_DATABASE", _("TenDB Cluster 清档")) + TENDBCLUSTER_MASTER_FAIL_OVER = EnumField("TENDBCLUSTER_MASTER_FAIL_OVER", _("TenDB Cluster 主故障切换")) + TENDBCLUSTER_MASTER_SLAVE_SWITCH = EnumField("TENDBCLUSTER_MASTER_SLAVE_SWITCH", _("TenDB Cluster 主从互切")) + TENDBCLUSTER_IMPORT_SQLFILE = EnumField("TENDBCLUSTER_IMPORT_SQLFILE", _("TenDB Cluster 变更SQL执行")) + TENDBCLUSTER_SEMANTIC_CHECK = EnumField("TENDBCLUSTER_SEMANTIC_CHECK", _("TenDB Cluster 模拟执行")) + TENDBCLUSTER_SPIDER_ADD_NODES = EnumField("TENDBCLUSTER_SPIDER_ADD_NODES", _("TenDB Cluster 扩容接入层")) + TENDBCLUSTER_SPIDER_REDUCE_NODES = EnumField("TENDBCLUSTER_SPIDER_REDUCE_NODES", _("TenDB Cluster 缩容接入层")) + TENDBCLUSTER_SPIDER_MNT_APPLY = EnumField("TENDBCLUSTER_SPIDER_MNT_APPLY", _("TenDB Cluster 添加运维节点")) + TENDBCLUSTER_SPIDER_MNT_DESTROY = EnumField("TENDBCLUSTER_SPIDER_MNT_DESTROY", _("TenDB Cluster 下架运维节点")) + TENDBCLUSTER_SPIDER_SLAVE_APPLY = EnumField("TENDBCLUSTER_SPIDER_SLAVE_APPLY", _("TenDB Cluster 部署只读接入层")) + TENDBCLUSTER_SPIDER_SLAVE_DESTROY = EnumField("TENDBCLUSTER_SPIDER_SLAVE_DESTROY", _("TenDB Cluster 只读接入层下架")) + TENDBCLUSTER_APPLY = EnumField("TENDBCLUSTER_APPLY", _("TenDB Cluster 集群部署")) + TENDBCLUSTER_ENABLE = EnumField("TENDBCLUSTER_ENABLE", _("TenDB Cluster 集群启用")) + TENDBCLUSTER_DISABLE = EnumField("TENDBCLUSTER_DISABLE", _("TenDB Cluster 集群禁用")) + TENDBCLUSTER_DESTROY = EnumField("TENDBCLUSTER_DESTROY", _("TenDB Cluster 集群销毁")) + TENDBCLUSTER_TEMPORARY_DESTROY = EnumField("TENDBCLUSTER_TEMPORARY_DESTROY", _("TenDB Cluster 临时集群销毁")) + TENDBCLUSTER_NODE_REBALANCE = EnumField("TENDBCLUSTER_NODE_REBALANCE", _("TenDB Cluster 集群容量变更")) + TENDBCLUSTER_FULL_BACKUP = EnumField("TENDBCLUSTER_FULL_BACKUP", _("TenDB Cluster 全库备份")) + TENDBCLUSTER_ROLLBACK_CLUSTER = EnumField("TENDBCLUSTER_ROLLBACK_CLUSTER", _("TenDB Cluster 定点回档")) + TENDBCLUSTER_FLASHBACK = EnumField("TENDBCLUSTER_FLASHBACK", _("TenDB Cluster 闪回")) + TENDBCLUSTER_CLIENT_CLONE_RULES = EnumField("TENDBCLUSTER_CLIENT_CLONE_RULES", _("TenDB Cluster 客户端权限克隆")) + TENDBCLUSTER_INSTANCE_CLONE_RULES = EnumField("TENDBCLUSTER_INSTANCE_CLONE_RULES", _("TenDB Cluster DB实例权限克隆")) + TENDBCLUSTER_AUTHORIZE_RULES = EnumField("TENDBCLUSTER_AUTHORIZE_RULES", _("TenDB Cluster 授权")) + TENDBCLUSTER_EXCEL_AUTHORIZE_RULES = EnumField("TENDBCLUSTER_EXCEL_AUTHORIZE_RULES", _("TenDB Cluster EXCEL-授权")) + + # Tbinlogdumper + TBINLOGDUMPER_INSTALL = EnumField("TBINLOGDUMPER_INSTALL", _("TBINLOGDUMPER 上架")) + TBINLOGDUMPER_REDUCE_NODES = EnumField("TBINLOGDUMPER_REDUCE_NODES", _("TBINLOGDUMPER 下架")) + TBINLOGDUMPER_SWITCH_NODES = EnumField("TBINLOGDUMPER_SWITCH_NODES", _("TBINLOGDUMPER 切换")) # REDIS REDIS_PLUGIN_CREATE_CLB = EnumField("REDIS_PLUGIN_CREATE_CLB", _("Redis 创建CLB")) @@ -168,12 +212,25 @@ def get_choice_value(cls, label: str) -> str: REDIS_CLOSE = EnumField("REDIS_PROXY_CLOSE", _("Redis 集群禁用")) REDIS_DESTROY = EnumField("REDIS_DESTROY", _("Redis 集群删除")) REDIS_PURGE = EnumField("REDIS_PURGE", _("Redis 集群清档")) - REDIS_SCALE = EnumField("REDIS_SCALE", _("Redis 扩缩容")) - PROXY_SCALE = EnumField("PROXY_SCALE", _("Proxy 扩缩容")) - REDIS_CLUSTER_SLAVE_CUTOFF = EnumField("REDIS_CLUSTER_SLAVE_CUTOFF", _("redis集群 slave 裁撤替换")) - REDIS_CLUSTER_MASTER_CUTOFF = EnumField("REDIS_CLUSTER_MASTER_CUTOFF", _("redis集群 master 裁撤替换")) - REDIS_CLUSTER_PROXY_CUTOFF = EnumField("REDIS_CLUSTER_PROXY_CUTOFF", _("redis集群 proxy 裁撤替换")) - REDIS_NEW_DTS_JOB = EnumField("REDIS_NEW_DTS_JOB", _("Redis 新建DTS任务")) + + REDIS_SCALE_UPDOWN = EnumField("REDIS_SCALE_UPDOWN", _("Redis 集群容量变更")) + REDIS_CLUSTER_CUTOFF = EnumField("REDIS_CLUSTER_CUTOFF", _("Redis 整机替换")) + REDIS_CLUSTER_AUTOFIX = EnumField("REDIS_CLUSTER_AUTOFIX", _("Redis 故障自愈")) + REDIS_CLUSTER_INSTANCE_SHUTDOWN = EnumField("REDIS_CLUSTER_INSTANCE_SHUTDOWN", _("Redis 故障自愈-实例下架")) + REDIS_MASTER_SLAVE_SWITCH = EnumField("REDIS_MASTER_SLAVE_SWITCH", _("Redis 主从故障切换")) + PROXY_SCALE_UP = EnumField("PROXY_SCALE_UP", _("Redis Proxy扩容")) + PROXY_SCALE_DOWN = EnumField("PROXY_SCALE_DOWN", _("Redis Proxy缩容")) + REDIS_ADD_DTS_SERVER = EnumField("REDIS_ADD_DTS_SERVER", _("Redis 新增DTS SERVER")) + REDIS_REMOVE_DTS_SERVER = EnumField("REDIS_REMOVE_DTS_SERVER", _("Redis 删除DTS SERVER")) + REDIS_DATA_STRUCTURE = EnumField("REDIS_DATA_STRUCTURE", _("Redis 集群数据构造")) + REDIS_DATA_STRUCTURE_TASK_DELETE = EnumField("REDIS_DATA_STRUCTURE_TASK_DELETE", _("Redis 数据构造记录删除")) + REDIS_CLUSTER_SHARD_NUM_UPDATE = EnumField("REDIS_CLUSTER_SHARD_NUM_UPDATE", _("Redis 集群分片数变更")) + REDIS_CLUSTER_TYPE_UPDATE = EnumField("REDIS_CLUSTER_TYPE_UPDATE", _("Redis 集群类型变更")) + REDIS_CLUSTER_DATA_COPY = EnumField("REDIS_CLUSTER_DATA_COPY", _("Redis 集群数据复制")) + REDIS_CLUSTER_ROLLBACK_DATA_COPY = EnumField("REDIS_CLUSTER_ROLLBACK_DATA_COPY", _("Redis 构造实例数据回写")) + REDIS_DATACOPY_CHECK_REPAIR = EnumField("REDIS_DATACOPY_CHECK_REPAIR", _("Redis 数据校验与修复")) + REDIS_CLUSTER_ADD_SLAVE = EnumField("REDIS_CLUSTER_ADD_SLAVE", _("Redis 新增slave节点")) + REDIS_DTS_ONLINE_SWITCH = EnumField("REDIS_DTS_ONLINE_SWITCH", _("Redis DTS在线切换")) # 大数据 KAFKA_APPLY = EnumField("KAFKA_APPLY", _("Kafka 集群部署")) @@ -219,6 +276,14 @@ def get_choice_value(cls, label: str) -> str: INFLUXDB_DESTROY = EnumField("INFLUXDB_DESTROY", _("InfluxDB 实例删除")) INFLUXDB_REPLACE = EnumField("INFLUXDB_REPLACE", _("InfluxDB 实例替换")) + # Riak + RIAK_CLUSTER_APPLY = EnumField("RIAK_CLUSTER_APPLY", _("RIAK 集群部署")) + RIAK_CLUSTER_SCALE_OUT = EnumField("RIAK_CLUSTER_SCALE_OUT", _("RIAK 集群扩容")) + RIAK_CLUSTER_SCALE_IN = EnumField("RIAK_CLUSTER_SCALE_IN", _("RIAK 集群缩容")) + RIAK_CLUSTER_DESTROY = EnumField("RIAK_CLUSTER_DESTROY", _("RIAK 集群销毁")) + RIAK_CLUSTER_ENABLE = EnumField("RIAK_CLUSTER_ENABLE", _("RIAK集群启用")) + RIAK_CLUSTER_DISABLE = EnumField("RIAK_CLUSTER_DISABLE", _("RIAK集群禁用")) + # 云区域组件 CLOUD_SERVICE_APPLY = EnumField("CLOUD_SERVICE_APPLY", _("云区域服务部署")) CLOUD_NGINX_APPLY = EnumField("CLOUD_NGINX_APPLY", _("云区域Nginx 服务部署")) @@ -244,66 +309,6 @@ def get_choice_value(cls, label: str) -> str: RESOURCE_IMPORT = EnumField("RESOURCE_IMPORT", _("资源池导入")) -# 单据动作与集群状态的映射 -TICKET_TYPE__CLUSTER_PHASE_MAP = { - # MySQL单据----MySQL phase - TicketType.MYSQL_HA_ENABLE.value: ClusterPhase.ONLINE.value, - TicketType.MYSQL_HA_DISABLE.value: ClusterPhase.OFFLINE.value, - TicketType.MYSQL_HA_DESTROY.value: ClusterPhase.DESTROY.value, - TicketType.MYSQL_SINGLE_ENABLE.value: ClusterPhase.ONLINE.value, - TicketType.MYSQL_SINGLE_DISABLE.value: ClusterPhase.OFFLINE.value, - TicketType.MYSQL_SINGLE_DESTROY.value: ClusterPhase.DESTROY.value, - # ES单据---ES phase - TicketType.ES_ENABLE.value: ClusterPhase.ONLINE.value, - TicketType.ES_DISABLE.value: ClusterPhase.OFFLINE.value, - TicketType.ES_DESTROY.value: ClusterPhase.DESTROY.value, - # Kafka单据---Kafka phase - TicketType.KAFKA_ENABLE.value: ClusterPhase.ONLINE.value, - TicketType.KAFKA_DISABLE.value: ClusterPhase.OFFLINE.value, - TicketType.KAFKA_DESTROY.value: ClusterPhase.DESTROY.value, - # Hdfs单据---Hdfs phase - TicketType.HDFS_ENABLE.value: ClusterPhase.ONLINE.value, - TicketType.HDFS_DISABLE.value: ClusterPhase.OFFLINE.value, - TicketType.HDFS_DESTROY.value: ClusterPhase.DESTROY.value, - # Pulsar单据---Pulsar phase - TicketType.PULSAR_ENABLE.value: ClusterPhase.ONLINE.value, - TicketType.PULSAR_DISABLE.value: ClusterPhase.OFFLINE.value, - TicketType.PULSAR_DESTROY.value: ClusterPhase.DESTROY.value, - # Influxdb单据---Influxdb phase - TicketType.INFLUXDB_ENABLE.value: ClusterPhase.ONLINE.value, - TicketType.INFLUXDB_DISABLE.value: ClusterPhase.OFFLINE.value, - TicketType.INFLUXDB_DESTROY.value: ClusterPhase.DESTROY.value, - # Spider单据---Spider phase - TicketType.TENDB_CLUSTER_ENABLE.value: ClusterPhase.ONLINE.value, - TicketType.TENDB_CLUSTER_DISABLE.value: ClusterPhase.OFFLINE.value, - TicketType.TENDB_CLUSTER_DESTROY.value: ClusterPhase.DESTROY.value, -} - -# 单据类型和集群类型的映射 -TICKET_TYPE__CLUSTER_TYPE_MAP = { - # MySQL - TicketType.MYSQL_SINGLE_APPLY: ClusterType.TenDBSingle, - TicketType.MYSQL_HA_APPLY: ClusterType.TenDBHA, - # 大数据 - TicketType.KAFKA_APPLY: ClusterType.Kafka, - TicketType.HDFS_APPLY: ClusterType.Hdfs, - TicketType.ES_APPLY: ClusterType.Es, - TicketType.PULSAR_APPLY: ClusterType.Pulsar, - TicketType.INFLUXDB_APPLY: ClusterType.Influxdb - # Redis TODO: redis集群类型太多了,但是单据类型就一种,如何区分? -} - -# 扩容单据合集 -SCALE_UP_TICKET_TYPES = [ - TicketType.REDIS_SCALE, - TicketType.ES_SCALE_UP, - TicketType.HDFS_SCALE_UP, - TicketType.KAFKA_SCALE_UP, - TicketType.PULSAR_SCALE_UP, - TicketType.PROXY_SCALE, -] - - class FlowType(str, StructuredEnum): """流程类型枚举""" @@ -362,3 +367,69 @@ def get_err_code(cls, err: Exception, retry_type: str) -> "FlowErrCode": err_code = cls.MANUAL_EXCLUSIVE_ERROR if retry_type == FlowRetryType.MANUAL_RETRY else cls.AUTO_EXCLUSIVE_ERROR return err_code + + +class SwitchConfirmType(str, StructuredEnum): + """ + 切换方式类型 + """ + + USER_CONFIRM = EnumField("user_confirm", _("需要人工确认")) + NO_CONFIRM = EnumField("no_confirm", _("无需确认")) + + +class SyncDisconnectSettingType(str, StructuredEnum): + """ + 同步断开设置 + """ + + AUTO_DISCONNECT = EnumField("auto_disconnect_after_replication", _("数据复制完成后自动断开同步关系")) + KEEP_SYNC = EnumField("keep_sync_with_reminder", _("数据复制完成后保持同步关系,定时发送断开同步提醒")) + + +class DataCheckRepairSettingType(str, StructuredEnum): + """ + 数据校验与修复设置 + """ + + DATA_CHECK_AND_REPAIR = EnumField("data_check_and_repair", _("数据校验并修复")) + DATA_CHECK_ONLY = EnumField("data_check_only", _("仅进行数据校验,不进行修复")) + NO_CHECK_NO_REPAIR = EnumField("no_check_no_repair", _("不校验不修复")) + + +class RemindFrequencyType(str, StructuredEnum): + """ + 提醒频率 + """ + + ONCE_DAILY = EnumField("once_daily", _("一天一次")) + ONCE_WEEKLY = EnumField("once_weekly", _("一周一次")) + + +class CheckRepairFrequencyType(str, StructuredEnum): + """ + 校验修复频率 + """ + + ONCE_AFTER_REPLICATION = EnumField("once_after_replication", _("一次")) + ONCE_EVERY_THREE_DAYS = EnumField("once_every_three_days", _("三天一次")) + ONCE_WEEKLY = EnumField("once_weekly", _("一周一次")) + + +class WriteModeType(str, StructuredEnum): + """ + 写入方式 + """ + + DELETE_WRITE = EnumField("delete_and_write_to_redis", _("删除同名key再写入")) + APPEND_WRITE = EnumField("keep_and_append_to_redis", _("保留同名key追加写入")) + FLUSH_WRITE = EnumField("flushall_and_write_to_redis", _("清空集群后写入")) + + +class TriggerChecksumType(str, StructuredEnum): + """ + 触发数据校验的类型 + """ + + NOW = EnumField("now", _("立刻触发")) + TIMER = EnumField("timer", _("定时触发")) diff --git a/dbm-ui/backend/ticket/exclusive_ticket.xlsx b/dbm-ui/backend/ticket/exclusive_ticket.xlsx index db3c4cb752..4bd66fb734 100644 Binary files a/dbm-ui/backend/ticket/exclusive_ticket.xlsx and b/dbm-ui/backend/ticket/exclusive_ticket.xlsx differ diff --git a/dbm-ui/backend/ticket/flow_manager/base.py b/dbm-ui/backend/ticket/flow_manager/base.py index f8896f7902..b644f536c6 100644 --- a/dbm-ui/backend/ticket/flow_manager/base.py +++ b/dbm-ui/backend/ticket/flow_manager/base.py @@ -9,14 +9,18 @@ specific language governing permissions and limitations under the License. """ import logging +import operator from abc import ABC, abstractmethod +from functools import reduce from typing import Any, Optional, Union +from django.db.models import Q from django.utils.translation import gettext as _ from backend.ticket import constants from backend.ticket.constants import FLOW_FINISHED_STATUS, FLOW_NOT_EXECUTE_STATUS, FlowErrCode, TicketFlowStatus -from backend.ticket.models import Flow +from backend.ticket.models import ClusterOperateRecord, Flow, InstanceOperateRecord +from backend.utils.basic import get_target_items_from_details logger = logging.getLogger("root") @@ -48,8 +52,10 @@ def status(self) -> str: return TicketFlowStatus.FAILED - if self.flow_obj.flow_obj_id or self.flow_obj.status == TicketFlowStatus.RUNNING: + if self.flow_obj.status in [TicketFlowStatus.RUNNING, TicketFlowStatus.FAILED]: # ticket flow 创建成功,查询对应状态 + # 1. 如果是running状态,则查询对应子flow的status + # 2. 如果是failed状态,但不包含err_msg,说明是异步任务导致的(inner flow),也要查询对应状态(因为存在重试可能) return self._status if not self.flow_obj.flow_obj_id: @@ -133,6 +139,47 @@ def flush_error_status_handler(self): self.ticket.status = constants.TicketStatus.RUNNING self.ticket.save(update_fields=["status", "update_at"]) + def create_operate_records(self, object_key, record_model): + """ + 写入操作记录的通用方法 + 每一轮flow在running时,需要更新当前集群的record.flow为当前flow,为新出现的集群增加record(如定点回档包含了新出现的集群) + """ + object_ids = set( + get_target_items_from_details(self.flow_obj.details, match_keys=[object_key, f"{object_key}s"]) + + get_target_items_from_details(self.ticket.details, match_keys=[object_key, f"{object_key}s"]) + ) + if not object_ids: + return + + record_filter = reduce( + operator.or_, [Q(**{object_key: obj_id, "ticket": self.ticket}) for obj_id in object_ids] + ) + + # 修改当前的flow引用,并批量更新 + to_update_records = record_model.objects.filter(record_filter) + for record in to_update_records: + record.flow = self.flow_obj + record_model.objects.bulk_update(to_update_records, fields=["flow"]) + + # 创建新加入的record,并批量创建 + object_ticket_tuples = list(to_update_records.values_list(object_key, "ticket")) + to_create_records = [ + record_model( + **{object_key: obj_id, "flow": self.flow_obj, "ticket": self.ticket, "creator": self.ticket.creator} + ) + for obj_id in object_ids + if (obj_id, self.ticket.id) not in object_ticket_tuples + ] + record_model.objects.bulk_create(to_create_records) + + def create_cluster_operate_records(self): + """写入集群操作记录""" + self.create_operate_records(object_key="cluster_id", record_model=ClusterOperateRecord) + + def create_instance_operate_records(self): + """写入示例的操作记录""" + self.create_operate_records(object_key="instance_id", record_model=InstanceOperateRecord) + def run(self): """执行流程并记录流程对象ID""" try: @@ -140,6 +187,9 @@ def run(self): except Exception as err: # pylint: disable=broad-except self.run_error_status_handler(err) return + else: + self.create_cluster_operate_records() + self.create_instance_operate_records() self.run_status_handler(flow_obj_id) diff --git a/dbm-ui/backend/ticket/flow_manager/inner.py b/dbm-ui/backend/ticket/flow_manager/inner.py index 99f0e59c2f..a89088c851 100644 --- a/dbm-ui/backend/ticket/flow_manager/inner.py +++ b/dbm-ui/backend/ticket/flow_manager/inner.py @@ -14,7 +14,6 @@ from datetime import date, datetime from typing import Dict, Union -from django.forms import model_to_dict from django.utils.translation import gettext as _ from backend.db_meta.exceptions import ClusterExclusiveOperateException @@ -23,7 +22,7 @@ from backend.ticket import constants from backend.ticket.constants import BAMBOO_STATE__TICKET_STATE_MAP, FlowCallbackType from backend.ticket.flow_manager.base import BaseTicketFlow -from backend.ticket.models import ClusterOperateRecord, Flow, InstanceOperateRecord +from backend.ticket.models import Flow from backend.utils.basic import get_target_items_from_details from backend.utils.time import datetime2str @@ -96,43 +95,6 @@ def _status(self) -> str: def _url(self) -> str: return f"/database/{self.ticket.bk_biz_id}/mission-details/{self.root_id}/" - def create_cluster_operate_records(self): - """ - 写入集群操作记录 - """ - cluster_ids = get_target_items_from_details(self.flow_obj.details, match_keys=["cluster_id", "cluster_ids"]) - records = [ - ClusterOperateRecord( - cluster_id=cluster_id, flow=self.flow_obj, ticket=self.ticket, creator=self.ticket.creator - ) - for cluster_id in cluster_ids - ] - - # 如果当前记录已经被创建过了,则不重复创建(这里主要是防止重试inner节点造成重复的记录) - if records and ClusterOperateRecord.objects.filter(**model_to_dict(records[0])).exists(): - return - - ClusterOperateRecord.objects.bulk_create(records) - - def create_instance_operate_records(self): - """ - 写入实例的操作记录 - """ - # TODO 这个函数暂时只考虑StorageInstance,后续应该会将StorageInstance和ProxyInstance合并 - instance_ids = get_target_items_from_details(self.flow_obj.details, match_keys=["instance_id", "instance_ids"]) - records = [ - InstanceOperateRecord( - instance_id=instance_id, flow=self.flow_obj, ticket=self.ticket, creator=self.ticket.creator - ) - for instance_id in instance_ids - ] - - # 如果当前已经被创建过了,则不重复创建 - if records and InstanceOperateRecord.objects.filter(**model_to_dict(records[0])).exists(): - return - - InstanceOperateRecord.objects.bulk_create(records) - def check_exclusive_operations(self): """判断执行互斥""" # TODO: 目前来说,执行互斥对于同时提单或者同时重试的操作是防不住的。 @@ -144,7 +106,9 @@ def check_exclusive_operations(self): # 考虑:如果单纯是为了防住同时操作,是不是设计一个全局锁就好了? ticket_type = self.ticket.ticket_type cluster_ids = get_target_items_from_details(obj=self.ticket.details, match_keys=["cluster_id", "cluster_ids"]) - Cluster.handle_exclusive_operations(cluster_ids=cluster_ids, ticket_type=ticket_type) + Cluster.handle_exclusive_operations( + cluster_ids=cluster_ids, ticket_type=ticket_type, exclude_ticket_ids=[self.ticket.id] + ) def handle_exclusive_error(self): """处理执行互斥后重试的逻辑""" @@ -162,14 +126,15 @@ def callback(self, callback_type: FlowCallbackType) -> None: def run(self) -> None: """inner flow执行流程""" - root_id = f"{date.today()}{uuid.uuid1().hex[:6]}".replace("-", "") + # 获取or生成inner flow的root id + root_id = self.flow_obj.flow_obj_id or f"{date.today()}{uuid.uuid1().hex[:6]}".replace("-", "") try: # 判断执行互斥 self.check_exclusive_operations() - # flow回调前置钩子函数 - self.callback(callback_type=FlowCallbackType.PRE_CALLBACK.value) # 由于 _run 执行后可能会触发信号,导致 current_flow 的误判,因此需提前写入 flow_obj_id self.run_status_handler(root_id) + # flow回调前置钩子函数 + self.callback(callback_type=FlowCallbackType.PRE_CALLBACK.value) self._run() except (Exception, ClusterExclusiveOperateException) as err: # pylint: disable=broad-except # 处理互斥异常和非预期的异常 @@ -182,12 +147,15 @@ def run(self) -> None: def _run(self) -> None: # 创建并执行后台任务流程 + self.flow_obj.refresh_from_db() root_id = self.flow_obj.flow_obj_id flow_details = self.flow_obj.details + controller_info = flow_details["controller_info"] controller_module = importlib.import_module(controller_info["module"]) controller_class = getattr(controller_module, controller_info["class_name"]) controller_inst = controller_class(root_id=root_id, ticket_data=flow_details["ticket_data"]) + return getattr(controller_inst, controller_info["func_name"])() diff --git a/dbm-ui/backend/ticket/flow_manager/manager.py b/dbm-ui/backend/ticket/flow_manager/manager.py index ae81b5f937..ac2c734aeb 100644 --- a/dbm-ui/backend/ticket/flow_manager/manager.py +++ b/dbm-ui/backend/ticket/flow_manager/manager.py @@ -8,6 +8,7 @@ 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 backend import env from backend.ticket import constants from backend.ticket.constants import FLOW_FINISHED_STATUS, FlowType from backend.ticket.flow_manager.delivery import DeliveryFlow, DescribeTaskFlow @@ -15,6 +16,10 @@ from backend.ticket.flow_manager.itsm import ItsmFlow from backend.ticket.flow_manager.pause import PauseFlow from backend.ticket.flow_manager.resource import ( + FakeResourceApplyFlow, + FakeResourceBatchApplyFlow, + FakeResourceBatchDeliveryFlow, + FakeResourceDeliveryFlow, ResourceApplyFlow, ResourceBatchApplyFlow, ResourceBatchDeliveryFlow, @@ -38,6 +43,17 @@ FlowType.RESOURCE_BATCH_DELIVERY: ResourceBatchDeliveryFlow, } +# 开启无资源池环境调试,从空闲机筛选机器伪造资源返回 +if env.FAKE_RESOURCE_APPLY_ENABLE: + SUPPORTED_FLOW_MAP.update( + { + FlowType.RESOURCE_APPLY: FakeResourceApplyFlow, + FlowType.RESOURCE_DELIVERY: FakeResourceDeliveryFlow, + FlowType.RESOURCE_BATCH_APPLY: FakeResourceBatchApplyFlow, + FlowType.RESOURCE_BATCH_DELIVERY: FakeResourceBatchDeliveryFlow, + } + ) + class TicketFlowManager(object): def __init__(self, ticket: Ticket): diff --git a/dbm-ui/backend/ticket/flow_manager/resource.py b/dbm-ui/backend/ticket/flow_manager/resource.py index 69b0ae2e63..14b8900d5e 100644 --- a/dbm-ui/backend/ticket/flow_manager/resource.py +++ b/dbm-ui/backend/ticket/flow_manager/resource.py @@ -8,27 +8,37 @@ 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. """ - +import copy import importlib +import logging import uuid from collections import defaultdict from datetime import date -from typing import Dict, List, Optional, Union +from typing import Any, Dict, List, Optional, Union +from django.core.cache import cache from django.utils.translation import gettext as _ +from backend import env from backend.components.dbresource.client import DBResourceApi +from backend.configuration.models import DBAdministrator +from backend.constants import DEFAULT_BK_CLOUD_ID from backend.db_meta.models import Spec -from backend.db_meta.models.spec import ClusterDeployPlan -from backend.db_services.dbresource.exceptions import ResourceApplyException +from backend.db_services.dbresource.exceptions import ResourceApplyException, ResourceApplyInsufficientException +from backend.db_services.ipchooser.constants import CommonEnum +from backend.db_services.ipchooser.query.resource import ResourceQueryHelper +from backend.tests.mock_data import ticket from backend.ticket import constants -from backend.ticket.constants import FlowCallbackType, FlowType +from backend.ticket.constants import AffinityEnum, FlowCallbackType, FlowType, ResourceApplyErrCode, TodoType from backend.ticket.flow_manager.base import BaseTicketFlow from backend.ticket.flow_manager.delivery import DeliveryFlow -from backend.ticket.models import Flow -from backend.utils.batch_request import request_multi_thread +from backend.ticket.models import Flow, Todo from backend.utils.time import datetime2str +logger = logging.getLogger("root") + +HOST_IN_USE = "host_in_use" + class ResourceApplyFlow(BaseTicketFlow): """ @@ -51,12 +61,37 @@ def _end_time(self) -> Optional[str]: def _summary(self) -> str: return _("资源申请状态{status_display}").format(status_display=constants.TicketStatus.get_choice_label(self.status)) + @property + def status(self) -> str: + # 覆写base的状态判断,资源池申请节点的状态判断逻辑不同 + return self._status + + def update_flow_status(self, status): + self.flow_obj.update_status(status) + return status + @property def _status(self) -> str: + # 任务流程未创建时未PENDING状态 + if not self.flow_obj.flow_obj_id: + return self.update_flow_status(constants.TicketStatus.PENDING.value) + + # 如果资源申请成功,则直接返回success if self.resource_apply_status: - return constants.TicketStatus.SUCCEEDED.value + return self.update_flow_status(constants.TicketStatus.SUCCEEDED.value) + + if self.flow_obj.err_msg: + # 如果是其他情况引起的错误,则直接返回fail + if not self.flow_obj.todo_of_flow.exists(): + return self.update_flow_status(constants.TicketStatus.FAILED.value) + # 如果是资源申请的todo状态,则判断todo是否完成 + if self.ticket.todo_of_ticket.exist_unfinished(): + return self.update_flow_status(constants.TicketStatus.RUNNING.value) + else: + return self.update_flow_status(constants.TicketStatus.SUCCEEDED.value) - return constants.TicketStatus.RUNNING.value + # 其他情况认为还在RUNNING状态 + return self.update_flow_status(constants.TicketStatus.RUNNING.value) @property def _url(self) -> str: @@ -72,7 +107,22 @@ def callback(self) -> None: callback_class = getattr(callback_module, callback_info[f"{FlowCallbackType.POST_CALLBACK}_callback_class"]) getattr(callback_class(self.ticket), f"{FlowCallbackType.POST_CALLBACK}_callback")() + def run(self): + """执行流程并记录流程对象ID""" + resource_flow_id = f"{date.today()}{uuid.uuid1().hex[:6]}".replace("-", "") + self.run_status_handler(resource_flow_id) + + try: + self._run() + except ResourceApplyInsufficientException as err: + # 如果是补货失败,则只更新错误信息,认为单据和flow都在运行中 + self.flow_obj.err_msg = err.message + self.flow_obj.save(update_fields=["err_msg", "update_at"]) + except Exception as err: # pylint: disable=broad-except + self.run_error_status_handler(err) + def _format_resource_hosts(self, hosts): + """格式化申请的主机参数""" return [ { # 没有业务ID,认为是公共资源 @@ -80,100 +130,155 @@ def _format_resource_hosts(self, hosts): "ip": host["ip"], "bk_cloud_id": host["bk_cloud_id"], "bk_host_id": host["bk_host_id"], - # 补充机器的内存,cpu和磁盘信息 + # 补充机器的内存,cpu和磁盘信息。(bk_disk的单位是GB, bk_mem的单位是MB) "bk_cpu": host["cpu_num"], - "bk_disk": sum([storage["size"] for _, storage in host["storage_device"].items()]), + "bk_disk": host["total_storage_cap"], "bk_mem": host["dram_cap"], } for host in hosts ] - def run(self): - """执行流程并记录流程对象ID""" - try: - resource_flow_id = f"{date.today()}{uuid.uuid1().hex[:6]}".replace("-", "") - self.run_status_handler(resource_flow_id) - self._run() - except Exception as err: # pylint: disable=broad-except - self.run_error_status_handler(err) - return - def apply_resource(self, ticket_data): + """资源申请""" apply_params: Dict[str, Union[str, List]] = { "for_biz_id": ticket_data["bk_biz_id"], - "bk_cloud_id": ticket_data["bk_cloud_id"], "resource_type": self.ticket.group, - "details": [], "bill_id": str(self.ticket.id), - "task_id": self.flow_obj.flow_obj_id, + "bill_type": self.ticket.ticket_type, + # 消费情况下的task id为inner flow + "task_id": self.ticket.next_flow().flow_obj_id, "operator": self.ticket.creator, + "details": self.fetch_apply_params(ticket_data), } - # 根据规格来申请相应的机器 - if "resource_spec" in ticket_data: - resource_spec = ticket_data["resource_spec"] - for role, role_spec in resource_spec.items(): - apply_params["details"].append( - Spec.objects.get(spec_id=role_spec["spec_id"]).get_apply_params_detail(role, role_spec["count"]) - ) - - # 根据部署方案来申请相应的机器 - if "resource_plan" in ticket_data: - resource_plan = ticket_data["resource_plan"] - deploy_plan = ClusterDeployPlan.objects.get(id=resource_plan["resource_plan_id"]) - apply_params["details"].extend(deploy_plan.get_apply_params_details()) - # 向资源池申请机器 resp = DBResourceApi.resource_pre_apply(params=apply_params, raw=True) - if resp["code"]: - raise ResourceApplyException(_("资源申请失败,错误信息: {}").format(resp["message"])) - - resource_request_id, apply_data = resp["request_id"], resp["data"] + if resp["code"] == ResourceApplyErrCode.RESOURCE_LAKE: + # 如果是资源不足,则创建补货单,用户手动处理后可以重试资源申请 + self.create_replenish_todo() + raise ResourceApplyInsufficientException(_("资源不足申请失败,请前往补货后重试")) + elif resp["code"] in [ + ResourceApplyErrCode.RESOURCE_LOCK_FAIL, + ResourceApplyErrCode.RESOURCE_MACHINE_FAIL, + ResourceApplyErrCode.RESOURCE_PARAMS_INVALID, + ]: + raise ResourceApplyException( + _("资源池相关服务出现系统错误,请联系管理员或稍后重试。错误信息: {}").format(ResourceApplyErrCode.get_choice_label(resp["code"])) + ) # 将资源池申请的主机信息转换为单据参数 + resource_request_id, apply_data = resp["request_id"], resp["data"] node_infos: Dict[str, List] = defaultdict(list) for info in apply_data: role = info["item"] host_infos = self._format_resource_hosts(info["data"]) - node_infos[role] = host_infos + # 如果是部署方案的分组,则用backend_group包裹。里面每一小组是一对master/slave; + # 否则就按角色分组填入 + if "backend_group" in role: + backend_group_name = role.rsplit("_", 1)[0] + node_infos[backend_group_name].append({"master": host_infos[0], "slave": host_infos[1]}) + else: + node_infos[role] = host_infos + + return resource_request_id, node_infos + + def create_replenish_todo(self): + """创建补货单""" + if Todo.objects.filter(flow=self.flow_obj, ticket=self.ticket, type=TodoType.RESOURCE_REPLENISH).exists(): + return + + from backend.ticket.todos.pause_todo import ResourceReplenishTodoContext + + user = self.ticket.creator + admins = DBAdministrator.get_biz_db_type_admins(self.ticket.bk_biz_id, self.ticket.group) + Todo.objects.create( + name=_("【{}】流程所需资源不足").format(self.ticket.get_ticket_type_display()), + flow=self.flow_obj, + ticket=self.ticket, + type=TodoType.RESOURCE_REPLENISH, + # 需要提单人和管理人员共同关注todo单 + operators=[user, *admins], + context=ResourceReplenishTodoContext( + flow_id=self.flow_obj.id, ticket_id=self.ticket.id, user=user, administrators=admins + ).to_dict(), + ) - # 针对批量申请的情况,获取当前index - index = ticket_data.get("index", None) + def fetch_apply_params(self, ticket_data): + """构造资源申请参数""" + bk_cloud_id: int = ticket_data.get("bk_cloud_id", DEFAULT_BK_CLOUD_ID) + details: List[Dict[str, Any]] = [] + + # 根据规格来填充相应机器的申请参数 + resource_spec = ticket_data["resource_spec"] + for role, role_spec in resource_spec.items(): + # 如果申请数量为0/规格ID不合法(存在spec id为0 --> 是前端表单的默认值),则跳过 + if not role_spec["count"] or not role_spec["spec_id"]: + continue + # 填充规格申请参数 + if role == "backend_group": + details.extend( + Spec.objects.get(spec_id=role_spec["spec_id"]).get_backend_group_apply_params_detail( + bk_cloud_id=bk_cloud_id, backend_group=role_spec + ) + ) + else: + details.append( + Spec.objects.get(spec_id=role_spec["spec_id"]).get_apply_params_detail( + group_mark=role, + count=int(role_spec["count"]), + bk_cloud_id=bk_cloud_id, + affinity=role_spec.get("affinity", AffinityEnum.NONE.value), + ) + ) - return resource_request_id, node_infos, index + if not details: + raise ResourceApplyException(_("申请的资源总数为0,资源申请不合法")) - def patch_resource_params(self, ticket_data): - if "resource_spec" in ticket_data: - resource_spec = ticket_data["resource_spec"] - for role, role_spec in resource_spec.items(): - resource_spec[role] = { - **Spec.objects.get(spec_id=role_spec["spec_id"]).get_spec_info(), - "count": role_spec["count"], - } - - if "resource_plan" in ticket_data: - # 如果是部署规格,默认规格角色是master和slave,如果需要其他角色类型在post callback钩子函数修改 - deploy_plan_id = ticket_data["resource_plan"]["resource_plan_id"] - ticket_data.update(deploy_plan_id=deploy_plan_id) - - deploy_plan = ClusterDeployPlan.objects.get(id=deploy_plan_id) - master_spec = slave_spec = {**deploy_plan.spec.get_spec_info(), "count": deploy_plan.machine_pair_cnt} - ticket_data["resource_spec"].update(master=master_spec, slave=slave_spec) + return details + + def patch_resource_params(self, ticket_data, spec_map: Dict[int, Spec] = None): + """ + 将资源池部署信息写入到ticket_data。 + @param ticket_data: 待填充的字典 + @param spec_map: 规格缓存数据, 避免频繁查询数据库 + """ + + spec_map = spec_map or {} + resource_spec = ticket_data["resource_spec"] + for role, role_spec in copy.deepcopy(resource_spec).items(): + # 如果该存在无需申请,则跳过 + if not role_spec["count"] or not role_spec["spec_id"]: + continue + + spec = spec_map.get(role_spec["spec_id"]) or Spec.objects.get(spec_id=role_spec["spec_id"]) + role_info = {**spec.get_spec_info(), "count": role_spec["count"]} + # 如果角色是backend_group,则默认角色信息写入master和slave + if role == "backend_group": + resource_spec["master"] = resource_spec["slave"] = role_info + else: + resource_spec[role] = role_info + + def write_node_infos(self, ticket_data, node_infos): + """将资源申请信息写入ticket_data""" + ticket_data.update({"nodes": node_infos}) def _run(self) -> None: next_flow = self.ticket.next_flow() if next_flow.flow_type != FlowType.INNER_FLOW: raise ResourceApplyException(_("资源申请下一个节点不为部署节点,请重新编排")) + # 提前为inner flow生成root id,要写入操作记录中 + next_flow.flow_obj_id = f"{date.today()}{uuid.uuid1().hex[:6]}".replace("-", "") + next_flow.save() + # 资源申请 - resource_request_id, node_infos, __ = self.apply_resource(self.flow_obj.details) + resource_request_id, node_infos = self.apply_resource(self.flow_obj.details) # 将机器信息写入ticket和inner flow - next_flow.details["ticket_data"].update({"nodes": node_infos, "resource_request_id": resource_request_id}) - # 将资源池部署信息写入到inner flow - self.patch_resource_params(ticket_data=next_flow.details["ticket_data"]) + self.write_node_infos(next_flow.details["ticket_data"], node_infos) + self.patch_resource_params(next_flow.details["ticket_data"]) next_flow.save(update_fields=["details"]) - + # 相关信息回填到单据和resource flow中 self.ticket.update_details(resource_request_id=resource_request_id, nodes=node_infos) self.flow_obj.update_details(resource_apply_status=True) @@ -188,7 +293,7 @@ def _run(self) -> None: class ResourceBatchApplyFlow(ResourceApplyFlow): """ - 内置批量的资源申请,一般用于批量的添加从库,添加proxy等 + 内置批量的资源申请,一般单据的批量操作。(比如mysql的添加从库) 内置格式参考: "info": [ { @@ -200,44 +305,40 @@ class ResourceBatchApplyFlow(ResourceApplyFlow): ] """ - def _run(self) -> None: - next_flow = self.ticket.next_flow() - if next_flow.flow_type != FlowType.INNER_FLOW: - raise ResourceApplyException(_("资源申请下一个节点不为部署节点,请重新编排")) - - # 目前可能存在一个单据多个集群同时操作,但是集群的云区域不同,故当前考虑是批量请求资源池接口 - # TODO: 资源池申请暂不支持跨云查询 - infos = self.flow_obj.details["infos"] - for index, apply_info in enumerate(infos): - apply_info.update(index=index) - - params_list = [{"ticket_data": info} for info in infos] - batch_apply_result = request_multi_thread( - func=self.apply_resource, - params_list=params_list, - get_data=lambda args: { - "resource_request_id": args[0], - "nodes": args[1], - "index": args[2], - }, - ) + def patch_resource_params(self, ticket_data): + spec_ids: List[int] = [] + for info in ticket_data["infos"]: + spec_ids.extend([data["spec_id"] for data in info["resource_spec"].values()]) - # 将获取的资源信息,序列化到inner flow中 - for apply_result in batch_apply_result: - infos[apply_result["index"]].update(apply_result["nodes"]) + # 提前缓存数据库查询数据,避免多次IO + spec_map = {spec.spec_id: spec for spec in Spec.objects.filter(spec_id__in=spec_ids)} + for info in ticket_data["infos"]: + super().patch_resource_params(info, spec_map) - next_flow.details["ticket_data"].update(infos=infos) - next_flow.save(update_fields=["details"]) - self.ticket.update_details(batch_apply_result=batch_apply_result) - self.flow_obj.update_details(resource_apply_status=True) + def write_node_infos(self, ticket_data, node_infos): + """ + 解析每个角色前缀,并将角色申请资源填充到对应的info中 + """ + for node_group, nodes in node_infos.items(): + # 获取当前角色组在原来info的位置,并填充申请的资源信息 + index, group = node_group.split("_", 1) + ticket_data["infos"][int(index)][group] = nodes - # 调用后继函数 - self.callback() + def fetch_apply_params(self, ticket_data): + """ + 将每个info中需要申请的角色加上前缀index, + 并且填充为统一的apply_details进行申请 + """ + apply_details: List[Dict[str, Any]] = [] + for index, info in enumerate(ticket_data["infos"]): + details = super().fetch_apply_params(info) + # 为申请的角色组表示序号 + for node_params in details: + node_params["group_mark"] = f"{index}_{node_params['group_mark']}" - # 执行下一个流程 - from backend.ticket.flow_manager.manager import TicketFlowManager + apply_details.extend(details) - TicketFlowManager(ticket=self.ticket).run_next_flow() + return apply_details class ResourceDeliveryFlow(DeliveryFlow): @@ -250,8 +351,12 @@ def confirm_resource(self, ticket_data): resource_request_id: str = ticket_data["resource_request_id"] nodes: Dict[str, List] = ticket_data.get("nodes") host_ids: List[int] = [] - for __, role_info in nodes.items(): - host_ids.extend([host["bk_host_id"] for host in role_info]) + for role, role_info in nodes.items(): + # 批量后台申请的角色为: {index}_backend_group + if "backend_group" in role: + host_ids.extend([host["bk_host_id"] for backend in role_info for host in backend.values()]) + else: + host_ids.extend([host["bk_host_id"] for host in role_info]) # 确认资源申请 DBResourceApi.resource_confirm(params={"request_id": resource_request_id, "host_ids": host_ids}) @@ -267,10 +372,85 @@ class ResourceBatchDeliveryFlow(ResourceDeliveryFlow): """ def _run(self) -> str: - confirm_params = self.ticket.details["batch_apply_result"] - confirm_params_list = [{"ticket_data": info} for info in confirm_params] - request_multi_thread( - func=self.confirm_resource, - params_list=confirm_params_list, + # 暂时与单独交付节点没有区别 + return super()._run() + + +class FakeResourceApplyFlow(ResourceApplyFlow): + def apply_resource(self, ticket_data): + """模拟资源池申请""" + + host_in_use = set(cache.get(HOST_IN_USE, [])) + + resp = ResourceQueryHelper.query_cc_hosts( + {"bk_biz_id": env.DBA_APP_BK_BIZ_ID, "bk_inst_id": 7, "bk_obj_id": "module"}, + [], + 0, + 1000, + CommonEnum.DEFAULT_HOST_FIELDS.value, + return_status=True, + bk_cloud_id=0, ) - return "ok" + count, apply_data = resp["count"], list(filter(lambda x: x["status"] == 1, resp["info"])) + + for item in apply_data: + item["ip"] = item["bk_host_innerip"] + + # 排除缓存占用的主机 + host_free = list(filter(lambda x: x["bk_host_id"] not in host_in_use, apply_data)) + + index = 0 + expected_count = 0 + node_infos: Dict[str, List] = defaultdict(list) + for detail in self.fetch_apply_params(ticket_data): + role, count = detail["group_mark"], detail["count"] + host_infos = host_free[index : index + count] + try: + if "backend_group" in role: + backend_group_name = role.rsplit("_", 1)[0] + node_infos[backend_group_name].append({"master": host_infos[0], "slave": host_infos[1]}) + else: + node_infos[role] = host_infos + except IndexError: + raise ResourceApplyException(_("模拟资源申请失败,主机数量不够")) + + index += count + expected_count += len(host_infos) + + if expected_count < index: + raise ResourceApplyException(_("模拟资源申请失败,主机数量不够:{} < {}").format(count, index)) + + logger.info(_("模拟资源申请成功(%s):%s"), expected_count, node_infos) + + # 添加新占用的主机 + host_in_use = host_in_use.union(list(map(lambda x: x["bk_host_id"], host_free[:index]))) + cache.set(HOST_IN_USE, list(host_in_use)) + + return count, node_infos + + +class FakeResourceBatchApplyFlow(FakeResourceApplyFlow, ResourceBatchApplyFlow): + pass + + +class FakeResourceDeliveryFlow(ResourceDeliveryFlow): + """ + 内置资源申请交付流程,暂时无需操作 + """ + + def confirm_resource(self, ticket_data): + pass + + def _run(self) -> str: + self.confirm_resource(self.ticket.details) + return super()._run() + + +class FakeResourceBatchDeliveryFlow(FakeResourceDeliveryFlow): + """ + 内置资源申请批量交付流程,主要是通知资源池机器使用成功 + """ + + def _run(self) -> str: + # 暂时与单独交付节点没有区别 + return super()._run() diff --git a/dbm-ui/backend/ticket/handler.py b/dbm-ui/backend/ticket/handler.py index 21bd0412b7..3c7262295d 100644 --- a/dbm-ui/backend/ticket/handler.py +++ b/dbm-ui/backend/ticket/handler.py @@ -12,7 +12,10 @@ from django.utils.translation import ugettext as _ +from backend import env from backend.db_meta.models import Cluster, StorageInstance +from backend.db_services.ipchooser.handlers.host_handler import HostHandler +from backend.ticket.constants import TicketType from backend.ticket.models import Ticket from backend.utils.basic import get_target_items_from_details @@ -68,3 +71,64 @@ def add_related_object(cls, ticket_data: List[Dict]) -> List[Dict]: ], } return ticket_data + + @classmethod + def fast_create_cloud_component_method(cls, bk_biz_id, bk_cloud_id, ips, user="admin"): + def _get_base_info(host): + return { + "bk_host_id": host["host_id"], + "ip": host["ip"], + "bk_cloud_id": host["cloud_id"], + } + + # 查询的机器的信息 + host_list = [{"cloud_id": bk_cloud_id, "ip": ip} for ip in ips] + host_infos = HostHandler.details(scope_list=[{"bk_biz_id": bk_biz_id}], host_list=host_list) + + # 构造nginx部署信息 + nginx_host_infos = [ + { + "bk_outer_ip": host_infos[1].get("bk_host_outerip") or host_infos[1]["ip"], + **_get_base_info(host_infos[1]), + } + ] + # 构造dns的部署信息 + dns_host_infos = [{**_get_base_info(host_infos[0])}, {**_get_base_info(host_infos[1])}] + # 构造drs的部署信息 + drs_host_infos = [ + {**_get_base_info(host_infos[0]), "drs_port": env.DRS_PORT}, + {**_get_base_info(host_infos[1]), "drs_port": env.DRS_PORT}, + ] + # 构造agent的部署信息 + agent_host_infos = [ + { + **_get_base_info(host_infos[0]), + "bk_city_code": host_infos[0].get("bk_idc_id") or 0, + "bk_city_name": host_infos[0].get("bk_idc_name", ""), + } + ] + # 构造gm的部署信息 + gm_host_infos = [ + agent_host_infos[0], # 允许将一个gm和agent部署在同一台机器 + { + **_get_base_info(host_infos[1]), + "bk_city_code": host_infos[1].get("bk_idc_id") or 1, + "bk_city_name": host_infos[1].get("bk_idc_name", ""), + }, + ] + + # 创建单据进行部署 + details = { + "bk_cloud_id": bk_cloud_id, + "dns": {"host_infos": dns_host_infos}, + "nginx": {"host_infos": nginx_host_infos}, + "drs": {"host_infos": drs_host_infos}, + "dbha": {"gm": gm_host_infos, "agent": agent_host_infos}, + } + Ticket.create_ticket( + ticket_type=TicketType.CLOUD_SERVICE_APPLY, + creator=user, + bk_biz_id=bk_biz_id, + remark=_("云区域组件快速部署单据"), + details=details, + ) diff --git a/dbm-ui/backend/ticket/models/ticket.py b/dbm-ui/backend/ticket/models/ticket.py index 4e91923438..a176108e3f 100644 --- a/dbm-ui/backend/ticket/models/ticket.py +++ b/dbm-ui/backend/ticket/models/ticket.py @@ -142,7 +142,7 @@ def current_flow(self) -> Flow: def next_flow(self) -> Flow: """ - 下一个流程,即 TicketFlow 中第一个 flow_obj_id 为空的流程 + 下一个流程,即 TicketFlow 中第一个为PENDING的流程 """ next_flows = Flow.objects.filter(ticket=self, status=TicketFlowStatus.PENDING) @@ -161,7 +161,7 @@ def create_ticket( remark: str, details: Dict[str, Any], auto_execute: bool = True, - ) -> None: + ) -> "Ticket": """ 自动创建单据 :param ticket_type: 单据类型 @@ -196,19 +196,33 @@ def create_ticket( logger.info(_("单据{}正在初始化流程").format(ticket.id)) TicketFlowManager(ticket=ticket).run_next_flow() + return ticket + class ClusterOperateRecordManager(models.Manager): - def filter_actives(self, cluster_id, **kwargs): - """获得集群正在运行的单据""" - return self.filter(cluster_id=cluster_id, flow__status=TicketFlowStatus.RUNNING, **kwargs) + def filter_actives(self, cluster_id, *args, **kwargs): + """获得集群正在运行的单据记录""" + return self.filter(cluster_id=cluster_id, ticket__status=TicketFlowStatus.RUNNING, *args, **kwargs) + + def filter_inner_actives(self, cluster_id, *args, **kwargs): + """获取集群正在运行的inner flow的单据记录。此时认为集群会在互斥阶段""" + # 排除特定的单据,如自身单据重试排除自身 + exclude_ticket_ids = kwargs.pop("exclude_ticket_ids", []) + return self.filter( + cluster_id=cluster_id, + flow__flow_type=FlowType.INNER_FLOW, + flow__status=TicketFlowStatus.RUNNING, + *args, + **kwargs, + ).exclude(flow__ticket_id__in=exclude_ticket_ids) def get_cluster_operations(self, cluster_id, **kwargs): - """集群上的操作列表""" + """集群上的正在运行的操作列表""" return [r.summary for r in self.filter_actives(cluster_id, **kwargs)] def has_exclusive_operations(self, ticket_type, cluster_id, **kwargs): """判断当前单据类型与集群正在进行中的单据是否互斥""" - active_tickets = self.filter_actives(cluster_id, **kwargs) + active_tickets = self.filter_inner_actives(cluster_id, **kwargs) exclusive_infos = [] for active_ticket in active_tickets: try: diff --git a/dbm-ui/backend/ticket/serializers.py b/dbm-ui/backend/ticket/serializers.py index 5ea03b8758..c4bb9125a7 100644 --- a/dbm-ui/backend/ticket/serializers.py +++ b/dbm-ui/backend/ticket/serializers.py @@ -188,7 +188,11 @@ class Meta: swagger_schema_fields = {"example": todo_operate_example} -class TicketTypeSerializer(serializers.Serializer): +class TicketTypeSLZ(serializers.Serializer): + is_apply = serializers.BooleanField(help_text=_("是否是部署类单据"), required=False, default=False) + + +class TicketTypeResponseSLZ(serializers.Serializer): key = serializers.CharField(help_text="ID") value = serializers.CharField(help_text=_("名称")) diff --git a/dbm-ui/backend/ticket/tasks/ticket_tasks.py b/dbm-ui/backend/ticket/tasks/ticket_tasks.py index c7e89dd58e..58f4136d60 100644 --- a/dbm-ui/backend/ticket/tasks/ticket_tasks.py +++ b/dbm-ui/backend/ticket/tasks/ticket_tasks.py @@ -23,7 +23,7 @@ from backend import env from backend.components import BKLogApi -from backend.db_meta.enums import InstanceInnerRole +from backend.db_meta.enums import ClusterType, InstanceInnerRole from backend.db_meta.models import Cluster, StorageInstance from backend.ticket.builders.common.constants import MYSQL_CHECKSUM_TABLE, MySQLDataRepairTriggerMode from backend.ticket.constants import FlowErrCode, TicketType @@ -127,8 +127,8 @@ def auto_create_data_repair_ticket(cls): for inst in StorageInstance.objects.select_related("machine").filter(inst_filters) } - master_data_repair_info: Dict[str, Any] = {} - slave_id__slave_data_repair_infos: Dict[int, Dict[str, Any]] = {} + data_repair_infos: List[Dict[str, Any]] = [] + master_slave_exists: Dict[str, Dict[str, bool]] = defaultdict(lambda: defaultdict(bool)) for log in checksum_logs: master_ip_port, slave_ip_port = ( f"{log['master_ip']}:{log['master_port']}", @@ -141,38 +141,40 @@ def auto_create_data_repair_ticket(cls): ): continue - # 如果数据校验一致,则跳过 + # 如果数据校验一致 or 重复的主从对,则跳过 is_consistent = log["master_crc"] == log["this_crc"] and log["master_cnt"] == log["this_cnt"] - if is_consistent: + if is_consistent or master_slave_exists[master_ip_port][slave_ip_port]: continue - # 填充需要校验的master信息 - if not master_data_repair_info: - master = ip_port__instance_id_map[master_ip_port] - master_data_repair_info = { - "id": master.id, - "bk_biz_id": log["bk_biz_id"], - "ip": log["master_ip"], - "port": log["master_port"], - "bk_host_id": master.machine.bk_host_id, - "bk_cloud_id": master.machine.bk_cloud_id, - } - - # 填充需要校验的slave信息,注意一个slave有可能被校验多次,这里只保留最近一份 + # 标记需要检验的master/slave,并缓存到修复信息中 + master_slave_exists[master_ip_port][slave_ip_port] = True + master = ip_port__instance_id_map[master_ip_port] + master_data_repair_info = { + "id": master.id, + "bk_biz_id": log["bk_biz_id"], + "ip": log["master_ip"], + "port": log["master_port"], + "bk_host_id": master.machine.bk_host_id, + "bk_cloud_id": master.machine.bk_cloud_id, + } slave = ip_port__instance_id_map[slave_ip_port] - if slave.id not in slave_id__slave_data_repair_infos: - slave_id__slave_data_repair_infos[slave.id] = { - "id": slave.id, - "bk_biz_id": log["bk_biz_id"], - "ip": log["ip"], - "port": log["port"], - "bk_host_id": slave.machine.bk_host_id, - "bk_cloud_id": slave.machine.bk_cloud_id, - "is_consistent": is_consistent, - } + slave_data_repair_info = { + "id": slave.id, + "bk_biz_id": log["bk_biz_id"], + "ip": log["ip"], + "port": log["port"], + "bk_host_id": slave.machine.bk_host_id, + "bk_cloud_id": slave.machine.bk_cloud_id, + "is_consistent": is_consistent, + } + # 注意这里要区别集群类型 + if cluster.cluster_type == ClusterType.TenDBCluster or not data_repair_infos: + data_repair_infos.append({"master": master_data_repair_info, "slaves": [slave_data_repair_info]}) + elif cluster.cluster_type == ClusterType.TenDBHA: + data_repair_infos[0]["slaves"].append(slave_data_repair_info) # 如果不存在需要修复的slave,则跳过 - if not slave_id__slave_data_repair_infos.values(): + if not data_repair_infos: logger.info(_("集群{}数据校验正确,不需要进行数据修复".format(cluster_id))) continue @@ -188,13 +190,17 @@ def auto_create_data_repair_ticket(cls): "infos": [ { "cluster_id": cluster_id, - "master": master_data_repair_info, - "slaves": list(slave_id__slave_data_repair_infos.values()), + "master": data_info["master"], + "slaves": data_info["slaves"], } + for data_info in data_repair_infos ], } + ticket_type = TicketType.MYSQL_DATA_REPAIR + if cluster.cluster_type == ClusterType.TenDBCluster: + ticket_type = TicketType.TENDBCLUSTER_DATA_REPAIR cls._create_ticket( - ticket_type=TicketType.MYSQL_DATA_REPAIR, + ticket_type=ticket_type, creator=cluster.creator, bk_biz_id=cluster.bk_biz_id, remark=_("集群{}存在数据不一致,自动创建的数据修复单据").format(cluster.name), @@ -222,14 +228,3 @@ def apply_ticket_task(ticket_id: int, func_name: str, params: dict = None, eta: res = _apply_ticket_task.apply_async((ticket_id, func_name, params), eta=eta) return res - - -# ----------------------------- 定时执行任务函数 ---------------------------------------- -@shared_task -def auto_retry_exclusive_inner_flow(): - TicketTask.retry_exclusive_inner_flow() - - -@shared_task -def auto_create_data_repair_ticket(): - TicketTask.auto_create_data_repair_ticket() diff --git a/dbm-ui/backend/ticket/todos/__init__.py b/dbm-ui/backend/ticket/todos/__init__.py index 31e5455cf8..b53deff086 100644 --- a/dbm-ui/backend/ticket/todos/__init__.py +++ b/dbm-ui/backend/ticket/todos/__init__.py @@ -86,7 +86,6 @@ def register_all_todos(path=os.path.dirname(__file__), module_path="backend.tick try: module_name = name.replace(".py", "") import_path = ".".join([module_path, module_name]) - print(f"register_all_todos: {import_path}") importlib.import_module(import_path) except ModuleNotFoundError as e: logger.warning(e) @@ -99,6 +98,7 @@ class ActionType(str, StructuredEnum): APPROVE = EnumField("APPROVE", _("确认执行")) TERMINATE = EnumField("TERMINATE", _("终止单据")) + RESOURCE_REAPPLY = EnumField("RESOURCE_REAPPLY", _("资源重新申请")) @dataclass diff --git a/dbm-ui/backend/ticket/todos/pause_todo.py b/dbm-ui/backend/ticket/todos/pause_todo.py index 3bef205d79..e350d92b2e 100644 --- a/dbm-ui/backend/ticket/todos/pause_todo.py +++ b/dbm-ui/backend/ticket/todos/pause_todo.py @@ -11,8 +11,9 @@ from dataclasses import dataclass from backend.ticket import todos -from backend.ticket.constants import TodoType +from backend.ticket.constants import TicketFlowStatus, TodoType from backend.ticket.flow_manager import manager +from backend.ticket.flow_manager.manager import TicketFlowManager from backend.ticket.todos import ActionType, BaseTodoContext @@ -21,6 +22,12 @@ class PauseTodoContext(BaseTodoContext): pass +@dataclass +class ResourceReplenishTodoContext(BaseTodoContext): + user: str + administrators: list + + @todos.TodoActorFactory.register(TodoType.APPROVE) class PauseTodo(todos.TodoActor): """来自主流程的待办""" @@ -37,3 +44,27 @@ def process(self, username, action, params): # 所有待办完成后,执行后面的flow if not self.todo.ticket.todo_of_ticket.exist_unfinished(): manager.TicketFlowManager(ticket=self.todo.ticket).run_next_flow() + + +@todos.TodoActorFactory.register(TodoType.RESOURCE_REPLENISH) +class ResourceReplenishTodo(todos.TodoActor): + """资源补货的代办""" + + def process(self, username, action, params): + """确认/终止""" + + # 终止单据 + if action == ActionType.TERMINATE: + self.todo.set_failed(username, action) + return + + # 尝试重新申请资源,申请成功则关闭todo单 + resource_apply_flow = TicketFlowManager(ticket=self.todo.ticket).get_ticket_flow_cls(self.todo.flow.flow_type)( + self.todo.flow + ) + resource_apply_flow.retry() + + # 注意这里需要刷新flow字段 + self.todo.refresh_from_db(fields=["flow"]) + if self.todo.flow.status == TicketFlowStatus.SUCCEEDED: + self.todo.set_success(username, action) diff --git a/dbm-ui/backend/ticket/views.py b/dbm-ui/backend/ticket/views.py index d8ece63a25..ae26ba5df8 100644 --- a/dbm-ui/backend/ticket/views.py +++ b/dbm-ui/backend/ticket/views.py @@ -22,7 +22,6 @@ from backend.bk_web import viewsets from backend.bk_web.swagger import PaginatedResponseSwaggerAutoSchema, ResponseSwaggerAutoSchema from backend.configuration.models import DBAdministrator -from backend.db_services.ipchooser.handlers.host_handler import HostHandler from backend.db_services.ipchooser.query.resource import ResourceQueryHelper from backend.iam_app.handlers.drf_perm import TicketIAMPermission from backend.ticket.builders import BuilderFactory @@ -43,7 +42,8 @@ RetryFlowSLZ, TicketFlowSerializer, TicketSerializer, - TicketTypeSerializer, + TicketTypeResponseSLZ, + TicketTypeSLZ, TodoOperateSerializer, TodoSerializer, ) @@ -246,12 +246,18 @@ def retry_flow(self, request, pk): @swagger_auto_schema( operation_summary=_("获取单据类型列表"), - responses={status.HTTP_200_OK: TicketTypeSerializer(many=True)}, + query_serializer=TicketTypeSLZ(), + responses={status.HTTP_200_OK: TicketTypeResponseSLZ(many=True)}, tags=[TICKET_TAG], ) - @action(methods=["GET"], detail=False, filter_fields=None, pagination_class=None) + @action(methods=["GET"], detail=False, filter_fields=None, pagination_class=None, serializer_class=TicketTypeSLZ) def flow_types(self, request, *args, **kwargs): - return Response([{"key": choice[0], "value": choice[1]} for choice in TicketType.get_choices()]) + is_apply = self.params_validate(self.get_serializer_class())["is_apply"] + ticket_type_list = [] + for choice in TicketType.get_choices(): + if not is_apply or choice[0] in BuilderFactory.apply_ticket_type: + ticket_type_list.append({"key": choice[0], "value": choice[1]}) + return Response(ticket_type_list) @swagger_auto_schema( operation_summary=_("节点列表"), @@ -451,66 +457,5 @@ def fast_create_cloud_component(self, request, *args, **kwargs): bk_cloud_id = validated_data["bk_cloud_id"] ips = validated_data["ips"] bk_biz_id = validated_data["bk_biz_id"] - self.fast_create_cloud_component_method(bk_biz_id, bk_cloud_id, ips, request.user.username) + TicketHandler.fast_create_cloud_component_method(bk_biz_id, bk_cloud_id, ips, request.user.username) return Response() - - @classmethod - def fast_create_cloud_component_method(cls, bk_biz_id, bk_cloud_id, ips, user="admin"): - def _get_base_info(host): - return { - "bk_host_id": host["host_id"], - "ip": host["ip"], - "bk_cloud_id": host["cloud_id"], - } - - # 查询的机器的信息 - host_list = [{"cloud_id": bk_cloud_id, "ip": ip} for ip in ips] - host_infos = HostHandler.details(scope_list=[{"bk_biz_id": bk_biz_id}], host_list=host_list) - - # 构造nginx部署信息 - nginx_host_infos = [ - { - "bk_outer_ip": host_infos[1].get("bk_host_outerip") or host_infos[1]["ip"], - **_get_base_info(host_infos[1]), - } - ] - # 构造dns的部署信息 - dns_host_infos = [{**_get_base_info(host_infos[0])}, {**_get_base_info(host_infos[1])}] - # 构造drs的部署信息 - drs_host_infos = [ - {**_get_base_info(host_infos[0]), "drs_port": env.DRS_PORT}, - {**_get_base_info(host_infos[1]), "drs_port": env.DRS_PORT}, - ] - # 构造agent的部署信息 - agent_host_infos = [ - { - **_get_base_info(host_infos[0]), - "bk_city_code": host_infos[0].get("bk_idc_id") or 0, - "bk_city_name": host_infos[0].get("bk_idc_name", ""), - } - ] - # 构造gm的部署信息 - gm_host_infos = [ - agent_host_infos[0], # 允许将一个gm和agent部署在同一台机器 - { - **_get_base_info(host_infos[1]), - "bk_city_code": host_infos[1].get("bk_idc_id") or 1, - "bk_city_name": host_infos[1].get("bk_idc_name", ""), - }, - ] - - # 创建单据进行部署 - details = { - "bk_cloud_id": bk_cloud_id, - "dns": {"host_infos": dns_host_infos}, - "nginx": {"host_infos": nginx_host_infos}, - "drs": {"host_infos": drs_host_infos}, - "dbha": {"gm": gm_host_infos, "agent": agent_host_infos}, - } - Ticket.create_ticket( - ticket_type=TicketType.CLOUD_SERVICE_APPLY, - creator=user, - bk_biz_id=bk_biz_id, - remark=_("云区域组件快速部署单据"), - details=details, - ) diff --git a/dbm-ui/backend/urls.py b/dbm-ui/backend/urls.py index ff12a67333..43863817f0 100644 --- a/dbm-ui/backend/urls.py +++ b/dbm-ui/backend/urls.py @@ -50,7 +50,7 @@ path("proxypass/", include("backend.db_proxy.urls")), path("monitor/", include("backend.db_monitor.urls")), path("event/", include("backend.db_event.urls")), - path("redisdts/", include("backend.db_services.redis_dts.urls")), + path("db_dirty/", include("backend.db_dirty.urls")), ] urlpatterns = [ diff --git a/dbm-ui/backend/utils/basic.py b/dbm-ui/backend/utils/basic.py index ff56e0c633..83e2f46265 100644 --- a/dbm-ui/backend/utils/basic.py +++ b/dbm-ui/backend/utils/basic.py @@ -234,3 +234,12 @@ def get_target_items_from_details( return list(set(target_items)) else: return target_items + + +def dictfetchall(cursor): + """ + Return all rows from a cursor as a dict. + Assume the column names are unique. + """ + columns = [col[0] for col in cursor.description] + return [dict(zip(columns, row)) for row in cursor.fetchall()] diff --git a/dbm-ui/backend/utils/batch_request.py b/dbm-ui/backend/utils/batch_request.py index 66fe5f28cf..024a00ea54 100644 --- a/dbm-ui/backend/utils/batch_request.py +++ b/dbm-ui/backend/utils/batch_request.py @@ -152,16 +152,17 @@ def sync_batch_request(func, params, get_data=lambda x: x["info"], limit=500, st return data -def request_multi_thread(func, params_list, get_data=lambda x: []): +def request_multi_thread(func, params_list, get_data=lambda x: [], in_order=False): """ 并发请求接口,每次按不同参数请求最后叠加请求结果 :param func: 请求方法 :param params_list: 参数列表 :param get_data: 获取数据函数,通常CMDB的批量接口应该设置为 get_data=lambda x: x["info"],其它场景视情况而定 + :param in_order: 按顺序处理线程结果,默认和请求参数有关,因此get_data要同时处理请求参数和结果 :return: 请求结果累计 """ - # 参数预处理,添加request_id + # 参数预处理,添加request_id for params in params_list: if "params" in params: params["params"]["_request"] = local.request @@ -169,8 +170,14 @@ def request_multi_thread(func, params_list, get_data=lambda x: []): result = [] with ThreadPoolExecutor(max_workers=settings.CONCURRENT_NUMBER) as ex: tasks = [ex.submit(RespectsLanguage(language=get_language())(func), **params) for params in params_list] - for future in as_completed(tasks): - result.append(get_data(future.result())) + + if in_order: + for index, future in enumerate(tasks): + result.append(get_data((params_list[index], future.result()))) + else: + for future in as_completed(tasks): + result.append(get_data(future.result())) + return result diff --git a/dbm-ui/backend/utils/excel.py b/dbm-ui/backend/utils/excel.py index 4b18f0e102..434ed59e1a 100644 --- a/dbm-ui/backend/utils/excel.py +++ b/dbm-ui/backend/utils/excel.py @@ -54,14 +54,19 @@ def _adapt_sheet_weight_height(cls, sheet: Worksheet, first_header_row: int = 1) sheet.column_dimensions[chr(ord("A") + col - 1)].width = max_col_dimensions[col] @classmethod - def paser(cls, excel: BytesIO, header_row: int = 0) -> List[Dict]: + def paser(cls, excel: BytesIO, header_row: int = 0, sheet_name: str = "") -> List[Dict]: """ - 解析excel文件为数据字典 :param excel: excel二进制文件 :param header_row: excel头部的行数(有可能存在excel的前几行是annotations的情况,因此需要用户指定) + :param sheet_name: sheet的名称 """ - excel_rows = list(openpyxl.load_workbook(excel).active.rows) + if not sheet_name: + excel_rows = list(openpyxl.load_workbook(excel).active.rows) + else: + excel_rows = list(openpyxl.load_workbook(excel)[sheet_name].rows) + header_list = [header.value for header in excel_rows[header_row]] excel_data_dict__list = [] for content_row in excel_rows[header_row + 1 :]: diff --git a/dbm-ui/backend/utils/time.py b/dbm-ui/backend/utils/time.py index 50ee3707ab..1fc1e0cfbc 100644 --- a/dbm-ui/backend/utils/time.py +++ b/dbm-ui/backend/utils/time.py @@ -13,7 +13,8 @@ from bisect import bisect_right from typing import List, Optional, Union -import dateutil +from dateutil.parser import ParserError as TimeParserError +from dateutil.parser import parse as time_parse from django.utils import timezone from django.utils.timezone import make_aware from django.utils.translation import ugettext_lazy as _ @@ -40,9 +41,9 @@ def remove_timezone(date_string: str, time_fmt: str = DATETIME_PATTERN) -> str: 去掉字符串中的时区 """ try: - datetime_obj = dateutil.parser.parse(date_string) + datetime_obj = time_parse(date_string) return datetime2str(datetime_obj, time_fmt) - except dateutil.parser.ParserError: + except TimeParserError: # 如果转换失败,则直接返回原值 return date_string diff --git a/dbm-ui/bin/build_frontend.sh b/dbm-ui/bin/build_frontend.sh index 017dd5adc8..0880580649 100755 --- a/dbm-ui/bin/build_frontend.sh +++ b/dbm-ui/bin/build_frontend.sh @@ -3,6 +3,7 @@ SCRIPT_DIR=`dirname $0` cd $SCRIPT_DIR && cd ../frontend || exit 1 npm config set registry https://mirrors.tencent.com/npm/ +export NODE_OPTIONS="--max_old_space_size=8192" npm install . && npm run build mkdir -p ../static/ cp -rf dist/* ../static/ diff --git a/dbm-ui/build.yml b/dbm-ui/build.yml new file mode 100644 index 0000000000..9431b57bfa --- /dev/null +++ b/dbm-ui/build.yml @@ -0,0 +1,25 @@ +version: v2.0 + +stages: + - name: "stage1" + jobs: + job1: + runs-on: + pool-name: docker-on-devcloud #docker-on-devcloud、docker、local、agentless + container: + image: mirrors.tencent.com/ci/tlinux3_ci:2.1.0 + needs: + jdk: "1.8.0_161" + steps: + - uses: syncLocalCode@latest + name: 同步文件到云端插件 + with: + syncGitRepository: true + - uses: CodeccCheckAtomDebug@4.* + name: 腾讯代码分析 + with: + beAutoLang: true + # languages: # 工程语言, 可取值:"C_CPP", "JAVA", "C_SHARP", "JS", "OC", "PYTHON", "PHP", "RUBY", "GOLANG", "SWIFT", "TYPESCRIPT", "KOTLIN", "LUA", "OTHERS" + # - "JAVA" + checkerSetType: "openScan" #openScan对应按开源治理要求配置规则集,epcScan对应按PCG EPC要求配置,normal对应自主配置规则集 + toolScanType: "0" # 扫描方式。0是全量扫描,1是增量扫描。 \ No newline at end of file diff --git a/dbm-ui/config/default.py b/dbm-ui/config/default.py index 23410e63cf..98e969399d 100644 --- a/dbm-ui/config/default.py +++ b/dbm-ui/config/default.py @@ -25,6 +25,11 @@ from backend import env +# django 3.2 默认的 default_auto_field 是 BigAutoField,django_celery_beat 在 2.2.1 版本已处理此问题 +# 受限于 celery 和 bamboo 的版本,这里暂时这样手动设置 default_auto_field 来处理此问题 +from django_celery_beat.apps import AppConfig +AppConfig.default_auto_field = "django.db.models.AutoField" + pymysql.install_as_MySQLdb() # Build paths inside the project like this: BASE_DIR / 'subdir'. @@ -77,11 +82,20 @@ "backend.dbm_init", "backend.db_proxy", "backend.db_monitor", - "backend.db_services.redis_dts", + "backend.db_services.redis.redis_dts", + "backend.db_services.redis.rollback", + "backend.db_services.redis.autofix", + "backend.db_dirty", + "apigw_manager.apigw", + "backend.db_periodic_task", ) MIDDLEWARE = ( + # JWT认证,透传的应用信息,透传的用户信息 + "apigw_manager.apigw.authentication.ApiGatewayJWTGenericMiddleware", + "apigw_manager.apigw.authentication.ApiGatewayJWTAppMiddleware", + "apigw_manager.apigw.authentication.ApiGatewayJWTUserMiddleware", # request instance provider "blueapps.middleware.request_provider.RequestProvider", "django.contrib.sessions.middleware.SessionMiddleware", @@ -106,6 +120,11 @@ "backend.bk_web.middleware.RequestProviderMiddleware", ) +AUTHENTICATION_BACKENDS = [ + *AUTHENTICATION_BACKENDS, + 'apigw_manager.apigw.authentication.UserModelBackend', +] + ROOT_URLCONF = "backend.urls" TEMPLATES = [ @@ -164,6 +183,7 @@ # blueapps BK_COMPONENT_API_URL = env.BK_COMPONENT_API_URL +BK_SAAS_HOST = env.BK_SAAS_HOST IS_AJAX_PLAIN_MODE = True # init admin list @@ -179,12 +199,23 @@ BK_IAM_SKIP = env.BK_IAM_SKIP BK_IAM_INNER_HOST = env.BK_IAM_INNER_HOST BK_IAM_SYSTEM_ID = env.BK_IAM_SYSTEM_ID -BK_IAM_USE_APIGATEWAY = True +BK_IAM_USE_APIGATEWAY = env.BK_IAM_USE_APIGATEWAY BK_IAM_APIGATEWAY_URL = env.BK_IAM_APIGETEWAY BK_IAM_MIGRATION_APP_NAME = "iam_app" BK_IAM_MIGRATION_JSON_PATH = "backend/iam_app/migration_json_files" BK_IAM_RESOURCE_API_HOST = env.BK_IAM_RESOURCE_API_HOST +# APIGW配置 +BK_APIGW_STATIC_VERSION = env.BK_APIGW_STATIC_VERSION +BK_APIGW_MANAGER_MAINTAINERS = env.BK_APIGW_MANAGER_MAINTAINERS +BK_APIGW_STAGE_NAME = env.BK_APIGW_STAGE_NAME +BK_API_URL_TMPL = f"{BK_COMPONENT_API_URL}/api/{{api_name}}/" +BK_APIGW_NAME = "bkdbm" +BK_APIGW_GRANT_APPS = env.BK_APIGW_GRANT_APPS +# TODO: apigw文档待补充 +BK_APIGW_RESOURCE_DOCS_ARCHIVE_FILE = "" +# 需将 bkapi.example.com 替换为真实的云 API 域名,在 PaaS 3.0 部署的应用,可从环境变量中获取 BK_API_URL_TMPL + # Password validation # https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators @@ -258,7 +289,7 @@ # "backend.bk_web.authentication.BKTicketAuthentication", "rest_framework.authentication.SessionAuthentication", ], - "DEFAULT_PERMISSION_CLASSES": ["backend.iam_app.handlers.drf_perm.IsAuthenticatedOrAPIGateWayPermission"], + "DEFAULT_PERMISSION_CLASSES": ["backend.iam_app.handlers.drf_perm.IsAuthenticatedPermission"], "DEFAULT_FILTER_BACKENDS": ["django_filters.rest_framework.DjangoFilterBackend"], "DEFAULT_RENDERER_CLASSES": [ "backend.bk_web.renderers.BKAPIRenderer", @@ -275,47 +306,20 @@ SPECTACULAR_SETTINGS = {"COMPONENT_SPLIT_REQUEST": True} -# 设置时区 +# DJANGO CELERY BEAT +CELERYBEAT_SCHEDULER = 'django_celery_beat.schedulers.DatabaseScheduler' +# CELERY 配置,申明任务的文件路径,即包含有 @task 装饰器的函数文件 +CELERY_IMPORTS = ( + "backend.db_periodic_task.local_tasks", + # TODO: 等celery service服务正式启动后,开启remote_tasks的注册 + # "backend.db_periodic_task.remote_tasks", +) + +# celery 配置 app.conf.enable_utc = False app.conf.timezone = "Asia/Shanghai" -app.conf.beat_schedule = "django_celery_beat.schedulers:DatabaseScheduler" app.conf.broker_url = env.BROKER_URL -# Load task modules from all registered Django apps. -app.autodiscover_tasks() - -app.conf.beat_schedule = { - "sync-local-notice-group-every-2min": { - "task": "backend.db_monitor.tasks.update_local_notice_group", - "schedule": crontab(minute="*/2"), - }, - "sync-monitor-notice-group-every-3min": { - "task": "backend.db_monitor.tasks.update_remote_notice_group", - "schedule": crontab(minute="*/3"), - }, - "sync-cc-dbmeta-every-2min": { - "task": "backend.db_meta.tasks.update_host_dbmeta", - "schedule": crontab(minute="*/2"), - }, - "update-app-every-20min": { - "task": "backend.db_meta.tasks.update_app_cache", - "schedule": crontab(minute="*/20"), - }, - "auto-retry-exclusive-inner-flow": { - "task": "backend.ticket.tasks.ticket_tasks.auto_retry_exclusive_inner_flow", - "schedule": timedelta(seconds=5), - }, - "routine-check-every-day": { - "task": "backend.ticket.tasks.ticket_tasks.auto_create_data_repair_ticket", - # 默认在2:03自动发起,后续拓展可以在页面配置 - "schedule": crontab(minute=3, hour=2), - }, - "push-nginx-service-conf-every-5min": { - "task": "backend.db_proxy.tasks.fill_cluster_service_nginx_conf", - "schedule": crontab(minute="*/1"), - }, -} - # 版本日志 VERSION_LOG = {"MD_FILES_DIR": os.path.join(PROJECT_ROOT, "release")} diff --git a/dbm-ui/frontend/.eslintrc.js b/dbm-ui/frontend/.eslintrc.js index e83aa098a6..8f36529e7e 100644 --- a/dbm-ui/frontend/.eslintrc.js +++ b/dbm-ui/frontend/.eslintrc.js @@ -31,8 +31,10 @@ module.exports = { 'vue/setup-compiler-macros': true, }, globals: { + defineModel: 'readonly', // value 为 true 允许被重写,为 false 不允许被重写 __RESOURCE_UNIQUE_KEY__: false, + ValueOf: false, }, rules: { '@typescript-eslint/no-explicit-any': 'off', diff --git a/dbm-ui/frontend/.gitignore b/dbm-ui/frontend/.gitignore index c35989b6b2..05e0b3dec8 100644 --- a/dbm-ui/frontend/.gitignore +++ b/dbm-ui/frontend/.gitignore @@ -27,6 +27,7 @@ build.yml *.sw? public/.DS_Store +*.bak *.bak.* .codecc .vscode \ No newline at end of file diff --git a/dbm-ui/frontend/.nvmrc b/dbm-ui/frontend/.nvmrc new file mode 100644 index 0000000000..6856e9ac48 --- /dev/null +++ b/dbm-ui/frontend/.nvmrc @@ -0,0 +1 @@ +v16.20.1 \ No newline at end of file diff --git a/dbm-ui/frontend/.stylelintrc.js b/dbm-ui/frontend/.stylelintrc.js index 4e8d56c6da..5c10c1b845 100644 --- a/dbm-ui/frontend/.stylelintrc.js +++ b/dbm-ui/frontend/.stylelintrc.js @@ -16,7 +16,6 @@ module.exports = { 'stylelint-config-standard', ], plugins: [ - 'stylelint-less', 'stylelint-order', ], rules: { diff --git a/dbm-ui/frontend/env.d.ts b/dbm-ui/frontend/env.d.ts index 28e65ede76..20b53996e0 100644 --- a/dbm-ui/frontend/env.d.ts +++ b/dbm-ui/frontend/env.d.ts @@ -57,4 +57,5 @@ interface URLSearchParams { keys(): string[]; } +type ValueOf = T[keyof T]; diff --git a/dbm-ui/frontend/public/bk-icon/demo.html b/dbm-ui/frontend/lib/bk-icon/demo.html similarity index 88% rename from dbm-ui/frontend/public/bk-icon/demo.html rename to dbm-ui/frontend/lib/bk-icon/demo.html index f8eec69c4f..a57b1f790d 100644 --- a/dbm-ui/frontend/public/bk-icon/demo.html +++ b/dbm-ui/frontend/lib/bk-icon/demo.html @@ -233,6 +233,14 @@

script-template

+
  • + +

    qiyeweixin

    +
  • +
  • + +

    sql

    +
  • version

    @@ -757,6 +765,94 @@

    wenjian

  • +
  • + +

    manual-2

    +
  • +
  • + +

    clock

    +
  • +
  • + +

    auto

    +
  • +
  • + +

    dirty-host

    +
  • +
  • + +

    entry

    +
  • +
  • + +

    gaojingcelve

    +
  • +
  • + +

    lunzhi

    +
  • +
  • + +

    gaojing

    +
  • +
  • + +

    yonghuzu

    +
  • +
  • + +

    pingbi

    +
  • +
  • + +

    hongqi

    +
  • +
  • + +

    jiankong

    +
  • +
  • + +

    mimasuijihua

    +
  • +
  • + +

    jiqiren

    +
  • +
  • + +

    qiyeweixin-2

    +
  • +
  • + +

    weixin-2

    +
  • +
  • + +

    youjian

    +
  • +
  • + +

    duanxin

    +
  • +
  • + +

    yuyin

    +
  • +
  • + +

    warn-lightning

    +
  • +
  • + +

    unlink

    +
  • +
  • + +

    mobanshili

    +
  • 为什么使用

      @@ -948,6 +1044,18 @@

      如何使用

      script-template

      +
    • + + + +

      qiyeweixin

      +
    • +
    • + + + +

      sql

      +
    • @@ -1734,6 +1842,138 @@

      如何使用

      wenjian

    • +
    • + + + +

      manual-2

      +
    • +
    • + + + +

      clock

      +
    • +
    • + + + +

      auto

      +
    • +
    • + + + +

      dirty-host

      +
    • +
    • + + + +

      entry

      +
    • +
    • + + + +

      gaojingcelve

      +
    • +
    • + + + +

      lunzhi

      +
    • +
    • + + + +

      gaojing

      +
    • +
    • + + + +

      yonghuzu

      +
    • +
    • + + + +

      pingbi

      +
    • +
    • + + + +

      hongqi

      +
    • +
    • + + + +

      jiankong

      +
    • +
    • + + + +

      mimasuijihua

      +
    • +
    • + + + +

      jiqiren

      +
    • +
    • + + + +

      qiyeweixin-2

      +
    • +
    • + + + +

      weixin-2

      +
    • +
    • + + + +

      youjian

      +
    • +
    • + + + +

      duanxin

      +
    • +
    • + + + +

      yuyin

      +
    • +
    • + + + +

      warn-lightning

      +
    • +
    • + + + +

      unlink

      +
    • +
    • + + + +

      mobanshili

      +

    为什么使用

      diff --git a/dbm-ui/frontend/lib/bk-icon/fonts/iconcool.eot b/dbm-ui/frontend/lib/bk-icon/fonts/iconcool.eot new file mode 100644 index 0000000000..78f271799d Binary files /dev/null and b/dbm-ui/frontend/lib/bk-icon/fonts/iconcool.eot differ diff --git a/dbm-ui/frontend/lib/bk-icon/fonts/iconcool.svg b/dbm-ui/frontend/lib/bk-icon/fonts/iconcool.svg new file mode 100644 index 0000000000..3c720f94a0 --- /dev/null +++ b/dbm-ui/frontend/lib/bk-icon/fonts/iconcool.svg @@ -0,0 +1,587 @@ + + + + + Created by font-carrier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dbm-ui/frontend/lib/bk-icon/fonts/iconcool.ttf b/dbm-ui/frontend/lib/bk-icon/fonts/iconcool.ttf new file mode 100644 index 0000000000..56e8b036e1 Binary files /dev/null and b/dbm-ui/frontend/lib/bk-icon/fonts/iconcool.ttf differ diff --git a/dbm-ui/frontend/lib/bk-icon/fonts/iconcool.woff b/dbm-ui/frontend/lib/bk-icon/fonts/iconcool.woff new file mode 100644 index 0000000000..a1f90a5673 Binary files /dev/null and b/dbm-ui/frontend/lib/bk-icon/fonts/iconcool.woff differ diff --git a/dbm-ui/frontend/lib/bk-icon/iconcool.js b/dbm-ui/frontend/lib/bk-icon/iconcool.js new file mode 100644 index 0000000000..3b947a29f5 --- /dev/null +++ b/dbm-ui/frontend/lib/bk-icon/iconcool.js @@ -0,0 +1,10 @@ +!(function () { + const svgCode = ''; + if (document.body) { + document.body.insertAdjacentHTML('afterbegin', svgCode); + } else { + document.addEventListener('DOMContentLoaded', () => { + document.body.insertAdjacentHTML('afterbegin', svgCode); + }); + } +}()); diff --git a/dbm-ui/frontend/lib/bk-icon/iconcool.json b/dbm-ui/frontend/lib/bk-icon/iconcool.json new file mode 100644 index 0000000000..4418399d64 --- /dev/null +++ b/dbm-ui/frontend/lib/bk-icon/iconcool.json @@ -0,0 +1 @@ +{"iconName":"bk-dbm","icons":[{"name":"check-circle-fill","svgCode":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t\n\n\n\n\n\n\n\n\n\n\n\n\n\n","codepoint":"\\e1a7"},{"name":"exclamation-fill","svgCode":"\n\n\n\n\n\n","codepoint":"\\e173"},{"name":"auth","svgCode":"","codepoint":"\\e152"},{"name":"apply","svgCode":"","codepoint":"\\e144"},{"name":"close-circle-shape","svgCode":"","codepoint":"\\e1be"},{"name":"right-shape","svgCode":"","codepoint":"\\e1a3"},{"name":"bulk-edit","svgCode":"","codepoint":"\\e15f"},{"name":"funnel","svgCode":"","codepoint":"\\e1bd"},{"name":"keyboard","svgCode":"","codepoint":"\\e1ae"},{"name":"draft","svgCode":"","codepoint":"\\e145"},{"name":"todolist","svgCode":"","codepoint":"\\e146"},{"name":"file","svgCode":"","codepoint":"\\e136"},{"name":"script","svgCode":"","codepoint":"\\e137"},{"name":"fast-script","svgCode":"","codepoint":"\\e151"},{"name":"homepage","svgCode":"","codepoint":"\\e158"},{"name":"template","svgCode":"","codepoint":"\\e147"},{"name":"unlock-line","svgCode":"","codepoint":"\\e166"},{"name":"sync-failed","svgCode":"","codepoint":"\\e18c"},{"name":"sync-pending","svgCode":"","codepoint":"\\e18f"},{"name":"sync-success","svgCode":"","codepoint":"\\e18d"},{"name":"sync-default","svgCode":"","codepoint":"\\e18e"},{"name":"pulsar","svgCode":"","codepoint":"\\e1b8"},{"name":"abnormal","svgCode":"","codepoint":"\\e187"},{"name":"normal","svgCode":"","codepoint":"\\e188"},{"name":"unknown","svgCode":"","codepoint":"\\e189"},{"name":"code","svgCode":"","codepoint":"\\e139"},{"name":"visible1","svgCode":"","codepoint":"\\e1c5"},{"name":"sync-waiting-01","svgCode":"","codepoint":"\\e18a"},{"name":"script-template","svgCode":"","codepoint":"\\e138"},{"name":"qiyeweixin","svgCode":"","codepoint":"\\e1d9"},{"name":"sql","svgCode":"","codepoint":"\\e1cb"},{"name":"version","svgCode":"","codepoint":"\\e156"},{"name":"copy","svgCode":"","codepoint":"\\e103"},{"name":"database","svgCode":"","codepoint":"\\e101"},{"name":"eyes","svgCode":"","codepoint":"\\e102"},{"name":"setting-fill","svgCode":"","codepoint":"\\e104"},{"name":"resource","svgCode":"","codepoint":"\\e105"},{"name":"ticket","svgCode":"","codepoint":"\\e106"},{"name":"edit","svgCode":"","codepoint":"\\e108"},{"name":"loading","svgCode":"","codepoint":"\\e1a8"},{"name":"help-fill","svgCode":"","codepoint":"\\e109"},{"name":"unlock-line-2","svgCode":"","codepoint":"\\e10b"},{"name":"lock-fill","svgCode":"","codepoint":"\\e10c"},{"name":"un-full-screen","svgCode":"","codepoint":"\\e10f"},{"name":"full-screen","svgCode":"","codepoint":"\\e110"},{"name":"minus-fill","svgCode":"","codepoint":"\\e111"},{"name":"plus-fill","svgCode":"","codepoint":"\\e112"},{"name":"add","svgCode":"","codepoint":"\\e116"},{"name":"delete-fill","svgCode":"","codepoint":"\\e117"},{"name":"refresh","svgCode":"","codepoint":"\\e118"},{"name":"more","svgCode":"","codepoint":"\\e119"},{"name":"arrow-fill","svgCode":"","codepoint":"\\e11e"},{"name":"attention-fill","svgCode":"","codepoint":"\\e11f"},{"name":"attention","svgCode":"","codepoint":"\\e120"},{"name":"up-big","svgCode":"","codepoint":"\\e121"},{"name":"down-big","svgCode":"","codepoint":"\\e123"},{"name":"arrow-down","svgCode":"","codepoint":"\\e122"},{"name":"arrow-up","svgCode":"","codepoint":"\\e124"},{"name":"right-big","svgCode":"","codepoint":"\\e125"},{"name":"arrow-right","svgCode":"","codepoint":"\\e126"},{"name":"arrow-left","svgCode":"","codepoint":"\\e127"},{"name":"mysql","svgCode":"","codepoint":"\\e128"},{"name":"redis","svgCode":"","codepoint":"\\e129"},{"name":"mongo-db","svgCode":"","codepoint":"\\e12a"},{"name":"kafka","svgCode":"","codepoint":"\\e12b"},{"name":"approval-node","svgCode":"","codepoint":"\\e155"},{"name":"down-shape","svgCode":"","codepoint":"\\e12c"},{"name":"import","svgCode":"","codepoint":"\\e12d"},{"name":"master","svgCode":"","codepoint":"\\e13a"},{"name":"cluster","svgCode":"","codepoint":"\\e13b"},{"name":"host","svgCode":"","codepoint":"\\e13c"},{"name":"proxy","svgCode":"","codepoint":"\\e13e"},{"name":"node","svgCode":"","codepoint":"\\e13d"},{"name":"check","svgCode":"","codepoint":"\\e13f"},{"name":"deploy","svgCode":"","codepoint":"\\e140"},{"name":"switch","svgCode":"","codepoint":"\\e141"},{"name":"refresh-2","svgCode":"","codepoint":"\\e142"},{"name":"stop","svgCode":"","codepoint":"\\e143"},{"name":"dns","svgCode":"","codepoint":"\\e148"},{"name":"member","svgCode":"","codepoint":"\\e14c"},{"name":"spec","svgCode":"","codepoint":"\\e149"},{"name":"timed-task","svgCode":"","codepoint":"\\e14d"},{"name":"history","svgCode":"","codepoint":"\\e14a"},{"name":"backup","svgCode":"","codepoint":"\\e14f"},{"name":"account","svgCode":"","codepoint":"\\e14b"},{"name":"note","svgCode":"","codepoint":"\\e150"},{"name":"single-node","svgCode":"","codepoint":"\\e14e"},{"name":"dba-config","svgCode":"","codepoint":"\\e154"},{"name":"db-config","svgCode":"","codepoint":"\\e157"},{"name":"default-node","svgCode":"","codepoint":"\\e159"},{"name":"position","svgCode":"","codepoint":"\\e15c"},{"name":"plus-circle","svgCode":"","codepoint":"\\e15d"},{"name":"minus-circle","svgCode":"","codepoint":"\\e15e"},{"name":"rtx","svgCode":"","codepoint":"\\e160"},{"name":"expand-line","svgCode":"","codepoint":"\\e161"},{"name":"star","svgCode":"","codepoint":"\\e162"},{"name":"search","svgCode":"","codepoint":"\\e163"},{"name":"plus-8","svgCode":"","codepoint":"\\e164"},{"name":"star-fill","svgCode":"","codepoint":"\\e165"},{"name":"unlock","svgCode":"","codepoint":"\\e167"},{"name":"return","svgCode":"","codepoint":"\\e168"},{"name":"link","svgCode":"","codepoint":"\\e169"},{"name":"new","svgCode":"","codepoint":"\\e16b"},{"name":"drag","svgCode":"","codepoint":"\\e1a4"},{"name":"warning-2","svgCode":"","codepoint":"\\e18b"},{"name":"exclamation","svgCode":"","codepoint":"\\e16c"},{"name":"close","svgCode":"","codepoint":"\\e16d"},{"name":"check-line","svgCode":"","codepoint":"\\e16e"},{"name":"early-warning","svgCode":"","codepoint":"\\e16f"},{"name":"warning","svgCode":"","codepoint":"\\e170"},{"name":"spce","svgCode":"","codepoint":"\\e171"},{"name":"password","svgCode":"","codepoint":"\\e172"},{"name":"redis-2","svgCode":"","codepoint":"\\e176"},{"name":"mysql-2","svgCode":"","codepoint":"\\e174"},{"name":"es","svgCode":"","codepoint":"\\e177"},{"name":"hdfs","svgCode":"","codepoint":"\\e175"},{"name":"delete","svgCode":"","codepoint":"\\e178"},{"name":"excel","svgCode":"","codepoint":"\\e179"},{"name":"history-2","svgCode":"","codepoint":"\\e17a"},{"name":"migration","svgCode":"","codepoint":"\\e17d"},{"name":"switch-2","svgCode":"","codepoint":"\\e17e"},{"name":"remote","svgCode":"","codepoint":"\\e17f"},{"name":"clearing","svgCode":"","codepoint":"\\e180"},{"name":"alert","svgCode":"","codepoint":"\\e181"},{"name":"rebuild","svgCode":"","codepoint":"\\e182"},{"name":"clone","svgCode":"","codepoint":"\\e183"},{"name":"associated","svgCode":"","codepoint":"\\e184"},{"name":"rollback","svgCode":"","codepoint":"\\e185"},{"name":"data","svgCode":"","codepoint":"\\e186"},{"name":"audit","svgCode":"","codepoint":"\\e190"},{"name":"yijinyong","svgCode":"","codepoint":"\\e19a"},{"name":"kuorongzhong","svgCode":"","codepoint":"\\e19d"},{"name":"zhongqizhong","svgCode":"","codepoint":"\\e19b"},{"name":"suorongzhong","svgCode":"","codepoint":"\\e19e"},{"name":"tihuanzong","svgCode":"","codepoint":"\\e19c"},{"name":"shanchuzhong","svgCode":"","codepoint":"\\e19f"},{"name":"jinyongzhong","svgCode":"","codepoint":"\\e1a0"},{"name":"qiyongzhong","svgCode":"","codepoint":"\\e1a1"},{"name":"help-fill-2","svgCode":"","codepoint":"\\e1a2"},{"name":"2-jiantou-you","svgCode":"","codepoint":"\\e1a5"},{"name":"2-jiantou-zuo","svgCode":"","codepoint":"\\e1a6"},{"name":"tools","svgCode":"","codepoint":"\\e1a9"},{"name":"todos","svgCode":"","codepoint":"\\e1aa"},{"name":"manual","svgCode":"","codepoint":"\\e1ac"},{"name":"minimap","svgCode":"","codepoint":"\\e1ad"},{"name":"backup-2","svgCode":"","codepoint":"\\e1af"},{"name":"host-select","svgCode":"","codepoint":"\\e1b0"},{"name":"batch-host-select","svgCode":"","codepoint":"\\e1b1"},{"name":"revoke","svgCode":"","codepoint":"\\e1b3"},{"name":"en","svgCode":"","codepoint":"\\e1b4"},{"name":"cn","svgCode":"","codepoint":"\\e1b5"},{"name":"list","svgCode":"","codepoint":"\\e1b7"},{"name":"influxdb","svgCode":"","codepoint":"\\e1ba"},{"name":"summation","svgCode":"","codepoint":"\\e1bb"},{"name":"folder-open","svgCode":"","codepoint":"\\e1bc"},{"name":"wenjian","svgCode":"","codepoint":"\\e1bf"},{"name":"drag","svgCode":"","codepoint":"\\e1c0"},{"name":"gaokeyong","svgCode":"","codepoint":"\\e1c2"},{"name":"fenbushijiqun","svgCode":"","codepoint":"\\e1c3"},{"name":"danjiedian","svgCode":"","codepoint":"\\e1c1"},{"name":"zhongkongji","svgCode":"","codepoint":"\\e1c4"},{"name":"wenjian","svgCode":"","codepoint":"\\e1c6"},{"name":"manual-2","svgCode":"","codepoint":"\\e1c7"},{"name":"clock","svgCode":"","codepoint":"\\e1c8"},{"name":"auto","svgCode":"","codepoint":"\\e1c9"},{"name":"dirty-host","svgCode":"","codepoint":"\\e1ca"},{"name":"entry","svgCode":"","codepoint":"\\e1d0"},{"name":"gaojingcelve","svgCode":"","codepoint":"\\e1d1"},{"name":"lunzhi","svgCode":"","codepoint":"\\e1d5"},{"name":"gaojing","svgCode":"","codepoint":"\\e1d2"},{"name":"yonghuzu","svgCode":"","codepoint":"\\e1d3"},{"name":"pingbi","svgCode":"","codepoint":"\\e1d4"},{"name":"hongqi","svgCode":"","codepoint":"\\e1d6"},{"name":"jiankong","svgCode":"","codepoint":"\\e1d7"},{"name":"mimasuijihua","svgCode":"","codepoint":"\\e1d8"},{"name":"jiqiren","svgCode":"","codepoint":"\\e1da"},{"name":"qiyeweixin-2","svgCode":"","codepoint":"\\e1db"},{"name":"weixin-2","svgCode":"","codepoint":"\\e1dc"},{"name":"youjian","svgCode":"","codepoint":"\\e1dd"},{"name":"duanxin","svgCode":"","codepoint":"\\e1de"},{"name":"yuyin","svgCode":"","codepoint":"\\e1df"},{"name":"warn-lightning","svgCode":"","codepoint":"\\e1e1"},{"name":"unlink","svgCode":"","codepoint":"\\e1e2"},{"name":"mobanshili","svgCode":"","codepoint":"\\e1e3"}]} \ No newline at end of file diff --git a/dbm-ui/frontend/public/bk-icon/style.css b/dbm-ui/frontend/lib/bk-icon/style.css similarity index 87% rename from dbm-ui/frontend/public/bk-icon/style.css rename to dbm-ui/frontend/lib/bk-icon/style.css index 84ca0d74ef..7aec12626c 100644 --- a/dbm-ui/frontend/public/bk-icon/style.css +++ b/dbm-ui/frontend/lib/bk-icon/style.css @@ -110,6 +110,12 @@ url("fonts/iconcool.eot?#iefix") format("embedded-opentype"); .db-icon-script-template:before { content: "\e138"; } +.db-icon-qiyeweixin:before { + content: "\e1d9"; +} +.db-icon-sql:before { + content: "\e1cb"; +} .db-icon-version:before { content: "\e156"; } @@ -503,3 +509,69 @@ url("fonts/iconcool.eot?#iefix") format("embedded-opentype"); .db-icon-wenjian:before { content: "\e1c6"; } +.db-icon-manual-2:before { + content: "\e1c7"; +} +.db-icon-clock:before { + content: "\e1c8"; +} +.db-icon-auto:before { + content: "\e1c9"; +} +.db-icon-dirty-host:before { + content: "\e1ca"; +} +.db-icon-entry:before { + content: "\e1d0"; +} +.db-icon-gaojingcelve:before { + content: "\e1d1"; +} +.db-icon-lunzhi:before { + content: "\e1d5"; +} +.db-icon-gaojing:before { + content: "\e1d2"; +} +.db-icon-yonghuzu:before { + content: "\e1d3"; +} +.db-icon-pingbi:before { + content: "\e1d4"; +} +.db-icon-hongqi:before { + content: "\e1d6"; +} +.db-icon-jiankong:before { + content: "\e1d7"; +} +.db-icon-mimasuijihua:before { + content: "\e1d8"; +} +.db-icon-jiqiren:before { + content: "\e1da"; +} +.db-icon-qiyeweixin-2:before { + content: "\e1db"; +} +.db-icon-weixin-2:before { + content: "\e1dc"; +} +.db-icon-youjian:before { + content: "\e1dd"; +} +.db-icon-duanxin:before { + content: "\e1de"; +} +.db-icon-yuyin:before { + content: "\e1df"; +} +.db-icon-warn-lightning:before { + content: "\e1e1"; +} +.db-icon-unlink:before { + content: "\e1e2"; +} +.db-icon-mobanshili:before { + content: "\e1e3"; +} diff --git a/dbm-ui/frontend/package.json b/dbm-ui/frontend/package.json index ae445e59c7..dc302768fd 100644 --- a/dbm-ui/frontend/package.json +++ b/dbm-ui/frontend/package.json @@ -5,7 +5,7 @@ "scripts": { "dev": "vite --mode test", "dev-https": "vite --https", - "build": "rimraf dist && vite build --mode production", + "build": "rimraf dist && cross-env NODE_OPTIONS=--max-old-space-size=8192 vite build --mode production", "preview": "vite preview", "prepare": "cd ../.. && husky install dbm-ui/frontend/.husky", "lint:lint-staged": "lint-staged", @@ -13,68 +13,68 @@ "lint:style": "stylelint --fix ./src/**/*.{vue,css,less} --custom-syntax" }, "dependencies": { - "@blueking/bk-weweb": "^0.0.15", - "@blueking/bkflow.js": "^0.1.10", - "@blueking/ip-selector": "0.0.1-beta.111", - "@vueuse/core": "^10.1.2", - "axios": "^1.2.1", - "bkui-vue": "0.0.1-beta.442", - "date-fns": "^2.28.0", - "dayjs": "^1.11.7", - "html-to-image": "^1.11.3", - "js-cookie": "^3.0.1", - "jsencrypt": "^3.3.1", - "lodash": "^4.17.21", - "mitt": "^3.0.0", - "monaco-editor": "^0.38.0", - "path": "^0.12.7", - "pinia": "^2.0.28", - "query-string": "^8.1.0", - "rimraf": "^5.0.0", - "screenfull": "^6.0.2", + "@blueking/bk-weweb": "0.0.15", + "@blueking/bkflow.js": "0.1.10", + "@blueking/ip-selector": "0.0.1-beta.116", + "@vueuse/core": "10.2.1", + "axios": "1.2.1", + "bkui-vue": "0.0.2-beta.36", + "date-fns": "2.28.0", + "dayjs": "1.11.9", + "html-to-image": "1.11.3", + "js-cookie": "3.0.1", + "jsencrypt": "3.3.1", + "lodash": "4.17.21", + "mitt": "3.0.1", + "monaco-editor": "0.39.0", + "path": "0.12.7", + "pinia": "2.0.28", + "query-string": "8.1.0", + "rimraf": "5.0.0", + "screenfull": "6.0.2", "tiny-pinyin": "1.3.2", - "tippy.js": "^6.3.7", - "unplugin-auto-import": "^0.15.3", - "vite-plugin-monaco-editor": "^1.1.0", - "vite-plugin-static-copy": "^0.15.0", - "vue": "^3.3.2", - "vue-i18n": "^9.2.2", - "vue-request": "^1.2.4", - "vue-router": "^4.0.15", - "vuedraggable": "^4.1.0" + "tippy.js": "6.3.7", + "unplugin-auto-import": "0.15.3", + "vite-plugin-monaco-editor": "1.1.0", + "vite-plugin-static-copy": "0.16.0", + "vue": "3.3.2", + "vue-i18n": "9.2.2", + "vue-request": "2.0.3", + "vue-router": "4.2.3", + "vuedraggable": "4.1.0" }, "devDependencies": { - "@commitlint/config-conventional": "^17.1.0", - "@types/lodash": "^4.14.191", - "@types/node": "^20.1.5", - "@vitejs/plugin-basic-ssl": "^1.0.1", - "@vitejs/plugin-vue": "^4.2.3", - "@vitejs/plugin-vue-jsx": "^3.0.0", - "@vue/eslint-config-typescript": "^11.0.0", - "@vue/tsconfig": "^0.4.0", - "commitlint": "^17.1.2", - "consola": "^3.1.0", - "eslint": "^8.40.0", - "eslint-config-tencent": "^1.0.4", - "eslint-plugin-simple-import-sort": "^10.0.0", - "eslint-plugin-vue": "^9.13.0", - "husky": "^8.0.1", - "less": "^4.1.2", - "lint-staged": "^13.1.0", - "postcss-html": "^1.4.1", - "postcss-less": "^6.0.0", - "stylelint": "^15.6.1", - "stylelint-config-standard": "^33.0.0", - "stylelint-less": "^1.0.5", - "stylelint-order": "^6.0.3", - "typescript": "^5.0.4", - "unplugin-vue-components": "^0.24.1", - "vite": "^4.3.6", - "vite-plugin-babel": "^1.1.3", - "vite-plugin-html-env": "^1.2.7", - "vite-plugin-imp": "^2.1.8", - "vite-plugin-style-import": "^2.0.0", - "vue-tsc": "^1.6.5" + "@commitlint/config-conventional": "17.1.0", + "@types/lodash": "4.14.191", + "@types/node": "20.4.0", + "@vitejs/plugin-basic-ssl": "1.0.1", + "@vitejs/plugin-vue": "4.2.3", + "@vitejs/plugin-vue-jsx": "3.0.0", + "@vue/eslint-config-typescript": "11.0.0", + "@vue/tsconfig": "0.4.0", + "commitlint": "17.1.2", + "cross-env": "^7.0.3", + "eslint": "8.44.0", + "eslint-config-tencent": "1.0.4", + "eslint-plugin-simple-import-sort": "10.0.0", + "eslint-plugin-vue": "9.15.1", + "husky": "8.0.1", + "less": "4.1.2", + "lint-staged": "13.1.0", + "postcss-html": "1.4.1", + "postcss-less": "6.0.0", + "stylelint": "^15.10.2", + "stylelint-config-standard": "33.0.0", + "stylelint-less": "1.0.7", + "stylelint-order": "6.0.3", + "typescript": "5.1.6", + "unplugin-vue-components": "0.25.1", + "vite": "4.4.4", + "vite-plugin-babel": "1.1.3", + "vite-plugin-html-env": "1.2.7", + "vite-plugin-imp": "2.1.8", + "vite-plugin-style-import": "2.0.0", + "vue-tsc": "1.8.4" }, "lint-staged": { "*.css": "stylelint", @@ -84,6 +84,6 @@ ] }, "engines": { - "node": ">=14.19.0" + "node": ">=16.0.1" } } diff --git a/dbm-ui/frontend/public/bk-icon/fonts/iconcool.eot b/dbm-ui/frontend/public/bk-icon/fonts/iconcool.eot deleted file mode 100644 index c05fc100aa..0000000000 Binary files a/dbm-ui/frontend/public/bk-icon/fonts/iconcool.eot and /dev/null differ diff --git a/dbm-ui/frontend/public/bk-icon/fonts/iconcool.svg b/dbm-ui/frontend/public/bk-icon/fonts/iconcool.svg deleted file mode 100644 index 85cbc396a8..0000000000 --- a/dbm-ui/frontend/public/bk-icon/fonts/iconcool.svg +++ /dev/null @@ -1,515 +0,0 @@ - - - - - Created by font-carrier - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/dbm-ui/frontend/public/bk-icon/fonts/iconcool.ttf b/dbm-ui/frontend/public/bk-icon/fonts/iconcool.ttf deleted file mode 100644 index d3c679e3b9..0000000000 Binary files a/dbm-ui/frontend/public/bk-icon/fonts/iconcool.ttf and /dev/null differ diff --git a/dbm-ui/frontend/public/bk-icon/fonts/iconcool.woff b/dbm-ui/frontend/public/bk-icon/fonts/iconcool.woff deleted file mode 100644 index cb9c59354d..0000000000 Binary files a/dbm-ui/frontend/public/bk-icon/fonts/iconcool.woff and /dev/null differ diff --git a/dbm-ui/frontend/public/bk-icon/iconcool.js b/dbm-ui/frontend/public/bk-icon/iconcool.js deleted file mode 100644 index 77bde219d2..0000000000 --- a/dbm-ui/frontend/public/bk-icon/iconcool.js +++ /dev/null @@ -1,10 +0,0 @@ -!(function () { - var svgCode = '' - if (document.body) { - document.body.insertAdjacentHTML('afterbegin', svgCode) - } else { - document.addEventListener('DOMContentLoaded', function() { - document.body.insertAdjacentHTML('afterbegin', svgCode) - }) - } -})() \ No newline at end of file diff --git a/dbm-ui/frontend/public/bk-icon/iconcool.json b/dbm-ui/frontend/public/bk-icon/iconcool.json deleted file mode 100644 index 74cc17f433..0000000000 --- a/dbm-ui/frontend/public/bk-icon/iconcool.json +++ /dev/null @@ -1 +0,0 @@ -{"iconName":"bk-dbm","icons":[{"name":"check-circle-fill","svgCode":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t\n\n\n\n\n\n\n\n\n\n\n\n\n\n","codepoint":"\\e1a7"},{"name":"exclamation-fill","svgCode":"\n\n\n\n\n\n","codepoint":"\\e173"},{"name":"auth","svgCode":"","codepoint":"\\e152"},{"name":"apply","svgCode":"","codepoint":"\\e144"},{"name":"close-circle-shape","svgCode":"","codepoint":"\\e1be"},{"name":"right-shape","svgCode":"","codepoint":"\\e1a3"},{"name":"bulk-edit","svgCode":"","codepoint":"\\e15f"},{"name":"funnel","svgCode":"","codepoint":"\\e1bd"},{"name":"keyboard","svgCode":"","codepoint":"\\e1ae"},{"name":"draft","svgCode":"","codepoint":"\\e145"},{"name":"todolist","svgCode":"","codepoint":"\\e146"},{"name":"file","svgCode":"","codepoint":"\\e136"},{"name":"script","svgCode":"","codepoint":"\\e137"},{"name":"fast-script","svgCode":"","codepoint":"\\e151"},{"name":"homepage","svgCode":"","codepoint":"\\e158"},{"name":"template","svgCode":"","codepoint":"\\e147"},{"name":"unlock-line","svgCode":"","codepoint":"\\e166"},{"name":"sync-failed","svgCode":"","codepoint":"\\e18c"},{"name":"sync-pending","svgCode":"","codepoint":"\\e18f"},{"name":"sync-success","svgCode":"","codepoint":"\\e18d"},{"name":"sync-default","svgCode":"","codepoint":"\\e18e"},{"name":"pulsar","svgCode":"","codepoint":"\\e1b8"},{"name":"abnormal","svgCode":"","codepoint":"\\e187"},{"name":"normal","svgCode":"","codepoint":"\\e188"},{"name":"unknown","svgCode":"","codepoint":"\\e189"},{"name":"code","svgCode":"","codepoint":"\\e139"},{"name":"visible1","svgCode":"","codepoint":"\\e1c5"},{"name":"sync-waiting-01","svgCode":"","codepoint":"\\e18a"},{"name":"script-template","svgCode":"","codepoint":"\\e138"},{"name":"version","svgCode":"","codepoint":"\\e156"},{"name":"copy","svgCode":"","codepoint":"\\e103"},{"name":"database","svgCode":"","codepoint":"\\e101"},{"name":"eyes","svgCode":"","codepoint":"\\e102"},{"name":"setting-fill","svgCode":"","codepoint":"\\e104"},{"name":"resource","svgCode":"","codepoint":"\\e105"},{"name":"ticket","svgCode":"","codepoint":"\\e106"},{"name":"edit","svgCode":"","codepoint":"\\e108"},{"name":"loading","svgCode":"","codepoint":"\\e1a8"},{"name":"help-fill","svgCode":"","codepoint":"\\e109"},{"name":"unlock-line-2","svgCode":"","codepoint":"\\e10b"},{"name":"lock-fill","svgCode":"","codepoint":"\\e10c"},{"name":"un-full-screen","svgCode":"","codepoint":"\\e10f"},{"name":"full-screen","svgCode":"","codepoint":"\\e110"},{"name":"minus-fill","svgCode":"","codepoint":"\\e111"},{"name":"plus-fill","svgCode":"","codepoint":"\\e112"},{"name":"add","svgCode":"","codepoint":"\\e116"},{"name":"delete-fill","svgCode":"","codepoint":"\\e117"},{"name":"refresh","svgCode":"","codepoint":"\\e118"},{"name":"more","svgCode":"","codepoint":"\\e119"},{"name":"arrow-fill","svgCode":"","codepoint":"\\e11e"},{"name":"attention-fill","svgCode":"","codepoint":"\\e11f"},{"name":"attention","svgCode":"","codepoint":"\\e120"},{"name":"up-big","svgCode":"","codepoint":"\\e121"},{"name":"down-big","svgCode":"","codepoint":"\\e123"},{"name":"arrow-down","svgCode":"","codepoint":"\\e122"},{"name":"arrow-up","svgCode":"","codepoint":"\\e124"},{"name":"right-big","svgCode":"","codepoint":"\\e125"},{"name":"arrow-right","svgCode":"","codepoint":"\\e126"},{"name":"arrow-left","svgCode":"","codepoint":"\\e127"},{"name":"mysql","svgCode":"","codepoint":"\\e128"},{"name":"redis","svgCode":"","codepoint":"\\e129"},{"name":"mongo-db","svgCode":"","codepoint":"\\e12a"},{"name":"kafka","svgCode":"","codepoint":"\\e12b"},{"name":"approval-node","svgCode":"","codepoint":"\\e155"},{"name":"down-shape","svgCode":"","codepoint":"\\e12c"},{"name":"import","svgCode":"","codepoint":"\\e12d"},{"name":"master","svgCode":"","codepoint":"\\e13a"},{"name":"cluster","svgCode":"","codepoint":"\\e13b"},{"name":"host","svgCode":"","codepoint":"\\e13c"},{"name":"proxy","svgCode":"","codepoint":"\\e13e"},{"name":"node","svgCode":"","codepoint":"\\e13d"},{"name":"check","svgCode":"","codepoint":"\\e13f"},{"name":"deploy","svgCode":"","codepoint":"\\e140"},{"name":"switch","svgCode":"","codepoint":"\\e141"},{"name":"refresh-2","svgCode":"","codepoint":"\\e142"},{"name":"stop","svgCode":"","codepoint":"\\e143"},{"name":"dns","svgCode":"","codepoint":"\\e148"},{"name":"member","svgCode":"","codepoint":"\\e14c"},{"name":"spec","svgCode":"","codepoint":"\\e149"},{"name":"timed-task","svgCode":"","codepoint":"\\e14d"},{"name":"history","svgCode":"","codepoint":"\\e14a"},{"name":"backup","svgCode":"","codepoint":"\\e14f"},{"name":"account","svgCode":"","codepoint":"\\e14b"},{"name":"note","svgCode":"","codepoint":"\\e150"},{"name":"single-node","svgCode":"","codepoint":"\\e14e"},{"name":"dba-config","svgCode":"","codepoint":"\\e154"},{"name":"db-config","svgCode":"","codepoint":"\\e157"},{"name":"default-node","svgCode":"","codepoint":"\\e159"},{"name":"position","svgCode":"","codepoint":"\\e15c"},{"name":"plus-circle","svgCode":"","codepoint":"\\e15d"},{"name":"minus-circle","svgCode":"","codepoint":"\\e15e"},{"name":"rtx","svgCode":"","codepoint":"\\e160"},{"name":"expand-line","svgCode":"","codepoint":"\\e161"},{"name":"star","svgCode":"","codepoint":"\\e162"},{"name":"search","svgCode":"","codepoint":"\\e163"},{"name":"plus-8","svgCode":"","codepoint":"\\e164"},{"name":"star-fill","svgCode":"","codepoint":"\\e165"},{"name":"unlock","svgCode":"","codepoint":"\\e167"},{"name":"return","svgCode":"","codepoint":"\\e168"},{"name":"link","svgCode":"","codepoint":"\\e169"},{"name":"new","svgCode":"","codepoint":"\\e16b"},{"name":"drag","svgCode":"","codepoint":"\\e1a4"},{"name":"warning-2","svgCode":"","codepoint":"\\e18b"},{"name":"exclamation","svgCode":"","codepoint":"\\e16c"},{"name":"close","svgCode":"","codepoint":"\\e16d"},{"name":"check-line","svgCode":"","codepoint":"\\e16e"},{"name":"early-warning","svgCode":"","codepoint":"\\e16f"},{"name":"warning","svgCode":"","codepoint":"\\e170"},{"name":"spce","svgCode":"","codepoint":"\\e171"},{"name":"password","svgCode":"","codepoint":"\\e172"},{"name":"redis-2","svgCode":"","codepoint":"\\e176"},{"name":"mysql-2","svgCode":"","codepoint":"\\e174"},{"name":"es","svgCode":"","codepoint":"\\e177"},{"name":"hdfs","svgCode":"","codepoint":"\\e175"},{"name":"delete","svgCode":"","codepoint":"\\e178"},{"name":"excel","svgCode":"","codepoint":"\\e179"},{"name":"history-2","svgCode":"","codepoint":"\\e17a"},{"name":"migration","svgCode":"","codepoint":"\\e17d"},{"name":"switch-2","svgCode":"","codepoint":"\\e17e"},{"name":"remote","svgCode":"","codepoint":"\\e17f"},{"name":"clearing","svgCode":"","codepoint":"\\e180"},{"name":"alert","svgCode":"","codepoint":"\\e181"},{"name":"rebuild","svgCode":"","codepoint":"\\e182"},{"name":"clone","svgCode":"","codepoint":"\\e183"},{"name":"associated","svgCode":"","codepoint":"\\e184"},{"name":"rollback","svgCode":"","codepoint":"\\e185"},{"name":"data","svgCode":"","codepoint":"\\e186"},{"name":"audit","svgCode":"","codepoint":"\\e190"},{"name":"yijinyong","svgCode":"","codepoint":"\\e19a"},{"name":"kuorongzhong","svgCode":"","codepoint":"\\e19d"},{"name":"zhongqizhong","svgCode":"","codepoint":"\\e19b"},{"name":"suorongzhong","svgCode":"","codepoint":"\\e19e"},{"name":"tihuanzong","svgCode":"","codepoint":"\\e19c"},{"name":"shanchuzhong","svgCode":"","codepoint":"\\e19f"},{"name":"jinyongzhong","svgCode":"","codepoint":"\\e1a0"},{"name":"qiyongzhong","svgCode":"","codepoint":"\\e1a1"},{"name":"help-fill-2","svgCode":"","codepoint":"\\e1a2"},{"name":"2-jiantou-you","svgCode":"","codepoint":"\\e1a5"},{"name":"2-jiantou-zuo","svgCode":"","codepoint":"\\e1a6"},{"name":"tools","svgCode":"","codepoint":"\\e1a9"},{"name":"todos","svgCode":"","codepoint":"\\e1aa"},{"name":"manual","svgCode":"","codepoint":"\\e1ac"},{"name":"minimap","svgCode":"","codepoint":"\\e1ad"},{"name":"backup-2","svgCode":"","codepoint":"\\e1af"},{"name":"host-select","svgCode":"","codepoint":"\\e1b0"},{"name":"batch-host-select","svgCode":"","codepoint":"\\e1b1"},{"name":"revoke","svgCode":"","codepoint":"\\e1b3"},{"name":"en","svgCode":"","codepoint":"\\e1b4"},{"name":"cn","svgCode":"","codepoint":"\\e1b5"},{"name":"list","svgCode":"","codepoint":"\\e1b7"},{"name":"influxdb","svgCode":"","codepoint":"\\e1ba"},{"name":"summation","svgCode":"","codepoint":"\\e1bb"},{"name":"folder-open","svgCode":"","codepoint":"\\e1bc"},{"name":"wenjian","svgCode":"","codepoint":"\\e1bf"},{"name":"drag","svgCode":"","codepoint":"\\e1c0"},{"name":"gaokeyong","svgCode":"","codepoint":"\\e1c2"},{"name":"fenbushijiqun","svgCode":"","codepoint":"\\e1c3"},{"name":"danjiedian","svgCode":"","codepoint":"\\e1c1"},{"name":"zhongkongji","svgCode":"","codepoint":"\\e1c4"},{"name":"wenjian","svgCode":"","codepoint":"\\e1c6"}]} \ No newline at end of file diff --git a/dbm-ui/frontend/public/fonts/iconcool.eot b/dbm-ui/frontend/public/fonts/iconcool.eot index 730536a7ad..0b8c528094 100644 Binary files a/dbm-ui/frontend/public/fonts/iconcool.eot and b/dbm-ui/frontend/public/fonts/iconcool.eot differ diff --git a/dbm-ui/frontend/public/fonts/iconcool.ttf b/dbm-ui/frontend/public/fonts/iconcool.ttf index 99071fc289..ca182c9edd 100644 Binary files a/dbm-ui/frontend/public/fonts/iconcool.ttf and b/dbm-ui/frontend/public/fonts/iconcool.ttf differ diff --git a/dbm-ui/frontend/public/fonts/iconcool.woff b/dbm-ui/frontend/public/fonts/iconcool.woff index be1b38deb9..d0e9cbf4e4 100644 Binary files a/dbm-ui/frontend/public/fonts/iconcool.woff and b/dbm-ui/frontend/public/fonts/iconcool.woff differ diff --git a/dbm-ui/frontend/src/common/const.ts b/dbm-ui/frontend/src/common/const.ts index c2ea38ff7a..9872331c5b 100644 --- a/dbm-ui/frontend/src/common/const.ts +++ b/dbm-ui/frontend/src/common/const.ts @@ -24,6 +24,7 @@ export enum DBTypes { ES = 'es', PULSAR = 'pulsar', INFLUXDB = 'influxdb', + SPIDER = 'spider', } export type DBTypesValues = `${DBTypes}` @@ -33,6 +34,7 @@ export type DBTypesValues = `${DBTypes}` export enum ClusterTypes { TENDBSINGLE = 'tendbsingle', TENDBHA = 'tendbha', + TENDBCLUSTER = 'tendbcluster', TWEMPROXY_REDIS_INSTANCE = 'TwemproxyRedisInstance', PREDIXY_TENDISPLUS_CLUSTER = 'PredixyTendisplusCluster', TWEMPROXY_TENDIS_SSD_INSTANCE = 'TwemproxyTendisSSDInstance', @@ -41,8 +43,52 @@ export enum ClusterTypes { HDFS = 'hdfs', PULSAE = 'pulsar', INFLUXDB = 'influxdb', + REDIS = 'redis', + PREDIXY_REDIS_CLUSTER = 'PredixyRedisCluster', + TWEMPROXY_TENDISPLUS_INSTANCE = 'TwemproxyTendisplusInstance', + REDIS_INSTANCE = 'RedisInstance', + TENDIS_SSD_INSTANCE = 'TendisSSDInstance', + TENDIS_PLUS_INSTANCE = 'TendisplusInstance', + REDIS_CLUSTER = 'RedisCluster', + TENDIS_PLUS_CLUSTER = 'TendisplusCluster', + MONGO_REPLICA_SET = 'MongoReplicaSet', + MONGO_SHARED_CLUSTER = 'MongoShardedCluster', + RIAK = 'riak', + SPIDER = 'tendbcluster' + +} + +// 机器类型 +export enum MachineTypes { + SPIDER = 'spider', + REMOTE = 'remote', + PROXY = 'proxy', + BACKEND = 'backend', + SINGLE = 'single', + PREDIXY = 'predixy', + TWEMPROXY = 'twemproxy', + REDIS = 'redis', + TENDISCACHE = 'tendiscache', + TENDISSSD = 'tendisssd', + TENDISPLUS = 'tendisplus', + ES_DATANODE = 'es_datanode', + ES_MASTER = 'es_master', + ES_CLIENT = 'es_client', + BROKER = 'broker', + ZOOKEEPER = 'zookeeper', + HDFS_MASTER = 'hdfs_master', + HDFS_DATANODE = 'hdfs_datanode', + MONGOS = 'mongos', + MONGODB = 'mongodb', + MONGO_CONFIG = 'mongo_config', + INFLUXDB = 'influxdb', + PULSAR_ZOOKEEPER = 'pulsar_zookeeper', + PULSAR_BOOKKEEPER = 'pulsar_bookkeeper', + PULSAR_BROKER = 'pulsar_broker', + RIAK = 'riak', } -export type ClusterTypesValues = `${ClusterTypes}`; + +export type ClusterTypesValues = keyof typeof clusterTypeInfos; /** * 集群类型对应配置 @@ -78,6 +124,9 @@ export const clusterTypeInfos = { [ClusterTypes.PULSAE]: { dbType: DBTypes.PULSAR, }, + [ClusterTypes.TENDBCLUSTER]: { + dbType: DBTypes.MYSQL, + }, }; export type ClusterTypeInfos = keyof typeof clusterTypeInfos; @@ -102,6 +151,7 @@ export enum TicketTypes { MYSQL_ADD_SLAVE = 'MYSQL_ADD_SLAVE', MYSQL_HA_TRUNCATE_DATA = 'MYSQL_HA_TRUNCATE_DATA', MYSQL_CHECKSUM = 'MYSQL_CHECKSUM', + TENDBCLUSTER_APPLY = 'TENDBCLUSTER_APPLY', REDIS_CLUSTER_APPLY = 'REDIS_CLUSTER_APPLY', REDIS_KEYS_EXTRACT = 'REDIS_KEYS_EXTRACT', REDIS_KEYS_DELETE = 'REDIS_KEYS_DELETE', @@ -110,6 +160,10 @@ export enum TicketTypes { REDIS_DESTROY = 'REDIS_DESTROY', REDIS_PROXY_OPEN = 'REDIS_PROXY_OPEN', REDIS_PROXY_CLOSE = 'REDIS_PROXY_CLOSE', + REDIS_PLUGIN_CREATE_CLB = 'REDIS_PLUGIN_CREATE_CLB', + REDIS_PLUGIN_DELETE_CLB = 'REDIS_PLUGIN_DELETE_CLB', + REDIS_PLUGIN_CREATE_POLARIS = 'REDIS_PLUGIN_CREATE_POLARIS', + REDIS_PLUGIN_DELETE_POLARIS = 'REDIS_PLUGIN_DELETE_POLARIS', ES_APPLY = 'ES_APPLY', ES_DISABLE = 'ES_DISABLE', ES_DESTROY = 'ES_DESTROY', @@ -159,6 +213,47 @@ export enum TicketTypes { PULSAR_REPLACE = 'PULSAR_REPLACE', PULSAR_SHRINK = 'PULSAR_SHRINK', PULSAR_SCALE_UP = 'PULSAR_SCALE_UP', + REDIS_CLUSTER_CUTOFF = 'REDIS_CLUSTER_CUTOFF', // redis 整机替换 + REDIS_PROXY_SCALE_UP = 'PROXY_SCALE_UP', // redis 接入层扩容 + REDIS_PROXY_SCALE_DOWN = 'PROXY_SCALE_DOWN', // redis 接入层缩容 + REDIS_SCALE_UPDOWN = 'REDIS_SCALE_UPDOWN', // redis 集群容量变更 + REDIS_SCALE_UP = 'REDIS_SCALE_UP', // redis 存储层扩容 + REDIS_SCALE_DOWN = 'REDIS_SCALE_DOWN', // redis 存储层缩容 + REDIS_MASTER_SLAVE_SWITCH = 'REDIS_MASTER_SLAVE_SWITCH', // redis 主故障切换 + REDIS_DATA_STRUCTURE = 'REDIS_DATA_STRUCTURE', // redis 定点构造 + REDIS_DATA_STRUCTURE_TASK_DELETE = 'REDIS_DATA_STRUCTURE_TASK_DELETE', // redis 构造销毁 + REDIS_CLUSTER_ADD_SLAVE = 'REDIS_CLUSTER_ADD_SLAVE', // redis 新建从库 + REDIS_CLUSTER_DATA_COPY = 'REDIS_CLUSTER_DATA_COPY', // redis 数据复制 + REDIS_CLUSTER_SHARD_NUM_UPDATE = 'REDIS_CLUSTER_SHARD_NUM_UPDATE', // redis 集群分片变更 + REDIS_CLUSTER_TYPE_UPDATE = 'REDIS_CLUSTER_TYPE_UPDATE', // redis 集群类型变更 + REDIS_DATACOPY_CHECK_REPAIR = 'REDIS_DATACOPY_CHECK_REPAIR', // redis 数据校验与修复 + REDIS_CLUSTER_ROLLBACK_DATA_COPY = 'REDIS_CLUSTER_ROLLBACK_DATA_COPY', // redis 数据回写 + TENDBCLUSTER_DISABLE = 'TENDBCLUSTER_DISABLE', // spider 集群禁用 + TENDBCLUSTER_ENABLE = 'TENDBCLUSTER_ENABLE', // spider 集群启用 + TENDBCLUSTER_DESTROY = 'TENDBCLUSTER_DESTROY', // spider 集群下架 + TENDBCLUSTER_SPIDER_SLAVE_DESTROY = 'TENDBCLUSTER_SPIDER_SLAVE_DESTROY', // spider 只读集群下架 + TENDBCLUSTER_SPIDER_REDUCE_MNT = 'TENDBCLUSTER_SPIDER_REDUCE_MNT', // spider 运维节点下架 + TENDBCLUSTER_SPIDER_ADD_NODES = 'TENDBCLUSTER_SPIDER_ADD_NODES', // Spider扩容接入层 + TENDBCLUSTER_SPIDER_REDUCE_NODES = 'TENDBCLUSTER_SPIDER_REDUCE_NODES', // Spider缩容接入层 + TENDBCLUSTER_SPIDER_SLAVE_APPLY = 'TENDBCLUSTER_SPIDER_SLAVE_APPLY', // Spider 部署只读接入层 + TENDBCLUSTER_SPIDER_MNT_APPLY = 'TENDBCLUSTER_SPIDER_MNT_APPLY', // Spider 添加运维节点 + TENDBCLUSTER_MASTER_SLAVE_SWITCH = 'TENDBCLUSTER_MASTER_SLAVE_SWITCH', // Spider remote 主从切换 + TENDBCLUSTER_RENAME_DATABASE = 'TENDBCLUSTER_RENAME_DATABASE', // Spider Tendbcluster 重命名 + TENDBCLUSTER_MASTER_FAIL_OVER = 'TENDBCLUSTER_MASTER_FAIL_OVER', // Spider remote主故障切换 + TENDBCLUSTER_DB_TABLE_BACKUP = 'TENDBCLUSTER_DB_TABLE_BACKUP', // Spider TenDBCluster 库表备份 + TENDBCLUSTER_FULL_BACKUP = 'TENDBCLUSTER_FULL_BACKUP', // Spider TenDBCluster 全备单据 + TENDBCLUSTER_NODE_REBALANCE = 'TENDBCLUSTER_NODE_REBALANCE', // Spider 集群remote节点扩缩容 + TENDBCLUSTER_ROLLBACK_CLUSTER = 'TENDBCLUSTER_ROLLBACK_CLUSTER', // Spider 定点回档 + TENDBCLUSTER_FLASHBACK = 'TENDBCLUSTER_FLASHBACK', // Spider 闪回 + TENDBCLUSTER_TRUNCATE_DATABASE = 'TENDBCLUSTER_TRUNCATE_DATABASE', // Spider tendbcluster 清档 + TENDBCLUSTER_SPIDER_MNT_DESTROY = 'TENDBCLUSTER_SPIDER_MNT_DESTROY', // Spider 运维节点下架 + TENDBCLUSTER_CHECKSUM = 'TENDBCLUSTER_CHECKSUM', // Spider checksum + TENDBCLUSTER_CLIENT_CLONE_RULES = 'TENDBCLUSTER_CLIENT_CLONE_RULES', // Spider 客户端权限克隆 + TENDBCLUSTER_INSTANCE_CLONE_RULES = 'TENDBCLUSTER_INSTANCE_CLONE_RULES', // Spider DB 实例权限克隆 + TENDBCLUSTER_AUTHORIZE_RULES = 'TENDBCLUSTER_AUTHORIZE_RULES', + TENDBCLUSTER_EXCEL_AUTHORIZE_RULES = 'TENDBCLUSTER_EXCEL_AUTHORIZE_RULES', + TENDBCLUSTER_PARTITION = 'TENDBCLUSTER_PARTITION', // Spider 分区管理 + TENDBCLUSTER_IMPORT_SQLFILE = 'TENDBCLUSTER_IMPORT_SQLFILE', // Spider SQL变更执行 } export type TicketTypesStrings = keyof typeof TicketTypes; @@ -178,6 +273,12 @@ export const mysqlType = { type: ClusterTypes.TENDBHA, dbType: DBTypes.MYSQL, }, + [TicketTypes.TENDBCLUSTER_APPLY]: { + id: TicketTypes.TENDBCLUSTER_APPLY, + name: t('TendbCluster分布式集群部署'), + type: ClusterTypes.TENDBCLUSTER, + dbType: DBTypes.MYSQL, + }, }; export type MysqlTypeString = keyof typeof mysqlType; @@ -274,7 +375,14 @@ export enum UserPersonalSettings { MYSQL_TOOLBOX_FAVOR = 'MYSQL_TOOLBOX_FAVOR', MYSQL_TOOLBOX_MENUS = 'MYSQL_TOOLBOX_MENUS', DBHA_SWITCH_EVENTS = 'DBHA_SWITCH_EVENTS', + REDIS_TOOLBOX_FAVOR = 'REDIS_TOOLBOX_FAVOR', + REDIS_TOOLBOX_MENUS = 'REDIS_TOOLBOX_MENUS', INFLUXDB_TABLE_SETTINGS = 'INFLUXDB_TABLE_SETTINGS', + SPECIFICATION_TABLE_SETTINGS = 'SPECIFICATION_TABLE_SETTINGS', + TENDBCLUSTER_TABLE_SETTINGS = 'TENDBCLUSTER_TABLE_SETTINGS', + TENDBCLUSTER_INSTANCE_TABLE = 'TENDBCLUSTER_INSTANCE_TABLE', + SPIDER_TOOLBOX_FAVOR = 'SPIDER_TOOLBOX_FAVOR', + SPIDER_TOOLBOX_MENUS = 'SPIDER_TOOLBOX_MENUS', } /** @@ -312,3 +420,31 @@ export const clusterInstStatus = { }; export type ClusterInstStatus = `${ClusterInstStatusKeys}`; +export enum PipelineStatus { + READY = 'READY', // 准备中 + RUNNING = 'RUNNING', // 运行中 + FINISHED = 'FINISHED', // 完成 + FAILED = 'FAILED' // 失败 +} + + +export enum LocalStorageKeys { + REDIS_ROLLBACK_LIST = 'REDIS_ROLLBACK_LIST', // 跨页使用的回写数据列表 + REDIS_DB_REPLACE_MASTER_TIP = 'REDIS_DB_REPLACE_MASTER_TIP', // 整机替换弹窗中的master提示 + REDIS_DATA_CHECK_AND_REPAIR = 'REDIS_DATA_CHECK_AND_REPAIR', // 跨页使用的数据检验与修复的行数据 + REDIS_DB_DATA_RECORD_RECOPY = 'REDIS_DB_DATA_RECORD_RECOPY', // 跨页使用的数据复制记录 +} + +/** + * 账号类型 + */ +export enum AccountTypes { + MYSQL = 'mysql', + TENDBCLUSTER = 'tendbcluster', +} +export type AccountTypesValues = `${AccountTypes}` + +/** + * 要排除的系统库名称 + */ +export const dbSysExclude = ['mysql', 'db_infobase', 'information_schema', 'performance_schema', 'sys', 'infodba_schema']; diff --git a/dbm-ui/frontend/src/common/importComps.ts b/dbm-ui/frontend/src/common/importComps.ts index 90e9a6548f..ac330079af 100644 --- a/dbm-ui/frontend/src/common/importComps.ts +++ b/dbm-ui/frontend/src/common/importComps.ts @@ -26,6 +26,8 @@ import DbStatus from '@components/db-status/index.vue'; import DbTable from '@components/db-table/index.vue'; import DbOriginalTable from '@components/db-table/OriginalTable.vue'; import DbTextarea from '@components/db-textarea/DbTextarea.vue'; +import FunController from '@components/function-controller/FunController.vue'; +import MoreActionExtend from '@components/more-action-extend/Index.vue'; import SmartAction from '@components/smart-action/index.vue'; import { ipSelector } from '@components/vue2/ip-selector'; @@ -46,4 +48,6 @@ export const setGlobalComps = (app: App) => { app.component('BkIpSelector', ipSelector); app.component('AuthComponent', AuthComponent); app.component('I18nT', Translation); + app.component('FunController', FunController); + app.component('MoreActionExtend', MoreActionExtend); }; diff --git a/dbm-ui/frontend/src/common/regex.ts b/dbm-ui/frontend/src/common/regex.ts index 35bf908152..51ce94e057 100644 --- a/dbm-ui/frontend/src/common/regex.ts +++ b/dbm-ui/frontend/src/common/regex.ts @@ -12,7 +12,9 @@ */ const ipv4Regex = '(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}'; +const ipv6Regex = '\\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?\\s*'; const portRegex = '([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])'; +const domain = '(?=^.{3,255}$)[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+'; /** * 以小写英文字符开头,且只能包含英文字母、数字、连字符- @@ -24,7 +26,34 @@ export const nameRegx = /^[a-z][a-z0-9-]*$/; */ export const ipv4 = new RegExp(`^${ipv4Regex}$`); +/** + * ipv6 正则 + */ +export const ipv6 = new RegExp(`^${ipv6Regex}$`); + /** * ip:port 正则 */ export const ipPort = new RegExp(`^${ipv4Regex}:${portRegex}$`); + +/** + * 正整数 正则 + */ +export const integerRegx = /^[1-9]+$/; + +export const domainRegex = new RegExp(`^${domain}$`); + +/** + * 域名:port 正则 + */ +export const domainPort = new RegExp(`^${domain}:${portRegex}$`); + +/** + * 管控区域:IP + */ +export const netIp = new RegExp(`^\\d+:${ipv4Regex}$`); + +/** + * 数据库/表名称限制正则 + */ +export const dbRegex = /^[a-zA-Z0-9][-_a-zA-Z0-9]*([a-zA-Z0-9]|%)$/; diff --git a/dbm-ui/frontend/src/common/tippy.ts b/dbm-ui/frontend/src/common/tippy.ts index 4137787f87..905c9cc047 100644 --- a/dbm-ui/frontend/src/common/tippy.ts +++ b/dbm-ui/frontend/src/common/tippy.ts @@ -12,10 +12,10 @@ */ import tippy, { + type Instance, type MultipleTargets, type Props, - type SingleTarget, -} from 'tippy.js'; + type SingleTarget } from 'tippy.js'; const dbTheme = 'db-tippy'; const dbDefaultProps = { theme: dbTheme }; @@ -23,7 +23,9 @@ const dbDefaultProps = { theme: dbTheme }; /** * tippy */ -export const dbTippy = (targets: MultipleTargets | SingleTarget, optionalProps?: Partial) => { +export function dbTippy (targets: MultipleTargets, optionalProps?: Partial): Instance[] +export function dbTippy (targets: SingleTarget, optionalProps?: Partial): Instance +export function dbTippy(targets: MultipleTargets | SingleTarget, optionalProps?: Partial) { const props = optionalProps ? { ...optionalProps } : optionalProps; // 添加 db-tippy 作用域 if (props) { @@ -35,4 +37,4 @@ export const dbTippy = (targets: MultipleTargets | SingleTarget, optionalProps?: return tippy(target, props || dbDefaultProps); } return tippy(targets, props || dbDefaultProps); -}; +} diff --git a/dbm-ui/frontend/src/components/app-selector/index.vue b/dbm-ui/frontend/src/components/app-selector/index.vue index 094e5815e2..9eb085244f 100644 --- a/dbm-ui/frontend/src/components/app-selector/index.vue +++ b/dbm-ui/frontend/src/components/app-selector/index.vue @@ -326,7 +326,6 @@ Message({ message: isFavored ? t('取消收藏成功') : t('收藏成功'), theme: 'success', - delay: 1500, }); }); }; diff --git a/dbm-ui/frontend/src/components/apply-items/BackendQPSSpec.vue b/dbm-ui/frontend/src/components/apply-items/BackendQPSSpec.vue new file mode 100644 index 0000000000..d44e85ef43 --- /dev/null +++ b/dbm-ui/frontend/src/components/apply-items/BackendQPSSpec.vue @@ -0,0 +1,358 @@ + + + + + diff --git a/dbm-ui/frontend/src/components/apply-items/ClusterAlias.vue b/dbm-ui/frontend/src/components/apply-items/ClusterAlias.vue index 764db4a913..8059aa911a 100644 --- a/dbm-ui/frontend/src/components/apply-items/ClusterAlias.vue +++ b/dbm-ui/frontend/src/components/apply-items/ClusterAlias.vue @@ -18,7 +18,7 @@ :rules="rules"> +
      + + + +
      + {{ item.spec_name }} + {{ item.count }} +
      + +
      +
      +
      + +
      + + + + + diff --git a/dbm-ui/frontend/src/components/business-selector/BusinessSelector.vue b/dbm-ui/frontend/src/components/business-selector/BusinessSelector.vue index e8e107a9e7..90e9655cff 100644 --- a/dbm-ui/frontend/src/components/business-selector/BusinessSelector.vue +++ b/dbm-ui/frontend/src/components/business-selector/BusinessSelector.vue @@ -127,6 +127,7 @@ diff --git a/dbm-ui/frontend/src/components/cluster-common/RenderNodeInstance.vue b/dbm-ui/frontend/src/components/cluster-common/RenderNodeInstance.vue index 04a6d2b32b..e0549f9442 100644 --- a/dbm-ui/frontend/src/components/cluster-common/RenderNodeInstance.vue +++ b/dbm-ui/frontend/src/components/cluster-common/RenderNodeInstance.vue @@ -27,10 +27,29 @@ {{ $t('不可用') }} -- @@ -176,6 +195,15 @@ isShowMore.value = false; }; + const handleCopyAllIps = () => { + const ips = [...new Set(props.originalList.map(item => item.ip))]; + if (ips.length < 1) { + messageWarn(t('没有可复制IP')); + return; + } + copy(ips.join('\n')); + }; + const handleCopyAll = () => { const instances = props.originalList.map(item => `${item.ip}:${item.port}`); if (instances.length < 1) { @@ -250,4 +278,34 @@ margin-bottom: 12px; } } + +.copy-popover { + padding: 4px 6px !important; + + .bk-pop2-arrow { + display: none; + } + + .copy-trigger { + display: inline-block; + padding: 0 4px; + font-size: 12px; + line-height: 24px; + vertical-align: middle; + border-radius: 2px; + + &:hover { + background-color: #F0F1F5; + } + } + + .copy-trigger-split { + display: inline-block; + width: 1px; + height: 18px; + margin: 0 4px; + vertical-align: middle; + background-color: #F0F1F5; + } +} diff --git a/dbm-ui/frontend/src/components/cluster-common/RenderOperationTag.vue b/dbm-ui/frontend/src/components/cluster-common/RenderOperationTag.vue index 4dc96fb35e..9362f54b6c 100644 --- a/dbm-ui/frontend/src/components/cluster-common/RenderOperationTag.vue +++ b/dbm-ui/frontend/src/components/cluster-common/RenderOperationTag.vue @@ -17,9 +17,9 @@ class="render-cluster-opration-tag"> @@ -49,10 +49,7 @@ type SingleTarget, } from 'tippy.js'; import { - nextTick, - onBeforeUnmount, ref, - watch, } from 'vue'; let activeTippyIns:Instance; @@ -129,5 +126,19 @@ display: inline-block; width: 38px; height: 16px; + margin-top: 2px; + + .tag-placeholder { + position: absolute; + top: 50%; + width: 38px; + height: 16px; + transform: translateY(-50%); + + .db-svg-icon { + width: 38px; + height: 16px; + } + } } diff --git a/dbm-ui/frontend/src/components/cluster-common/SpecDetailForPopover.vue b/dbm-ui/frontend/src/components/cluster-common/SpecDetailForPopover.vue new file mode 100644 index 0000000000..d493646466 --- /dev/null +++ b/dbm-ui/frontend/src/components/cluster-common/SpecDetailForPopover.vue @@ -0,0 +1,95 @@ + + + diff --git a/dbm-ui/frontend/src/components/cluster-common/big-data-host-table/HdfsHostTable.vue b/dbm-ui/frontend/src/components/cluster-common/big-data-host-table/HdfsHostTable.vue index 61e00edf32..c9e99c0eae 100644 --- a/dbm-ui/frontend/src/components/cluster-common/big-data-host-table/HdfsHostTable.vue +++ b/dbm-ui/frontend/src/components/cluster-common/big-data-host-table/HdfsHostTable.vue @@ -140,7 +140,7 @@ render: ({ data }: {data: HostDetails}) => data.ip, }, { - label: t('部署NameNodes_2台'), + label: t('部署NameNode_2台'), width: '180px', render: ({ data }: {data: HostDetails}) => { const isDisabled = isNameNodeCheckDisabled.value && !nameNodeCheckedMap.value[data.host_id]; @@ -161,7 +161,7 @@ }, }, { - label: t('部署Zookeepers_JournalNodes_3台'), + label: t('部署Zookeeper_JournalNode_3台'), width: '250px', render: ({ data }: {data: HostDetails}) => { const isDisabled = isZookeeperCheckDisabled.value && !zookeeperCheckedMap.value[data.host_id]; diff --git a/dbm-ui/frontend/src/components/cluster-common/big-data-host-table/es-host-table/components/EditHostInstance.vue b/dbm-ui/frontend/src/components/cluster-common/big-data-host-table/es-host-table/components/EditHostInstance.vue new file mode 100644 index 0000000000..b57910f750 --- /dev/null +++ b/dbm-ui/frontend/src/components/cluster-common/big-data-host-table/es-host-table/components/EditHostInstance.vue @@ -0,0 +1,70 @@ + + + + + diff --git a/dbm-ui/frontend/src/components/cluster-common/big-data-host-table/es-host-table/components/EditHostNode.vue b/dbm-ui/frontend/src/components/cluster-common/big-data-host-table/es-host-table/components/EditHostNode.vue deleted file mode 100644 index 716ea932f4..0000000000 --- a/dbm-ui/frontend/src/components/cluster-common/big-data-host-table/es-host-table/components/EditHostNode.vue +++ /dev/null @@ -1,70 +0,0 @@ - - - - - diff --git a/dbm-ui/frontend/src/components/cluster-common/big-data-host-table/es-host-table/index.vue b/dbm-ui/frontend/src/components/cluster-common/big-data-host-table/es-host-table/index.vue index f2ad02bb65..fb943b54b6 100644 --- a/dbm-ui/frontend/src/components/cluster-common/big-data-host-table/es-host-table/index.vue +++ b/dbm-ui/frontend/src/components/cluster-common/big-data-host-table/es-host-table/index.vue @@ -15,47 +15,51 @@
      -